linux内核编程.rtf
《linux内核编程.rtf》由会员分享,可在线阅读,更多相关《linux内核编程.rtf(83页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Linux 内核编程 目目 录录1HELLO,WORLD.EXHELLO.C.11 内核模块的编译文件.1.2 多文件内核模块.2字符设备文件字符设备文件.21 多内核版本源文件.3/PROC 文件系统文件系统.4使用使用/PROC 进行输入进行输入.5和设备文件对话(写和和设备文件对话(写和 IOCTLS).6启动参数启动参数.7系统调用系统调用.8阻塞进程阻塞进程.9替换替换 PRINTKS.10调度任务调度任务.11中断处理程序中断处理程序.11.1 INTEL 结构上的键盘.12对称多处理对称多处理.常见的错误常见的错误.2.0 和和 2.2 版本的区别版本的区别.除此以外除此以外.其
2、他其他.GOODS AND SERVICES.GNU GENERAL PUBLIC LICENSE.注注.11Hello,world当第一个穴居的原始人程序员在墙上凿出第一个“洞穴计算机”的程序时,那是一个打印出用羚羊角上的图案表示的“Hello world”的程序。罗马编程教科书上是以“Salut,Mundi”的程序开始的。我不知道如果人们打破这个传统后会有什么后果,但我认为还是不要去发现这个后果比较安全。一个内核模块至少包括两个函数:init_module,在这个模块插入内核时调用;cleanup_module,在模块被移出时调用。典型情况下,init_module 为内核中的某些东西注册
3、一个句柄,或者把内核中的程序提换成它自己的代码(通常是进行一些工作以后再调用原来工作的代码)。Clean_module 模块要求撤销 init_module 进行的所有处理工作,使得模块可以被安全的卸载。Exhello.c/*hello.c *Copyright(C)1998 by Ori Pomerantz*Hello,world-the kernel module version.*/*The necessary header files*/*Standard in kernel modules*/#include /*Were doing kernel work*/#include /*
4、Specifically,a module*/*Deal with CONFIG_MODVERSIONS*/#if CONFIG_MODVERSIONS=1#define MODVERSIONS#include#endif /*Initialize the module*/int init_module()printk(Hello,world-this is the kernel speakingn);2 /*If we return a non zero value,it means that *init_module failed and the kernel module *cant b
5、e loaded*/return 0;/*Cleanup-undid whatever init_module did*/void cleanup_module()printk(Short is the life of a kernel modulen);11 内核模块的编译文件内核模块的编译文件 一个内核模块不是一个可以独立执行的文件,而是需要在运行时刻连接入内核的目标文件。所以,它们需要用-c 选项进行编译。而且,所有的内核模块都必须包含特定的标志:_KERNEL_这个标志告诉头文件此代码将在内核模块中运行,而不是作为用户进程。MODULE这个标志告诉头文件要给出适当的内核模块的定义。LI
6、NUX从技术上讲,这个标志不是必要的。但是,如果你希望写一个比较正规的内核模块,在多个操作系统上编译,这个标志将会使你感到方便。它可以允许你在独立于操作系统的部分进行常规的编译。还有其它的一些可被选择包含标志,取决于编译模块是的选项。如果你不能明确内核怎样被编译,可以在 in/usr/include/linux/config.h 中查到。_SMP_对称多线程。在内核被编译成支持对称多线程(尽管在一台处理机上运行)是必须定义。如果是这样,还需要做一些别的事情(参见第 12 章)。CONFIG_MODVERSIONS如果 CONFIG_MODVERSIONS 被激活,你需要在编译是定义它并且包含文
7、件/usr/include/linux/modversions.h。这可以有代码自动完成。ex MakefileMakefile#Makefile for a basic kernel moduleCC=gccMODCFLAGS:=-Wall-DMODULE-D_KERNEL_-DLINUXhello.o:hello.c/usr/include/linux/version.h$(CC)$(MODCFLAGS)-c hello.cecho insmod hello.o to turn it onecho rmmod hello to turn if off3echoecho X and kern
8、el programming do not mix.echo Do the insmod and rmmod from outside 所以,并不是剩下的事情就是 root(你没有把它编译成 root,而是在边缘(注 1.1)。对吗?),然后就在你的核心内容里插入或移出 hello。当你这样做的时候,要注意到你的新模块在/proc/modules 里。而且,编译文件不推荐从 X 下插入的原因是内核有一条需要用 printk 打印的消息,它把它送给了控制台。如果你不使用 X,它就送到了你使用的虚拟终端(你用 Alt-F选择的哪个)并且你可以看到。相反的,如果你使用了 X,就有两种可能性。如果用
9、xterm C 打开了一个控制台,输出将被送到哪里。如果没有,输出将被送到虚拟终端 7被 X“覆盖”的那个。如果你的内核变得不稳定,你可以在没有 X 的情况下得到调试消息。在 X 外,printk可以直接从内核中输出到控制台。而如果在 X 里,printk 输出到一个用户态的进程(xterm C)。当进程接收到 CPU 时间,它会将其送到 X 服务器进程。然后,当 X 服务器进程接收到 CPU 时间,它将会显示,但是一个不稳定的内核意味着系统将会崩溃或重起,所以你不希望显示错误的消息,然后可能被解释给你什么发生了错误,但是超出了正确的时间。1.2 多文件内核模块多文件内核模块有些时候在几个源文
10、件之间分出一个内核模块是很有意义的。在这种情况下,你需要做下面的事情:1.在除了一个以外的所有源文件中,增加一行#define _NO_VERSION_。这是很重要的,因为 module.h 一般包括 kernel_version 的定义,这是一个全局变量,包含模块编译的内核版本。如果你需要 version.h,你需要把自己把它包含进去,因为如果有_NO_VERSION_的话 module.h 不会自动包含。2.象通常一样编译源文件。3.把所有目标文件联编成一个。在 X86 下,用 ld m elf_i386 r o.o 这里给出一个这样的内核模块的例子。ex start.c /*start.
11、c *Copyright(C)1999 by Ori Pomerantz*Hello,world-the kernel module version.*This file includes just the start routine*/*The necessary header files*/*Standard in kernel modules*/#include /*Were doing kernel work*/#include /*Specifically,a module*/4/*Deal with CONFIG_MODVERSIONS*/#if CONFIG_MODVERSION
12、S=1#define MODVERSIONS#include#endif /*Initialize the module*/int init_module()printk(Hello,world-this is the kernel speakingn);/*If we return a non zero value,it means that *init_module failed and the kernel module *cant be loaded*/return 0;ex stop.c /*stop.c *Copyright(C)1999 by Ori Pomerantz*Hell
13、o,world-the kernel module version.This *file includes just the stop routine.*/*The necessary header files*/*Standard in kernel modules*/#include /*Were doing kernel work*/#define _NO_VERSION_ /*This isnt the file *of the kernel module*/#include /*Specifically,a module*/#include /*Not included by *mo
14、dule.h because *of the _NO_VERSION_*/5/*Deal with CONFIG_MODVERSIONS*/#if CONFIG_MODVERSIONS=1#define MODVERSIONS#include#endif /*Cleanup-undid whatever init_module did*/void cleanup_module()printk(Short is the life of a kernel modulen);ex Makefile#Makefile for a multifile kernel moduleCC=gccMODCFLA
15、GS:=-Wall-DMODULE-D_KERNEL_-DLINUXhello.o:start.o stop.old-m elf_i386-r-o hello.o start.o stop.ostart.o:start.c/usr/include/linux/version.h$(CC)$(MODCFLAGS)-c start.cstop.o:stop.c/usr/include/linux/version.h$(CC)$(MODCFLAGS)-c stop.c62字符设备文件字符设备文件那么,现在我们是原始级的内核程序员,我们知道如何写不做任何事情的内核模块。我们为自己而骄傲并且高昂起头来。
16、但是不知何故我们感觉到缺了什么东西。患有精神紧张症的模块不是那么有意义。内核模块同进程对话有两种主要途径。一种是通过设备文件(比如/dev 目录中的文件),另一种是使用 proc 文件系统。我们把一些东西写入内核的一个主要原因就是支持一些硬件设备,所以我们从设备文件开始。设备文件的最初目的是允许进程同内核中的设备驱动通信,并且通过它们和物理设备通信(modem,终端,等等)。这种方法的实现如下:每个设备驱动都对应着一定类型的硬件设备,并且被赋予一个主码。设备驱动的列表和它们的主码可以在 in/proc/devices 中找到。每个设备驱动管理下的物理设备也被赋予一个从码。无论这些设备是否真的安
17、装,在/dev 目录中都将有一个文件,称作设备文件,对应着每一个设备。例如,如果你进行 ls l/dev/hdab*操作,你将看见可能联结到某台机器上的所有的 IDE硬盘分区。注意它们都使用了同一个主码,3,但是从码却互不相同。(声明:这是在 PC 结构上的情况,我不知道在其他结构上运行的 linux 是否如此。)在系统安装时,所有设备文件在 mknod 命令下被创建。它们必须创建在/dev 目录下没有技术上的原因,只是一种使用上的便利。如果是为测试目的而创建的设备文件,比如我们这里的练习,可能放在你编译内核模块的的目录下更加合适。设备可以被分成两类:字符设备和块设备。它们的区别是块设备有一个
18、用于请求的缓冲区,所以它们可以选择用什么样的顺序来响应它们。这对于存储设备是非常重要的,读取相邻的扇区比互相远离的分区速度会快得多。另一个区别是块设备只能按块(块大小对应不同设备而变化)接受输入和返回输出,而字符设备却按照它们能接受的最少字节块来接受输入。大部分设备是字符设备,因为它们不需要这种类型的缓冲。你可以通过观看 ls-l 命令的输出中的第一个字符而知道一个设备文件是块设备还是字符设备。如果是 b 就是块设备,如果是 c 就是字符设备。这个模块可以被分成两部分:模块部分和设备及设备驱动部分。Init_module 函数调用module_register_chrdev 在内核得块设备表里
19、增加设备驱动。同时返回该驱动所使用的主码。Cleanup_module 函数撤销设备的注册。这些操作(注册和注销)是这两个函数的主要功能。内核中的函数不是象进程一样自发运行的,而是通过系统调用,或硬件中断或者内核中的其它部分(只要是调用具体的函数)被进程调用的。所以,当你向内和中增加代码时,你应该把它注册为具体某种事件的句柄,而当你把它删除的时候,你需要注销这个句柄。设备驱动完全由四个设备_action函数构成,它们在希望通过有主码的设备文件实现一些操作时被调用。内核调用它们的途径是通过 file_operation 结构 Fops。此结构在设备被注册是创建,它包含指向这四个函数的指针。另一点
20、我们需要记住的是,我们不能允许管理员随心所欲的删除内核模块。这是因为如果设备文件是被进程打开的,那么我们删除内核模块的时候,要使用这些文件就会导致访问正常的函数(读/写)所在的内存位置。如果幸运,那里不会有其他代码被装载,我们将得到一个恶性的错误信息。如果不行,另一个内核模块会被装载到同一个位置,这将意味着7会跳入内核中另一个程序的中间,结果将是不可预料的恶劣。通常你不希望一个函数做什么事情的时候,会从那个函数返回一个错误码(一个负数)。但这在 cleanup_module 中是不可能的,因为它是一个 void 型的函数。一旦cleanup_module 被调用,这个模块就死掉了。然而有一个计
21、数器记录着有多少个内核模块在使用这个模块,这个计数器称为索引计数器(/proc/modules 中没行的最后一个数字)。如果这个数字不是 0,删除就会失败。模块的索引计数器包含在变量 mod_use_count_中。有定义好的处理这个变量的宏(MOD_INC_USE_COUNT 和 MOD_DEC_USE_COUNT),所以我们一般使用宏而不是直接使用变量 mod_use_count_,这样在以后实现变化的时候会带来安全性。ex chardev.c /*chardev.c *Copyright(C)1998-1999 by Ori Pomerantz*Create a character de
22、vice(read only)*/*The necessary header files*/*Standard in kernel modules*/#include /*Were doing kernel work*/#include /*Specifically,a module*/*Deal with CONFIG_MODVERSIONS*/#if CONFIG_MODVERSIONS=1#define MODVERSIONS#include#endif /*For character devices*/#include /*The character device *definitio
23、ns are here*/#include /*A wrapper which does *next to nothing at *at present,but may *help for compatibility *with future versions *of Linux*/*In 2.2.3/usr/include/linux/version.h includes *a macro for this,but 2.0.35 doesnt-so I add *it here if necessary.*/8#ifndef KERNEL_VERSION#define KERNEL_VERS
24、ION(a,b,c)(a)*65536+(b)*256+(c)#endif/*Conditional compilation.LINUX_VERSION_CODE is *the code(as per KERNEL_VERSION)of this version.*/#if LINUX_VERSION_CODE KERNEL_VERSION(2,2,0)#include /*for put_user*/#endif#define SUCCESS 0/*Device Declarations*/*The name for our device,as it will appear *in/pro
25、c/devices*/#define DEVICE_NAME char_dev/*The maximum length of the message from the device*/#define BUF_LEN 80/*Is the device open right now?Used to prevent *concurent access into the same device*/static int Device_Open=0;/*The message the device will give when asked*/static char MessageBUF_LEN;/*Ho
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 内核 编程
限制150内