首页 > 社交 > 科普中国

25

常驻编辑 科普中国 2022-06-02 子系统   网络   队列   内核   网卡   函数   源码   内存   过程   数据   用户
gEf拜客生活常识网

gEf拜客生活常识网

其中代表一条连接的 socket 内核对象更为具体一点的结构图如下。gEf拜客生活常识网

gEf拜客生活常识网

为了避免喧宾夺主,accept 详细的源码过程这里就不介绍了,感兴趣请参考 《图解 | 深入揭秘 epoll 是如何实现 IO 多路复用的!》。一文中的第一部分。gEf拜客生活常识网

今天我们还是把重点放到数据发送过程上。gEf拜客生活常识网

四、发送数据真正开始

4.1 send 系统调用实现

send 系统调用的源码位于文件 net/socket.c 中。在这个系统调用里,内部其实真正使用的是 sendto 系统调用。整个调用链条虽然不短,但其实主要只干了两件简单的事情,gEf拜客生活常识网

  • 第一是在内核中把真正的 socket 找出来,在这个对象里记录着各种协议栈的函数地址。
  • 第二是构造一个 struct msghdr 对象,把用户传入的数据,比如 buffer地址、数据长度啥的,统统都装进去.

剩下的事情就交给下一层,协议栈里的函数 inet_sendmsg 了,其中 inet_sendmsg 函数的地址是通过 socket 内核对象里的 ops 成员找到的。大致流程如图。gEf拜客生活常识网

gEf拜客生活常识网

有了上面的了解,我们再看起源码就要容易许多了。源码如下:gEf拜客生活常识网

//file: net/socket.c  
SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len,  
  unsigned int, flags)  
{  
 return sys_sendto(fd, buff, len, flags, NULL, 0);  
}  
  
SYSCALL_DEFINE6(......)  
{  
 //1.根据 fd 查找到 socket  
 sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  
 //2.构造 msghdr  
 struct msghdr msg;  
 struct iovec iov;  
  
 iov.iov_base = buff;  
 iov.iov_len = len;  
 msg.msg_iovlen = 1;  
  
 msg.msg_iov = &iov;  
 msg.msg_flags = flags;  
 ......  
  
 //3.发送数据  
 sock_sendmsg(sock, &msg, len);  
}  

从源码可以看到,我们在用户态使用的 send 函数和 sendto 函数其实都是 sendto 系统调用实现的。send 只是为了方便,封装出来的一个更易于调用的方式而已。gEf拜客生活常识网

在 sendto 系统调用里,首先根据用户传进来的 socket 句柄号来查找真正的 socket 内核对象。接着把用户请求的 buff、len、flag 等参数都统统打包到一个 struct msghdr 对象中。gEf拜客生活常识网

接着调用了 sock_sendmsg => __sock_sendmsg ==> __sock_sendmsg_nosec。在__sock_sendmsg_nosec 中,调用将会由系统调用进入到协议栈,我们来看它的源码。gEf拜客生活常识网

//file: net/socket.c  
static inline int __sock_sendmsg_nosec(...)  
{  
 ......  
 return sock->ops->sendmsg(iocb, sock, msg, size);  
}  

通过第三节里的 socket 内核对象结构图,我们可以看到,这里调用的是 sock->ops->sendmsg 实际执行的是 inet_sendmsg。这个函数是 AF_INET 协议族提供的通用发送函数。gEf拜客生活常识网

4.2 传输层处理

1)传输层拷贝

在进入到协议栈 inet_sendmsg 以后,内核接着会找到 socket 上的具体协议发送函数。对于 TCP 协议来说,那就是 tcp_sendmsg(同样也是通过 socket 内核对象找到的)。gEf拜客生活常识网

在这个函数中,内核会申请一个内核态的 skb 内存,将用户待发送的数据拷贝进去。注意这个时候不一定会真正开始发送,如果没有达到发送条件的话很可能这次调用直接就返回了。大概过程如图:gEf拜客生活常识网

gEf拜客生活常识网

我们来看 inet_sendmsg 函数的源码。gEf拜客生活常识网

//file: net/ipv4/af_inet.c  
int inet_sendmsg(......)  
{  
 ......  
 return sk->sk_prot->sendmsg(iocb, sk, msg, size);  
}  

在这个函数中会调用到具体协议的发送函数。同样参考第三节里的 socket 内核对象结构图,我们看到对于 TCP 协议下的 socket 来说,来说 sk->sk_prot->sendmsg 指向的是 tcp_sendmsg(对于 UPD 来说是 udp_sendmsg)。

相关阅读:

  • 量子子系统的新理论
  • 无法检验的科学是科学吗?
  • 伊朗成功发射太空拖船“萨曼轨道传输器”
  • HUAWEI
  • 纳莱迪人儿童遗骸发现谜团重重
  • 并不是所有的物联网平台都适合系统集成
  • 网络销售怎么去聊客户(网络销售出单难吗)
  • 多益网络怎么样(广州多益网络工作感受)
  • 网络短视频内容审核标准细则发布:短视频节目不得未经
  • 移动网络怎么样(移动300兆相当于电信多少兆)
    • 网站地图 |
    • 声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。文章内容仅供参考,不做权威认证,如若验证其真实性,请咨询相关权威专业人士。