Linux kernel 分析之十七:设计模式-用C来实现继承和模板

来源:互联网 发布:linux tshark抓RTP包 编辑:程序博客网 时间:2024/05/22 03:17
多态实现了,封装呢?基本上,C的结构体是不设防的,谁都可以访问。从这一点来看,C很难实现封装。尽管C中有static关键字,可以保证函数和变量的作用仅限于本文件,尽管内核可以通过控制导出符号表(EXPORT_SYMBOL)来控制提供给下层模块的函数和变量,但这些与C++中的封装相去甚远。好在内核的原则是“相信内核不会自己伤害自己”。所以就不苛求啦。
那么继承呢?这个也基本上很难。不过我们可以通过组合来模拟“继
承”。ext2_inode_info是ext2的inode在内存中的结构体(注意,不是ext2_inode,这个是在硬盘上),有个成员变量是struct inode*,指向inode。使用时,用EXT2_I()宏来实现类型的down_cast。这样很不直观。当然我们也可以理解为ext2_inode_info与inode是两个完全不同的结构体,ext2_inode_info包含了inode。但是这样的理解显然更简单轻巧:ext2_inode_info继承了inode,并且加了自己的私有成员变量。
这是一种实现继承,ext2_inode_info实实在在地继承了inode 的全部“财产”- 成员变量,还有一种接口继承,也使用得非常广泛。如ext2_dir_inode_operations(定义了对ext2的目录节点的操作)对inode_operations的继承。与通常理解的“继承”不同,在C中并没有生成新的结构体,而只是定义了一个inode_operations类型的变量。这种继承只是把里面的函数指针加以实现。这种继承很辛苦,因为接口除了声明,什么也没做。
有了父类子类,自然也少不了类型之间的转换,以及运行中的类型识别(RTTI)。由于内核中只是用组合来模拟继承关系,即用子类包含父类的方式,所以从子类转换到父类就很方便。如子类是struct Derive d里有个成员变量struct Base *b,从子类转换到父类只要d->b就行,但是从父类down cast,向下类型转换到子类就比较麻烦了。然而这种情况非常常见,如设备驱动接口file_operations中
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
如果我们开发一个叫scull的字符设备,通常要定义自己的结构体scull_dev,此外还要继承cdev(表现为组合).可是ioctl接口里没有scull_dev!幸好inode->i_cdev指向的就是cdev。那么如何通过cdev得到scull_dev呢?内核提供了container_of()宏。struct scullc_dev *dev;
dev = container_of(inode->i_cdev, struct scullc_dev, cdev);
这可比down_cast之类复杂多了。里面使用了黑客手段,有兴趣可以看看container_of的实现。
好像还忘记了什么。。。对了,模板呢?
说内核不需要模板是不可能的。光是链表一项,就有很多地方要用到,进程之
间,dentry之间。。。如果为每种情况写一套链表操作,那是很可怕的事。理论上,我们有两种选择,以循环链表,task_struct为例:1.把指针pprev,next放到task_struct中,然后写一套宏,把类型作为参数传进去,实现对循环链表的操作。这个是最自然的思路,最接近C++的模板。但是,问题来了,如果task_struct同时属于好几个链表怎么办(虽然听起来这个想法很怪,但task_struct的确有这样的需求)?
2.对第一种方法的改进:实现一个对最简单的结构体list_head的链表操作。然后把list_head等包含到task_struct 结构体里。如果要对task_struct所在链表进行操作,只要操作对应的list_head就可以了。所以解决了1的问题。至于怎么通过list_head获得task_struct,可以参考container_of()宏的做法。
问题是解决了,但是与前面几种模拟办法相比,这种是最不直观的。因为当我在一个结构体里发现了list_head后,根本不知道它所在的链表究竟放的是什么结构体。事实上只要某个结构体有list_head这个成员,就可以放到链表里。有些类似于MFC的CObjectList。这就有些恐怖了。
不过只要编程者清楚里面放了些什么,就没有问题。“相信内核不会伤害自己”
不过我很好奇,如果换成C++,如何实现list_head的效果呢?能否实现一种新的multi_list模板,它与list的区别在于节点可以属于多个链表。
读核感悟-设计模式-文件系统和设备的继承和接口
接着,我们可以看一下文件系统几个关键的结构体之间的关系。显然,一个inode对应多个dentry,一个dentry对应多个file。任何一本介绍文件系统的书对此都有介绍。那么inode和块设备block_device,字符设备cdev的关系如何呢。我们知道,inode是对很多事物的抽象。在2.4内核中。inode结构体中有一个union,记录了几十种类型,包括各种文件系统的inode,各种设备(块设备,字符设备,socket设备等等)的inode,我们可以把block_device和cdev理解为特殊的节点。他们除了普通节点的一些特性外,还有自己的接口。
如block_device有个成员gendisk,有自己的接口block_device_operations。而对于ide硬盘来说,它是一种特殊的gendisk,它有自己的结构体ide_driver_t。 它还实现了block_device_operations接口,即idedisk_ops。
整体的继承关系:
block_device,gendisk(类)|block_device_operations(接口)| is_a(继承) 
ide_drive_t(类)|idedisk_ops(实现)
这种分析方法有助于理清内核中众多结构体之间的关系。与此类似,我们也可以分析一下两种重要的设备PCI和USB设备:
内核中用来表示pci设备的结构体是pci_dev,此外,还有一个接口pci_driver,定义了一组PCI设备的操作。
内核中用来表示usb设备的结构体是usb_device,此外,还有一个接口usb_driver,定义了一组USB设备的操作。
有趣的是,pci_dev和usb_device有一个共同的成员变量类型为
device,pci_driver和usb_driver有一个共同的成员变量类型为device_driver。
由此,我们可以猜出他们的关系。
pci_dev和usb_device可以看成对device的继承,pci_driver和usb_driver可以看作是从device_driver接口的继承。
在设备驱动中,各种层次显得非常分明。内核通过对usb和pci驱动通用框架的设计,减轻了驱动的开发人员的负担。实际上,内核为驱动开发人员提供的是一个框架(framework)。有些类似于MFC。开发人员只要实现一些接口就可以了。
此外,我们还可以总结出一些有趣的现象:比如,接口该如何定义呢?
Linux把目录和设备看成文件,这使文件操作接口的定义有两种选择:
1.取普通文件,目录文件,设备文件接口的交集,为,把各种“文件”特有的接口放到各种“文件”自定义自己的接口。这样的好处是继承关系比较清楚。不过继承层次比较深。
2. 取普通文件,目录文件,设备文件接口的并集,压缩继承层次。各种“文件”实现自己能实现的接口,把不能实现的函数指针置为NULL
事实上,file_operations就是这么做的。它里面有通用的操作(read write)。有针对目录文件的操作(getdents)。有针对设备的操作(ioctl)。与之类似的还有
inode_operations,包括了普通文件节点(create),设备文件节点(mknod),目录文件节点(mkdir)等等。
通过这样的设计,大大简化了整个层次结构。我们还可以归纳出更多东西:
1. 为了保证扩展性,很多结构体提供一个private指针,如file::private_data2. 如果只是为了代码重用,就提供普通库函数和普通结构体。如果为了提供接口以便继承,就提供接口。在C中,接口和普通成员函数很容易区分,前者一般定义成函数指针。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 六个月婴儿呕奶怎么办 刚出生婴儿呕奶怎么办 宝宝拉颗粒便便怎么办 奶水不够宝宝又不吃奶粉怎么办 一岁突然不吃饭怎么办 母乳不够吃宝宝不吃奶粉怎么办 八个月不吃辅食怎么办 孩子长得太快怎么办 反复发烧到39度怎么办 儿童发烧到39度怎么办 7岁儿童发烧40度怎么办 7岁反复发烧39度怎么办 宝宝烧到39.5度怎么办 3岁儿童发烧39度怎么办 孩子发高烧怎么办39度5 3岁宝宝不吃水果怎么办 2岁宝宝不吃水果怎么办 4岁宝宝不吃水果怎么办 过早竖抱婴儿了怎么办 3个月宝宝认生怎么办 10天婴儿不拉屎怎么办 3个月宝宝不吃奶粉怎么办 婴儿吃青菜吃多怎么办 2月宝宝消化不好怎么办 吃母乳的宝宝便秘怎么办 婴儿拉肚子怎么办大便绿色的 10个月宝宝睡眠不好怎么办 婴幼儿消化不良引起的腹泻怎么办 小孩晚上睡觉不盖被子怎么办 镜子对着房间门怎么办 高血压引起的眼底出血怎么办 墙砖颜色选深了怎么办 墙砖颜色太深怎么办 30岁了没有朋友怎么办 产检宝宝腿短怎么办 2岁宝宝不学说话怎么办 4岁了还不会说话怎么办 两周岁还不说话怎么办 2岁了不会说话怎么办 一岁宝宝蛀牙了怎么办 分手了想联系他怎么办