论坛首页 编程语言技术论坛

Linux2.6.36内核源码中链表、红黑树实现风格的讨论

浏览 18024 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-01-11  
rubynroll 写道
thxg 写道
rubynroll 写道
thxg 写道
这个宏的用法类似于其它语言中的forEach之类的循环,比如下面的代码:

 

struct my_data {
    int value;
    list_head node;
};
//...
struct my_data *pos;
struct list_head *head;
//...
list_for_each_entry(pos, head, node) {
    //...
}
if (pos != NULL) printf("%d\n", pos->value);
 

在我们的编程习惯中,循环遍历结束后,数据指针(pos)要么是最后一个值,要么是NULL。可是这里,如果我们有一个专门的链表头,即专门用一个list_head做头,而没有用无数据的my_data做头的话,上面的代码就会Segment fault了。

 

你要用Linux的list_head的话,就一定要留一个专门作表头,你违反了它的用法,怎么能说是“陷阱”? 在我看来,Linux的链表实现比其他大多数系统都优雅很多,可以说整个Linux内核的各种复杂关系如果没有这么一个高超的链表实现将会是一团糟。

 

单单是免去node的内存管理,可以减少无数的野指针风险。各种for_each宏使C代码看起来就像具备了高级语言才有的“闭包”功能,大大提高了可读性以及降低出错几率。

 

 

 

当然用了专门的表头,即然是专门的表头,配合Linux节约内存的出发点,应该仅用一个list_head就行了吧,那么list_for_each_entry遍历完之后,pos指向表头所在的container里,就成了非法指针。

 

如果你告诉我表头也要用含数据的结构体,那就算了,我情愿自己留意循环外不要再用pos。

 

初始化一个list_head就可以了,不用包含在数据的结构体里面:

 

static LIST_HEAD(head);

 

既然是自然结束list_for_each,为何还要继续引用pos? 正如 for(int i = 0; i < MAX; i++), 如果是正常结束,你就不能期待i还在0-MAX范围。

 

这不是什么问题,一个基本的要遵循的逻辑而已。

 

 

 

 

这只是Linux内核开发人员遵循的逻辑吧。我就经常判断变量以确定循环是中途退出了还是完成了,这样就不用再另设标志(想起上学时的老师,总是这一个标记那一个标记,写出来的代码总是比我长很多,这种老师居然还出书,无语)。

 

顺便说一下,你那for语句是C++语法。还有“闭包”的概念,你确定你搞清楚了什么叫闭包?

0 请登录后投票
   发表时间:2012-01-11  
bookjovi 写道
LZ显然读的C程序代码不够多,对Linux kernel理解还不是很深刻,记住C就是C,C不是JAVA,不要用看java代码的思维去看C程序!

>Linux的编程风格有很多并不符合近来越来越被重视的软件工程学。看Linux源码只有一个感觉,为了让系统飙起来,不择手段,管你好不好维护呢
Linux kernel在20年的持续开发中,可维护性比性能更重要,LZ需要时间去理解这句话。


可能我对Linux内核开发的这个“上下文环境”了解不多。很想听一下Linux内核的软件工程理念,可否介绍一二或给些参考资料?
0 请登录后投票
   发表时间:2012-01-11  
thxg 写道
rubynroll 写道
thxg 写道
rubynroll 写道
thxg 写道
这个宏的用法类似于其它语言中的forEach之类的循环,比如下面的代码:

 

struct my_data {
    int value;
    list_head node;
};
//...
struct my_data *pos;
struct list_head *head;
//...
list_for_each_entry(pos, head, node) {
    //...
}
if (pos != NULL) printf("%d\n", pos->value);
 

在我们的编程习惯中,循环遍历结束后,数据指针(pos)要么是最后一个值,要么是NULL。可是这里,如果我们有一个专门的链表头,即专门用一个list_head做头,而没有用无数据的my_data做头的话,上面的代码就会Segment fault了。

 

你要用Linux的list_head的话,就一定要留一个专门作表头,你违反了它的用法,怎么能说是“陷阱”? 在我看来,Linux的链表实现比其他大多数系统都优雅很多,可以说整个Linux内核的各种复杂关系如果没有这么一个高超的链表实现将会是一团糟。

 

单单是免去node的内存管理,可以减少无数的野指针风险。各种for_each宏使C代码看起来就像具备了高级语言才有的“闭包”功能,大大提高了可读性以及降低出错几率。

 

 

 

当然用了专门的表头,即然是专门的表头,配合Linux节约内存的出发点,应该仅用一个list_head就行了吧,那么list_for_each_entry遍历完之后,pos指向表头所在的container里,就成了非法指针。

 

如果你告诉我表头也要用含数据的结构体,那就算了,我情愿自己留意循环外不要再用pos。

 

初始化一个list_head就可以了,不用包含在数据的结构体里面:

 

static LIST_HEAD(head);

 

既然是自然结束list_for_each,为何还要继续引用pos? 正如 for(int i = 0; i < MAX; i++), 如果是正常结束,你就不能期待i还在0-MAX范围。

 

这不是什么问题,一个基本的要遵循的逻辑而已。

 

 

 

 

这只是Linux内核开发人员遵循的逻辑吧。我就经常判断变量以确定循环是中途退出了还是完成了,这样就不用再另设标志(想起上学时的老师,总是这一个标记那一个标记,写出来的代码总是比我长很多,这种老师居然还出书,无语)。

 

顺便说一下,你那for语句是C++语法。还有“闭包”的概念,你确定你搞清楚了什么叫闭包?

 

好吧,我承认这是Linux内核开发人员遵循的逻辑,或者任何一个超过5年C经验的程序员应该有的逻辑。

 

for(int i ....)  知道什么是C99标准么?

 

我说for_each_xxx宏提供了“类似”闭包的功能,原因是:

1)for_each_xxx后面可以带一个“程序块” (有些语言以'匿名函数’形式)

2)所带的“程序块“内部可以直接访问for_each_xxx所处的位置的上下文中的变量。

 

当然,我是说“类似”,而不是真的就有闭包功能,这是C的语言特性决定的。但是宏确实可以提供非常方便的“闭包”特性。

 

 

 

 

 

 

0 请登录后投票
   发表时间:2012-01-11  
好吧,我无视C99,真正有价值的对C的改进,我还没看到。C++自以为改进了C,却被Java和C挤在排行榜的后面。Java很聪明,它改得彻底而成功。

据我简单统计Linux内核中出现for (int i的只有一处,出现for (i 的有193处,可以说明问题了。还有内核中出现过把循环累加量在循环体外作为函数返回值return的。估计写这段代码的人没有5年C经验。
0 请登录后投票
   发表时间:2012-01-11  
忘了说明,那唯一的一处,是段C++代码。我并不想纠结for里for外声明或是哪个标准好,我只想说,你用这个来证明内核开发人员的经验是没有道理的。事实胜于雄辩。

还有闭包,不要误导别人。那个宏与闭包的概念,连形似都算不上。
0 请登录后投票
   发表时间:2012-01-11  
关于for(int i ...) 我只是举了个例子说明for正常退出后iterator超出范围是正常逻辑,不是问题。你却钻了牛角尖说是C++的语法,当我指出你不懂C99中是可以如此用,你确搬出Linux内核中使用这一用法的统计数据...完全不顾讨论的重点在于for_list_each_entry用法问题。

你可以认为出书的老师水平比你差,可以轻易怀疑别人不懂什么叫闭包,可以无视C99,可以认为C++的改进是“自以为” ... 既然如此自负,估计Linux内核代码在你眼里那应该都是垃圾了,还研究什么啊。

作为一个老程序员,我奉劝你学习要有一颗谦卑的心,可以自信,但不能自大。还有,别钻牛角尖。

0 请登录后投票
   发表时间:2012-01-11   最后修改:2012-01-11
thxg 写道
忘了说明,那唯一的一处,是段C++代码。我并不想纠结for里for外声明或是哪个标准好,我只想说,你用这个来证明内核开发人员的经验是没有道理的。事实胜于雄辩。

还有闭包,不要误导别人。那个宏与闭包的概念,连形似都算不上。


你看你看,典型的钻牛角尖了。我什么时候说过“哪个标准好”?你又何必“纠结”呢?

再说了,Linux内核中根本就不采用C99标准编译,我只是举了个再简单不过的例子说明 for正常结束后iterator超出范围是基本逻辑 以此说明 for_list_each_entry正常结束后,pos指向无效地址是正常逻辑。 这个我已经说过太多遍了,实在不想重复。

就是这点逻辑,无它。

至于我说“象闭包”,我可以举个例子:

-- ruby --
a = 0
list.each {
  a += 1
}

--- c ---
a = 0;
for_each_list_entry(pos, list_head, member) {
   a += 1;
}

参考我前面说过的两点,我说有点象,你说呢?

0 请登录后投票
   发表时间:2012-01-11  
c++躺着也中枪呀...
0 请登录后投票
   发表时间:2012-01-11  
open solaris的代码写的确实美观!
0 请登录后投票
   发表时间:2012-01-11  
rubynroll 写道
我只是举了个再简单不过的例子说明 for正常结束后iterator超出范围是基本逻辑 以此说明 for_list_each_entry正常结束后,pos指向无效地址是正常逻辑


谢谢你的黑体字,终于回到正题。闭包我们不讨论,我想探讨的就是软件工程中的一些习惯,没有对错,只是可能每个人都有不同理解。

for正常结束后超出范围是正常逻辑,但for_list_each_entry结束后,不是简单的i >= MAX,而变成了一个危险的不能用的指针。i >= MAX之后,i值仍可以用,且如我前文所说,Linux内核源码中有几处就是这么用了i值。

所以 for和for_list_each_entry宏有了区别,for_list_each_entry要求程序员不要再企图引用pos的值。而Linux内核源码中也并未添加注释说明。说它存在陷阱,我想并不为过。请不要说什么X年经验的程序员都知道,我反感这样的话。想象一下gcc中的各种警告,想象一下QA工具的态度,如果它们有能力知道你的代码中存在一个危险的变量,它们会不会给你一个WARNING?从这个角度上讲,这种甚至没有加以说明的代码风格,算不算质量可控性差?

把此场景放回到内核开发环境中,也许不是什么问题。“风格”本来就是那么回事。

并不是我钻牛角尖,如果你这样的老鸟也只知道写完代码了事,那就太可惜了。如果把问题看作工程学里的问题,那它就值得深思一下了。

前面几楼提到FreeBSD,openSolaris源码风格好,很想听听你们对“风格”的见解。BSD和SUN应该都有些学院派作风,BSD本身就是大学机构,看Java的规范就知道SUN的作风。

不过光看作风似乎也没什么用。PostgreSQL再先进,大家还是喜欢MySQL(据称管理挺混乱),SUN则直接改姓了。尽管个中原因复杂,但我们是否能思考些什么呢?软件的工程学是不是需要巅覆一下呢?
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics