dev_alloc_skb也是一个缓冲区分配函数,它主要被设备驱动使用,通常用在中断上下文中。这是一个 alloc_skb函数的包装函数,它会在请求分配的大小上增加16字节的空间以优化缓冲区的读写效率,它的分配要求使用原子操作 (GFP_ATOMIC),这是因为它是在中断处理函数中被调用的。 static inline struct sk_buff *dev_alloc_skb(unsigned int length) { return _ _dev_alloc_skb(length, GFP_ATOMIC); } static inline struct sk_buff *_ _dev_alloc_skb(unsigned int length, int gfp_mask) { struct sk_buff *skb = alloc_skb(length + 16, gfp_mask); if (likely(skb)) skb_reserve(skb, 16); return skb; } 如果没有体系架构相关的实现,缺省使用__dev_alloc_skb的实现。 5.2. Freeing memory: kfree_skb and dev_kfree_skb 这两个函数释放缓冲区,并把它返回给缓冲池(缓存)。kfree_skb可以直接调用,也可以通过包装函数 dev_kfree_skb调用。后面这个函数一般被设备驱动使用,与之功能相反的函数是dev_alloc_skb。dev_kfree_skb仅是一 个简单的宏,它什么都不做,只简单地调用kfree_skb。这些函数只有在skb->users为1地情况下才释放内存(没有人引用这个结构)。 否则,它只是简单地减小 skb->users。如果缓冲区有三个引用者,那么只有第三次调用dev_kfree_skb或kfree_skb时才释放内存。 图6中的流程图显示了分配一个缓冲区所需要的步骤。当sk_buff释放后,dst_release同样会被调用以减小相关dst_entry数据结构的引用计数。 如果destructor被初始化过,相应的函数会在此时被调用. 在图5中,我们看到,一个简单的场景是:一个sk_buff结构与另一个内存块相关,这个内存块里存储的是真正的数据。 当然,内存块底部的skb_shared_info数据结构可以包含指向其他分片的指针(参见图5)。如果存在分片,kfree_skb同样会释放这些分 片所占用的内存。最后,kfree_skb 把sk_buff结构返回给skbuff_head_cache缓存。 5.3. Data reservation and alignment: skb_reserve, skb_put, skb_push, and skb_pull skb_reserve可以在缓冲区的头部预留一定的空间,它通常被用来在缓冲区中插入协议头或者在某个边界上对齐。这 个函数改变data和tail指针,而data和tail指针分别指向负载的开头和结尾,图4(d)展示了调用skb_reserve(skb,n)的结 果。这个函数通常在分配缓冲区之后就调用,此时的 data和tail指针还是指向同一个地方。 如果你查看某个以太网设备驱动的收包函数(例如,drivers/net/3c59x.c中的vortex_rx), 你就会发现它在分配缓冲区之后,在向缓冲区中填充数据之前,会调用下面的函数: skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ Figure 6. kfree_skb function
由于以太网帧的头部长度是14个八位组,这个函数把缓冲区的头部指针向后移动了2个字节。这样,紧跟在以太网头部之后的IP头部在缓冲区中存储时就可以在16字节的边界上对齐。如图7所示。 Figure 7. (a) before skb_reserve, (b) after skb_reserve, and (c) after copying the frame on the buffer
图8展示了一个在发送过程中使用skb_reserve的例子。 Figure 8. Buffer that is filled in while traversing the stack from the TCP layer down to the link layer