版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Linux设备驱动开发设备驱动开发2010.3提纲提纲n 设备驱动概述n 如何编写内核模块n 如何编写设备驱动n 字符设备驱动实例预备知识:设备控制器的功能及接口预备知识:设备控制器的功能及接口n 每一个物理设备都有自己的硬件控制器,每一个硬件控制器都有自己的控制和状态寄存器(CSR),不同的设备是不同的;n 设备控制器的功能:命令寄存器和译码器(接收和识别命令)、数据寄存器(数据交换)、状态寄存器(设备状态的了解和报告)、地址寄存器(地址识别)n 设备控制器与CPU的接口:数据线、地址线、控制线n 设备控制器与设备的接口:数据信号、状态信号、控制信号预备知识:系统对设备的管理预备知识:系统对
2、设备的管理n 设备控制表(DCT):每个设备一张表,表结构如下:设备标识符、设备类型、设备地址或设备号、设备状态、等待队列指针、重复执行的次数和时间、I/O控制器指针n 系统设备表(SDT):整个系统一张表,表结构如下:DCT指针、正在使用设备的进程标识、设备驱动程序入口驱动原理:什么是设备驱动?驱动原理:什么是设备驱动?n 设备控制器的CSR用于启动和停止设备,初始化设备和诊断它的问题n 管理这些硬件控制器的代码不是放在每一个应用程序里边,而是放在Linux内核中。这些处理或管理硬件控制器的软件就叫做设备驱动程序。n 一般来说,一个驱动程序控制一种硬件设备;n Linux核心的设备驱动程序是
3、Linux操作系统的一部分,它运行于核心态。驱动原理:驱动架构驱动原理:驱动架构硬件设备驱动块设备字符设备缓冲区文件子系统进程控制子系统进程调度内存管理调进程间通讯系统调用接口硬件层内核层内核层用户层库函数用户程序驱动原理:内核何时使用驱动程序?驱动原理:内核何时使用驱动程序?n Linux内核使用“设备无关”的I/O子系统来为所有的设备服务。每个设备都提供标准接口给内核,从而尽可能地隐藏了自己的特性。n 用户程序使用一些基本的系统调用从设备读取数据并且将它们存入缓冲。n 每当一个系统调用被使用时,内核就转到相应的设备驱动例程来操纵硬件。 驱动原理:驱动原理: Linux的的I/O子系统子系统
4、驱动原理:驱动原理:Linux设备与文件的关系设备与文件的关系n Linux将所有外部设备看成是一类特殊文件,称之为“设备文件”,如果说系统调用是Linux内核和应用程序之间的接口,那么设备驱动程序则可以看成是Linux内核与外部设备之间的接口。n 设备驱动程序向应用程序屏蔽了硬件在实现上的细节,使得应用程序可以像操作普通文件一样来操作外部设备。驱动原理:设备文件驱动原理:设备文件nLinux抽象了对硬件的处理,使得所有的硬件设备都可以像普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。
5、nLinux系统中的所有硬件设备都使用一个特殊的设备文件来表示,例如,系统中的第一个IDE硬盘在文件系统中使用/dev/hda表示。nLinux通过分配设备号来标识每个设备,每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序,有关主设备号分配见linux/include/linux/major.h 。例如软驱主设备号是2,IDE硬盘的主设备号是3;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。例如,一台PC有两块IDE硬盘,它们的主设备号都是 3,但是第一个硬盘的次设备号为1,另一个次设备号为2。设备文件的主设备号必须与设备驱动程序在登记
6、该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。驱动原理:设备驱动程序的主要功能驱动原理:设备驱动程序的主要功能n 对设备进行初始化;n 使设备投入运行和退出服务;n 从设备接收数据并将它们送回内核;n 将数据从内核送到设备;n 检测和处理设备出现的错误。驱动原理:字符设备和块设备驱动原理:字符设备和块设备n 在Linux操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。n 字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。如鼠标、键盘、串口
7、n 块设备则是利用一块系统内存作为缓冲区,当用户进程对设备进行读写请求时,驱动程序先查看缓冲区中的内容,如果缓冲区中的数据能满足用户的要求就返回相应的数据,否则就调用相应的请求函数来进行实际的I/O操作。如硬盘、光驱等块设备主要是针对磁盘等慢速设备设计的,其目的是避免耗费过多的CPU时间来等待操作的完成。驱动原理:设备文件与驱动程序的关系驱动原理:设备文件与驱动程序的关系n 所有已经注册(即已经加载了驱动程序)的硬件设备的主设备号可以从/proc/devices文件中得到。n 使用mknod命令可以创建指定类型的设备文件,同时为其分配相应的主设备号和次设备号。mknod /dev/device
8、_name device_type major_number minor_number其中:device_name是此设备的文件device_type是此设备的类型,c表示字符设备,b表示块设备例如,建立一个主设备号为6,次设备号为0的字符设备文件/dev/lp0 :# mknod /dev/lp0 c 6 0 n 当应用程序对某个设备文件进行系统调用时,Linux内核会根据该设备文件的设备类型和主设备号调用相应的驱动程序,并从用户态进入到核心态,再由驱动程序判断该设备的次设备号,最终完成对相应硬件的操作。驱动机制:设备管理驱动机制:设备管理n 1、设备管理:系统对已经登记的字符设备的管理是由
9、chrdevs来管理的。n Linux对设备进行访问时,访问文件系统中相应的设备文件,通过文件系统和文件的属性描述块,可以找到该文件系统或设备对应的设备号。在实际访问列表时,以chrdevsMAJORMINOR 形式访问。static struct char_device_struct struct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct;const char *name;struct *fops;struct cdev *cdev;/* will die */ *chrdev
10、sMAX_PROBE_HASH;驱动机制:驱动机制:I/O请求管理请求管理n 2、I/O请求管理:系统会把一部分系统内存作为块设备驱动程序与文件系统接口之间的一层缓冲区,每个缓冲区与某台设备中的特定区域相联系,文件系统首先试图查找相应的缓冲区,如未找到才向该设备发出I/O读写请求,由设备驱动程序对这些请求进行处理。驱动机制:中断请求驱动机制:中断请求n 3、中断请求:设备进行实际的输入/输出时,如果时间过长而始终占用CPU,就会影响系统的效率,所以Linux采用中断的机制。驱动机制:中断与轮询驱动机制:中断与轮询n设备被执行某个命令时,如“将读取磁头移动到软盘的第42扇区上”,设备驱动可以从轮
11、询方式和中断方式中选择一种以判断设备是否已经完成此命令。n轮询方式意味着需要经常读取设备的状态,一直到设备状态表明请求已经完成为止。如果设备驱动被连接进入内核,这时使用轮询方式将会带来灾难性后果:内核将在此过程中无所事事,直到设备完成此请求。但是轮询设备驱动可以通过使用系统定时器,使核心周期性调用设备驱动中的某个例程来检查设备状态。 定时器过程可以检查命令状态及Linux软盘驱动的工作情况。使用定时器是轮询方式中最好的一种,但更有效的方法是使用中断。 n基于中断的设备驱动会在它所控制的硬件设备需要服务时引发一个硬件中断。如以太网设备驱动从网络上接收到一个以太数据报时都将引起中断。Linux内核
12、需要将来自硬件设备的中断传递到相应的设备驱动。这个过程由设备驱动向内核注册其使用的中断来协助完成。此中断处理例程的地址和中断号都将被记录下来。在/proc/interrupts文件中你可以看到设备驱动所对应的中断号及类型。驱动机制:高速缓冲驱动机制:高速缓冲n 4、高速缓冲区:为了加速物理设备的访问速度,Linux将块缓冲区放在cache内,块缓冲区是由buff_head 连成的链表结构。struct buff_head unsigned long b_blocknr 逻辑块号unsigned long b_size 块大小kdev_t b_dev 虚拟设备标志符kdev_t b_rdev 实
13、际设备标志符unsigned long b_state 缓冲区状态标志char * b_data 指向缓冲区的指针unsigned long b_rsector 实际设备上的块号.驱动机制:驱动机制: DMAn 有时设备与系统要进行大批量的数据传输,采用中断的方式会给CPU带来很大的负荷,降低了系统的效率。n DMA操作是外设直接与内存进行数据传输,而无须CPU的干预。如硬盘数据传输、网卡数据传输等。n DMA控制器没有任何虚拟内存的概念,它只存取系统中的物理内存,不能在进程虚拟地址空间内直接使用DMA。驱动接口:设备驱动程序与外界的接口驱动接口:设备驱动程序与外界的接口n 每种类型的驱动程序
14、,不管是字符设备还是块设备都为内核提供相同的调用接口,因此内核能以相同的方式处理不同的设备。Linux为每种不同类型的设备驱动程序维护相应的数据结构,以便定义统一的接口并实现驱动程序的可装载性和动态性,n Linux设备驱动程序与外界的接口分为3部分:驱动程序与内核的接口,这是通过数据结构来完成的;驱动程序与系统引导的接口,这部分利用驱动程序对设备进行初始化;设备驱动程序与设备的接口,这部分描述了驱动程序如何与设备进行交互,这与具体设备密切相关;它们之间的关系如下页图所示:驱动接口:驱动程序与外界的接口驱动接口:驱动程序与外界的接口 设备驱动程序设备驱动程序 操作系统内核操作系统内核 接口 数
15、据结构数据结构 实现实现 系统引导系统引导 接口 各设备各设备 初始化初始化 具体设备具体设备 接口 进行交互进行交互 驱动程序与设备间驱动程序与设备间 图 设备驱动程序与外界的接口 设备驱动程序的特点设备驱动程序的特点(1)内核代码:设备驱动程序是内核的一部分,如果驱动程序出错,则可能导致系统崩溃。 (2)内核接口:设备驱动程序必须为内核或者其子系统提供一个标准接口。比如,一个终端驱动程序必须为内核提供一个文件I/O接口;一个SCSI设备驱动程序应该为SCSI子系统提供一个SCSI设备接口,同时SCSI子系统也必须为内核提供文件的I/O接口及缓冲区。 (3)内核机制和服务:设备驱动程序使用一
16、些标准的内核服务,如内存分配等。 (4)可装载:大多数的Linux操作系统设备驱动程序都可以在需要时装载进内核,在不需要时从内核中卸载。 (5)可设置:Linux 操作系统设备驱动程序可以集成为内核的一部分,并可以根据需要把其中的某一部分集成到内核中,这只需要在系统编译时进行相应的设置即可。 (6)动态性:在系统启动且各个设备驱动程序初始化后,驱动程序将维护其控制的设备。如果该设备驱动程序控制的设备不存在也不影响系统的运行,那么此时的设备驱动程序只是多占用了一点系统内存罢了思考思考n 设备驱动与文件的关系?提纲提纲n 设备驱动概述n 如何编写内核模块n 如何编写设备驱动n 字符设备驱动实例内核
17、模块内核模块n在开始编写设备驱动之前,先需要了解内核模块的概念。Linux内核是一个整体的结构,因此向内核添加任何东西或者删除某些功能,都十分困难。为了解决这个问题,Linux引入了内核模块机制,从而可以动态地在内核中添加或者删除模块nLinux内核中采用可加载的模块化设计 (LKMs, Loadable Kernel Modules) ,一般情况下编译的 Linux 内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其他的代码可以选择在内核中,或者编译为内核的模块文件。 n常见的驱动程序也是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而 Linux最基础的驱动,如CPU、
18、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则直接编译在内核文件中。有时也把内核模块叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如 ext3 文件系统的驱动。因此,加载驱动时就是加载内核模块。n模块不被编译在内核中,因而控制了内核的大小。然而模块一旦被插入内核,它就和内核其他部分一样。这样一来就会增加一部分系统开销。同时,如果模块出现问题,也许会带来系统崩溃。所以在进行模块开发时,要特别注意与应用程序开发的区别。n 常用的模块命令如下:lsmod显示当前系统已经加载的模块rmmod是用于将当前模块卸载。insmod 和 modprobe 是用于加载当前模块但 i
19、nsmod 不会自动解决依存关系而modprobe则可以根据模块间依存关系以及/etc/modules.conf文件中的内容自动插入模块。n mknod是用于创建设备节点。如果需要加载的模块是一个设备驱动模块,它占用一个主设备号,那么在加载此模块前,必须先为它在文件系统中建立好相应的设备节点 。n 例如:为设备驱动模块test.ko建立字符设备节点test,其主设备号为254,次设备为0n mknod /dev/test c 254 0举例:简单的内核模块举例:简单的内核模块下面举一个简单的内核模块例子,以此引出内核的机制和框架:/* hello.c */#include #include #
20、include static int hello_init(void)printk(KERN_ALERT Hello, worldn);return 0;static void hello_exit(void)printk(KERN_ALERT Goodbye, cruel worldn);module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE(GPL);编译模块编译模块n 下面是一个典型的2.6内核下模块的Makefile#Make hello.c file#KDIR:=/lib/modules/$(shell uname
21、 -r)/buildobj-m:=hello.odefault: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modulesclean: $(RM) .*.cmd *.mod.c *.o *.ko -r .tmp*n 执行make即可生成hello.ko测试模块测试模块1、以root用户登录,运行tail -f /var/log/messages2、在另外一个终端以root用户登录,装载模块insmod ./hello.ko3、这时会在/var/log/messages看到如下信息:Mar 25 16:50:51 wpon kernel: Hello, world4、卸
22、载模块Now remove the module:rmmod hello5、这时会在/var/log/messages看到如下信息:Mar 25 16:51:08 wpon kernel: Goodbye, cruel world模块机制模块机制n从上面的例子可以看出,加载模块的方式与以往的应用程序开发有很大的不同。以往在开发应用程序时都有一个 main函数作程序的入口点,而在模块开发时却没有 main 函数,模块在调用 insmod命令时被加载,此时的入口点是module_init函数。同样,模块在调用 rmmod函数时被卸载,此时的出口点是module_exit函数。模块初始化时执行mod
23、ule_init入口点模块不再使用时(手工或关机时)执行module_exit出口点编写模块的规则编写模块的规则n 不能访问C库如 printf(), strcat(),只能使用内核提供的头文件n 内核有自己的C功能函数,如类似于printf()的printk()n 只能使用内核提供的头文件n 不要使用浮点数n 声明变量为static的,防止命名空间污染模块调试技术模块调试技术n 内核对调试的支持:配置内核时make menuconfig注意选择debug,编程时CONFIG_DEBUG_INFO等宏的引用n printk及信息显示级别n /proc系统提供的信息printkn就如同在编写用户
24、空间的应用程序,打印信息有时是很好的调试手段,也是在代码中很常用的组成部分。但是与用户空间不同,在内核空间要用函数 printk 而不能用平常的函数printf。printk和 printf很类似,都可以按照一定的格式打印消息,所不同的是,printk还可以定义打印消息的优先级。这些不同优先级的信息可以输出到控制台上、/var/log/messages里。其中,对输出给控制台的信息有一个特定的优先级console_loglevel。若优先级小于这个整数值时,则消息才能显示到控制台上,否则,消息会显示在/var/log/messages 里。若不加任何优先级选项,则消息默认输出到/var/log
25、/messages文件中。 printkn include/linux/kernel.h下对不同日志级别的定义#define KERN_EMERG /* system is unusable */#define KERN_ALERT /* action must be taken immediately */#define KERN_CRIT /* critical conditions */#define KERN_ERR /* error conditions */#define KERN_WARNING /* warning conditions */#define KERN_NOTIC
26、E /* normal but significant condition */#define KERN_INFO /* informational */#define KERN_DEBUG /* debug-level messages */n 优先级console_loglevel被指定为18之间的整数值。如果设定为1,则只有级别为0的信息才能到达控制台。如果设定为8,则包括调试信息在内的所有消息都显示出来/procn /proc 文件系统是一个伪文件系统,它是一种内核和内核模块用来向进程发送信息的机制。这个伪文件系统让用户可以和内核内部数据结构进行交互,获取有关进程的有用信息,在运行时通
27、过改变内核参数改变设置。与其他文件系统不同,/proc存在于内存之中而不是硬盘上,在加载模块成功后,可以使用查看/proc/device文件获得相关设备的主设备号。 思考思考n 内核何时调用模块?lsmod和/proc/modules当内核需要的某功能不存在时通过运行modprobe调入相应模块,modprobe运行前检查依赖关系/lib/modules/$(shell uname -r)/modules.depinsmodrmmodn 模块是如何工作的?通过module_init告诉内核本模块提供的操作函数当内核需要操作模块时则调用模块提供的函数模块不再使用时执行module_exit,手工
28、或关机时提纲提纲n 设备驱动概述n 如何编写内核模块n 如何编写设备驱动n 字符设备驱动实例设备驱动编写流程设备驱动编写流程 n在上一节中已经提到,模块在调用 insmod命令时被加载,此时的入口点是module_init函数,通常在该函数中完成设备的注册。同样,模块在调用 rmmod函数时被卸载,此时的入口点是module_exit函数,在该函数中完成设备的卸载。在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,如read、write等,而驱动程序就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作。设备驱动程序流程图如下所示:驱动接口:向内核提供的设备操作
29、接口驱动接口:向内核提供的设备操作接口nLinux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过include/linux/fs.h中的数据结构来完成的:struct loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); unsigned int (*poll) (struct fi
30、le *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); ; n这个结构的每一个成员的名字都对应着一个系统调用.用户进程利用系统调用在对设备文件
31、进行如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数.例如,当应用程序对设备文件执行读操作时,内核将调用结构中的read函数。驱动结构:驱动程序的结构驱动结构:驱动程序的结构n Linux的设备驱动程序大致可以分为如下几个部分: 驱动程序的注册与注销 设备的打开与释放 设备的读写操作 设备的控制操作 设备的中断和轮询处理驱动结构:驱动程序的注册与注销驱动结构:驱动程序的注册与注销 n旧方式:向系统增加一个驱动程序意味着要赋予它一个主设备号,这可以通过在驱动程序的初始化过程中调用register_chr
32、dev( )或者register_blkdev( )来完成。而在关闭字符设备或者块设备时,则需要通过调用unregister_chrdev( )或unregister_blkdev( )从内核中注销设备,同时释放占用的主设备号。 n新方式:#include n注册:void cdev_init(struct cdev *dev,struct *fops);int cdev_add(struct cdev *dev,dev_t num,unsigned int count);n注销:void cdev_del(struct cdev *dev);驱动结构:设备的打开与释放驱动结构:设备的打开与释
33、放 n 打开设备是通过调用结构中的函数open( )来完成的,它是驱动程序用来为今后的操作完成初始化准备工作的。在大部分驱动程序中,open( )通常需要完成下列工作:1.检查设备相关错误,如设备尚未准备好等。2.如果是第一次打开,则初始化硬件设备。3.识别次设备号,如果有必要则更新读写操作的当前位置指针f_ops。4.分配和填写要放在file-private_data里的数据结构。n 释放设备是通过调用结构中的函数release( )来完成的,这个设备方法有时也被称为close( ),它的作用正好与open( )相反,通常要完成下列工作: 1.释放在file-private_data中分配的
34、内存。2.关闭设备。驱动结构:设备的读写操作驱动结构:设备的读写操作 n 字符设备的读写操作相对比较简单,直接使用函数read( )和write( )。n 如果是块设备,则需要调用函数block_read( )和block_write( )来进行数据读写,这两个函数将向设备请求表中增加读写请求,以便Linux内核可以对请求顺序进行优化。由于是对内存缓冲区而不是直接对设备进行操作的,因此能很大程度上加快读写速度。如果内存缓冲区中没有所要读入的数据,或者需要执行写操作将数据写入设备,那么就要执行真正的数据传输,这是通过调用数据结构blk_dev_struct中的函数request_fn( )来完成
35、的。 nssize_t read(struct file *filp,char _user *buff,size_t count, loff_t *offp);nssize_t write(struct file *filp,const char _user *buff,size_t count, loff_t *offp);驱动结构:设备的读写操作注意驱动结构:设备的读写操作注意n在read和write参数里的_user表明,这个缓冲是用户空间的指针,因此它不能直接被内核代码复引用。原因如下:在内核执行的过程中,用户空间的指针也许还是无效的(如:还没有给这个地址映射,或者它指向别的空间,或者
36、是随机的数据);即使这个指针指的就是内核空间的同一空间,但是用户空间是基于页的,而在这里讨论的这个内存空间在系统调用执行的时候也许还不在RAM中。直接引用用户空间的内存,会产生缺页错误;用户空间提供的这个指针也许会有错误,也许是有恶意的。这样如果驱动程序盲目地引用用户空间的指针,就会给应用程序提供一个访问内核空间的后门。n因此,驱动程序需要使用内核提供的特别函数控制这些对指针的访问,以完成任务,这可以通过提供的内核函数,在这里就要使用 copy_to_user 或copy_from_user 函数,它们就是用来实现用户空间和内核空间的数据交换的驱动结构:设备的控制操作驱动结构:设备的控制操作
37、n 除了读写操作外,应用程序有时还需要对设备进行控制,这可以通过设备驱动程序中的函数ioctl( )来完成。n ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。n ioctl( )的用法与具体设备密切关联,因此需要根据设备的实际情况进行具体分析。n 设备驱动程序的ioctl函数:int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); 命令号:类型、序号、方向、大小
38、。include/asm/ioctl.hDocumentation/ioctl-number.txt驱动结构:用户对设备的控制操作驱动结构:用户对设备的控制操作n 在用户空间内调用的ioctl函数:int ioctl(int fd, unsigned long cmd, .);n fd就是用户程序打开设备时使用open函数返回的文件标识符,cmd就是用户程序对设备的控制命令,后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。驱动模块:驱动模块说明驱动模块:驱动模块说明n Linux下的设备驱动程序可以按照两种方式进行编译,一种是直接静态编译成内核的一部分,另一种则是
39、编译成可以动态加载的模块。如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态地卸载,不利于调试,所以推荐使用模块方式。n 从本质上来讲,模块也是内核的一部分,它不同于普通的应用程序,不能调用位于用户态下的C或者C+库函数,而只能调用Linux内核提供的函数,在/proc/kallsyms中可以查看到内核提供的所有函数。驱动模块:模块化驱动的编写说明驱动模块:模块化驱动的编写说明n 在以模块方式编写驱动程序时,要实现两个必不可少的函数module_init ( )和module_exit( ),而且至少要包含两个头文件(、 )。n 编译生成的模块(一般为.ko文件)可以使用
40、命令insmod载入Linux内核,从而成为内核的一个组成部分,此时内核会调用模块中的函数module_init ( )。当不再需要该模块时,可以使用rmmod命令进行卸载,此时内核会调用模块中的函数module_exit( )。任何时候都可以使用命令lsmod查看目前已经加载的模块以及正在使用该模块的用户数。 思考思考n register_chrdev ()与misc_register ()有何联系与区别?misc_register ()用来注册其他类型设备的,它的主设备是10调用register_chrdev(),设备名和函数指针通过miscdevice结构获得,同时结构体还保存设备的次设
41、备号提纲提纲n 设备驱动概述n 如何编写内核模块n 如何编写设备驱动n 字符设备驱动实例基于字符设备的基于字符设备的LinuxLinux驱动程序设计实例驱动程序设计实例n 实验目的掌握Linux设备模块驱动开发的方法掌握Linux字符设备驱动的开发n 实验内容调试一个简单字符设备驱动程序用提供的应用程序测试驱动程序n 下面举一个虚拟的字符驱动程序举例基于内存拷贝的简单字符驱动程序设计。实验步骤实验步骤n编写字符驱动程序(test.c); 确定设备名称与主设备号根据需要编写设备文件对内核上层的接口,比如open, release, read, write, ioctl等(test.c);n写Ma
42、kefile,编译生成模块目标文件(test.ko)n用insmod将动态加载驱动模块n在目录/dev下用mknod命令建立相应的设备文件n在用户态下编写应用程序测试,使用该设备驱动 (mytest.c)n编译运行测试用例验证驱动正确性字符驱动程序代码字符驱动程序代码test.c#include #include #include #include #include #include #include #include #include #include #include MODULE_LICENSE(Dual BSD/GPL);#define DEVICE_NAME teststatic u
43、nsigned int test_major = 0;字符驱动程序代码字符驱动程序代码test.cstatic int read_test(struct file * *buf,size_t count,loff_t *ppos) int left; if (access_ok(VERIFY_WRITE,buf,count) = -EFAULT ) return -EFAULT; for(left = count ; left 0 ; left-) _put_user(1,buf); buf+; return count;static int write_test(struct file * *buf,size_t count,loff_t *ppos) return count;将获得的数据送到用户空间中检查用户空间中的内存块是否可用字符驱动程序代码字符驱动程序代码test.cstatic int open_test(struct inode * inode , struct file
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 学生读书会管理制度
- 2024年修订版:离婚协议的附加条款
- 2024年化工原料及制品出口合同
- 2023-2024学年四年级下学期数学第三单元买文具(学案)
- 中班语言公开课教案及教学反思《云彩和风儿》
- 4 认识图形(一)(教案) 一年级上册数学 人教版
- 大班健康教案详案《让自己高兴》
- 一年级下册数学教案-第6单元100以内的加法和减法(一)第2课时 两位数加一位数(不进位)、整十数|人教新课标
- 2023-2024学年四年级下学期数学6.生活中的负数 教案
- 2024年农产品定期购销合同
- 病历用药点评工作表
- 住院患者健康教育检查表
- (完整版)遴选答题纸(A4纸直接打印)
- IMD(模内转印)工艺详解
- 循证教学评价:数智化时代下高校教师教学评价的新取向
- 《各种管道的护理》PPT课件.ppt
- HLA-LJ-185混流式水轮机导水机构安装作业指导书2
- 220千伏变电站现场运行通用规程
- 海绵城市建设难点与对策
- 绿化工程竣工资料(全套)
- 中班语言:我眼中的秋天
评论
0/150
提交评论