Linux TCP/IP协议栈的通用编码模式解析

时间:2007-05-26 07:41:51  来源:站长资讯收集整理  作者:佚名

我们来看一个例子。当一个设备驱动向内核注册一个网络设备后,内核会执行一些与设备类型无关的函数。在某些点上,它会调用net_device结构中的一些函数指针来让设备驱动做一些事情。设备驱动可以把这些函数指针初始化为自己的函数,也可以把它置为NULL,这表示内核执行缺省函数就可以了。

在调用函数指针前,要先检查一下函数指针的值,以避免引用空指针。下面是一个从register_netdevice取得的快照例子:

if (dev->init && dev->init(dev) != 0)

{

...

}

函数指针有一个主要的缺点:它使得阅读源代码变得困难一些。当阅读一个给定的代码路径时,你可能会关注其中的函数指针调用。在这种情况下,你需要先了解这个函数指针是如何被初始化的之后,才能阅读后续的代码。函数指针初始化与很多不同的因素有关:

如果函数指针的赋值与特定的数据有关,比如协议标识或者收到包的是某个特定的驱动程序。这种情况下,很容易找到真正的函数。比如,如果drivers/net/3c59x.c驱动收到一个包,你可以在设备初始化函数中找到 net_device结构中的函数指针被赋值为那些值。.

如果函数指针的赋值与更复杂的逻辑相关,比如L3到L2地址映射中的状态值。这种情况下,函数指针被赋为何值与外部的事件相关,因而难以预测。

一组函数指针放到一个数据结构中,通常叫做一个虚函数表(VFT)。当虚函数表被用做两个子系统间的接口,比如L3和L4间的接口;或者被导出做为某个内核组件的接口(一组对象)时,它里面可能包含很多不同的指针,这些指针在不同的协议或功能中使用。每一个功能可能只用到一小部分函数指针。当然,如果虚函数表用得过度,就会变得非常庞大,这种情况下,可能需要重新设计你的数据结构。

6. goto 语句

没有哪个c程序员会喜欢goto语句的。如果不看goto语句的历史(计算机编程历史上最长也是最有名的争论之一),我的总结是,goto语句是个过时的东西,但是为什么linux内核还在使用它?

任何一段使用goto语句的代码都可以用无goto语句的代码重写。使用goto语句会降低代码的可读性,同时会增加调试的难度。因为你不能完全确定执行goto之后的语句所需的条件。

让我们来做这样的类比:给定树中的任意节点,你可以明确知道从根到节点的路径。但是如果是随机缠绕的葡萄藤,你就不能总是得到一条从根到节点的唯一路径。

但是,由于c语言没有提供异常捕获机制(其他语言里。异常捕获通常也是被禁止的,因为使用异常捕获会导致性能下降,并且增加代码的复杂性),小心地使用goto语句可以很容易地将代码跳转到异常处理代码中。在内核编程中,特别是网络代码,异常事件很常见,所以,goto语句就成了一个方便的工具。虽然内核中使用了goto语句,但是我并不主张开发人员滥用它。尽管内核中有超过 30,000条goto语句,但是它们主要用于在同一个函数里面返回不同的值,或者跳出超过一层的嵌套。

7. 容器Vector定义

某些情况下,一个数据结构会在末尾包含一个可选的数据块。如下面的例子所示

struct abc {

int age;

char *name[20];

...

char placeholder[0];

}

可选块从placeholder开始。请注意,placeholder被定义成长度为0的Vector。这就意味着,在给abc分配空间时,同时也分配了一个可选块,placeholder就是块的指针。如果不需要可选块, placeholder就只是一个指向结构末尾的指针,它不占用任何空间。

文章评论

共有 位CH网友发表了评论 查看完整内容