ip_append_page函数

当应用层调用sendmsg将数据包从用户空间地址移动到内核空间地址,这个复制是ip_append_data函数的输入参数getfrag函数完成。内核还为应用层提供了sendfile接口,它优化数据包的发送复制,这个接口称为零拷贝。sendfile接口只有当网络设备支持Scatter/Gather I/O功能才能使用,这时的ip_append_page不需要复制任何数据,内核只需要将frags数组初始化指向接受数据包缓冲区位置,在必要的时候计算传输层的校验和。ip_append_page收到数据包的位置void*指针,就直接用收到的页面指针和偏移量来初始化frag数组的一个成员。

ip_append_data和ip_append_page的唯一不同在处理Scatter/Gather I/O功能,代码如下:

i = skb_shinfo(skb)->nr_frags;
		if (len > size)
			len = size;
		//检查数据段是否可以和已有的数据段合并
		if (skb_can_coalesce(skb, i, page, offset)) {
			//可以合并,就加上数据段的长度
			skb_shinfo(skb)->frags[i-1].size += len;
		} else if (i < MAX_SKB_FRAGS) {
			get_page(page);
			//不能合并出事一个新的数据段
			skb_fill_page_desc(skb, i, page, offset, len);
		} else {
			err = -EMSGSIZE;
			goto error;
		}

向页面加入一个新的数据段时,ip_append_page首先判断新的数据段是否可以和已有的数据段合并,这个检查由skb_can_coalesce函数完成,skb_can_coalesce检查新的数据段指针是否是页面中最后一个成员指针的结束位置,如果可以合并就更新页面中数据段的长度。

如果不能合并就调用skb_fill_page_desc初始化一个新的数据段,这时get_pae对页面的引用计数加1。

static inline void skb_fill_page_desc(struct sk_buff *skb, int i,
				      struct page *page, int off, int size)
{
	skb_frag_t *frag = &skb_shinfo(skb)->frags[i];

	frag->page		  = page;
	frag->page_offset	  = off;
	frag->size		  = size;
	//引用计数加1
	skb_shinfo(skb)->nr_frags = i + 1;
}

目前只有UDP使用ip_append_page函数,TCP在tcp_sendmsg中实现了同样的逻辑对应零拷贝接口TCP使用do_tcp_sendpage。

猜你喜欢

转载自blog.csdn.net/City_of_skey/article/details/84206387
今日推荐