LinuxBSP实战课(网络篇):数据包的发送过程

科技布道师 2024-03-07 01:27:18

send 系统调用实现

传输层发送处理

传输层拷贝

传输层发送

网络层发送处理

邻居子系统

网络设备子系统

fec 网卡驱动发送

总结

send 系统调用实现

send 系统调用的源码位于文件 net/socket.c 中。在这个系统调用里,内部其实真正使用的是 sendto 系统调用。主要干了两件事情:

在内核中把真正的 socket 找出来,在这个对象里记录着各种协议栈的函数地址。构造一个 struct msghdr 对象,把用户传入的数据,比如 buffer地址、数据长度啥的,统统都装进去。//net/socket.cSYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len,unsigned int, flags){return sys_sendto(fd, buff, len, flags, , 0);}SYSCALL_DEFINE6(......){//1.根据 fd 查找到 socket sock = sockfd_lookup_light(fd, &err, &fput_needed);//2.构造 msghdrstruct 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 只是为了方便,封装出来的一个更易于调用的方式而已。)

在 sendto 系统调用里,首先根据用户传进来的 fd 句柄号来查找真正的 socket 内核对象。接着把用户请求的 buff、len、flag 等参数都统统打包到一个 struct msghdr 对象中。然后调用了 sock_sendmsg => __sock_sendmsg ==> __sock_sendmsg_nosec。在__sock_sendmsg_nosec 中,调用将会由系统调用进入到协议栈,我们来看它的源码。

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

这里调用的是 sock->ops->sendmsg 实际执行的是 inet_sendmsg。(inet_sendmsg 函数的地址是通过 socket 内核对象里的 ops 成员找到的,这个函数是 AF_INET 协议族提供的通用发送函数。)

大致流程如图所示:

传输层发送处理传输层拷贝

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

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

0 阅读:0

科技布道师

简介:感谢大家的关注