Socker Buffer的克隆

在处理网络数据包时,为了提高性能并允许不同进程无冲突地读取同一数据包,内核使用sk_buff克隆功能。sk_buff克隆仅复制数据结构而不复制数据包本身,通过增加引用计数确保资源管理。skb_clone函数用于实现克隆,其内部调用__skb_clone完成详细复制工作,包括设置克隆标识、用户计数和数据包引用计数。克隆后的sk_buff不归属任何套接字,且只能进行读操作,避免并发访问问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 skb克隆

在什么时候需要对Socket Buffer进行克隆,某些时候,同一个Socket Buffer会由不同的进程进行独立处理,但这些进程所需要操作的只是sk_buff数据结构描述符,不需要对数据包本身做改动。这是为了提高处理性能,内核不需要对整个Socket Buffer(sk_buff数据结构和数据包缓冲区)做完全的复制,只对sk_buff数据结构做完全的复制,并对数据包的引用计数(dataref)加1,以此防止在还有进程使用该Socket Buffer的数据包情况下,缓冲区被释放。这就是sk_buff克隆函数的功能。这时原sk_buff和克隆的ks_buff共享同一个数据包。实现克隆操作的函数为:struct sk_buff *sk_clone(struct sk_buff *skb, gfp_t gtp_mask)。skb_clone产生一个队skb的克隆数据结构,返回指向克隆出来的sk_buff数据结构的指针。

克隆的sk_buff都具有以下特点:

  • 不放任何sk_buff的管理队列
  • 不属于任何套接字,即没有任何套接字拥有克隆的sk_buff.
  • 两个sk_buff的结构的skb->cloned域都需要设置为1,克隆出来的sk_buff的sk->users域应设置为1,这样当要释放克隆出来的sk_buff数据结构时,第一次对他的释放就能成功了。
  • 当一个sk_buff被克隆后,他的数据包中的值就不能再修改,这时我们访问数据包是可以不加锁,因为只能对数据包作读操作,也就不存在并发访问的问题

2 skb_clone函数

struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
{
	struct sk_buff_fclones *fclones = container_of(skb,
						       struct sk_buff_fclones,
						       skb1);
	struct sk_buff *n = &fclones->skb2;

	if (skb_orphan_frags(skb, gfp_mask))
		return NULL;

	if (skb->fclone == SKB_FCLONE_ORIG &&
	    n->fclone == SKB_FCLONE_FREE) {
		n->fclone = SKB_FCLONE_CLONE;
		atomic_inc(&fclones->fclone_ref);
	} else {
		if (skb_pfmemalloc(skb))
			gfp_mask |= __GFP_MEMALLOC;

		n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
		if (!n)
			return NULL;

		kmemcheck_annotate_bitfield(n, flags1);
		n->fclone = SKB_FCLONE_UNAVAILABLE;
	}

	return __skb_clone(n, skb);
}

实际调用的时__skb_clone

static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
{
#define C(x) n->x = skb->x

	n->next = n->prev = NULL;
	n->sk = NULL;
	__copy_skb_header(n, skb);

	C(len);
	C(data_len);
	C(mac_len);
	n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
	n->cloned = 1; 	//克隆标识 
	n->nohdr = 0;
	n->destructor = NULL;
	C(tail);
	C(end);
	C(head);
	C(head_frag);
	C(data);
	C(truesize);
	atomic_set(&n->users, 1);  //设置标识位 

	atomic_inc(&(skb_shinfo(skb)->dataref)); //引用计数加1
	skb->cloned = 1;  //克隆标识

	return n;
#undef C
}

__skb_clone主要做了4件事

  • 对Socket Buffer数据结构各元素做复制
  • skb->cloned设置为1
  • 克隆的数据包skb->users设置为1
  • skb->dataref引用计数加1

 

C#有几个开源的Socket框架可以选择使用。其中Helios是一套高性能的Socket通信中间件,使用C#编写,受到Netty的启发,使用非阻塞的事件驱动模型架构来实现高并发高吞吐量。它简化了Socket编程,处理了高并发情况下的解包、粘包、buffer管理等等。另一个是SuperSocket,它是一个轻量级、跨平台且可扩展的.Net/Mono Socket服务器程序框架。使用SuperSocket可以轻松开发出各种类型的Socket服务器端软件,如游戏服务器、GPS服务器、工业控制服务和数据采集服务器。此外,还有DotNetty,它可以帮助快速开发高性能、高可靠性的网络服务器和客户端程序。 另外,如果你需要一个类似于Socket的接口,但是支持N:M的关系,可以考虑使用ZMQ(ZeroMQ)。ZMQ是一个类似于Socket的接口,但是它支持N:M的关系,使得网络编程更加简单。ZMQ用于节点(可以是主机或进程)之间的通信,屏蔽了传统Socket编程的细节,让网络编程更为简单。 还有一个开源选择是Jafka,它是一个高性能、跨语言的分布式消息系统,最早是由Apache孵化的Kafka克隆而来。Jafka支持开放式的数据传输协议,除了Java,还支持Python、Ruby、C和C等其他语言。 综上所述,C#有多个开源的Socket框架可供选择,包括Helios、SuperSocket和DotNetty。如果需要支持N:M的通信关系,可以考虑使用ZMQ。另外,如果需要一个分布式消息系统,可以考虑使用Jafka。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [.NET/C# 开源资源收集](https://blog.youkuaiyun.com/lllooollpp/article/details/59111394)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [一共81个,开源大数据处理工具汇总](https://blog.youkuaiyun.com/Together_CZ/article/details/68107023)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值