linux内核代码-注释详解:udp_recvmsg

/*linux-5.10.x\net\ipv4\udp.c
 *负责接收UDP数据包,并进行数据处理、复制和相关的控制消息处理。也处理接收过程中的错误情况,并提供时间戳和丢弃计数等统计信息
	1、参数解析:函数接收struct sock类型的套接字指针sk和struct msghdr类型的消息头指针msg作为参数
	2、数据接收:函数通过调用sk_recv_datagram函数从套接字的接收队列中获取一个数据包,
				 该函数会处理接收缓冲区的相关逻辑,包括数据包的复制和消耗等
	3、数据处理:函数根据套接字的类型和配置,对接收到的数据包进行处理,例如校验和验证、解析IP头部、解析UDP头部等
	4、数据复制:函数将接收到的数据复制到msg结构体中的缓冲区,并更新复制的数据长度
	5、控制消息处理:函数根据套接字的配置,处理控制消息,例如获取源地址信息、处理IP层的控制消息等
	6、错误处理:函数处理接收过程中可能出现的错误情况,例如校验和错误、无法放入接收队列等,同时更新相关的统计信息
	7、时间戳和丢弃计数:函数根据套接字的类型和配置,获取接收数据包的时间戳和丢弃的数据包计数,
						 并将其存储在msg结构体中的msg_control字段中
	8、返回值:函数返回已经复制的数据长度
*/
int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
		int flags, int *addr_len)
{
	struct inet_sock *inet = inet_sk(sk);  							// 获取套接字的底层数据结构 inet_sock 的指针
	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 	// 将消息头部的 msg_name 强制转换为 sockaddr_in 类型
	
	struct sk_buff *skb;  											// 用于处理网络数据包
	unsigned int ulen, copied;  									// UDP 数据包长度和拷贝长度
	int off, err, peeking = flags & MSG_PEEK;  						// 偏移量 off、错误码 err 和标志位 peeking,标志是否进行 MSG_PEEK 操作
	int is_udplite = IS_UDPLITE(sk);  								// 判断是否为 UDPLite 协议(轻型 UDP)
	bool checksum_valid = false;  									// 标志校验和是否有效

	if (flags & MSG_ERRQUEUE)  										// 如果flags参数中包含了MSG_ERRQUEUE标志
		return ip_recv_error(sk, msg, len, addr_len);  				// 处理接收错误队列中的数据包,并返回结果

try_again:  														/* 设置标签try_again,在需要重新尝试接收数据包时跳转到该标签 */
	off = sk_peek_offset(sk, flags);  								// 获取接收缓冲区的偏移量
	skb = __skb_recv_udp(sk, flags, noblock, &off, &err);  			// 从套接字的接收队列中获取一个UDP数据包的skb
	if (!skb)
		return err;  												// 如果获取数据包失败,则返回错误码

	ulen = udp_skb_len(skb);  										// 获取接收到的 UDP 数据包的总长度ulen
	copied = len;    												// 将拷贝长度 copied 设置为用户请求的数据长度len
	if (copied > ulen - off)
		copied = ulen - off;  										// 若拷贝长度超过数据包剩余长度(ulen - off),将拷贝长度设置为数据包剩余长度
	else if (copied < ulen)
		msg->msg_flags |= MSG_TRUNC; 								// 如果拷贝长度小于数据包总长度,则设置消息头部的 msg_flags 标志位为 MSG_TRUNC,表示数据被截断
	
	
	if (copied < ulen || peeking ||									// 如果已拷贝的数据长度小于期望接收的数据长度(ulen)或是 peeking 操作(即只查看数据而不拷贝)
	    (is_udplite && UDP_SKB_CB(skb)->partial_cov)) {				// 或者是UDPLite协议并且该数据包是部分覆盖的
	    checksum_valid = udp_skb_csum_unnecessary(skb) ||			// 检查校验和是否是不必要的
	            !__udp_lib_checksum_complete(skb);					// 检查校验和是否完整
	    if (!checksum_valid)										// 如果校验和无效
	        goto csum_copy_err;									// 跳转到错误处理标签csum_copy_err
	}

	
	if (checksum_valid || udp_skb_csum_unnecessary(skb)) {			// 检查校验和是否有效,如果有效或者校验和不必要,则进行数据包拷贝操作
	    
	    if (udp_skb_is_linear(skb))									// 如果数据包的缓冲区是线性存储的(连续存储)
	        err = copy_linear_skb(skb, copied, off, &msg->msg_iter);// 在线性缓冲区内拷贝 skb 数据包的一部分到用户空间
	        /*
		        skb:    指向 sk_buff 数据包的指针
				copied:已经拷贝到用户空间的数据长度
				off:    从数据包的偏移量开始拷贝数据到用户空间
				msg->msg_iter:指向消息迭代器的指针(用于记录当前拷贝位置和剩余长度)
	        */
	    else														// 如果数据包的缓冲区不是线性存储的
	        err = skb_copy_datagram_msg(skb, off, msg, copied);  	// 将相关的散列缓冲区数据逐个复制到用户空间消息结构体中,直到拷贝完成或者到达指定的拷贝长度
	} else {														// 如果校验和无效
	    err = skb_copy_and_csum_datagram_msg(skb, off, msg);  		// 将 skb(sk_buff)数据包的一部分拷贝到用户空间,并在拷贝过程中计算校验和

	    if (err == -EINVAL)
	        goto csum_copy_err;  									// 如果拷贝时出现无效参数错误,则跳转到 csum_copy_err 标签处处理
	}

	if (unlikely(err)) {											// 如果拷贝过程中发生错误,则进行相应处理
	    if (!peeking) {
	        atomic_inc(&sk->sk_drops);  							// 如果非 MSG_PEEK 操作,则增加套接字对象的 sk_drops 计数
	        UDP_INC_STATS(sock_net(sk),
	                      UDP_MIB_INERRORS, is_udplite);  			// 增加 UDP 错误统计信息:输入错误的数据报数量
	    }
	    kfree_skb(skb);  											// 释放 sk_buff 对象的内存空间
	    return err;  												// 返回错误码给调用函数
	}

	if (!peeking)													// 如果拷贝成功且非 MSG_PEEK 操作,则增加套接字对象的相应统计信息
	    UDP_INC_STATS(sock_net(sk),
	                  UDP_MIB_INDATAGRAMS, is_udplite);
	sock_recv_ts_and_drops(msg, sk, skb);  							// 在接收数据包时获取时间戳和丢弃的数据包计数

	/* 复制地址信息 */
	if (sin) {														// 如果sin不为空指针
	    sin->sin_family = AF_INET;									// 复制协议族,存储到sin结构体中
	    sin->sin_port = udp_hdr(skb)->source;  						// 复制源端口号,存储到sin结构体中
	    sin->sin_addr.s_addr = ip_hdr(skb)->saddr;  				// 复制源 IP 地址,存储到sin结构体中
	    memset(sin->sin_zero, 0, sizeof(sin->sin_zero));			// 设置其他字段为0
	    *addr_len = sizeof(*sin);									// 将addr_len设置为sin结构体的大小

	    if (cgroup_bpf_enabled)										// 如果启用了cgroup BPF,则在接收数据包之前运行相关的BPF程序
	        BPF_CGROUP_RUN_PROG_UDP4_RECVMSG_LOCK(sk,
	                        (struct sockaddr *)sin);
	}

	if (udp_sk(sk)->gro_enabled)										// 如果套接字启用了 GRO(Generic Receive Offload)
	    udp_cmsg_recv(msg, sk, skb);									// 处理相关的控制消息

	if (inet->cmsg_flags)												// 如果 inet 结构体的 cmsg_flags 不为零
	    ip_cmsg_recv_offset(msg, sk, skb, sizeof(struct udphdr), off);	// 处理IP层的控制消息,偏移量为 sizeof(struct udphdr)

	err = copied;														// 将err设置为已经复制的数据长度copied
	if (flags & MSG_TRUNC)												// 如果数据是被截断的
	    err = ulen;														// 将err设置为UDP数据包的总长度ulen

	skb_consume_udp(sk, skb, peeking ? -err : err);						// 消耗已经复制的数据,并更新套接字的内部状态
	return err;															// 返回复制的数据长度err

csum_copy_err:															/* 进行错误处理 */
	if (!__sk_queue_drop_skb(sk, &udp_sk(sk)->reader_queue, skb, flags,
	                 udp_skb_destructor)) {								//  如果不能将数据包skb放入套接字的读队列中
	    UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);	// 增加UDP的校验和错误统计
	    UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite);		// 增加UDP的接收错误统计
	}
	kfree_skb(skb);														// 释放数据包的内存空间

	cond_resched();														// 重新开始处理新的数据包,但如果需要让出 CPU,则进行调度
	msg->msg_flags &= ~MSG_TRUNC;										// 将msg的msg_flags字段的MSG_TRUNC标志位清除
	goto try_again;  													// 返回 try_again 标签处重新尝试接收数据包
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
内核代码为基础,介绍了完整的linux内核实现原理 ·选择早期linux内核进行介绍,排除了目前内核中复杂而庞大的实现细节 ·整个内核代码仅有14000行 ·提供可运行相关内核的完整实验系统 本书对早期的linux操作系统内核(v0.11)全部源代码文件进行了详细的注释和说明,旨在让读者能够在短时间内对linux的工作机理获得全面而深刻的理解,为进一步学习和研究linux系统打下坚实的基础。书中首先介绍了linux系统的发展历史,着重说明了各个内核版本之间的重要区别,给出了选择0.11版作为研究对象的原因;然后依据内核代码的组织结构对所有代码进行了详细注释。在注释的同时,还介绍了读者应该了解的相关知识,并给出了相关的硬件信息。本书还介绍了内核代码的组织结构及相互关系。 本书适合作为计算机专业学生学习操作系统课程的实践教材和参考书,也适合linux操作系统爱好者自学,还可供具有一定基础的技术人员作为嵌入式开发应用的参考书。 *************************************************************** 请注意: 下载完,评论的同时,请点击评论框上方的五角星(共5个五角星),这样你的被扣的积分就可以返还了。 如果只评论,不点击小五角星,积分不会返还。 一定要先下载完,再评论。如果先评论后下载,或者在下载的过程中评论,积分同样不会返还。 *************************************************************** 更多linux、ARM和C语言资源请参考: http://blog.csdn.net/arkofnoach/archive/2010/10/23/5960560.aspx

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值