深入理解Linux网络:第三章-内核是如何与用户进程协作的

本章主要讲用户进程接收并处理数据,主要是介绍,同步阻塞和多路IO复用方案。

同步阻塞

一个进程维护一个链接,同时为了等待数据到来需要阻塞进程,还要切换进程上下文。

  1. 创建Socket
  2. 进入内核态,开始 recv data
  3. 没有当前socket到达的数据时(接收队列里面没有当前socket接收的数据),当前socket 进入等待队列
  4. 修改当前进程状态为 TASK_INTERRUPTIPLE 可中断状态,同时当前进程进入到阻塞状态,等待被唤醒。
  5. 数据包到达网卡(外界数据进来)
  6. 网卡把帧DMA到内存(RingBuffer)
  7. 硬中断通知CPU
  8. CPU 向内核线程发起软中断
  9. 内核线程从RingBuffer上摘下skb(数据包),并创建一个新的skb放上去
  10. 内核线程把数据放到socket接收队列上
  11. 唤醒等待队列上的进程

为什么叫做同步阻塞?

在第3步,socket 进入等待队列中,第4步,修改当前进程状态,然后当前进行就进入阻塞状态,然后就不能执行其他的命令了,需要等待对应socket的数据到来。

为什么会性能低呢?

有两次进程上下文的切换,第4步和第11步。第4步:为了等待当前进行阻塞状态,从CPU上拿下来,第10步:睡眠进程被唤醒执行。进程上下文的切换没有意义的工作。

IO多路复用-epoll

一个进程维护上万条链接,性能高效。

epoll

epoll 对象

  • wait_queue_head_t wq 等待队列链表,软中断数据就绪的时候会通过wq 来找到阻塞在epoll对象上的用户进程。
  • rb_root rbr 一颗红黑树,为了支持对海量链接的高效查找、删除、插入等。通过使用这棵树来管理用户进程下添加进来的所有socket链接。
  • list_head rdlist 就绪的描述符的链表,当有链接就绪的时候,内核会把就绪的链接放到 rdllist 里面,这样应用进程只需要判断链表就能找出就绪链接。

epoll 函数

  • epoll_create : 创建一个epoll对象
  • epoll_ctl : 想 epoll 对象添加要管理的链接
    • 分配一个红黑树节点对象epitem。
    • 将等待事件添加到socket的等待队列中,同时会注册一个 ep_poll_callback 函数,ep_poll_callback 目的是软中断将数据收到socket的接收队列后,会通过这个ep_poll_callback 函数进行回调,会讲当前socket 添加到rdlist(就绪链表中)。
    • 将epitem 插入到epoll 对象的红黑树。
  • epoll_wait : 等待其管理的链接上的IO事件 rdlist 是否有数据(就绪状态的socket)
    • 有就绪状态的socket,返回socket
    • 无就绪状态的socket,当前进程添加到 wq 等待队列中,然后挂起当前进程。等待有数据进来的时候被唤醒。

执行场景

方法名

功能

执行场景

epoll_create

创建一个epoll对象

程序初始化的时候创建

epoll_ctl

想 epoll 对象添加要管理的链接

socket建立链接的时候,socket创建好了,会创建epoll内核对象

epoll_wait

等待其管理的链接上的IO事件 rdlist 是否有数据

1、程序主动初始化的时候会开始epoll_wait 一次

2、等待队列被唤醒的时候会执行

有数据进来时

  1. 将数据保存到socket的接收队列中(这个队列是socket的,不是epoll的)
  2. 找到对应的回调函数,在创建socket的时候就会给这个socket注册一个回调函数(ep_poll_callback) 该过程在上述的epoll_ctl 函数中执行。
  3. 执行回调函数
    1. 通过红黑树找到对应的 epitem。
    2. 将当前 epitem 添加到对应的 epoll 的就绪队列中。
    3. 查看epoll 的等待队列是否有进程等待,如果有就唤醒进程。

问题

同步阻塞IO到底是有哪些开销?

  1. recv 系统调用的时候,如果没有数据了,当前进程就会从当前CPU上拿下来。导致一次进程上下文的切换。消耗cpu
  2. socket有需要就绪状态就要唤醒对应的进程起来处理数据。又一次进程上下文的切换。消耗cpu
  3. 一次请求需要一个进程,一个进程需要不少的内存。消耗内存

epoll 也是阻塞的?为什么可以提高网络性能?

epoll的执行流程肯定也是阻塞的,在执行epoll_wait 的时候,如果没有就绪的socket,那他就会被阻塞挂起。当时这并不影响他的性能,因为一个epoll 管理上万个链接,所以上万个链接没有就绪状态的情况比较少。所以并不会出现频繁的挂起现象。这其实就是它性能好的关键。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值