Linux设备驱动程序(第三版)的学习(一)

来源:互联网 发布:《规划算法》 编辑:程序博客网 时间:2024/04/27 20:59

为自己编写的模块配置并构造好内核树

1. 应用程序和内核模块的区别

应用程序:

1).应用程序从头到尾执行单个任务

2).应用程序在退出时,可以不管资源的释放或者其他的清除工作

3).应用程序可以调用它并未定义的函数,这是因为在链接过程能够解析外部引用从而使用适当的函数库.

4).应用程序开发过程中的段错误是无害的,并且总是可以使用调试器跟踪到源代码中的问题所在

5).应用程序运行在用户空间

内核模块:

1).模块只是预先注册自己以便服务于将来的某个请求,然后它的初始化函数就立即结束.换句话说,模块初始化函数的任务就是为以后调用模块函数预先做好准备.这就像模块再说,“我在这儿,并且我能做这些工作,尽管调用我吧”

2).模块的退出函数:将在模块被卸载之前调用.它告诉内核:"我要离开了,不要再让我做任何事情了"

3).模块的退出函数必须仔细撤销初始化函数所做的一切,否则,在系统重启之前某些东西会残留在系统中.

4).模块化驱动编程有助于模块的开发周期.(当测试新驱动的一系列版本的时候不需要总是来开关机重新启动系统了)

5).模块仅仅被连接到内核,因此它只能调用由内核导出的函数,不存在任何可连接的函数库.

6).内核错误即使不影响整个系统,也至少会杀死当前进程.

7).模块运行在内核空间

**********************************************************************************************************************

2. 模块的Makefile的编写(编译模块)

1). 对于单个源文件来说

obj-m := hello.o #------>意思是:有一个模块要从目标文件hello.o中构造,这个模块的名称是hello.ko

2). 对于多个源文件来说

obj-m := module.o

module-objs := file1.o file2.o

#################################################################################################

# 指定内核源码所在路径,将内核编译进模块

make   -C   /lib/modules/2.6.38-8-generic/build              M='pwd'                                   modules

                             -C改变目录到指定内核源码路径            返回到当前模块源代码目录       生成指向obj-m变量所设定的模块  

3. insmod,modeprobe将模块插入正在运行的内核中

1). insmod

      a. 将模块的代码和数据装入内核,然后使用内核的符号表解析模块中任何未解析的符号

      b. insmod可以接受一些命令行选项,并且可以在模块链接到内核之前给模块的整型和字符串变量赋值.

      c. #    insmod  hello.ko

2). modprobe

      a. 和insmod一样将模块加载到内核中

      b. 和insmod的区别在于modprobe可以同时插入多个模块,也就是说如果有一个模块A要依赖于另一模块B的话,那么在插入A的时候B将自动的插入到内核

4. rmmod从内核中删除插入的模块.

      a. 如果模块正在使用状态是移除不掉的,最好重启系统.

      b. #  rmmod   hello                  ------------>注意删除的时候不用加后缀.ko

5. lsmod列出当前装载在内核中的所有模块

      a. lsmod其实是通过读取/proc/modules虚拟文件来获得这些信息的

      b. 执行  cat   /proc/modules一样可以获取这些信息

6. 内核符号表

      a. 公共的内核符号表中包含了所有的全局内核项的地址,所谓内核项目,其实也就是函数变量

      b. 当模块被装入内核后,它所导出的任何符号都会变成内核符号表的一部分.

      c. 在其他模块上层叠新的模块,即新的模块会调用该模块导出的函数,变量等

              d. 一个模块向其他模块导出符号的方法

EXPORT_SYMBOL(name);

EXPORT_SYMBOL_GPL(name);#--------->导出的模块只能被GPL许可证下的模块使用.

1). 符号必须在模块文件的全局部分导出,不能在函数中导出

2). 导出的变量必须是全局的

7. 专门用于模块的头文件,所有的模块代码必须包含

# include <linux/module.h>                --------------------->包含大量有可装载模块需要的大量符号和函数的定义

# include <linux/init.h>---------------------->指定初始化,和清除函数

8. 有关对模块描述的宏

MODULE_LICENSE("GPL");-------------->指定代码所使用的许可证,可以不指定,但建议最好指定

MODULE_AUTHOR(“Bruce”);--------------->指定模块的作者

MODULE_DESCRIPTION(“”):--------------->用来说明模块用途的简短说明

MODULE_VERSION(“”);---------------->模块的版本设定

MODULE_ALIAS("");---------------->模块的别名

MODULE_DEVICE_TABLE("");---------------->用来告诉用户空间模块所支持的设备

说明:

这些宏可以放在模块源代码函数外的任何地方.

9. 模块初始化函数

static   int   __init   func_init(void)------------------>函数名可以自定义

{

           代码段

}

module_init(func_init);------------------>注册初始化函数

几点说明:

        1).初始化函数应该被声明为static,因为这种函数在特定文件之外没有其他意义.如果一个模块函数要对内核其他部分可见,必须被显示导出.也就是说static可以不加

2). __init,(是可选的),它对内核来说是一种暗示,用来表明该函数仅仅在初始化期间使用,在模块被装载之后,模块装载器就会将初始化函数扔掉,这样可将该函数占用的内存释放出来.

3). module_init():这个宏函数是必须要使用的,这个宏会在模块的目标代码中增减一个特殊段,用于说明内核初始化函数所在的位置.说白了也就是注册初始化函数,如果不注册初始化函数,那么初始化函数将永远不会被调用.

10. 模块清除函数

static  void  __exit  func_exit(void)------------------->函数名自定义

{

  代码段

}

module_exit(func_exit);------------------->注册模块退出函数

说明:

1). __exit标示的函数只有在模块被卸载或者系统停止时调用

2). 如果模块没有定义清理函数,内核不会允许模块被卸载.














0 0
原创粉丝点击