2、非线性区域
在1、中,可以看到每张sk_buff的图: 在end指针紧挨着一个非线性区域 ;
在struct sk_buff中没有指向skb_shared_info结构的指针,利用end指针,,可以用skb_shinfo宏来访问:
#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB)))
其中skb_end_pointer函数如下,返回end指针
static inline unsigned char *skb_end_pointer(const struct sk_buff *skb)
{
return skb- >end;
}
具体地,struct skb_shared_info如下:
struct skb_shared_info {
__u8 __unused;
__u8 meta_len;
//数组frags包含的元素个数
__u8 nr_frags;
__u8 tx_flags;
unsigned short gso_size;
/* Warning: this field is not always filled in (UFO)! */
unsigned short gso_segs;
struct sk_buff *frag_list;
struct skb_shared_hwtstamps hwtstamps;
unsigned int gso_type;
u32 tskey;
/*
* Warning : all fields before dataref are cleared in __alloc_skb()
*/
//结构skb_shared_info 的引用计数器
atomic_t dataref;
/* Intermediate layers must ensure that destructor_arg
* remains valid until skb destructor */
void * destructor_arg;
/* must be last field, see pskb_expand_head() */
skb_frag_t frags[MAX_SKB_FRAGS];
};
其中skb_frag_t如下:
typedef struct skb_frag_struct skb_frag_t;
struct skb_frag_struct {
struct {
//指向文件系统缓存页的指针
struct page *p;
} page;
#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536)
//数据起始地址在文件系统缓存页中的偏移
__u32 page_offset;
//数据在文件系统缓存页中使用的长度
__u32 size;
#else
__u16 page_offset;
__u16 size;
#endif
};
nr_frags,frags,frag_list与IP分片存储有关。
frag_list的用法:
判断是否存在非线性缓冲区:
如下所示,没有开启分片的报文len = x,data_len = 0:
如下所示在Linux内核中,使用skb_is_nonlinear函数判断是否存在分片,即通过判断data_len的大小是否为0:
static inline bool skb_is_nonlinear(const struct sk_buff *skb)
{
return skb- >data_len;
}
普通聚合分散I/O的报文:
采用聚合分散I/O的报文, frag_list为 NULL,nf_frags不等于0 ,说明这不是一个普通的分片,而是聚合分散I/O的报文。
如下所示:
nr_frags为2,而frag_list为NULL,说明这不是普通的分片,而是聚合分散I/O分片,数量为2,这两个分片指向同一物理分页,各自在分页中的偏移和长度分别是0/S1和S1/S2。
FRAGLIST类型的分散聚合I/O的报文:
采用FRAGLIST类型的分散聚合I/O报文, ** frag_list不为NULL,nf_frags等于0 ,** 数据长度len为x+S1,data_len为S1,
以上从struct sk_buff的四大指针以及操作、非线性区域对套接字缓存(socket buffer)进行分析,更多sk_buff的分析、实操等将在以后的文章中梳理。
全部0条评论
快来发表一下你的评论吧 !