关键数据结构
1. sk_buf
sk_buf{
布局字段
通用字段
功能专用字段
管理函数
}
1.1 布局字段
内核为了方便搜寻和组织数据结构,使用了双向链表来维护所有的sk_buf结构,但是该表的组织比传统的双向链表更为复杂。
struct sk_buf_head{
struct sk_buf *next
struct sk_buf *prev
__u32 qlen //元素的数目。
spinloct_t lock //防止对表的并发访问。
}
struct sk_buf {
struct sk_buf *next
struct sk_buf *prev
struct sk_buf_head *list //指向专一的sk_buf_head数据结构。
struct sock *sk //指向缓冲区的套接字数据结构,当数据由进程接收时,就需要这个指针。T4以及用户程序使用。
unsigned int len //缓冲区数据块的大小(含数据包的头)。
unsigned int data_len //只计算片段中的数据大小。
unsigned int mac_len //mac报头的大小。
atomic users //引用计数,使用sk_buf实例的数目。
unsigned int truesize //此缓冲区的总大小。
unsigned char *head //缓冲区数据的开端。
unsigned char *end //缓冲区数据的尾端。
unsigned char *data //缓冲区实际数据的开端。
unsigned char *tail //缓冲区实际数据的尾端。
void (*destructor)(...) //函数指针可初始化为一个函数,但缓冲区被删除时,可完成某项工作。
}
1.2 通用字段
struct sk_buf {
...
struct timeval stamp //时间戳,表示封包何时被接收,或有时用于封包预定传输的时间。
struct net_device *dev //用于描述一个网络设备。
struct net_device *input_dev //已被接收封包来自的设备。
struct net_device *real_dev //此字段只对虚拟设备有意义。
union {...} h //h是针对L4(传输层)。
union {...} nh //nh是针对L3(网络层)。
union {...} mac //mac是针对L2(网络接口层)。
struct dst_entry dst //这个结构由路由子系统使用。
char cb[40] //“控制缓冲区”,为每一层内部起维护作用。
unsigned int csum //校验和。
unsigned char ip_summed //状态标识。
unsigned char cloned //相当于一个boolean标识位,改结构是不是另一个sk_buf拷贝来的。
unsigned char pkt_type //根据帧的L2目的地址进行划分。
__u32 priority //标示真被传输或转发的封包QoS等级。
unsigned short protocol //从L2层来看,用于下一层较高的协议。
unsigned short security //封包的安全级别。
}
1.3 功能专用字段
struct sk_buf {
...
/**
以下参数由防火墙使用。
*/
unsigned long mfmark
__u32 nfcache
__u32 nfctinfo
struct nf_conntrack *nfct
unsigned int nf debug
struct nf_brudge_info *nf_bridge
union {...} private //这个联合由HIPPI(高性能并行串口)使用。
/**
以下参数由于流量控制使用。
*/
__u32 tc_index
__u32 tc_verd
__u32 tc_classid
struct sec_path *sp //由IPsec协议组使用,已记录转换信息。
}
1.4 管理函数
分配内存:alloc_skb和dev_alloc_skb
alloc_skb是分配缓冲区的主要函数。alloc_skb通过调用kmem_cache_alloc函数,从一个缓存中渠道sk_buff数据结构,然后调用kmalloc以取得一个数据缓冲区。
skb = kmem_cache_alloc(...)
...
size = SKB_DATA_ALIGN(size)
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask)
dev_alloc_skb是由设备驱动程序使用的缓冲区分配函数,应该在中断模式中执行。此函数只是包裹了alloc_skb的函数,为了优化的原因在申请之上加了16个字节。因为此函数是由中断事件处理的函数调用,所以要求原子操作(GFP_ATOMIC)。
释放内存:kfree_skb和dev_kfree_skb
这两个函数会释放一个缓冲区,使其返回缓存池子。dev_kfree_skb不做任何的事情,只是简单的调用kfree_skb。只有当skb->users = 1的时候,这个函数才会释放缓冲区。否则,递减该计数器。
数据预留及对齐:skb_reserve、skb_put、skb_push以及skb_pull
skb_resever会在缓冲区头部预留一些空间,通常允许插入一些包头,或者强迫数据对齐某个边界。注意,sk_reserve并没有将任何东西移入数据缓冲区内,只是更新了两个指针。
skb_push会把一个数据块添加到缓冲区的开端,而skb_put会把一个数据块添加到缓冲区的结尾。skb_pull是吧head指针向前移,吧一个数据块从缓冲区的头部删除。
skb_shared_info结构和skb_shinfo函数
skb_shared_info用以保持数据块区域的附加信息,此数据结构标记数据尾端的end指针后。
struct skb_shared_info {
automic_t dataref //数据块的用户数目。
/**
以下字段用来处理IP片段。
*/
unsigned int nr_frags
struct sk_buff *frag_list
skb_frag_t frags[MAX_SKB_FRAGS]
/**
以下字段用来处理TCP节段卸载。
*/
unsigned short tso_size
unsigned short tso_seqs
}
skb_buff结构没有指向skb_shared_info的字段,为了访问该结构,必须使用返回end指针的skb_shinfo宏。
缓冲区的克隆和拷贝
当一个缓冲区需要被不同消费者处理时,那些消费者可能需要sk_buf描述符的内容。skb_clone只克隆sk_buff结构,共享数据缓冲区。当一个缓冲区被克隆时,数据不能被修改。当程序员想要修改缓冲区的数据的时候可以使用pskb_copy和skb_copy。