Linux设备驱动开发入门本文以快捷而简单的形式讲解怎样像一个内核开发者那样开发linux设备驱动源作者:Xa**erCalbet版权:GNUFreeDocumentationLicense翻译:顾宏军()英文版权:创作共用.署名-非商业用途-保持一致知识打算要开发Linux设备驱动,须要把握以下知识:C编程须要把握深入一些的C语言知识,例如,表针的使用,位处理函数红旗linux官网,等。微处理器编程须要理解微机的内部工作原理:存储器地址,中断,等。那些内容对一个汇编程序员应当比较熟Linux下有好几种不同的设备。为简单起见,本文只涉及以模块方式加载的字符设备。使用2.6.x的内核。(非常是DebianSarge使用的2.6.8内核。)用户空间和内核空间当你开发设备驱动时,须要理解“用户空间”和内核空间之间的区别。10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:25:ddd内核空间:Linux操作系统,非常是它的内核,用一种简单而有效的方式管理机器的硬件,给用户提供一个简捷而统一的编程插口。同样的,内核,非常是它的设备驱动程序,是联接最终用户/程序员和硬件的一坐桥或则说是插口。
任何子程序或则函数只要是内核的一部份(比如:模块,和设备驱动),那它也就是内核空间的一部份。用户空间.最终用户的应用程序,像UNIX的shell或则其它的GUI的程序(比如,gedit),都是用户空间的一部份。很其实,这种应用程序须要和系统的硬件进行交互。并且,她们不是直接进行,而是通过内核支持的函数进行。它们的关系可以通过右图表示:应用程序留驻在用户空间,模块和设备驱动留驻在内核空间26:27:28:29:30:31:32:33:34:35:36:37:38:39:40:ddd用户空间和内核空间之间的插口函数内核在用户空间提供了好多子程序或则函数,它们容许用户应用程序员和硬件进行交互。一般,在UNIX或则Linux系统中,这些交互是通过函数或则子程序进行的便于文件的读和写操作。这是由于从用户的视角看,UNIX的设备就是一个个文件。从另一方面看,在Linux内核空间同样提供了好多函数或则子程序以在底层直接地对硬件进行操作linux设备驱动开发入门与编程实践 下载,而且容许从内核向用户空间传递信息。一般,用户空间的每位函数(用于使用设备或则文件的),在内核空间中都有一个对应的功能相像而且可将内核的信息向用户传递的函数。这些关系可从下表看下来。
目前这个表是空的,在我们前面每位表项就会填入对应的函数。设备驱动风波和它们在内核和用户空间的对应的插口函数风波用户函数内核函数加载模块打开设备读设备写设备关掉设备卸载模块内核空间和硬件设备之间的插口函数在内核空间同样有可以控制设备或则在内核和硬件之间交换信息的函数。表2解释了这种概念。同样的,这个表将在介绍到相应内容时填写上。设备驱动风波和它们在内核空间与硬件设备之间对应的插口函数风波内核函数41:42:43:44:45:46:47:48:49:50:51:52:53:54:55:56:57:58:59:60:61:62:63:64:65:66:67:68:69:70:71:72:73:74:75:76:77:78:79:80:81:82:83:84:ddd读数据写数据第一个驱动:在用户空间加载和卸载驱动这一节将向你展示怎样开发你的第一个Linux设备驱动,该驱动作为一个内核模块存首先,写一个文件名为nothing.c的文件linux设备驱动开发入门与编程实践 下载,代码如下:nothing.c#includelinux/module.hMODULE_LICENSE("DualBSD/GPL");内核从2.6.x开始,编译模块显得稍稍复杂些。
首先,你须要有一份完整的,编译了的内核源代码树。假如你使用的是DebianSarge系统,你可以根据附表B(在本文末尾)的步骤进行操作。在以下的内容里,假定你使用的是2.6.8内核。接出来,你须要撰写一个makefile。本事例所用的makefile文件名称为Makefile,内容如下:Makefile1nothing.o和之前版本的内核不同,你须要使用和你当前系统所用内核版本相同的代码来编译即将加载和使用的模块。编译该模块linux查看磁盘空间,可以使用以下命令:/usr/src/kernel-source-2.6.8M=`pwd`modules这个极其简单的模块在加载以后,将属于内核空间,是内核空间86:87:88:89:90:91:92:93:94:95:96:97:98:99:100:101:102:103:104:105:106:107:108:109:110:111:112:113:114:115:ddd的一部份。在用户空间,你可以以root帐号加载该模块,命令如下:insmodnothing.koinsmod命令用于将模块安装到内核里。并且这个特殊的模块不常要查看模块是否早已安装完成,可以通过查看所有已安装模块来进行:lsmod最后,模块可以通过以下命令从内核中移除:rmmodnothing同样的,使用lsmod命令,可以用于验证该模块已不在内核中。
主要内容整理在如下表格里。设备驱动风波和它们在用户空间,内核空间对应的插口函数。EventsUserfunctionsKernelfunctionsLoadmoduleinsmodOpendeviceReaddeviceWritedeviceClosedeviceRemovemodulermmod“helloworld”驱动:在内核空间加载和移除驱动当一个模块设备驱动被加载到内核时,一些一般要做的事情包括:设备复位,初始化RAM,初始化中断,初始化输入/输出端116:117:118:119:120:121:122:123:124:125:126:127:128:129:130:131:132:133:134:135:136:137:138:139:140:141:142:143:144:145:146:147:148:149:150:151:152:153:154:155:156:157:ddd这种动作在内核空间进行,通过下边将介绍的两个函数进行:module_init和module_exit;它们和用户空间的用于安装和卸载模块的命令insmodrmmod对应。也可以说,用户空间的命令insmod和rmmod使用内核空间的函数module_init和module_exit进我们通过一个最基本的helloworld程序,看实际的事例:hello.c#includelinux/init.h#includelinux/module.h#includelinux/kernel.hMODULE_LICENSE("DualBSD/GPL");staticinthello_init(void)printk("1Helloworld!n");returnstaticvoidhello_exit(void)printk("1Bye,cruelworldn");module_init(hello_init);module_exit(hello_exit);实际的函数hello_init和hello_exit可以用任何其他名称。
并且为了158:159:160:161:162:163:164:165:166:167:168:169:170:171:172:173:174:175:176:177:178:179:180:181:182:ddd使系统才能正确的辨识它们是加载和卸载函数,须要把它们作为module_init和module_exit的参数。以上代码里还包括了printk函数。它和我们十分熟悉的printf函数很相像,只是它只在内核内有效。符号1表示该消息的优先级(数字)。这样就可以通过内核的日志文件里看见该消息,该消息也会在系统控制台北显示。这个模块可以使用和之前那种相同的命令进行编译,其实前提是把它的名子加在Makefile文件里。Makefile2nothing.ohello.o本文中,把写makefile的事情留给读者自行练习。在附表A里,有一个完整的可以编译所有模块的Makefile。当模块被加载或是卸除时,在printk申明里的消息将复印在系统控制台上。假如这个消息没有在控制台上显示,可以通过dmesg命令,或则查看系统的日志文件cat/var/log/syslog命令见到。
填入了两个新函数。设备驱动风波和在内核空间和用户空间之间实现该功能的函数EventsUserfunctionsKernelfunctionsLoadmoduleinsmodmodule_init()OpendeviceReaddeviceWritedeviceClosedeviceRemovemodulermmodmodule_exit()183:184:185:186:187:188:189:190:191:192:193:194:195:196:197:198:199:200:201:202:203:204:205:206:207:208:209:210:211:212:213:214:215:216:217:218:219:220:221:222:223:224:225:ddd如今我开始建立一个完整的设备驱动:memory.c。可以从这个设备读取和写入一个字符。其实这个设备没时么用途,但提供了个挺好的样例,它是一个完整的驱动;很容易实现,由于它不操作实际的硬件设备(它是笔记本内部模拟的硬件)。
要开发驱动,一些在设备驱动中很常见的#include申明,须要首先要加进来:memoryinitialNecessaryincludesdevicedrivers#includelinux/init.h#includelinux/config.h#includelinux/module.h#includelinux/kernel.h#includelinux/slab.h#includelinux/fs.h#includelinux/errno.herrorcodes#includelinux/types.h#includelinux/proc_fs.h#includelinux/fcntl.h#includeasm/system.hcli(),*_flags#includeasm/uaccess.hMODULE_LICENSE("DualBSD/GPL");memory.cfunctionsintmemory_open(structinode*inode,structfile*filp);