socket数据发送流程
数据发送方需要将数据从用户应用空间Buffer复制到内核空间的Socket Buffer中。然后Kernel空间中添加数据包头,进行数据封装。通过一系列多层网络协议(TCP、UDP、IP等)的数据包处理工作,数据才被Push到NIC网卡中的Buffer进行网络传输。
消息接受方接受从远程机器发送的数据包后,要将数据包从NIC buffer中复制数据到Socket Buffer。然后经过一些列的多层网络协议进行数据包的解析工作。解析后的数据被复制到相应位置的用户应用空间Buffer。这个时候再进行系统上下文切换,用户应用程序才被调用。下图展示了传统的TCP/IP协议层的工作原理。
报文发送
用户态调用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