linux设备模型介绍.docx

上传人:小飞机 文档编号:4886101 上传时间:2023-05-21 格式:DOCX 页数:29 大小:223.35KB
返回 下载 相关 举报
linux设备模型介绍.docx_第1页
第1页 / 共29页
linux设备模型介绍.docx_第2页
第2页 / 共29页
linux设备模型介绍.docx_第3页
第3页 / 共29页
linux设备模型介绍.docx_第4页
第4页 / 共29页
linux设备模型介绍.docx_第5页
第5页 / 共29页
亲,该文档总共29页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《linux设备模型介绍.docx》由会员分享,可在线阅读,更多相关《linux设备模型介绍.docx(29页珍藏版)》请在三一办公上搜索。

1、第一节基本概念个 ktypeKobject-ksetKobjecl- -paivinK object*kobject. const char char st ruct st ruct st ruct st ruct st ruct st. ructkreflist kobject ksetkobj_type dent ryheadwait_quei_ie_head_t* k_name;nameKOBJ_NAME_LEN; kref;e nt ry;* parent;kset;夫 ktype;* dentry;poll;Kset: frsysis中表示一种层秋结构Kiype: Subject的

2、I 计h i出叫“.【RkobjecH导问在设备模型里面,所有的东西都是kobject,这也是linux建立设备设计模型的目的(对比2.4 之前),实现了统一的实体;我们理解上,却可以分为两个层次,一个是kobject,一个是管 理kobject的kobject(可以把它叫做kset虽然有点绕,但是没有办法了,毕竟就像那个“世 界上先有鸡还是先有蛋的哲学问题一下”);kobject 结构1)前面两个顾名思义,就是name 了,为什么会有两个呢? k_name就是指向name的,如 何知道呢,呵呵,看一下代码int kobject_add(struct kobject * kobj) error

3、 = 0;* parent;if (1 (kobj = kobject_get(kobj)- ;(1kobj-k_name)kobj-k_name = kobj-name;2)kref就是一个内核的原子计数结构,因为涉及内核的操作基本都需要是原子性的,为了 大家的方便,kobject就把它包括进来了,所以大家就不必要各自定义自己的计数了(一般 情况下:),poll也是类似,把等待队列包括进来;3)entry这个名字比较让人误解,其实看它的类型知道是list成员,它就是加入到kset的list 的那个零部件;4)ktpye要理解这个成员就稍微麻烦些了,先看一下定义struct kobj_type

4、 void (*release) ( struct kobject *);sysfs_ops大 sysfs_ops;大大 defrs ;Default_attrs就是一种比较简单的设置属性文件的方法,它其实跟我们自己调用 sysfs_create_file没有什么区别,呵呵,看一下代码就知道了,所以大家基本上可以把它忽略 掉:),调用关系为 kobject_add-create_dir-populate_dirstatic int populate_dir(struct kobject * kobj)大七=(kobj);struct attribute * attr;int error = ;

5、int i;if (t & t-default_attrs) (i = ; (att r = t-default_attrsi) 1= NULL; i + +) (error = sysfs_create_file (kobj f att r) brea;return error;把一个忽略掉,剩下的两个就比较重要了;每个对象一般都有多个属性,用面向对象的角度 来看,我们可以把对属性的操作抽象为show和store这一对方法,那么多个属性就会有多 对show和store的方法;那么,为了实现对这些方法的统一调用,就利用ktype中的sysfs_ops 实现了多态;这样一来,对于sysfs中的普

6、通文件读写操作都是由kobject-ktype-sysfs_ops 来完成的;release也是类似,大部分的设备在退出的时候也是需要清理资源的,这里也实 现多态(不过和上面的那个多态实现上有所区别:),在kobject释放的时候,会调用一下这个release (如果不为NULL的话),如下:: static inline st.ruct. kobj_t.ype * get_ktype (struct kobject. * kJ : :if (k-kset & k-kset-ktype):return k-kset-ktype;:else |:return k-ktype;i: void ko

7、bject_cleanup(struct kobject * kobj) _* t =(kobj);st ruct kset * s = kobj-kset;st ruct kobject. * parent = kobj-parent;_debug (r,kobj ect %s : cleaning up nrrf kobj ect_name (kobj );if (kobj-k_name != kobj-name)kf ree(kobj-k_name);kobj-k_name = NULL;if (t & t-release)所以我们基本上可以这样理解,ktpye就是把kobject的一些

8、方法给拿了出来,实现了这些方 法的多态的统一接口;其实看名字ktpye大概也理解得到:),看一下get_ktpye函数的实现, 一个kobject的ktype是优先选择kset-ktpye,如果它为NULL的话,才会去用kobject自己 的ktpye,所以说,kset是管理kobject的kobject,就像一个小孩首先要有父母管,父母不 管的话,那就自己管,但是自己都不管自己的话,那就麻烦了;这种情况是存在的,kobject 是可以没有ktpye的,这样的话相当于没有对外的属性接口和release方法了;5)parent这个就好理解了,就是形成sys的树形结构,在sysfs中每一个目录都对

9、应一个 kobject.这些kobject都有自己的parent。在没有指定parent的情况下,都会指向它所属的 kset-object,要是这个也没有的话,那就会向组织靠拢,直接挂到/sys目录下;注意,这里 的优先级顺序跟前面的那个ktpye是相反的;6)Dentry我们可以先不用太关心,大概知道dentry是跟文件系统目录有关的就可以了管理 kobject 的 kojbect (即是 kset)kset. * subsys;* ktype;list;list_lock;kobj ;* uevent_ops;1)subsys这个概念基本上就是kset的概念,在比较新的内核版本里面已经去掉

10、了,所以忽 略它2)Ktype这个在介绍kobject的时候已经讲过了,这里只说一点,kset里面也有一个kobject, 那么这个kobject里面也会有一个ktype,表明这个kset (其实也是kobject)的ktype是什么, 大家不要把这两个概念混淆;3)List就是用来管理kobject的链表头4)Uevent_ops又是一个麻烦的东西,看一下定义st ruct kset._uevent_ops (int (*f ilte r) (st ruct ksetf st ruct kobj ect. *kobj);const char *(struct kset *ksetf stru

11、ct kobject *kobj);int vent) (st ruct kset kset f st ruct kob j ect. *kobj f char 古大已口vp- int rii_im_envpf char fer int bufter_size);里面无非就是些函数指针,作用是什么呢?要讲清楚的话,就得费一番功夫了,且容我一 一道来:)前面说过,现在的内核驱动模型里面,所有的东西都是kobject,以前的/dev目录是由内核 维护的(现在时代不同了,什么东西都得热插拔,甚至包括cpu),今天/dev的维护工作交 给了一个叫做/sbin/udevd的用户进程(udev文件系统是针

12、对2.6内核,提供一个基于用户 空间的动态设备节点管理和命名的解决方案),但是任何东西都是从底下向上面传递的,内 核需要一个机制向用户态发送消息,确切的说是kobject需要一个途径向用户态传递,那么 这个途径便是uevent ops 了;但是为什么它不放到kobject或者ktpye里面呢?其实我也说 不太上来,套用我前面说的那个概念就是这个kset是一个管理kobject的kobject吧:) 当一个kobject的事件发生时(比如创建或者删除等等),就会调用到一个叫做kobject_uevent 的接口,如下kobj ect_actionKOBJ_ADD=(force kobject_a

13、ction_tKOBJ_REMOVE =(force kobject._action_tKOBJ_CHANGE =(force kobject_action_tKOBJ_MOUNT =(force kobject_action_tKOB J_UMOUbIT =(force kobject_action_tKOB J_OFFLIbIE= (f0rce kobject_actiKOBJ_ONLINE =( l -force kobject_action_tvoid kobj ect-_lievent (st ruct kobj ect *kobj f enurtL kobj ect_action

14、action)这个接口里面有一个比较关键的地方或许能够进一步帮助我们理解kset的功能,/* search the kset we belong to */ top_kobj = kobj ;(1top_kobj-kset & top_kobj-parent) do top_kobj = top_kobj-parent; while (1 t.op_kobj- kset & t.op_kobj-parent); (1top_kobj-kset) return;kset = t.op_kobj-kset;uevent_ops = kset-uevent_ops;可以看出来,代码里面会尽力(不断向

15、上循环,找parent的parent等等)找到管理这个kobject 的kset或者是kobject的parent的kset或者是parent的parent的等等,然后得到它的uevent ops (它有可能为NULL),所以从这里大概能够感觉到这个kset (管理kobject的kobject)的 重要性了,其实也可以没有,但是什么都没有的话,那我们就什么功能都没有实现了:) 最后,会通过netlink发送到用户态,代码如下/* send netlink message( )struct sk_buff *skb;size_t len;/* allocate message with the

16、 maximum possible size */ len = strlen(action_string) + strler (devpath) + 2 ;skb = alloc_skb(len + BUFFER_SIZEf GFP_KERNEL);if (skb) /* add headsr */scratch = skb_put(skbf len);(scrat.chf %sf action_st. ringf devpath);/* copy keys to our continuous event, payload buffer */ for (i = 2; envpi; i+) (l

17、en = strlen(envpi) + 1;scratch = skb_put(skbf len);strcpy(scratch, envpi);)NETLINK_CB (skb) . dst_gr0i_ip = 1;netlink_broadcast(u:k, skbf 0 f lf GF P_KERNEL);讲到这里,终于可以告一段落了,总结一下,就是开始讲的那一句话:所有的东西都是kobject,理解上可以分为两个层次,一个是kobject,一个是管理kobject 的 kobject有了这些基本概念后,下一节给大家讲讲具体实践(字符设备和块设备)第二节字符设备和块设备字符和块设备的p

18、roc文件,下面两个数据结构其实也就仅仅是为了 proc存在的,大家看到 它们可以忽略:)这样我们可以把注意力集中到核心的地方1) charstatic struct char_device_struct struct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct;char name64;struct file_operations *fops;struct cdev *cdev; /* will die */ *chrdevsCHRDEV_MAJOR_HASH_SIZE;2) bl

19、ockstatic struct blk_major_name struct blk_major_name *next;int major;char name16; *major_namesBLKDEV_MAJOR_HASH_SIZE;registe r_blkde v (unsigned int rna j o 匚. const char)( _s t. ruct b 1 k_m a j o r_name 土古 n 土 p;int index,r ret = U;mutex_lock i: &b 1 o ck_subsys_l o ck);/ 此 t e m po ra ry 此 /if (

20、ma j 0 r = 0) f o r (indei = ARRAY_SI ZE (ma j o r_names ) - _; index U ; index- ) if (ma j ::: r_names index = NULL) break;以上这两个函数名字取的是挺好的,register xxx,但是我们千万不要被它们骗了,这年头 不要以貌取人,看看它里面究竟做了什么才是王道,最终我们发现,其实里面做的事情就是 仅仅跟proc文件的显示有关,其他的事情都没有做,所以我们忽略它们:)主次设备号与kobject的联系1) 什么是主次设备号(20)#define MAJOR(dev) (un

21、signed int) (dev) MINORBITS)#define MINOR(dev) (unsigned int) (dev) & MINORMASK)2) 桥梁为 kobj_mapstruct kobj_map struct probe struct probe *next;dev_t dev;unsigned long range;struct module *owner;kobj_probe_t *get;int (*lock)(dev_t, void *);void *data; *probes255;struct mutex *lock;根据主设备号进行( 255)hash,

22、按照range (可以理解为注册时候的次设备号的个数,注 册的时候通常不只是一个)的大小进行升序排列(所以,最后形成的list与主设备号或者次 设备号没有直接的映射逻辑,但是有间接逻辑:),是按照主设备号hash);关键点在于data,其实它就是一个kobject的子类对象,通过get方法可以得到kobject;桥梁kobj_map的建立1)字符设备通过cdev_add这个接口实现,这里的kobject是个桥梁,字符设备通过设备号最终要找到的koject不是它,而是由各个字符设备自己维护的kobject (比如在 /sys/class/misc/iodir_control下),所以这个充当桥梁

23、功能的kobject不会出现到sys系统 下(并不是全部的kobject都会出现到sys下,因为sys系统只是kobject的一个子集功 能而已);而一个字符设备最终会有一个设备号与/dev下面的设备文件名相对应(在各 个字符设备产生自己的kobject实体的时候,会通过kobject_uevent(&class_dev-kobj, KOBJ_ADD)接口给用户态的一个维护/dev目录的进程发送消息,进而产生/dev相关的文 件),用户态可以通过对/dev/xx的open操作打开设备文件,最终会通过设备号和这个 kobject桥梁到达字符设备真正的kobject;struct cdev str

24、uct kobj ect kobj ;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;in.t z:|iev_add (struct cdev dev_t devf unsigned count)p-dev = dev;p-count = count;return kobj_map(cdev_map7 dev, countf NULL, exact_match, exact_lock, p);2)块设备通过add_disk- blk

25、_register_region这个接口实现桥梁,(这里的kobject是出现在sys系统里 面的,struct gendisk,因为 add_disk- register_disk会挂载到 sys 系统)(dev_t dev, unsigned long rangefstruct kobject * (*probe) (dev_tr int *f void i:nt (*lock) (dev_t, void *) 7 void *data)struct*) /modulemodule.kobj_map(bdev_map7/* Not exported voiddev, rangej modu

26、le7 probe lockf data);helper to add_dis k () . *./ (struct gendis k *disk)struct block device *bdev;char A s;int i;struct h.d_striict *p; int err;strlcpy(disk-kobj.name,disk-disk_name,KOBJ_NAME_LEN);/* ewww. . . some of these buggers have / in. name. . .s = strchr (disk-kiobj .rLamef 1 / 1 );if (m)*

27、s = 1 ! 1 ;if (err = kobject_add(idisk-kobj) return;disk svsfs svmlinks(disk;exit:/* announce disk after possible partitions ar kob j ect_u.even.t (&di sk- kob j T KOBJ_ADD);/* announce possible partitions */ for (i = j. ; i minors; i+) p = disk-partli-1;if ( !p | | !p-nr_sects) continue;kobj ect_ue

28、vent(&p-kobj T KOBJ_ADD);) 可以看到,一个disk的kobject节点为(1+N),其中N为分区数,不过这里的N不是直接包 含在disk里面的,而是disk作为parent的形式,所以struct gendisk里面有个part指针;disk 的核心结构如下:struct gendisk int ma j or;/ * major nuniber of driverint first_minor;int minors;/* maximum nur* disks 1char disk_name _32 ;/* name of major drivestruct hd_s

29、trnct;/* indexed by mirint part_uevent_s uppre s s;struct block_device_operations *fops;struct r eque s t_qiLeu.e * queue;flags;*drive rfs_dev;kobj ;*holde r_di r;*slave_di r;分区对象的数据结构如下:st ruct hd_st ruct s e ct o r_t start_sect;sector_t nr_sects;struct kobject kobj;struct kobject *holder_dir;unsig

30、ned ios2f sectors ;/* READS and WRITESpolicy, partno;Add partition的函数,可以看到1+N的关系如下:void add_partition(struct gendisk *diskf int partf sector_t start,tp-KoDj - namef, wsp宅crelse7 (p-kobj . namef 1f rrsd,F,p-kobj . parent. = &disk-kobj ;p-kobj.ktype = &ktype_part;_init (&p-kobj);_add (&p-kobj );在disk的

31、核心结构里面,我们可以看一下几个成员的意义,其中major为主设备号(块设备 比较特殊,不像字符设备一样,一个主设备号代表一种类型,块设备通常是好几个主设备号 都是一种类型,比如说sd设备,就包括好几个主设备号),Block devices:1 ramdisk3 ideO7 loopS sd9 md65 sd66 sd67 sd6S sd69 sd70 sd71 sd12S sd129 sd130 sd131 sd132 sd133 sd134 sd135 sd253 device-mapper254 mdpfirst_minor为这块disk的起始minor号(通过它可以知道自己是第几块di

32、sk,方法就是除以 n,n为调用alloc_disk的时候的参数,这个参数就是minors,是为了记录最大的可分区数目, 最大的可分区数为minors-1,之所以减一是出于一个disk的kobject节点为(1+N)的考虑, 因为一个disk设备却可以拥有(1+N)个设备号,比如说root1qcsIhost iodir_CDntEDl# brw-r 1 root disk 3r 0 Aug brw-r1 root disk 3r 1 Aug brw-r 1 root disk 3r 2 Aug root 01 oca lht31 i mli r control 1 #1113/dev/hda*

33、18:1318:1913:18/dev/hdaB/dev/hdar/dev/hda2/aya/block/hd其对应于 /sys/block/hda /sys/block/had/hda1上面的一个disk拥有三个设备号/sys/block/had/hda2三个kobject);如果我们调用alloc_disk的时候的参数为1,那么分区数 为minors-1 = 1 -1 =0,那么就相当于不能有分区,但是仍然有一个设备号对应到目录 /dev/hda;同时也只有一个 kobject 对应U/sys/block/hda桥梁kobj_map的使用1)字符设备在open的时候通过设备号dev_t,可

34、以找到kobject;进一步可以找到字符设 备int chrdev_open(struct inode * inode, struct file * filp)struct cdev ;struct cdev *new = NULL;int ret = Cl;spin_lock(&cdev_lock);p inode-i_cdev;if ( !p) 1struct kobject *kobj;int idx;spin_unlock(&cdev_lock);kobj = kobj_lao kup(cdev_map7 inode-i_rdev, &idx);if (!kobj)return -EN

35、XZO; T T I F/ 1 I r 1 d I jrJ T Imfc 1 (通过设备号映射为kobject)new = container_of(kobj T struct cdev7 kobj); spin_lock (&cdev_loci_cdev;if (3p) inode-i_cdev = p = new; 一 _(得到 struct cdev)return ret;filp-f_op = fops_get(p-ops);if (!filp-f_op) cdev_pat(p);return -ENXZO;)if (filp-f_op-0pen) loc k_kernel ();re

36、t = filp-f_op-open(inodeffilp);uni o c k_ke r ne 1 ();(调用struct cdev的f_op-open,在这里面就是找到真正的字符设备的kobject实体) 2)块设备与字符设备不一样,struct cdev可以理解为kobject的子类,但是struct block_device却没有kobject成员,那么如何找到关系呢?struct block_device dev_tbd_dev; /* not a kdev_t - it1s a search key *struct inode * bd_inQde; /* will die */

37、 intbd_openers;struct mutexbd_mutex;/* open/close mutex */struct mutexbd_mount_miitex; /* mount mutex. */struct list_headbd_inodes;void 文bd_h.older;intbd holders;#ifdef CONFIG_SYSFSstruct list_head bd_holder_list;#endifstruct block_device *bd_contains;unsignedbd_blQck_size;*/struct hd_striict * bd_p

38、art;/* number of times partitions within this device have been opened.unsignedbd_part_caunt;intbd_invalidated;struct gendisk *bd_disk;struct list_headbd_list;struct backing_dev_inf0 #bd_inode_backing_dev_inf0;/* Private data _ You must have bd_claim,ed the block_device山 to use this. NOTE: bd_claim a

39、llows an owner to claim* the same device multiple times, the owner must take special* care to not mess up bd_priva.te for that case.*/unsigned longbd_private/blkdev_open-do_open-get_gendiskfstruct gendisk *get_gmn(isg(dev_t dev, int *partdev.part);struct kobject *kobj = kobj_lookup(bdev_mapf return

40、kobj ? to_disk(kobj) : NULL;这里找到了我们关心的bdev_map桥梁了,通过它找到的是struct gendisk这个kobject实体,当然这个实体是以disk为单位的,而不是以设备号为单位(前面已经解释过了:)第三节block层呵呵,前面讲了这么久,始终没有讲到我们关心的重点问题,大家是否觉得我写跑题了:) 但是相信各位通过前面的介绍,现在对设备驱动模型已经基本理解了,个人觉得这点还是比 较重要的,因为内核源码到处都涉及到相关的驱动模型,以后大家在看到相关代码的时候就 可以举一反三,融会贯通了(授人以渔);好了,不罗嗦了,进入主题吧其实对于block层,自己接触

41、的时间不长,没有太深入的研究过,只能就目前的理解大概写 写,望能够达到抛砖引玉的作用,入口点是哪里呢?三个关键的数据结构1) Request Queue Descriptors2) Request Descriptors3)Bio关系图如下:Figure 4,2,: Read and write requests arc collcctcKl in rr.qvest queues, This structure includes & pointer to a donhly-linked list which contains the requests. Each request has & p

42、ointer to a so-called bio (block I/O) structure which maps a hlock to a page instance in memory (figure 4,3),Figure 43: A rpqiiest holds a pointer tn a list of bio struct nr ps. whereas each BTO h:a a pednter to a vector array with the corresponding meiuory page information. The kernel these structu

43、re to transfer data blocks from a block device to memory or vice verda.三个数据流入口1)generic_make_request(通用块层的数据入口,一次传入一个bio?)2) scsi_execute scsi_execute_async(这两基本上是差不多的,只是一个同 步和异步的差别,底层的调用都是blk_execute_rq_nowait)三个关键的异步点说明:这里说的异步点描述的是普遍可能的一种情况,并不是说一个命令下发的过程中一定 会发生(好像有点绕,但是大家阅读完之后便明白了);1)blk_get_reque

44、st入队列需要的资源(request)14.3.2.1 Managing the allocation of request descriptorsThe limited amount of free dynamic memory may become, under very heavy loads and high disk activity, a bottleneck for processes that want to add a ne queue q. To cope with this kind of situation, each request_queue descriptor

45、includes a request_list data structure, which consists of: A pointer to a memory pool of request descriptors (see the section Memory Pools in Chapter 8. Two counters for the number of requests descriptors allocated for read and write requests, respectively. Two flags indicating whether a recent allocation for a read or write request, respectively, failed. Two wait queues storing the processes sleeping for available READ and WRITE request descriptors, respectively. A wait queue for the processes waiting for a request queue to be flushed (emptied).The blk_get_request ( ) fu

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号