【linux驱动笔记】linux模块机制浅析

 

1.   模块module

操作系统分微内核和宏内核,微内核优点,可以使操作系统仅作很少的事,其它事情如网络处理等都作为应用程序来实现,微内核精简的同时,必然带来性能的下降。而linux的宏内核设计,保证了设计性能,但linux作为一个通用操作系统,必然会兼容很多硬件,而linux本身的宏内核设计,导致了如果同时兼容所有的硬件,其编译代码将庞大无比,为了解决这个问题,linux效仿微内核,采用了模块这一天才思想。当内核配置make menuconfig时,可以选择M,将驱动作为模块来加载,其生成的文件后缀为*.ko. 即kernel object,内核对象。采用模块设计,解决了上述问题,但是本质上来说,模块加载进内核后,还是运行在内核态,所以驱动的bug有可能引起系统崩溃,这是在驱动设计时需要特别注意的。

1.1.  模块实例

#include <linux/init.h>

#include <linux/module.h>

 

static int hellokernel_init(void)

{

   

    printk("Hello driver!\n");

    return 0;

}

 

static void hellokernel_exit(void)

{

    printk("Bye driver!\n");

}

 

module_init(hellokernel_init);

module_exit(hellokernel_exit);

 

上面都的module_init,module_exit都干了什么,当加载模块insmod 和卸载模块rmmod时又干了什么,下面将说明。

 

1.2.  module_init,module_exit

\include\linux\Init.h 中有:

#ifndef MODULE

#define module_init(x)  __initcall(x);

#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn)   __define_initcall(fn, 6)

 

#define module_exit(x)  __exitcall(x);

#define __exitcall(fn) \

  static exitcall_t __exitcall_##fn __exit_call = fn

#else

  /* Each module must use one module_init(). */

#define module_init(initfn)           \

  static inline initcall_t __inittest(void)    \

  { return initfn; }             \

  int init_module(void) __attribute__((alias(#initfn)));

 

/* This is only required if you want to be unloadable. */

#define module_exit(exitfn)           \

  static inline exitcall_t __exittest(void)    \

  { return exitfn; }             \

  void cleanup_module(void) __attribute__((alias(#exitfn)));

#endif

 

 

从上述处理可以看出,当以非模块编译时,module_init等通过如下路径被执行,其被静态编译进内核。

#define __define_initcall(fn, id) \

   static initcall_t __initcall_##fn##id __used \

   __attribute__((__section__(".initcall" #id ".init"))) = fn

 

而上述用__define_initcall(fn, id)定义的函数都是在linux内核启动时被执行的。

start_kernel→rest_init→kernel_init→kernel_init_freeable→do_basic_setup→do_initcalls

 

 

而如果是模块编译时,为动态编译

int init_module(void) __attribute__((alias(#initfn)));

这段代码的作用是给我们的加载函数定义一个别名,别名就是init_module

 

 

1.3.  加载模块

将hello.ko拷贝到/home/tang/wk-tzp/prj/nfs/fs/ramdisk_fs/lib/modules/3.10.53的目录或者其子目录下,

必须拷贝到对应的版本号如3.10.53下,否则会出问题。

 

insmod hello.ko

 

 

insmod是个应用程序,在嵌入式中,这些应用包括ls,pwd等都来自于大名鼎鼎的busybox。

因此需要阅读busybox的源代码,才能知道insmod究竟干了什么。

在busybox-1.22.1\modutils的insmod.c中:有:

insmod_main→bb_init_module

    →

    …

try_to_mmap_module //通过mmap函数将 xxx.ko文件内容映射到内存中。

init_module(image, image_size, options) //是宏syscall(__NR_init_module, mod, len, opts),调用linux系统调用

// image:指向***.ko文件内容开始,image_size为***.ko模块文件大小,options为参数

 

在arch/arm/include/asm/unistd.h中,有#define __NR_init_module     (__NR_SYSCALL_BASE+128)

在linux/arch/arm/kernel/calls.S有:

/* 125 */  CALL(sys_mprotect)

       CALL(sys_sigprocmask)

       CALL(sys_ni_syscall)     /* was sys_create_module */

       CALL(sys_init_module)

       CALL(sys_delete_module)

可见,__NR_init_module   对应 sys_init_module,所以insmod最终调用sys_init_module

在Module.c (src\kernel) 中有:

SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)

在Syscalls.h (src\include\linux) 中有:#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

#define SYSCALL_DEFINEx(x, sname, ...)              \

   SYSCALL_METADATA(sname, x, __VA_ARGS__)          \

   __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

 

而#define __SYSCALL_DEFINEx(x, name, ...)               \

    asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));  \

 

由上述可见,SYSCALL_DEFINE3(init_module…定义了 sys_init_module函数。

 

继续调用:

SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)

→load_module→do_init_module→do_one_initcall(mod->init)→ret = fn();即执行了module_init(hello_init)中定义的函数

hello_init。

 

 

1.4.  卸载模块

rmmod hello, 不能用 rmmod hello.ko

 

仍然从应用层开始,rmmod是由工具箱busybox实现的,

rmmod.c (busybox-1.22.1\modutils): int rmmod_main(int argc UNUSED_PARAM, char **argv)

→bb_delete_module // 宏定义为syscall(__NR_delete_module, mod, flags),执行linux系统调用sys_delete_module

 

同insmod讨论的一样,最后会执行如下的语句

在Module.c (src\kernel) 中有:

SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags)

→mod->exit(); //即module_exit(exit_func)定义的函数exit_func

1.5.  细节

src\Makefile:make modules  : 目标是modules, 编译好后需要安装在制作的文件系统

make  modules_install  INSTALL_MOD_PATH=~/fs/ramdisk_fs

 

 

THIS_MODULE: http://www.cnblogs.com/ziziwu/archive/2012/07/06/2578283.html

 

insmod/modprobe    rmmod   lsmod modinfo

       insmod  hellokernel.ko  irq=100 pstr=china fish=1,2,3

       echo 200 > /sys/module/hellokernel/parameters/irq   

       echo 10,20,30 > /sys/module/hellokernel/parameters/fish

权限问题:

  非0:在/sys/module/模块名/paramters/文件

  通过修改这个文件完成对变量的内容修改

  问题:会占用内存的资源

 

  权限为0:就不会有一个文件存在,只能在模块  加载的时候才能修改

 

1.6.  函数集合

insmod/modprobe   

rmmod  

lsmod

modinfo

 

1.7.  文献

http://www.cnblogs.com/fanzhidongyzby/p/3730131.html

欢迎转载,转载时需保留作者信息,谢谢。

邮箱:[email protected]

博客园地址:http://www.cnblogs.com/embedded-tzp

Csdn博客地址:http://blog.csdn.net/xiayulewa

时间: 2024-10-15 23:39:32

【linux驱动笔记】linux模块机制浅析的相关文章

Linux模块机制浅析

Linux模块机制浅析   Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! 我们通过创建一个简单的模块进行测试.首先是源文件main.c和Makefile. [email protected]:~/module$ cat main.c #include<linux/module.h> #include<linux/init.h> static int __i

webpack模块机制浅析【一】

webpack模块机制浅析[一] 今天看了看webpack打包后的代码,所以就去分析了下代码的运行机制. 下面这段代码是webpack打包后的最基本的形式,可以说是[骨架] (function(root,fn){ if(typeof exports ==='object'&&typeof module === 'object'){ module.exports = fn();//exports和module同时存在,说明时在node的CommonJs规范下,这个时候使用module.exp

【linux驱动】linux驱动总览

欢迎转载,转载时需保留作者信息,谢谢. 邮箱:[email protected] 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http://blog.csdn.net/xiayulewa   1.1.  应用,设备,总线,驱动关系,应用到驱动 为了简化问题,上图省略了app层与driver层中间的libc层. linux驱动的开发步骤:设备号→设备(struct cdev,struct input_dev等)→驱动(struct file

linux驱动编写之poll机制

一.概念 1.poll情景描述 以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值.这样做的效果是:如果有按键按下了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行:倘若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生. 这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们需要一个时间节点.倘若没有按键按下,那么超过多少时间之后,也要返回超时错误信息,进程能够继续得到执行

linux驱动: 如何向模块传递参数, module_param和module_param_array

如何向模块传递参数,Linux kernel 提供了一个简单的框架.    1.  module_param(name, type, perm); name 既是用户看到的参数名,又是模块内接受参数的变量;     type 表示参数的数据类型,是下列之一:byte, short, ushort, int, uint, long, ulong, charp, bool, invbool;     perm 指定了在sysfs中相应文件的访问权限.访问权限与linux文件访问权限相同的方式管理,如

【linux驱动笔记】字符设备驱动相关数据结构与算法

欢迎转载,转载时需保留作者信息,谢谢. 邮箱:[email protected] 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http://blog.csdn.net/xiayulewa   1.1.1.   设备号 alloc_chrdev_region(&dev, 0, 1, "buttons") /  register_chrdev_region: 动态申请设备号, 设备号组成了链表节点. 最后的结果为:所有的ch

入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖

文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键,打印键值: 目录 概要 poll机制 异步通知 同步互斥阻塞 定时器防抖 概要: 查询方式: 12-3 缺点:占用CPU99%的资源.中断方式:12-4 缺点:调用read函数后如果没有按键按下,该函数永远不会结束,一直在等待按键按下. 优点:使用到了休眠机制,占用cpu资源极少.poll机制: 1

Linux驱动开发--linux下的DMA编程

DMA编程 DMA是一种无需要CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制,使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来,从而大大提高系统的吞吐率 DMA方式的数据传输由DMA控制器控制,在传输期间,CPU可以并发地执行其他任务,当DMA结束后,DMA控制器通过中断通知CPU数据传输已经结束,然后由CPU执行相应的中断服务程序进行后序处理.DMA可以用做内存与外设之间传输数据的方式,这种传输方式之下,数据并不需要经过CPU中转. 1.  DMA与Cache一

Linux自学笔记——linux进程及作业管理

内核的功用主要有进程管理.文件系统.网络功能.内存管理.驱动程序.安全功能这几个方面,本文主要讨论linux进程及作业管理. 进程管理: 进程,process,运行中程序的一个副本,且存在生命周期: 进程分为CPU bound和I/Obound. CPU bound:CPU密集型(类似于高清视频) I/O bound:IO密集型(编辑器等频繁IO操作) Linux内核存储进程信息的固定格式为:task struct(结构体) 多个任务的task struct组件的链表:task list 进程创