Linux socket 数据发送过程深入分析

本文深入探讨Linux socket数据发送流程,从用户空间Buffer到内核Socket Buffer,再到NIC网卡Buffer,详细阐述数据包的封装与解封装过程。同时,分析了报文发送的内核态处理及socket文件描述符与struct sock的关系,揭示了如何通过socket描述符找到对应的内核结构体。

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

socket数据发送流程

数据发送方需要将数据从用户应用空间Buffer复制到内核空间的Socket Buffer中。然后Kernel空间中添加数据包头,进行数据封装。通过一系列多层网络协议(TCP、UDP、IP等)的数据包处理工作,数据才被Push到NIC网卡中的Buffer进行网络传输。

消息接受方接受从远程机器发送的数据包后,要将数据包从NIC buffer中复制数据到Socket Buffer。然后经过一些列的多层网络协议进行数据包的解析工作。解析后的数据被复制到相应位置的用户应用空间Buffer。这个时候再进行系统上下文切换,用户应用程序才被调用。下图展示了传统的TCP/IP协议层的工作原理。
socket数据收发流程,本图来源于优快云博客

报文发送

用户态调用sendto()或sendmsg()发送报文的内核态处理流程如下,由套接字层最终会调用到packet_sendmsg()组装struct sk_buff结构体,最后调用dev_queue_xmit函数通过网卡发送出去。

sys_sendto()->sock_sendmsg()->__sock_sendmsg()->packet_sendmsg()->dev_queue_xmit()

socket 文件描述符与struct sock对应关系

平常我们使用socket函数创建一个socket,成功的话返回一个描述符,这里简称SockFd。linux内核为每个socket维护了一个struct sock的全局变量。用户使用write、send函数都需要使用SockFd,那么内核是如何通过SockFd找到对应struct sock的?这就需要分析socket创建的内核层代码:

//description:socket() creates an endpoint for communication and returns a descriptor.
int socket(int domain, int type, int protocol);

/*socket系统调用的处理程序sys_socket*/
asmlinkage long sys_socket(int, int, int);

/*socket函数内核层的实现*/
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
   
	int retval;
	struct socket *sock;
	int flags;

	/* Check the SOCK_* constants for consistency.  */
	BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
	BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
	BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
	BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

	flags = type & ~SOCK_TYPE_MASK;
	if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
		return -EINVAL;
	type &= SOCK_TYPE_MASK;

	if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
		flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
	
	/*创建socket,分配struct socket *sock资源*/
	retval = sock_create(family, type, protocol, &sock);
	if (retval < 0)
		goto out;
	
	/*	1.获取一个全局资源文件描述符fd
		2.创建一个file structures,stored in sock->file
		3.fd与sock->file建立映射关系*/
	retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
	if (retval < 0)
		goto out_release;

out:
	/* It may be already another descriptor 8) Not kernel problem. */
	return retval;

out_release:
	sock_release(sock);
	return retval;
}

struct sock结构体定义如下:

//kernel版本3.18  include/net/sock.h
/**
  *	struct sock - network layer representation of sockets
  *	/
  *struct sock
   {
		/*其他字段*/
		struct {
			atomic_t	rmem_alloc;
			int		len;
			struct sk_buff	*head;
			struct sk_buff	*tail;
		} sk_backlog;
		#define sk_rmem_alloc sk_backlog.rmem_alloc	/*已经申请的read memory*/
		atomic_t		sk_drops;
		int				sk_rcvbuf;	/*接收缓冲区大小*/
		spinlock_t	sk_dst_lock;
		atomic_t		sk_wmem_alloc;	/*已经申请的write memory*/
		atomic_t		sk_omem_alloc;
		int				sk_sndbuf;	/*发送缓冲区大小*/
		/*其他字段*/
	}

上层应用调用send函数发送用户数据时,发送成功的话,数据填充到该socket的send buffer,那么send buffer对应的是struct sock那个成员变量呢?

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

When the message does not fit into the send buffer of the socket, send
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值