Linux TCP/IP协议栈的通用编码模式解析
在以下情况下,需要增加数据结构的引用计数:
两个数据结构有紧密的关系。在这种情况下,一个结构会包含一个初始化指向另一个结构的指针。
一个定时器函数需要访问一个数据结构。在定时器运行时,会增加这个数据结构的引用计数,这样做的原因是:在定时器执行完成前,你不希望这个数据结构被提前释放了。
成功地在一个链表或哈希表中找到一个匹配的表项。在大多数情况下,这个查找到的表项会被查找者使用。因此,在查找函数里面,成功匹配的数据结构需要增加它的引用计数,而这个引用计数会被查找者减小。
当数据结构的最后一个引用被释放后,这个数据结构就可以被释放了,因为它现在已没 有什么作用了。当然,这并不是一个必须的操作。
4. 垃圾收集
内存是一种共享并且有限的资源,所以不应该浪费,尤其是在内核中。因为内核没有使用虚拟内存。大多数的内核子系统都实现了某种类型的垃圾收集机制去收集那些无用的或者过期的数据结构所占用的内存。从已有的实现来看,主要有以下两种垃圾收集机制:
异步
这种类型的垃圾收集机制与具体的事件无关。它使用一个定时器函数去定时检查一组数据结构并把可以释放的数据结构释放掉。判断一个数据结构是否可以被释放的条件依赖于子系统的功能和内部逻辑,但是一个基本的条件就是这个数据结构的引用计数为0。
同步
当内存短缺时,如果不能等到异步的垃圾收集函数定时运行,内核可以激活一个立即执行的垃圾收集函数。在这个函数里,判断一个数据结构是否可以被释放的条件可以和异步的垃圾收集函数不一样(例如,它可以释放一些引用计数不为0的数据结构)。
5. 函数指针和虚拟函数表 (VFTs)
使用函数指针是一个可以得到清晰的C语言代码的同时又能利用面向对象语言的某些优点的好方法。在定义数据结构时(对象),你可以包含一组函数指针(方法)。一部分或全部的数据结构操作可以通过这些函数来完成。在C语言中,数据结构中的函数指针如下所示:
struct sock {
...
void (*sk_state_change)(struct sock *sk);
void (*sk_data_ready)(struct sock *sk, int bytes);
...
};
使用函数指针最大的好处是在初始化对象时,可以根据对象的不同或对象作用的不同而把函数指针赋为不同的值。这样,同样是调用sk_state_change函数,可以就会激活不同sock对象的不同函数。
在网络代码中,函数指针被大量的使用。以下就是一些例子:
在路由子系统中,处理进入或发出包时,它会初始化sk_buff中的两个函数指针。
当一个包准备好往网络设备发送时,它会被传递给net_device结构中的hard_start_xmit函数指针。这个指针在网络设备驱动和网络设备绑定时被赋值。
当L3的协议发送一个包时,它会调用一组函数指针中的一个。这些指针在L3的地址解析协议处理中被初始化。具体调用哪个函数要看它被初始化成哪个函数,L3到L2的地址解析过程是透明的(例如,IPv4使用arp协议)。如果地址解析过程没有必要,这些函数指针会被赋为其他值。
在上面的例子中,我们可以看到,函数指针可以被用作不同内核组件之间的接口;或者是一个子系统在不同的条件下调用不同函数的机制;或者是被用做允许不同的协议,驱动程序或功能可以使用各自不同的方法的机制。


















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