第12章 添加最简单的Linux内核模块
- 格式:ppt
- 大小:2.00 MB
- 文档页数:16
Linux设备驱动程序原理及框架-内核模块入门篇内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块内核模块介绍Linux采用的是整体式的内核结构,这种结构采用的是整体式的内核结构,采用的是整体式的内核结构的内核一般不能动态的增加新的功能。
为此,的内核一般不能动态的增加新的功能。
为此,Linux提供了一种全新的机制,叫(可安装) 提供了一种全新的机制,可安装) 提供了一种全新的机制模块” )。
利用这个机制“模块”(module)。
利用这个机制,可以)。
利用这个机制,根据需要,根据需要,在不必对内核重新编译链接的条件将可安装模块动态的插入运行中的内核,下,将可安装模块动态的插入运行中的内核,成为内核的一个有机组成部分;成为内核的一个有机组成部分;或者从内核移走已经安装的模块。
正是这种机制,走已经安装的模块。
正是这种机制,使得内核的内存映像保持最小,的内存映像保持最小,但却具有很大的灵活性和可扩充性。
和可扩充性。
内核模块内核模块介绍可安装模块是可以在系统运行时动态地安装和卸载的内核软件。
严格来说,卸载的内核软件。
严格来说,这种软件的作用并不限于设备驱动,并不限于设备驱动,例如有些文件系统就是以可安装模块的形式实现的。
但是,另一方面,可安装模块的形式实现的。
但是,另一方面,它主要用来实现设备驱动程序或者与设备驱动密切相关的部分(如文件系统等)。
密切相关的部分(如文件系统等)。
课程内容内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块应用层加载模块操作过程内核引导的过程中,会识别出所有已经安装的硬件设备,内核引导的过程中,会识别出所有已经安装的硬件设备,并且创建好该系统中的硬件设备的列表树:文件系统。
且创建好该系统中的硬件设备的列表树:/sys 文件系统。
(udev 服务就是通过读取该文件系统内容来创建必要的设备文件的。
)。
LINUX内核模块编程指南Linux内核模块编程是一种在Linux操作系统中向内核添加新功能的方法。
内核模块是一段特殊的代码,可以被编译并加载到内核中,从而实现对操作系统底层的修改和扩展。
下面将介绍一些关键的步骤和注意事项,以帮助初学者入门Linux内核模块编程。
首先,要编写一个内核模块,需要有合适的开发环境。
一般来说,需要一台运行Linux的计算机,并安装有相应的开发工具链(如GCC编译器)和Linux内核源代码。
编写内核模块的第一步是定义模块的入口函数。
在C语言中,入口函数的原型通常是`int init_module(void)`。
在该函数中,可以注册一些回调函数来处理特定的事件,或者进行其他必要的初始化工作。
接下来,可以添加自定义的函数和数据结构,以实现模块的功能。
这些函数和数据结构可以被其他模块或内核中的其他代码调用和使用。
由于内核是一个复杂的系统,因此在编写内核模块时需要特别关注数据的正确性和同步性。
在编写模块的过程中,还需要了解一些内核编程的特殊机制,如内核的锁机制、中断处理和内存管理等。
对这些内容的学习需要一定的时间和精力,但是它们是实现高性能和高可靠性内核模块的关键。
完成模块的编写后,需要对其进行编译和加载。
一般来说,可以使用Makefile来编译模块,并且可以使用insmod命令来加载模块。
加载模块时,内核将会调用模块的入口函数,从而完成初始化工作。
在模块编写和调试的过程中,需要注意一些常见的问题。
例如,内核模块应该遵守内核的编程规范和风格,避免对内核进行不必要的修改或侵入。
此外,要时刻注意内存管理和锁机制,以防止出现内存泄漏或并发访问的问题。
最后,需要强调的是,Linux内核模块编程是一项复杂而底层的技术。
对于初学者来说,学习和掌握这些知识需要一定的时间和耐心,并且需要有一定的计算机基础和编程经验。
但是,一旦熟练掌握了这些技术,就能够更加深入地了解和使用Linux操作系统,并且为其添加新的功能。
linux 内核模块路径【最新版】目录1.Linux 内核模块的概述2.Linux 内核模块的编写方法3.Linux 内核模块的优点和缺点4.如何使用 Linux 内核模块5.总结正文一、Linux 内核模块的概述Linux 内核模块是一种可以直接插入到 Linux 内核中的二进制代码,它在 Ring 0(x86_64 处理器中执行最低和受保护程度最低的执行环)上运行,拥有最高的权限。
它可以访问系统中的所有内容,并且运行速度很快。
在 Linux 系统中,内核模块可以实现与硬件的交互和访问系统中特权信息的操作。
二、Linux 内核模块的编写方法编写 Linux 内核模块的方法相对较为复杂,需要对 Linux 内核的架构和编程模型有一定的了解。
一般来说,编写 Linux 内核模块需要以下几个步骤:1.创建一个新的内核模块目录,例如:“my_module”。
2.在“my_module”目录下创建一个名为“Makefile”的文件,用于指定内核模块的编译规则。
3.在“my_module”目录下创建一个名为“hello.c”的文件,用于编写内核模块的源代码。
4.使用“make”命令编译内核模块,生成一个名为“hello.ko”的二进制文件。
5.使用“sudo insmod”命令将编译好的内核模块加载到 Linux 内核中。
6.使用“lsmod”命令查看已经加载的内核模块,使用“dmesg”命令查看内核日志,可以看到模块已经被加载。
三、Linux 内核模块的优点和缺点Linux 内核模块的优点在于它可以直接插入到 Linux 内核中,运行速度快,并且可以访问系统中的所有内容。
这使得内核模块在需要与硬件交互或者访问系统特权信息时非常有用。
然而,Linux 内核模块也有一些缺点。
首先,内核模块的编写难度较大,需要对 Linux 内核的架构和编程模型有一定的了解。
其次,内核模块的编写和加载需要特定的权限,普通用户无法进行。
Linux内核模块使用指南一、模块简介Windows NT是一种微内核的结构,其内核的功能块被划分成独立的模块,在这些功能块之间有严格的通信机制;而Linux则不同,它是一种monolithic(单一大块)结构,也就是说,整个内核是一个单独的、非常大的程序。
在这种结构中,部件的添加和删除都相当麻烦,需要重新编译内核。
为了解决这个问题,不知道从哪个版本的内核开始,Linux引入了一种称为module (模块)的技术,可以把某些功能代码作为模块动态装载到内核中使用。
模块是一种目标对象文件,需要在内核空间执行,可以把它看作是一组已经编译好而且已经链接成可执行文件的程序。
在需要的时候,内核就会实用某种方法调用这些程序来执行特定的操作,实现特定的功能。
内核在内核符号表中维护了一个模块的链表,每个符号表对应一个模块,在把模块加载进内核时正确地对其进行解释,并将模块作为内核的一部分来执行;加载进内核中的模块具有所有的内核权限。
模块可以在系统启动时加载到系统中,也可以在系统运行的任何时刻加载;在不需要时,可以将模块动态卸载。
这样就不用每次修改系统的配置时都要重新编译内核了。
二、模块的优缺点内核模块的这种动态装载特性具有以下的优点:1、可以把内核映像文件保持在最小。
在编译内核时可以选择把一部分内容当成模块进行编译,这样在最终生成的内核映像文件中就可以不包含这部分内容,从而生成最小的内核映像文件。
2、灵活性好。
如果需要实用新的模块,不必重新编译内核,只要把新的模块编译后装载进系统中就可以了。
如果对内核源程序进行了修改,也不需要重新编译整个内核,只需要修改对应的部分就可以了。
但是,内核模块的引入也带来了一些问题:1、这种动态加载的特性不利于系统的性能和内存的利用,会带来负面的影响。
2、装入内核的模块和其他内核部分一样具有最高的权限,使用不当就可能引起系统的崩溃。
3、内核版本和模块版本的不兼容也会导致系统的崩溃,因此必须进行严格的版本检查,这样就使模块的编写变得更加复杂了。
Linux内核模块Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件。
1.1linux内核模块简介Linux内核的整体结构非常庞大,其包含的组件非常多。
我们如何把需要的部分都包含在内核中呢?●把需要的功能都编译到linux内核。
●以模块方式扩展内核功能。
为了使学生对模块建立初步的感性认识,我们先来看一个最简单的内核模块”hello world”,代码如下:#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void){printk("hello world\n”);return 0;}static void hello_exit(void){printk(1 "hello module exit\n ");}module_init(hello_init);module_exit(hello_exit);MODULE_AUTHOR("zky");MODULE_DESCRIPTION("A simple hello Module ");MODULE_VERSION("V1.0");这个最简单的内核模块只包含内核加载函数、卸载函数和对Dual BSD/GPL许可权限的声明以及一些描述信息。
编译会产生hello.ko目标文件,通过”insmod ./hello.ko”命令可以加载它,通过”rmmod hello”命令可以卸载它,加载时输出”hello world”,卸载时输出”hello module exit”,查看输出信息可通过dmesg命令。
内核模块中用于输出的函数式内核空间的printk()而非用户空间的printf(),printk()的用法和printf()相似,但前者可定义输出级别。
linux 内核模块路径(原创实用版)目录1.Linux 内核模块的概述2.Linux 内核模块的编写方法3.如何编写一个简单的 Linux 内核模块4.Linux 内核模块的使用与加载5.总结正文一、Linux 内核模块的概述Linux 内核模块是一种可以扩展 Linux 内核功能的机制,它是一段编译后的二进制代码,可以直接插入到 Linux 内核中,并在 ring0(x86_64 处理器中执行最低和受保护程度最低的执行环)上运行。
内核模块可以访问系统中的所有内容,但运行速度很快且不受检查。
二、Linux 内核模块的编写方法要编写一个 Linux 内核模块,首先需要了解 Linux 内核的架构和相关 API。
通常,我们需要创建一个源文件(例如:hello.c),然后在其中包含必要的头文件(如:linux/init.h、linux/module.h 和linux/kernel.h 等),并声明模块的许可证和作者信息。
接下来,编写模块的初始化函数(如:helloinit())和清洁函数(如:hello_exit()),以及实现模块功能的函数(如:hello_world() 等)。
最后,使用 make 命令编译模块,并使用 insmod 命令加载到内核中。
三、如何编写一个简单的 Linux 内核模块以下是一个简单的 Linux 内核模块示例:1.创建一个源文件 hello.c,包含以下代码:```c#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h> MODULELICENSE("GPL"); MODULEAUTHOR("tang");static int helloinit(void){printk(KERN_INFO "Hello, world! ");return 0;}static void hello_exit(void){printk(KERN_INFO "Goodbye, world! ");}module_init(helloinit);module_exit(hello_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("tang");MODULE_DESCRIPTION("A simple Linux kernel module");```2.使用 make 命令编译模块:```bashmake -C /lib/modules/`uname -r`/build M=$(pwd) modules```3.使用 insmod 命令加载模块:```bashsudo insmod hello.ko```4.使用 lsmod 和 dmesg 命令查看已加载的内核模块和模块日志:```bashlsmoddmesg```四、Linux 内核模块的使用与加载在实际开发过程中,我们可能会遇到需要访问系统特权信息或与硬件交互的情况,这时就需要使用内核模块。
linux内核动态加载模块一、安装内核模块:一般步骤:(1) 在/usr/src/linux/下运行make menuconfig把需要编译成模块的项打上(M),保存并退出。
(2) 运行make modules,这一步将在/usr/src/linux/下生成*.o或*.ko文件。
(3) 运行make modeules_install来安装,这步会把生成的.o或ko 文件拷贝到/lib/modules/`uname -r`/下。
如果你只要编译某一个或几个模块,就可以用下面这个快速的方法:(1) 找到编译内核所需要的.config文件。
在/usr/src/linux/arch目录下有若干编译内核所用的配置。
选择我们想要的配置,将它复制到/usr/src/linux目录下,改名为.config。
cp /usr/src/linux/arch/x86/xxconfig /usr/src/linux/.config(2) 修改.config文件,去掉不用的模块,加上自己想要的模块。
打开.config,有许多XXXX=m的项,这些都是要被编译为模块的项,因为我们不希望编译这些模块,所以要把XXXX=m的项统统去掉。
然后再加上我们想要的模块,例如将# CONFIG_NTFS_FS is not set 改为CONFIG_NTFS_FS=m 当然,可以用你熟悉各种工具来做这件事。
(3) 编译NTFS模块。
在/usr/src/linux目录下运行命令make modules来编译我们想要的模块。
(4) 安装模块。
编译后得到的.o文件在/usr/src/linux/目录下,手动将它复制到正确的目录下。
例如cp /usr/src/linux/fs/ntfs/ntfs.o /lib/modules/2.2.16-22/fs/注意:千万不能运行命令make modules_install,否则将带来严重的后果,它会删除你系统中的所有模块,只安装刚刚编译的模块(ntfs.o)。
添加内核模块1.编写helloworld模块,了解模块的编程方法。
首先编写一个helloworld程序,如下图:图1 HelloWorld模块测试c程序两个头文件linux/module.h和linux/init.h是编写模块所必需的;程序里面有两个函数,第一个函数moduletest_init实现模块加载时的动作,第二个函数moduletest_exit实现模块卸载是的动作。
两个函数的函数名可以任意指定。
module_init和module_exit是两个宏,括号里的函数名才是真正的模块加载和卸载时要执行的函数,即不管上面的两个函数的函数名是什么,只要经过这两个宏的指定,就会称为模块加载和卸载时运行的函数。
最后一句表示模块遵循公共许可证,一般来说都要加上,不加会出现警告。
然后编译内核模块。
⑴编写Makefile文件;在与源文件同目录下,新建文件名为Makefile,注意M要大写。
编辑Makefile文件,添加以下内容并保存,如图:图2 helloworld模块的Makefile文件⑵编译内核模块;打开终端,进入当前所在的目录。
执行命令 make命令成功执行后,会在当前目录下生成许多文件,HelloWorld.o HelloWorld.ko HelloWorld.mod.o HelloWorld.mod.c Modules.symvers 其中,HelloWorld.ko是我们要加载的模块。
最后加载和卸载模块:终端在当前目录下,输入命令 insmod ./HelloWorld.ko;输入命令lsmod,能找到名为HelloWorld的模块,说明模块已经加载;输入命令dmesg,查看最后一行,会有模块加载时调用的函数输出;输入命令rmmod HelloWorld ,卸载模块(注意与加载时不同),然后输入lsmod,已经找不到HelloWorld模块,说明模块已经卸载;输入命令dmesg,查看模块卸载是调用的函数输出。
Linux操作系统软件模块和内核安装配置内容提要•1 软件安装与配置•2 开机和关机•3 系统服务•4 内核管理•5 模块管理•6 核心参数1 软件安装与配置•本部分将讨论两种最常用的软件安装方法:使用Red Hat Package Manager软件包管理工具软件(RPM)和自行编译源代码。
•本部分中所有需要输入的命令都是以根用户的身份输入的。
因此最好是直接以根用户身份登录进入系统。
RPM Package Manager•Manage software packages–Install, upgrade, remove, verify, query, build •Package files referred to as RPMs–Distributed by the vendor–Include files to be installed plus some installscripts•Source RPMs contain the source code –e.g., kernel-2.4.9-e.24.src.rpm•Binary RPMs contain the pre-built binaries –e.g., kernel-2.4.9-e.24.i686.rpm–Choose the highest architecture the machinecan use-m)•e.g., i686, i586, i486, i386 (unameuname -m1 安装新的软件包•普通安装:–#rpm -i bc-1.05a-4.i386.rpm•升级软件:–#rpm -U bc-1.05a-4.i386.rpm•强行安装:–#rpm -i --force -nodeps packagename.rpm •其他参数–-h 使用符号“#”指示安装进度,与-v参数一起使用时显示效果更好–-v 告诉RPM报告每一步操作的情况–--test 这个参数并不进行真正的安装;它只是用来检查安装能否成功地完成。
Linux内核模块简单⽰例1. Linux 内核的整体结构⾮常庞⼤,其包含的组件也⾮常多,使⽤这些组件的⽅法有两种:①直接编译进内核⽂件,即zImage或者bzImage(问题:占⽤内存过多)②动态添加 * 模块本⾝并不被编译进内核⽂件 * 根据需求,在内核运⾏期间动态安装或卸载2. 内核模块动态安装与卸载①安装 insmod例:insmod /home/dnw_usb.ko②卸载 rmmod例:rmmod dnw_usb③查看 lsmod例: lsmod3. 模块声明① MODULE_LICENSE()② MODULE_AUTHOR()③ MODULE_DESCRIPTION()④ MODULE_VERSION()4. 模块参数①模块参数⽤于在加载模块时传递参数给模块②通过宏module_param指定保存模块参数的变量 module_param(name, type, perm) * name:变量的名称 * type:变量类型。
bool:布尔型,int:整型,charp:字符串型 * perm是访问权限。
S_IRUGO:读权限,S_IWUSR:写权限③简单⽰例int a = 3;char* ptr = NULL;module_param(a, int, SIRUGO);module_param(ptr, charp, SIRUGO);命令⾏:insmod xxx.ko a=10 ptr="HelloWorld"5. 符号导出①如果内核模块中的函数或者全局变量想让其他模块使⽤,必须进⾏导出②内核符号导出使⽤宏 EXPORT_SYMBOL(符号名) EXPORT_SYMBOL_GPL(符号名) 注:其中EXPORT_SYMBOL_GPL只能⽤于包含GPL许可证的模块6. 内核模块简单⽰例①模块代码:#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("Kevin Wu");MODULE_DESCRIPTION("For study Linux module");MODULE_VERSION("1.0");int a = 3;char* ptr = NULL;module_param(a, int, S_IRUGO);module_param(ptr, charp, S_IRUGO);static int hello_init(){printk(KERN_WARNING"Hello world initlizing\n");printk(KERN_WARNING"a = %d\n", a);printk(KERN_WARNING"ptr = %s\n", ptr);return0;}static void hello_exit(){printk(KERN_WARNING"Hello world exiting\n");}module_init(hello_init);module_exit(hello_exit);② Makefile:obj-m := helloworld.oKDIR := /home/Linux/Kernal/linux-2.6.29all:make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm clean:rm -f *.o *.ko *.order *.symvers。
实验三添加内核模块一、实验目的:学习Linux模块的基本概念和原理,学习内核模块编程的基本技术,利用内核模块编程访问进程描述符,操作内核的基本数据结构,加深对进程的理解;理解proc文件系统的作用,学习proc文件的创建方法,掌握这种用户态和核心态通信的方法。
二、实验平台:虚拟机:VMWare9操作系统:Ubuntu12.04编辑器:Gedit | Vi三、实验内容:(1)阅读内核模块实例hello.c,掌握内核模块的主要构成;阅读Makefile文件,理解内核模块的编译方法及执行过程;掌握模块安装、卸载,以及查看模块信息的方法。
查看模块信息:卸载模块:(2)设计一个模块,功能是列出系统中所有内核进程的程序名、PID号和进程状态。
主要步骤:阅读内核源代码,了解进程描述符task_struct中与本实验有关的成员项,以及访问进程队列的宏for_each_process;编写readprocess模块,获取进程信息;修改Makefile文件,编译、安装模块,查看输出信息;查看模块信息,卸载模块。
readprocess.c:#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/init_task.h>// 初始化函数static int hello_init(void){struct task_struct *p;p = NULL;p = &init_task;printk(KERN_ALERT"名称\t进程号\t状态\t优先级\t父进程号\t");for_each_process(p){if(p->mm == NULL){ //内核线程的mm成员为空printk(KERN_ALERT"%s\t%d\t%ld\t%d\n",p->comm,p->pid,p->state,p->normal_prio,p->parent->pid);}}return 0;}// 清理函数static void hello_exit(void){printk(KERN_ALERT"goodbye!\n");}// 函数注册module_init(hello_init);module_exit(hello_exit);// 模块许可申明MODULE_LICENSE("GPL");Makefile代码:ifneq ($(KERNELRELEASE),)obj-m:=readprocess.oelseKDIR:= /lib/modules/$(shell uname -r)/buildPWD:= $(shell pwd)default:$(MAKE) -C $(KDIR) M=$(PWD) modulesclean:$(MAKE) -C $(KDIR) M=$(PWD) cleanendif将process.c和Makefile文件放在同一个文件夹下使用make函数生成后缀为.ko文件:使用命令载入模块,如图:使用lsmod命令显示载入系统的模块,如图:(3)利用内核模块编程,在/proc目录下用自己的学号创建一个目录,如/proc/201300834101然后在学号目录下创建一个processinfo文件,如/proc/201300834101/processinfo,此文件为只读文件,用于显示所有内核进程的程序名、PID号和进程状态。
实验5 Linux的内核模块邢卫2008-11-12 修订实验目的学习如何编写Linux中的内核模块程序,如何安装和卸载;初步了解Linux内核模块的实现机理;学习虚拟文件系统。
实验内容1.学习内核模块的编写、编译、安装、使用、卸载过程2.学习通过内核模块,增加虚拟文件系统/proc的功能3.学习虚拟文件系统的初步概念和使用4.选做:通过阅读Linux内核源程序,学习Linux内核模块的实现机制实验步骤一、编写一个简单的内核模块1.以stu帐号或者root帐号,创建模块helloworld1)编辑●在源程序中增加一行:MODULE_LICENSE("GPL");2)编译gcc –DMODULE–2.以root帐号完成模块的安装和卸载1)查看系统中现有的内核模块的安装情况使用lsmod或者cat /proc/modules 命令2)安装内核模块若遇到版本不匹配问题,可使用insmod -f3)查看内核模块的安装情况,验证安装成功4)卸载内核模块rmmod helloworld5)再次查看内核模块的安装情况,验证卸载成功二、通过内核模块在/proc文件系统中增加应用1.以stu帐号或者root帐号,创建模块proc_example1)编辑vi proc_example.c●第15行,修改为:#define FOOBAR_LEN 255●第197行,修改为:MODULE_LICENSE("GPL");2)编译gcc –DMODULE –D__KERNEL__ –I/usr/src/linux/include–c proc_example.c2.以root帐号完成模块的安装和卸载,以stu帐号完成使用1)(以root帐号)准备工作:确认内核源代码目录树中存在恰当的头文件# cd /usr/src# tar zxvf linux-.tar.gz# cd linux# make config 均选缺省选项(一直“回车”)2)查看系统中现有的内核模块的安装情况使用lsmod或者cat /proc/modules 命令3)(以root帐号)安装内核模块insmod proc_example.o若遇到版本不匹配问题,可使用insmod -f proc_example.o4)查看内核模块的安装情况,验证安装成功5)使用/proc/proc_example 中的虚拟文件●cd /proc●使用 ls –l验证存在proc_example目录●cd proc_example●使用 ls –l验证存在foo,bar,jiffies等文件●使用cat foo和cat bar命令查看文件内容●(以root帐号)使用vi foo命令修改foo文件内容,注意文件总大小不要超过200字节长度●再次使用cat foo命令查看foo文件内容●反复使用cat jiffies命令查看jiffies文件内容,注意观察系统时钟的变化●其他你认为合适的操作6)(以root帐号)卸载内核模块rmmod proc_example7)再次查看内核模块的安装情况,验证卸载成功8)验证/proc目录下,proc_example目录及其文件均已消失三、(选做)课外预习或课后自学《边干边学》第4章“内核模块”参考资料●《边干边学》第4章,“内核模块”●《边干边学》第1.2节,“查看Linux内核状况”附:源程序#include <linux/module.h>MODULE_LICENSE("GPL");int init_module(void){printk("<1>Hello, world!\n");return 0;}void cleanup_module(void){printk("<1>goodbye!\n");}/**/#define __NO_VERSION__#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/sched.h>#include <asm/uaccess.h>#define MODULE_VERSION "1.0"#define MODULE_NAME "proc_example" #define FOOBAR_LEN 255// 8struct fb_data_t {char name[FOOBAR_LEN + 1]; char value[FOOBAR_LEN + 1]; };static struct proc_dir_entry *example_dir, *foo_file, *bar_file; static struct proc_dir_entry *jiffies_file, *tty_device, *symlink;struct fb_data_t foo_data, bar_data;static int proc_read_jiffies(char *page, char **start,off_t off, int count, int *eof, void *data){int len;MOD_INC_USE_COUNT;len = sprintf(page, "jiffies = %ld\n",jiffies);MOD_DEC_USE_COUNT;return len;}static int proc_read_foobar(char *page, char **start,off_t off, int count, int *eof, void *data){int len;struct fb_data_t *fb_data = (struct fb_data_t *)data;MOD_INC_USE_COUNT;len = sprintf(page, "%s = '%s'\n",fb_data->name, fb_data->value);MOD_DEC_USE_COUNT;return len;}static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data){int len;struct fb_data_t *fb_data = (struct fb_data_t *)data;MOD_INC_USE_COUNT;if(count > FOOBAR_LEN)len = FOOBAR_LEN;elselen = count;if(copy_from_user(fb_data->value, buffer, len)) {MOD_DEC_USE_COUNT;return -EFAULT;}fb_data->value[len] = '\0';MOD_DEC_USE_COUNT;return len;}int init_module(){int rv = 0;/* create the directory */example_dir = proc_mkdir(MODULE_NAME, NULL);if(example_dir == NULL) {rv = -ENOMEM;goto out;}example_dir->owner = THIS_MODULE;/* create the read-only file "jiffies" */jiffies_file = create_proc_read_entry("jiffies",0444, example_dir,proc_read_jiffies,NULL);if(jiffies_file == NULL) {rv = -ENOMEM;goto no_jiffies;}jiffies_file->owner = THIS_MODULE;/* create file "foo" and "bar" */foo_file = create_proc_entry("foo", 0644, example_dir); if(foo_file == NULL) {rv = -ENOMEM;goto no_foo;}strcpy(foo_, "foo");strcpy(foo_data.value, "foo");foo_file->data = &foo_data;foo_file->read_proc = proc_read_foobar;foo_file->write_proc = proc_write_foobar;foo_file->owner = THIS_MODULE;bar_file = create_proc_entry("bar", 0644, example_dir); if(bar_file == NULL) {rv = -ENOMEM;goto no_bar;}strcpy(bar_, "bar");strcpy(bar_data.value, "bar");bar_file->data = &bar_data;bar_file->read_proc = proc_read_foobar;bar_file->write_proc = proc_write_foobar;bar_file->owner = THIS_MODULE;/* create device file "tty" */tty_device = proc_mknod("tty", S_IFCHR | 0666,example_dir, MKDEV(5, 0));if(tty_device == NULL) {rv = -ENOMEM;goto no_tty;}tty_device->owner = THIS_MODULE;/* create link file "jiffies_too" */symlink = proc_symlink("jiffies_too", example_dir,"jiffies");if(symlink == NULL) {rv = -ENOMEM;goto no_symlink;}symlink->owner = THIS_MODULE;/* all creation are sucessful */printk(KERN_INFO "%s %s initialised\n",MODULE_NAME, MODULE_VERSION);return 0;/* error handling */no_symlink:remove_proc_entry("tty", example_dir);no_tty:remove_proc_entry("bar", example_dir);no_bar:remove_proc_entry("foo", example_dir);no_foo:remove_proc_entry("jiffies", example_dir);no_jiffies:remove_proc_entry(MODULE_NAME, NULL);out:return rv;}void cleanup_module(){remove_proc_entry("jiffies_too", example_dir); remove_proc_entry("tty", example_dir);remove_proc_entry("bar", example_dir);remove_proc_entry("foo", example_dir);remove_proc_entry("jiffies", example_dir);remove_proc_entry(MODULE_NAME, NULL);printk(KERN_INFO "%s %s removed\n",MODULE_NAME, MODULE_VERSION);}MODULE_DESCRIPTION("proc examples");MODULE_LICENSE("GPL");EXPORT_NO_SYMBOLS;/* the end */。