linux驱动程序设计入门.pdf

所需积分/C币:9 2012-09-22 11:09:04 651KB PDF

linux驱动程序设计入门.pdf
Edited by Foxit Reader Copyright(C)by Foxit Software Company, 2005-2008 For Evaluation Only (2)编译成一个可加载和删除的模块,使用 insmod加载( modprobe和 insmod命令类 似,但依赖于相关的配置文件),mmod删除。这种方式控制了内核的大小,而模块一旦 被插入内核,它就和内核其他部分一样。 下面我们给出一个内核模块的例子 include< linux/ 'module h>〃所有模块都需要的头文件 # include< clinux/ init. h>/init&exit相关宏 MODULE LICENSEC"GPL") static int init hello init( void) printk("Ilello module init\n") return 0: static void exit hello exit(void) printk("Hello module exitn"); module init(hello init) module exit(hello exit 分析上述程序,发现一个Liuⅸx内核模块需包含模块初始化和模块卸载函数,前者在 insmod的时候运行,后者在 rmmod的时候运行。初始化与卸载函数必须在宏 module init 和 module exit使用前定义,否则会出现编详错误。 程序中的 MODULE LICENSE("GPL")用于声明模块的许可证。 如果要把上述程序编详为一个运行时加载和删除的模块,则编详命令为: gcc-D KERNEL-DMODULE-DLINUX-I/usr/local/src/linux2. 4/includc-c-o hello o hello.c 由此可见, Linux内核模块的编译需要给gcc指示- KERNEL_- DMODULE DLINUX参数。工选项跟着 Linux内核源代码中 Include目录的路径 下列命令将可加载helo模块: insmod /hello. o Edited by Foxit Reader Copyright(C)by Foxit Software Company, 2005-2008 For Evaluation Only 下列命令完成相反过程: rmmod hello 如果要将其直接编译入 Linux内核,则需要将源代码文件拷叭入 Linux内核源代码的相 应路径里,并修改 Makefile 我们有必要补充一下 Linux内核编程的一些基本知识 内存 在 Linux内核模式下,我们不能使用用户态的 malloc0和freO函数申请和释放内存。进 行内核编程时,最常用的内存申请和释放函数为在 include/inux/ kernel文件中声明的 kmalloco和keO,其原型为: void*kmalloc(unsigned int len, int priority ) void kfree(void* ptr) kmalloc的 priority参数通常设置为 GFP KERNEL,如果在中断服务程序里申请内存则 要用 GFP ATOMIC参数,因为使用 GFP KERNEL参数可能会引起睡眠,不能用于非进程 上下文中(在中断中是不允许睡眠的)。 由于内核态和用户态使用不同的内存定义,所以二者之间不能直接访问对方的内存。而 应该使用 Linux中的用户和内核态内存交互函数(这些函数在 include/asm/ access. h中被声 明) unsigned long copy from user(void*to, const void*from, unsigned long n); unsigned long copy to user(void to, void from, unsigned long len) copy from user、 copy to user函数返回不能被复制的字节数,因此,如果完全复制成 功,返回值为0。 include/asm/ access.h中定义的 put user和 get user用于内核空间和用户空间的单值交互 (女char、int、long) 这里给出的仅仅是关于内核中内存管理的皮毛,关于 Linux内存管理的更多细节知识, 我们会在本文第9节《内存与IO操作》进行更加深入地介绍 输出 Edited by Foxit Reader Copyright(C)by Foxit Software Company, 2005-2008 For Evaluation Only 在内核编程中,我们不能使用用户态C库函数中的 printi0函数输岀信息,而只能使用 printed。但是,内核中 printed函数的设计月的并不是为了和用户交流,它实际上是内核的 种日志机制,用来记录下日志信息或者给出警告提示 每个pntk都会有个优先级,内核共有8个优先级,它们都有对应的宏定义。如果未 指定优先级,内核会选拌默认的优先级 DEFAULT MESSAGE LOGLEVEL。如果优先级数 字比 int console loglevc变量小的话,消息就会打印到控制台上。如果 syslogd和 klogd守护 进程在运行的话,则不管是否向控制台输岀,消息都会被追加进/ar/ log/messages文件。 klogd 只处理内核消息, syslogd处理其他系统消息,比如应用稈序。 模块参数 24内核下, include/linux/module h中定义的宏 MODULE PARM( var, type)用于向模块 传递命令行参数。var为接受参数值的变量名,type为采取如下格式的字符串 min-max]]{b,h,i,s}amin及max用于表示当参数为数组类型时,允许输入的数组元素的个 数范围;b:byte;h: short;i;int;1;long;s: string 在裝载内核模块吋,用户可以向模块传递一些参数: insmod modname var=value 如果用户未指定参数,var将使用模块内定义的缺省值。 深入浅出 Linux设备驱动之字符设备驱动程序 Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得 Windows的设各操作犹如文件般。在应用程序看来,硬件设备只是一个设备文件,应用 程序可以象捰作普通文件一样对使件设备进行操作,如open(、 close()、 read o、 write o等。 Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符 的形式进行:而块设备则以整个数据缓冲区的形式进行。字符设备的驱动相对比较简单 卜面我们来假设一个非常简单的虚拟字符设备:这个设备中只有一个4个字节的全局变 量 int global var,而这个设备的名字叫做" gobalvar"。对" gobalvar"设备的读写等操作即是对 其中全局变量 global var的操作。 Edited by Foxit Reader Copyright(C)by Foxit Software Company, 2005-2008 For Evaluation Only 驱动稈序是内核的一部分,因此我们需要给其添加模块初始化函数,该函数用来完成对 所控设备的初始化工作,并调用 register chrdevO函数注册字符设备: static int alvar init(void) if(register chrdev(MAjOr NUM,"gobalvar"&gobalvar fops)) 注册失败 1.注册成功 其中, register chrdev函数中的参数 MAJOR NUM为主设备号," gobalvar"为设各名 gobalvar fops为包含基本函数入口点的结构体,类型为 file operations。当 gobalvar模块被 加载时, gobalvar init被执行,它将调用内核函数 register chrdev,把驱动程序的基本入口点 指针存放在内核的字符设备地址表中,在用户进程对该设备执行系统调用时提供入口地址 与模块初始化函数对应的就是模块卸载函效,需要调用 register chrdeve的"反函数” unregister chrdcv(: static void exit gobalvar exit(void) if (unregister chrdev(MA OR NUM, " gobalvar ) 1.卸载失败 els 卸载成功 随着内核不断増加新的功能, Hle operations结构体已逐渐变得越来越大,但是大多数 的驱动程序只是利用了其中的一部分。对于字符设各来说,要提供的主要入口有: open o release O、 read o、 write o、 ioctl、 nIseko、pol(O等。 open()函数对设备特殊文件进行open(系统调用时,将调用驱动程序的open(函数: Edited by Foxit Reader Copyright(C)by Foxit Software Company, 2005-2008 For Evaluation Only int (open)(struct inode struct file") 其中参数 inode为设备特殊文件的 inode(索引结点)结构的指针,参数fle是指向这 设备的文件结构的指针。open(O)的主要任务是确定硬件处在就绪状态、验证次设备号的合法 性(次设备号可以用 MINOR( inode->i-rdeν)取得)、控制使用设备的进程数、根据执行情况 返回状态码(0表示成功,负数表示存在错误)等 release函数当最后一个打开设备的用户进程执行 close o系统调用时,内核将调用驱 动程序的 release0函数: void (release)(struct inode *, struct file * release函数的主要任务是清理未结束的输入/输岀操作、释敚资源、用户自定义排他标 志的复位等。 reado函数当对设备特殊文件进行 reado系统调用时,将调用驱动程序 reado函数: sSizc t rcad)(struct filc char sizc t, loff t ); 用米从设备中读取数据。当该函数指针被赋为NULL值时,将导致read系统调用出错 并返回- EINVAL(" Invalid argument,非法参数")。函数返回非负值表示成功读取的字节数 (返回值为" signed size"数据类型,通常就是目标半台上的固有整数类型)。 globalvar read函数中内核空间与用户空间的内存交互需要借助第2节所介绍的函数: static ssize t globalvar read(struct file*filp, char buf, size t len, loff t*off copy to user(buf, &global var, sizeof(int)) write()函数当设备特殊文件进行 write o系统调用时,将调用驱动栏序的 write o函 ssize t(write)(struct file const char size t, loff t*); 向设备发送数据。如果没有这个函数, write系统调用会向调用程序返回一个- EINVAL。 Edited by Foxit Reader Copyright(C)by Foxit Software Company, 2005-2008 For Evaluation Only 如果返回值非负,则表示成功写入的字节数。 globalvar write函数中内核空间与用户空间的内存交互需要借助第2节所介绍的函数: static ssize t globalvar write(struct file filp, const char buf, size t len, loff t * off) copy from user(&global var, buf, sizeof(int)) ioctl函数该函数是特殊的控制函数,可以通过它向设备传递控制信息或从改备取得 状态信息,函数原型为: int ( *ioctl)(struct inode + struct file unsigned int, unsigned long); unsigned int参数为设备驱动程序耍执行的命令的代码,由用户自定义, unsigned long 参数为相应的命令提供参数,类型可以是整型、指针等。如果设备不提供 ioctl入口点,则 对于任何内核未预先定义的请求, ioctl系统调用将返冋错误(- ENOTTY," No such ioctl fordevice,该设备无此 ioctl命令")。如果该设备方法返回一个非负值,那么该值会被返回 给调用程序以衣示调用成功。 nIseko函数该函数用来修改文件的当前读写位置,并将新位置作为(正的)返回值返 回,原型为 loff t(llseek)(struct file loff t, int); pol(O函数poll方法是poll和 select这两个系统调用的后端实现,用来查洵设备是否 可读或可写,或是否处于某种特殊状态,原型为 unsigned int ("poll)(struct file struct poll table struct * 我们将在"设备的阻塞与非阻塞操作”一节对该函数进行更深入的介绍 设备" gobalvar"的驱动稈序的这些函数应分别命名为 gobalvar open、 gobalvar release gobalvar read、 gobalvar write、 gobalvar ioctl,因此设备" gobalvar"的基本入口点结构变量 gobalvar fops赋值如下 struct file operations gobalvar fops=( read: gobalvar read write: gobalvar write Edited by Foxit Reader Copyright(C)by Foxit Software Company, 2005-2008 For Evaluation Only 上述代码中对 gobalvar fops的初始化方法并不是标准C所支持的,属于GNU扩展语 完整的 globalvar.c文件源代码如下 #include <linux/module h include <linux/init. h> include <linux/fs h #include <asm/uaccessh> MODULE LICENSECGPL") # define major num254主设备号 static ssize t globalvar read(struct file char, size t, loff t) static ssize t globalvar write(struct file const char size t, loff t*) 初始化字符设备驱动的 file operations结构体 Ict filc opcrations globaly read: globalvar read, write: globalvar w static int global var=0;/" globalvar"设备的全局变量 static int init globalvar init(void) ∥注册设备驱动 ret=register chrdev(MA IOR NUM,"gle &globalvar fops ); printk("globalvar register failure"); else printk(globalvar register success ) static void exit globaly Int ret: ∥注销设备驱动 ret=unregister chrdev (MA OR NUM, globalvar if (ret) printk(globalvar unregister failure") printk(" globalvar unregister success"); static ssize t globalvar read( struct file filp, char * buf, size t len, loff t*off) /将 global var从内核空间复制到用户空间 if (copy to user(buf, &global var, sizeof(int))) return-EFAULT return sizeof(int) static ssize t globalvar write(struct file *filp, const char *buf, size t len, loff t *off 将用户空间的数据复制到内核空间的 global var if(copy from user(&global var, buf, sizeof(int))) turn- EFAULt return sizeof(int) odule init(global module exit(globalvar exit) 运行: gcc-D KERNEL -DMODULE-DLINUX-I/usr/local/src/linux 2.4 include -c-o globalvaro globalvar.c 编译代码,运行

...展开详情
img
hermitbin

关注 私信 TA的资源

上传资源赚积分,得勋章
最新资源