设为首页 收藏本站
查看: 278|回复: 0

[经验分享] 从著名的list_head看linux内核中OO

[复制链接]

尚未签到

发表于 2016-3-11 12:35:01 | 显示全部楼层 |阅读模式
  如果有人问我最欣赏linux的什么,我会毫不犹豫地回答:list_head。这个小小的结构向世人说明了用c语言写成的linux内核也在实现着OO,下面我就具体来说一下下。先看list_head
  struct list_head {
  struct list_head *next, *prev;
  };
  就 这吗?就这!你别看它小,它却可以充当任何在内核中存在的东西,几乎所有的内核管理设施都用到了它,用OO的语言讲就是它作为基类,几乎所有的内核设施都 继承了它,OO中一直争论不休的就是用继承还是用包含,“遗憾”的是,linux内核利用list_head将这两者集成在了一起,让人叹为观止!举几个 例子:
  struct tast_struct{
  ...
  list_head tasts;
  ...
  };
  struct sched_entity {
  ...
  struct rb_node run_node;
  struct list_head group_node;
  };
  仅此二例就可以说明list_head可以作为task_struct还可以作为sched_entity.这仅仅是单向的转化,可以说sched_entity 继承了list_head,也可以说tas_struct继承了list_head,那么到底是谁呢?需要的时候怎么转化过来呢?这个一会再说,先说说这 么做的意义,众所周知,继承的意义就是子类可以有父类的行为,还有自己的行为,那么list_head的行为是什么呢?链表嘛,就是插入,删除,查找,再 问一个,内核数据结构作为子类为何要以拥有这些操作的list_head作为它们的父类呢?原因就是提取公因子的结果,想想看,task_struct是管理进程的,net_device是管理网卡的,这些能有什么一样的地方,要认识到这是在内核里面,内核的功能就是管理一切,正是需要被管理对象的注册,注册的结构都要放到链表里面,于是几乎所有的数据结构都需要链表的操作,这样天经地义的继承list_head就没有什么问题了,下面看看怎么取出原对象,这也就是多态的概念范畴了,list_head到底是什么样的对象呢?
  #define list_entry(ptr, type, member) /
  container_of(ptr, type, member)
  #define container_of(ptr, type, member) ({ /
  const typeof( ((type *)0)->member ) *__mptr = (ptr); /
  (type *)( (char *)__mptr - offsetof(type,member) );})
  上面的代码只要你懂c语言,并且指针玩得好,一眼就看懂了,举个例子巴:
  inode *inode;
  struct list_head * tmp = next;
  inode = list_entry(tmp, struct inode, i_sb_list);
  这里只有一个list_head,但是我们需要的是一个inode,那么调用list_entry就可以取到inode了,什么意思呢?就是说inode结构的i_sb_list字段表示一个list_head,现在有了list_head,那么此list_head此的地址减去i_sb_list 相对于inode结构的偏移就是inode结构体了,其实现代的面向对象语言底层实现机制也是这样的,只不过偏移相对固定,比如c++语言中的类的对象在 内存中的布局就是把基类放到这个对象固定偏移的地方,根据编译器不同这个位置也不同,很一般的就是基类放到最前面,以下才是子类私有的数据,linux内 核和现代c++语言如出一辙,至于java,本质上也是这样,只不过没有c++那么直观罢了,java的对象由jvm创建并由jvm管理,看看jvm的源 代码就知道了,我看过一些,jvm管理对象的方式和linux内核的实现更加相似一些。但是这里的list_entry并没有进行类型检查,这实际上是不必要的,它可是内核啊,出了问题崩溃掉自然比强行恢复要好得多,再者说一般人接触不到内核,再内核的调试阶段问题几乎就都解决了,如果还有问题只好听老天 安排了,内核中少了类型检查会节省很大一笔开销的,另外一个更有意思并且更有意义的原因就是保持最简单最天真的形式,这样黑客就不会想方设法去避开类型检查了,因为压根就没有检查,如果你有问题,那么很简单,直接崩溃,不给黑客任何可乘之机,这印证了一条哲理:只要有检查,总能避开,最简单的方式就是最好 的方式,最危险的反而最安全!   
内核中不光实体设施用了list_head,就连另外的管理设施也用,比如klist
  truct klist {
  spinlock_t k_lock;
  struct list_head k_list;
  void (*get)(struct klist_node *);
  void (*put)(struct klist_node *);
  };
  struct klist_node {
  struct klist * n_klist;
  struct list_head n_node;
  struct kref n_ref;
  struct completion n_removed;
  };
  如果说刚才的论述令你不知所云的话,这两个应该比较简单了,毕竟它们都是list,一个klist继承list_head从字面意义上讲要比一个task_struct继承list_head好理解得多。   
最后谈一下继承和包含,就以klist为例吧,你说klist继承一个list_head呢还是包含一个list_head呢?实际上都是,为什么这么说呢?要明确list_head可以代表klist自身,因为可以用list_entry转化,我们说list“是”一个list_head,这个意义上就是继承,但是另一方面,一个klist确实“有”一个list_head,就像结构中写的那样,list_head确实是klist的一个字段,这里list_head不是作为一个klist存在,而是作为一个真实的链表存在,这个意义上讲,klist包含一个list_head,有点文字游戏了,呵呵!linux总是在人们争论不休的问题两端之间游走,有种行走于江湖的味道,这往往让一些人不知所措,谁让它的创始人长得那么滑稽呢?呵呵!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-189419-1-1.html 上篇帖子: 鸟哥的Linux私房菜-基础学习篇3 下篇帖子: linux的页表为什么没有实现自映射
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表