更多内容请参考Linux设备驱动程序学习----目录linux
内核模块和应用程序之间的不一样之处:编程
大多数中小规模的应用程序是从头至尾执行单个任务,而模块却只是预先注册本身以便服务于未来的某个请求,而后初始化函数当即结束。即模块初始化函数(hello_init)的任务就是为之后调用模块函数预先作准备。模块的退出函数(hello_exit)将在模块被卸载以前调用。数据结构
这和事件驱动编程有点相似,但不是全部的应用程序都是事件驱动的,而每一个内核模块都是这样的。事件驱动程序和内核模块之间的另外一个区别是,应用程序在退出时,能够无论资源的释放或者其余的清除工做,但模块的退出函数必须撤销初始化函数所作的一切,保证没有多余内容残留在系统中。多线程
应用程序能够调用它并未定义的函数,由于连接过程可以解析外部引用,从而连接使用适当的函数库。而模块仅仅被连接到内核,所以模块能调用的函数仅仅是由内核导出的那些函数,而不存在任何可连接的函数库。由于没有任何函数库会和模块连接,所以源文件中不能包含一般应用程序的头文件。内核模块只能使用做为内核一部分的函数。和内核相关的大多数相关头文件保存在include/linux和include/asm目录中。并发
应用程序和内核编程的处理错误的方式不一样,应用程序的段错误可使用调试器跟踪到源代码中的问题,而内核错误即便不影响系统,也会杀死当前进程。异步
模块卸载的好处,有助于缩短模块化驱动程序的开发周期。模块化
模块运行在内核空间,应用程序运行在用户空间。函数
在Unix中,内核运行在最高级别,即超级用户态,这个级别能够进行全部的操做。而应用程序运行在最低级别,即用户态,这个级别处理器控制着对硬件的直接访问及对内存的非受权访问。布局
内核空间和用户空间,不只说明两种模式具备不一样的优先级等级,还说明每一个模式都有本身的内存映射,即本身的地址空间。学习
当应用程序执行系统调用或者被硬件中断挂起时,Unix将执行模式从用户空间切换到内核空间。执行系统调用的内核代码运行在进程上下文中,表明调用进程执行操做。所以可以访问进程地址空间的全部数据。而处理器硬件中断的内核代码和进程时异步的,和任何一个进程无关。
模块化代码在内核空间运行,用于扩展内核功能。驱动程序要执行两类任务,模块中的某些函数做为系统调用的一部分执行;其余函数则负责中断处理。
内核编程和应用程序编程的区别在于对并发的处理。大部分应用程序,除了多线程应用程序外,一般都是顺序执行的。内核代码的运行环境更加复杂,即便是最简单的内核模块,都须要注意:同一时刻,可能会有不少事情发生。
内核编程必须考虑并发问题的缘由:
Linux内核代码(包括驱动程序)必须是可重入的,必须可以同时运行在多个上下文中。内核数据结构要保证多个线程分开执行,访问共享数据的代码必须避免破坏共享数据。驱动要可以处理并发问题,同时避免竞态。内核代码不能假定在给定代码段中可以独占处理器。
虽然内核模块不像应用程序那样顺序地执行,然而内核执行的大多数操做仍是和某个特定进程相关。内核代码可经过访问全局项current来得到当前进程。
current在<asm/current.h>中定义,是一个指向struct task_struct的指针。current指针指向当前正在运行的进程。能够经过访问struct task_struct的某些成员来打印当前进程的进程ID和命令名:
printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid);
存储在current->comm成员中的命令名是当前进程所执行的程序文件的基本名称,裁剪在15个字符之内。
应用程序在虚拟内存中布局,并具备一块很大的栈空间。栈用来保存函数调用历史及当前活动函数中的自动变量。而内核具备很小的栈,可能只有一个4096字节大小的页空间。驱动的函数必须和整个内核空间调用链一同共享这个栈。所以,不能声明大的自动变量,若是须要大的结构,应该在调用时动态分配该结构。
在内核API中,具备双下划线(__)的函数名称,一般是接口的底层组件,应谨慎使用。
内核代码不能实现浮点数运算,内核代码中不须要浮点运算。
更多内容请参考Linux设备驱动程序学习----目录