欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > PPT文档下载  

    第9章 Linux设备管理.ppt

    • 资源ID:2976530       资源大小:421KB        全文页数:78页
    • 资源格式: PPT        下载积分:8金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要8金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    第9章 Linux设备管理.ppt

    第9章 设备管理与模块机制,Linux设备分类与识别设备转换表设备驱动程序设备注册与注销块设备的请求队列模块机制,Linux的设备管理,在 Linux 系统中,用户通过文件系统与设备接口。利用标准的系统调用可在设备上进行打开、关闭、读取或写入操作。当用户进程发出I/O请求时,系统将请求处理的权限放在文件系统,文件系统通过驱动程序提供的接口将任务下放到驱动程序。驱动程序根据需要,对设备控制器进行操作,设备控制器再去控制设备本身进行I/O操作。驱动程序向文件系统提供的接口屏蔽了设备的物理特性。,用户进程,硬件设备本身,设备控制器,设备驱动程序,文件系统中的设备文件,进程请求设备服务的流程,在内核中设备文件和普通文件的索引节点有区别,可以确认出是硬件设备还是普通文件,确定要访问相应的设备驱动程序还是文件。,Linux系统的设备管理,每个设备都对应文件系统中的一个VFS inode,都有一个设备文件名。在设备文件对应的VFS inode中,i_mode域的值标明了它的类型:S_IFBLK:表示它是一个块设备;S_IFCHR:表示它是一个字符设备;同时,设备文件对应的VFS inode中没有块列表,但却有一个名为i_rdev的域,其中存储着它所标识设备的主、次设备号。内核可以根据该域将设备文件名转化为设备的主、次设备号。,Linux系统的设备管理,每个设备都对应文件系统中的一个索引节点,都有一个设备文件名。应用程序通常可以通过系统调用open()打开设备文件,建立起与目标设备的连接。对设备的使用类似于对文件的存取。设备驱动程序都是系统内核的一部分,它们必须为系统内核或者它们的子系统提供一个标准的接口。设备驱动程序也使用一些标准的内核服务,如内存分配等,设备的分类,字符设备(c)所有能够象字节流一样访问的设备都通过字符设备来实现,通常在/dev/目录下面无需缓冲直接读写设备,通常只允许按顺序访问。如打印机、键盘,终端等。一般要包含open,read,write,close等系统调用的实现块设备(b)通常是指诸如磁盘、内存、Flash等可以容纳文件系统的存储设备。将数据按可寻址的块为单位进行处理,可以随机访问,利用缓冲技术块设备也是通过文件系统来访问网络接口设备通常它指的是硬件设备,但有时也可能是一个软件设备(如回环接口loopback),它们由内核中网络子系统驱动,负责发送和接收数据包。它们的数据传送往往不是面向流的,因此很难将它们映射到一个文件系统的节点上。查看系统中的设备:/proc/devices。查看设备及其类型“ls-l/dev”,主设备号和次设备号,主设备号和次设备号主设备号和次设备号能够唯一地标识一个设备128(V2.0以前),256(V2.0以后)主设备号相同的设备使用相同的驱动程序,次设备号用于区分具体设备的实例动态获取主设备号设备文件 Linux使用设备文件来统一对设备的访问接口,将设备文件放在/dev/目录下设备的命名一般为设备文件名+数字或者字母表示的子类,例如/dev/hda1,/dev/hda2等,/dev/hda 代表系统中的第一个IDE 硬盘,/dev/hdb 代表系统中的第二个 IDE 硬盘;hda1代表第1个分区,hda2代表第2个分区,注意:字符设备与块设备具有独立的编号,例如,块设备(3,0)不同于字符设备(3,0)设备文件通常可以表示一个硬件设备,例如磁盘/dev/hda或硬件设备的某一物理或逻辑分区,例如磁盘分区/dev/hda2或一个虚拟的逻辑设备(不会与任何硬件设备相关联),例如/dev/null代表一个“黑洞”对内核而言,一个设备文件的名字是无关紧要的,关键在于设备文件的类型及其主次设备号,如果一个系统含有两个具有相同主设备号和次设备号的设备文件,而且都是字符设备。当有两个进程想要同时打开这个字符设备时,会发生什么情况?关闭这个设备时又会发生什么情况?当有两个进程想要同时打开这个字符设备时,它将在各自的文件表中增加一次引用计数值。关闭这个设备时可能会发生导致对这个设备是一个无法辨认的状态。从而使这个设备变成一个无用的设备。,设备文件通常包含在/dev目录中一些设备文件的例子,Devfs设备文件,使用主、次设备号标识设备存在局限性在/dev中的大多数设备是不存在的8位长的主次设备号不够用不容易记忆上述原因以及其他的一些因素综合起来,促使了devfs设备文件的产生Linux 2.4以后引入了设备文件系统(devfs)的概念,所有的设备文件作为一个可以挂装的文件系统,这样就可以被文件系统统一管理,从而设备文件就可以挂装到任何需要的地方。一般将主设备建立一个目录,再将具体的子设备文件建立在此目录下。例如,/dev/mtdblock0,Devfs设备文件,有了Devfs设备文件之后,文件系统允许设备驱动程序通过名字而不是主次设备号注册设备,例如所有的磁盘可以放在/dev/disks目录下/dev/hda/dev/disks/disc0/dev/hdb/dev/disks/disc1使用devfs文件系统的I/O驱动程序通过调用devfs_register()注册设备一个注册了的设备文件,自动出现在devfs的虚拟目录下,设备文件的VFS处理,进程访问普通文件时,通过文件系统访问磁盘分区中的数据块当进程访问设备文件时,却可以驱动硬件设备途径:VFS在设备文件打开时使用与设备相关的函数调用替换其缺省的文件操作这些设备相关函数调用对硬件设备进行操作,基本概念,驱动程序使用的2个重要结构struct filestruct file_operations,设备文件:struct file数据结构,Struct fileStruct dentry*f_dentry;/文件对应的目录项结构Struct file_operations*f_op;/文件操作的结构指针Unsigned short f_flags;/文件标志,主要进行阻塞/非阻塞型操作时检查Undigned short f_count;Mode_t f_mode;/标识文件的读写权限Loff_t f_pos;当前读写位置Unsigned long f_reada,f_ramax,f_reddb,f_rawin;Struct file*f_next,f_prev;Struct fown_struct f_owner;Int f_error;Unsigned long f_version;Void*private_data;/驱动程序一般将它指向已经分配的数据struct inode*f_inode;,设备文件:i节点,系统所有的设备文件都存放在dev设备子目录下。每一个设备文件也使用唯一的i节点来标识;在i节点中包含设备的主设备号和次设备号,并且和一组文件操作函数的指针相联系。设备文件i节点不指向文件系统中的任何实际的物理块,不占用数据空间,通过这个i节点可以访问相应的设备驱动程序,对设备文件的操作就是直接对设备本身进行相应的操作。,设备文件:操作,文件描述符fd是系统调用与设备驱动程序的接口。在操作时,内核通过fd找到代表该文件的struct file结构,得到打开文件的i节点。在i节点中检查文件类型,如果是设备(块或字符)文件,则从索引节点中提取主设备号和次设备号,再通过主设备号定位块或者字符设备转换表中相应的设备驱动程序。根据用户发出的系统调用的功能,进一步确定应该调用哪一个操作函数,并确定传递的参数。启动该函数。,两个设备转换表,设备转换表是一个数据结构,由它定义了每个设备必须支持的操作入口点,由fops,bdops描述。每个驱动程序提供了这些功能的特定实现,都在其对应数组中占据一个项。块设备转换表(blk_dev)字符设备转换表(chrdevs)这些驱动函数的功能由各个驱动程序具体实现。根据设备的类型和主设备号可以在设备表中找到相应的驱动函数。一旦内核想在某台设备上完成某个动作,它必须首先在转换表中定位该设备的驱动程序,再调用相应的驱动程序函数。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。,字符设备转换表(chrdevs),用于注册字符设备的数据结构Struct device_structconst char*name;Struct file_operations*fops;Static struct device_struct chrdevsMAX_CHRDEV;device_struct结构由两项构成:name:指向已登记的设备名称的指针fops:指向设备驱动程序的入口函数:完成指定的设备操作。,字符设备转换表(chrdevs),字符设备初始化时,它的设备驱动将被添加到chrdevs中,从而将其注册到Linux内核中。设备的主设备标识符是固定的。使用主设备号用来对chrdevs数组进行索引。当访问某台设备时,它首先必须在字符转换表中找到该设备的驱动程序,然后控制权转交给设备驱动程序,由设备驱动程序再调用相应的驱动程序函数执行。,字符设备转换表的使用,字符设备转换表(p371),include/linux/fs.hstruct file struct file_operations*f_op;struct file_operations Struct module*owner;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*);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*);int(*fsync)(struct file*,struct dentry*,int datasync);int(*fasync)(int,struct file*,int);,(1)llseek(file,offset,whence):修改文件的读写指针。(2)read(file,buf,count,offset):从设备文件的offset 处开始读出count个字节,然后增加offset的值。(3)write(file,buf,count,offset):从设备文件的offset处写入count个字节,然后增加*offset的值。(4)ioctl(inode,file,cmd,arg):向一个硬件设备发命令,对设备进行控制。(5)mmap(file,vma):将设备空间映射到进程地址空间。(6)open(inode,file):打开并初始化设备。(7)release(inode,file):关闭设备并释放资源。(8)fsync(file,dentry):实现内存与设备之间的同步通信。(9)fasync(file,on):实现内存与设备之间的异步通信。,字符设备的操作:打开文件操作当打开字符设备文件时,使用设备的主标识符来索引chrdevs数组,得到文件操作函数指针。建立起描述此字符设备文件的file结构,使其文件操作指针指向此设备驱动中的文件操作指针集合。这样,所有应用对它进行的文件操作都被映射到此字符设备的文件操作集合上。,字符设备的操作,注册与注销函数:int register_chrdev(unsigned int major,const char*name,struct file_operations*fops):chrdevs表最初为空,注册函数向表中插入一个新项int unregister_chrdev(unsigned int major,const char*name);注:major即设备的主设备号,注册后就是访问数组chrdevs的索引(下标)。例:register_chrdev(6,”lp”,&lp_fops):将并口打印机的相应驱动程序插入到chrdevs表中,字符设备的注册与注销,块设备转换表,fs/block_dev.cstatic struct const char*name;struct block_device_operations*bdops;blk_devsMAX_BLKDEV;,blk_devs结构数组描述了一系列在系统中登记的块设备。内容包括指向已登记的设备名的指针和指向block_device_operations结构的指针。在block_device_operations结构中包含指向有关操作的函数指针。它们由设备驱动程序定义,并分别完成指定的设备操作数组blk_devs也使用主设备号作为索引。,块设备转换表,block_device_operations,struct block_device_operations int(*open)(struct inode*,struct file*);int(*release)(struct inode*,struct file*);int(*ioctl)(struct inode*,struct file*,unsigned,unsigned long);int(*check_media_change)(kdev_t);/检查介质是否已经变化int(*revalidate)(kdev_t);/检查块设备是否持有有效数据;,块设备的注册与管理,注册与注销函数register_blkdev(unsigned int major,const char*name,struct block_device_operations*bdops)int unregister_blkdev(unsigned int major,const char*name),除了文件操作接口外,块设备驱动程序还必须提供缓冲区缓存接口。块设备在内核中的数据结构比字符设备要复杂一些,主要是因为内核为块设备提供缓冲区高速缓存(buffer cache),实际的读写操作由缓冲区缓存协调调用,因此,还需要驱动程序为缓冲区缓存提供接口。,块设备的操作:缓冲区,块设备请求,虽然块设备驱动程序可以一次传送一个单独的数据块,但是内核并不会为磁盘上的每个被访问的数据块都单独执行一次I/O操作,而是试图将几个块合并在一起,并作为一个整体来处理,这样就减少了磁头平均移动时间延迟:块设备发出I/O请求时,将请求放在相应的请求队列中,稍后执行每个块设备都有一个请求队列,一提高磁盘性能的方式对请求进行排序。块设备请求由request结构描述(include/linux/blkdev.h)请求队列:是一个请求描述符的链表,,当系统中有进程发出一个读写块设备的请求时,内核会首先调用block_read()和block_write()对数据缓冲区进行读写。当数据缓冲区不能满足用户进程的一个读写请求或者一个文件需要交换到物理磁盘上时,必须调用块设备的驱动程序来进行实际的数据传输。为此,在Linux中,为完成真正的数据传输请求,又为每一个块设备设计了一个接口blk_dev_structblk_dev_struct结构包含请求过程的地址以及指向请求request数据结构链表的指针,每个代表一个从buffer cache中来让设备进行数据读写的请求。,块设备的操作,每个块设备都有一个blk_dev_struct 结构(块设备驱动程序描述符),所有的blk_dev_struct结构由向量blk_dev集中管理,可以使用主设备号进行索引。struct blk_dev_struct request_queue_t request_queue;queue_proc*queue;void*data;struct blk_dev_struct blk_devMAX_BLKDEV;blk_dev记录各个设备的请求队列request_queue:请求队列*queue:函数指针,当这个指针非0时,就调用这个函数来找到具体设备的请求队列。*data:辅助queue函数找到特定设备的请求队列,块设备的操作,块设备的操作,在blk_dev_struct中有三个域,驱动程序主要是设置第一个域request_queue中的request_fn参数,该参数是指向相应块设备的I/O操作请求函数的指针。当block_read()和block_write()不能满足I/O操作请求时,内核便调用该设备驱动程序的请求函数request。它可以通过blk_devMAJOR(dev).request_queue.request_fn获得。每个具体设备的请求函数都不同,它们在内核在初始化时被赋值。struct request_queue/include/linux/blkdev.hrequest_fn_proc*request_fn;.,块设备驱动程序数据结构示意图,struct request struct list_head queue;int elevator_sequence;volatile int rq_status;#define RQ_INACTIVE(-1)#define RQ_ACTIVE1#define RQ_SCSI_BUSY0 xffff#define RQ_SCSI_DONE0 xfffe#define RQ_SCSI_DISCONNECTING0 xffe0kdev_t rq_dev;/*请求所访问的设备*/int cmd;/*要执行的操作READ or WRITE*/int errors;unsigned long sector;/*本次请求要传输的第一个扇区号*/unsigned long nr_sectors;/*当前请求要传输的扇区数目*/unsigned long hard_sector,hard_nr_sectors;unsigned int nr_segments;unsigned int nr_hw_segments;unsigned long current_nr_sectors;void*special;char*buffer;/*将被写入或者读出数据的缓冲区*/struct completion*waiting;struct buffer_head*bh;/*本次请求对应的缓冲区链表中的第一个缓冲区,即缓冲区头*/struct buffer_head*bhtail;request_queue_t*q;,Request请求队列,默认情况下,每个主设备号并没有这样一个对应的队列。块设备驱动程序必须通过blk_init_queue初始化这一队列。Blk_init_queue(request_queue_t*queue,request_fn_proc*request);Blk_cleanup_queue(request_queue_t*queue)Blk_init_queue函数建立请求队列,并将驱动程序的request函数关联到队列。,缓冲区接口,每当缓冲区缓存要从注册设备中读取数据或向设备写入数据时,缓冲区缓存就会在 blk_dev_struct 中添加一个 request 结构。每个 request 结构中包含一个指向 buffer_head 结构的指针,每个 buffer_head 结构定义要读取或写入的数据块。进行读写操作时,buffer_head 结构由缓冲区缓存锁定,这时,等待该缓冲区操作结束的进程被阻塞。当请求被添加到空的请求队列时,驱动程序的请求函数(request_fn)被调用,由该函数完成请求队列的处理。每个请求有指向一个或多个buffer_head结构的指针,每个请求读写一块由 buffer_head 结构定义的数据块。如buffer cache对buffer_head结构上锁,则进程会等待到对此缓冲的块操作完成。一旦设备驱动完成了请求则它必须将每个buffer_head结构从request结构中清除,将它们标记成已更新状态并解锁之。对buffer_head的解锁将唤醒所有等待此块操作完成的睡眠进程。,块设备工作流程,(1)加载模块时,insmod命令调用init_module()函数,该函数调用resgiter_blkdev()和blk_init_queue(),分别进行驱动程序的注册和请求队列的初始化。(2)register_blkdev()把块设备驱动程序接口block_device_operations 加入到blkdevs表中。(3)blk_init_queue()初始化一个默认的请求队列,将其放入blk_dev表中,并将该驱动程序的Request函数关联到该队列。(4)卸载模块时,rmmod命令调用cleanup_module()函数,该函数调用unregister_ blkdev()和blk_ cleanup_queue(),分别进行驱动程序的注销和请求队列的清除。,设备驱动程序,系统调用是内核核应用程序之间的接口,而设备驱动程序是内核和机器硬件之间的接口。设备驱动程序Linux设备驱动程序是一组运行在特权级上的,内存驻留的底层硬件处理共享库。集成在内核中,负责管理各个设备。基本特征:设备处理的抽象。所有硬件设备都被看成普通文件;可以通过和操纵普通文件相同的标准系统调用来打开、关闭、读取和写入设备。驱动程序的任务就是把这些标准化的系统调用映射到具体设备对于实际硬件的特定操作上驱动程序在用户态和内核态之间传递数据,设备驱动程序的功能,对设备进行初始化使设备投入运行和退出服务从设备接收数据并将它们送回内核将数据从内核送到设备检测和处理设备出现的错误,(1)内核代码:设备驱动是内核的一部分,出错将导致系统的严重损害。编写得很差的设备驱动甚至能使系统崩溃并导致文件系统的破坏和数据丢失。(2)内核接口:设备驱动必须为Linux核心或者其从属子系统提供一个标准接口。例如终端驱动为Linux核心提供了一个文件I/O接口,而SCSI设备驱动为SCSI子系统提供了一个SCSI设备接口,同时此子系统为核心提供了文件I/O和buffer cache接口。(3)核心机制与服务:设备驱动可以使用标准的核心服务如内存分配、中断发送和等待队列等等。,设备驱动程序的特点,(4)动态可加载:多数Linux设备驱动可以在核心模块发出加载请求时加载,同时在不再使用时卸载。这样核心能有效地利用系统资源。(5)可配置:Linux设备驱动可以连接到核心中。当核心被编译时,可配置决定哪些核心被连入核心。(6)动态性:当系统启动及设备驱动初始化时将查找它所控制的硬件设备。如果某个设备的驱动为一个空过程并不会有什么问题。此时此设备驱动仅仅是一个冗余的程序,它除了会占用少量系统内存外不会对系统造成什么危害。,设备驱动程序的特点,设备驱动程序,由于设备众多,Linux对设备和外界的接口作了规定,可以分为三个部分:设备驱动程序与操作系统内核的接口:通过数据结构file_operation完成设备驱动程序与系统引导程序的接口:利用驱动程序对设备进行初始化设备驱动程序与设备的接口:描述了驱动程序如何与设备进行交互,与具体设备关系密切,设备驱动程序,Linux设备驱动程序的程序功能分为以下几部分设备驱动程序的注册和注销设备的打开和关闭设备的读写操作设备的控制操作:通过驱动程序中的ioctl()完成,例如:对软驱的控制floppy_ioctl()Static int floppy_ioctl(struct inode*inode,struct file*filp,unsigned int cmd,unsigned long param),”FDEJECT”设备的中断和查询处理,设备驱动分层结构示意图,设备驱动程序,是一个软件层,使得硬件设备能够响应预定义好的编程接口,就是一组控制设备的VFS函数接口open,read,lseek,ioctl等上述函数的具体实现由设备驱动程序提供此外设备驱动程序必须首先注册并初始化自己并在进行数据传送的时候监控I/O操作,注册时机如果设备驱动程序被静态编译进内核,则注册发生在内核初始化阶段如果作为一个内核模块来编译,则在装入模块的时候注册(并在卸载模块时注销),设备驱动程序的初始化,对设备驱动程序进行注册与初始化是两件不同的事情注册应当尽早:使得用户可以使用设备文件初始化应当推迟到最后可能的时候原因:初始化就意味着需要分配系统中的稀缺资源,例如:1,中断向量(动态分配的情况下)2,用于DMA传送的缓冲区的页框3,包括DMA通道本身,驱动程序的编写,有专门的书Linux设备驱动程序来讲如何写Linux下的驱动程序我们这里用最简单的例子来尝试驱动程序的编写,1、确定设备名称与主次设备号(动态分配的不指定)2、编写设备文件对内核上层的接口file_operations包括:init,open,release,read,write,ioctl等3、编译并加载设备驱动(两种方法)静态加载 将初始化函数加入内核驱动初始化部分 修改相应的Makefile,增加驱动的目标文件 重新编译内核,启用新的内核 动态加载先编译成Linux模块目标文件,再用insmod将驱动模块加载,还有rmmod,lsmod命令,可查看man得知相应的功能。,4、在目录/dev下建立相应的设备文件。mknod 创建设备对象,参数:设备名 设备类型 主设备号 次设备号5、在用户态下编写应用程序测试,使用该设备驱动,模块机制(Module),从结构上来分,可将操作系统分为微内核结构和单块结构两类。Windows 和 MINIX 是典型的微内核操作系统,而Linux 则是单块结构的操作系统。微内核结构可方便地在系统中添加新的组件,而单块结构却不容易做到这一点。Linux的单块结构(monolithic)使得其可扩展性较差.为此,Linux系统使用可动态加载和卸载的内核模块(Loadable Kernel Modules,LKMs),可方便地在内核中添加新的组件或卸载不再需要的内核组件,提高了linux内核的可扩展性,可以让内核保持很小的尺寸并非常灵活。模块机制可以无需重构内核并频繁重新启动来尝试运行新内核代码。用户可以根据自己系统的需要构筑自己的私有内核。,Linux内核模块,Linux操作系统的内核是单一体系结构(monolithic kernel)有了模块机制后,提高Linux操作系统的可扩充性什么是模块呢?模块的全称是“动态可加载内核模块”(Loadable Kernel Module,LKM)模块在内核空间运行模块实际上是一种目标对象文件没有链接,不能独立运行,但是其代码可以在运行时链接到系统中作为内核的一部分运行或从内核中取下,从而可以动态扩充内核的功能这种目标代码通常由一组函数和数据结构组成,Linux内核模块的优点与缺点,优点使得内核更加紧凑和灵活修改内核时,不必全部重新编译整个内核。系统如果需要使用新模块,只要编译相应的模块,然后使用insmod将模块装载即可模块的目标代码一旦被链接到内核,它的作用域和静态链接的内核目标代码完全等价缺点由于内核所占用的内存是不会被换出的,所以链接进内核的模块会给整个系统带来一定的性能和内存利用方面的损失;装入内核的模块就成为内核的一部分,可以修改内核中的其他部分,因此,模块的使用不当会导致系统崩溃;为了让内核模块能访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改符号表;模块会要求利用其它模块的功能,所以,内核要维护模块之间的依赖性.,Linux内核模块与应用程序的区别,C语言程序 Linux内核模块运行 用户空间 内核空间入口 main()module_init()指定;出口 无 module_exit()指定;编译 gcc c Makefile连接 ld insmod运行 直接运行 insmod调试 gdb kdbug,kdb,kgdb等,模块相关命令,insmod module parametersLoad the module注意,只有超级用户才能使用这个命令Rmmod卸载模块lsmod列出所有已安装进内核的模块这个命令和cat/proc/modules等价modprobe-r 加载指定的模块及该模块所依赖的没噢快,模块依赖,一个模块A引用另一个模块B所导出的符号,我们就说模块B被模块A引用。如果要装载模块A,必须先要装载模块B。否则,模块B所导出的那些符号的引用就不可能被链接到模块A中。这种模块间的相互关系就叫做模块依赖。,最简单的内核模块例子,#include#include#include static int _init hello_init(void)printk(KERN_INFO Hello worldn);return 0;static void _exit hello_exit(void)printk(KERN_INFO Goodbye worldn);module_init(hello_init);module_exit(hello_exit);,static int _init hello_init(void)static void _exit hello_exit(void)Static声明,因为这种函数在特定文件之外没有其它意义_init标记,该函数只在初始化期间使用。模块装载后,将该函数占用的内存空间释放_exit标记 该代码仅用于模块卸载。Init/exit宏:module_init/module_exit声明模块初始化及清除函数所在的位置装载和卸载模块时,内核可以自动找到相应的函数 module_init(hello_init);module_exit(hello_exit);,编译内核模块,Makefile文件obj-m:=hello.oall:make-C/lib/modules/$(shell uname-r)/build M=$(shell pwd)modulesclean:make-C/lib/modules/$(shell uname-r)/build M=$(shell pwd)cleanModule includes more filesobj-m:=hello.ohello-objs:=a.o b.o,装载和卸载模块,相关命令lsmodinsmod hello.kormmod hello.ko,模块参数传递,有些模块需要传递一些参数参数在模块加载时传递#insmod hello.ko test=2参数需要使用module_param宏来声明 module_param的参数:变量名称,类型以及访问许可掩码支持的参数类型Byte,short,ushort,int,uint,long,ulong,bool,charpArray(module_param_array(name,type,nump,perm),#include#include#include#include static int test;module_param(test,int,0644);static int _init hello_init(void)printk(KERN_INFO“Hello world test=%d n”,test);return 0;static void _exit hello_exit(void)printk(KERN_INFO Goodbye worldn);MODULE_LICENSE(GPL);MODULE_DESCRIPTION(Test);MODULE_AUTHOR(xxx);module_init(hello_init);module_exit(hello_exit);,导出符号表,如果一个模块需要向其他模块导出符号(方法或全局变量),需要使用:EXPORT_SYMBOL(name);EXPORT_SYMBOL_GPL(name);*注意:符号必须在模块文件的全局部分导出,不能在函数部分导出。更多信息可参考 文件Modules仅可以使用由Kernel或者其他Modules导出的符号不能使用Libc/proc/kallsyms 可以显示所有导出的符号,(9)Linux2.6内核中有关模块部分的改变,模块引用计数器 Linux2.4中在linux/module.h中定义了三个宏来维护实用计数:_MOD_INC_USE_COUNT 当前模块计数加一_MOD_DEC_USE_COUNT 当前模块计数减一_MOD_IN_USE 计数非0时返回真在Linux2.6中,模块引用计数器由系统自动维护,所以程序中有关这些宏都可以注释掉。关于符号导出列表(list of exported symbols)Linux2.4中会用EXPORT_NO_SYMBOLS宏,来表示不想导出任何变量或函数。在Linux2.6中这个宏也已经消失。系统默认为不导出任何变量或函数。,模块程序编译方法的改变Linux2.4中命令为:gcc Wall DMODULE D_KERNEL_-DLINUX c 源文件名.c 其中:_KERNEL_:即告诉头文件这些代码将在内核模式下运行 MODULE:即告诉头文件要给出适当的内核模块的定义 LINUX:并非必要-Wall:显示所有warning信息。Linux2.6中必须写makefile。通过make命令编译程序。Linux2.6中makefile的写法:(以helloworld为例)/Makefile obj-m+=hello.o all:make-C/lib/modules/$(shell uname-r)/build M=$(PWD)modules clean:make-C/lib/modules/$(shell uname-r)/build M=$(PWD)clean*注意:all下一行需要有一个“Tab”键,不要写成空格或略去,不然系统无法识别,报错:nothing to be done for“all”。,用以加载的目标文件类型已经改变Linux2.4中用以加载为模块的目标文件扩展名为.oLinux2.6中中用以加载为模块的目标文件扩展名为.ko在Linux2.4中,函数init_module()和函数cleanup_module()是必不可少的;而在Linux2.6 中,同样功能的函数并非一定要起这两个函数名。,内核符号表,为方便起见,将Linux看成一个母模块。则模块与模块之间如何交互?方法:共享变量和函数。但是,并不是模块中的每个变量和函数都能够被共享,内核仅把各个模块中的主要的变量和函数放在一个特定的区段,这些变量和函数统称为符号。导出符号表:并不是所有的内核符号均在符号表中导出,而只有一些特殊的符号才被添加到符号表中。在kernel/ksyms.c中定义了可以导出的符号如:进程管理子系统可以导出的符号定义:EXPORT_SYMBOL(do_mmap_pgoff);EXPORT_SYMBOL(do_munmap);EXPORT_SYMBOL(do_brk);EXPORT_SYMBOL(exit_mm);EXPORT_SYMBOL(exit_files);EXPORT_SYMBOL(exit_fs);EXPORT_SYMBOL(exit_sighand);,内核符号表,仅仅知道这些符号的名字不够,还必须知道它们在内核空间的地址。内核定义了如下结构来描述模块的符号:Struct module_symbolUnsigned long value;/符号在内核空间的地址Const char*name;/符号名可以从/proc/ksyms文件中读取所有内核模块的导出符号。内核维护一个由 module_list 指针指向的 module 链表,其中第一个 module 数据结构保存有内核的导出符号表,装入 VFAT 和

    注意事项

    本文(第9章 Linux设备管理.ppt)为本站会员(仙人指路1688)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开