1.linux设备驱动程序的作用
简单的说,驱动程序告诉了我们可以提供了那些能力,但是没有告诉我们怎么使用这些能力。按照《linux设备驱动程序》上的说法,前一部分叫做”机制”,后面的叫作”策略”。linux设备驱动程序完成来自上层软件的抽象调用,让上层不用去关心底层到底怎么实现。比如说音频设备驱动,提供了一个接口 playsound().那么上层只要去调用这个接口,就可以发音,而不必关心到底怎么让音频设备发音。
2.内核模块和用户空间的应用程序的区别
内核模块工作在内核态,应用程序工作在用户态。这个概念是操作系统理论的基础,不了解的,罚抄一编《操作系统》… 不同于应用程序是从头到尾处理一个任务,内核模块只是注册下自己,以便服务将来的请求。模块初始化的任务是为了将来调用模块的函数作准备,就好像是说”我在这里, 这是我能做的”。当模块退出时,就好像是在说,”我不在这里了,不要请我作任何事了”。
内核模块和应用程序的区别还有,内核模块在卸载的时候必须小心翼翼的将初始化时生成的资源给还原。虽然感觉很麻烦,但是无独有偶,这也方便了我们测试程序,不用为了修改模块后再调试而必须重启。
在用户空间写程序时,可以调用并非自己写的函数,比如printf,但是在内核中,因为没有库链接到内核中,所以绝大部分不能调用普通的头文件,当然有例外。在内核中大部分的头文件都定义在了include/linux和include/asm中。
还有一点是,如果用户空间的程序出错了,没啥事,但是内核模块出错了,至少会终结当前的进程,极有可能导致系统崩溃。
3.hello world程序
理论暂且放一遍…看代码.一个超级入门的程序,hello world.c。
这个模块很简单,只有四个部分。
我们先看最后两行。module_init和module_exit是两个宏,用来声明模块初始时调用的函数和卸载时调用的函数。参数就是对应的函数名。然后看初始化函数hello_init。函数声明为static并不是必须的。一般来说,这个设备的函数并不需要被其他的内核模块访问,声明为static就可以了。然后是__init,和__exit一样是个标志,告诉内核这些函数或者结构体只是在初始化或者退出的时候用下,其他的时候不用。hello_init和hello_exit中只用到了一个printk函数,类似printf,不过在输出时,需要先输出消息的级别,比如KERN_ALERT。到此,一个非常简单的hello world就完成了。下面编译。
4.Makefile文件
在编译之前讲Makefile是必须的,因为如果没有makefile的话每次编译将会非常痛苦。至于Makefile,不讲太多,给一个简单的linux2.6下的模板吧.
内核模块的makefile和一般C/c++程序的makefile有点不一样,因为使用了kbuild。想了解makefile和kbuild的可以看linux的Documention。
5.编译,加载内核模块
将makefile写好后,就可以在终端下切换到当前目录,然后make就好了。当前前提是你有的include的文件和kernel sources tree.在/usr/src/linux-2.6*下有源代码。在/usr/include/linux下有init.h和module.h。make成功后,目录下应该生成了一个helloworld.ko,这是2.6的内核模块的后缀。之后是加载到运行的内核中。sudo make install桌这手动输入sudo insmod ./helloworld.ko也可以。还有一种加载内核模块的命令是modprobe,这个比insmod更强大,可以自动搜索依赖。加载成功后,我们可以用lsmod来查看当前内核中的模块,或者cat /proc/modules,都可以。而如果想看到helloworld模块加载时输出的信息,在Ubuntu 下可以去/var/log/kern.log文件中查看,或者使用dmesg命令。dmesg | tail -10. 如果想卸载模块,可以用rmmod命令。sudo rmmod helloworld。