当用户执行完recvfrom调用后,用户进程就通过系统调用进行到内核态工作了 。如果接收队列没有数据,进程就进入睡眠状态被操作系统挂起 。这块相对比较简单,剩下大部分的戏份都是由Linux内核其它模块来表演了 。
首先在开始收包之前,Linux要做许多的准备工作:
- 1. 创建ksoftirqd线程,为它设置好它自己的线程函数,后面指望着它来处理软中断呢
- 2. 协议栈注册,linux要实现许多协议,比如arp,icmp,ip,udp,tcp,每一个协议都会将自己的处理函数注册一下,方便包来了迅速找到对应的处理函数
- 3. 网卡驱动初始化,每个驱动都有一个初始化函数,内核会让驱动也初始化一下 。在这个初始化过程中,把自己的DMA准备好,把NAPI的poll函数地址告诉内核
- 4. 启动网卡,分配RX,TX队列,注册中断对应的处理函数
当数据到来了以后,第一个迎接它的是网卡(我去,这不是废话么):
- 1. 网卡将数据帧DMA到内存的RingBuffer中,然后向CPU发起中断通知
- 2. CPU响应中断请求,调用网卡启动时注册的中断处理函数
- 3. 中断处理函数几乎没干啥,就发起了软中断请求
- 4. 内核线程ksoftirqd线程发现有软中断请求到来,先关闭硬中断
- 5. ksoftirqd线程开始调用驱动的poll函数收包
- 6. poll函数将收到的包送到协议栈注册的ip_rcv函数中
- 7. ip_rcv函数再将包送到udp_rcv函数中(对于tcp包就送到tcp_rcv)
理解了整个收包过程以后,我们就能明确知道Linux收一个包的CPU开销了 。首先第一块是用户进程调用系统调用陷入内核态的开销 。第二块是CPU响应包的硬中断的CPU开销 。第三块是ksoftirqd内核线程的软中断上下文花费的 。后面我们再专门发一篇文章实际观察一下这些开销 。
另外网络收发中有很多末支细节咱们并没有展开了说,比如说no NAPI,GRO,RPS等 。因为我觉得说得太对了反而会影响大家对整个流程的把握,所以尽量只保留主框架了,少即是多!
来自公众号:开发内功修炼
推荐阅读
- 三种使用AI攻击网络安全的方法
- 理解了Linux I/O机制,才能真的明白“什么是多线程”
- Linux网络配置
- 带你重新认识Linux系统的inode
- 古剑奇谭网络版野外pvp 古剑奇谭ol剧情
- 从Linux源码看Socket的listen及连接队列
- linux网络编程常见API详解
- 了解神经网络和模型泛化
- TCP/IP 基础知识总结
- 如何在电脑上安装网络打印机?详细教程全部教给你