从 Linux 源码看 Socket 的阻塞和非阻塞( 二 )

首先我们看下tcp_recvmsg的函数签名:

从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
显然我们关注焦点在(int nonblock这个参数上):
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
上面的逻辑归结起来就是:
(1)在设置了nonblock的时候,如果copied>0,则返回读了多少字节,如果copied=0,则返回-EAGAIN,提示应用重复调用 。
(2)如果没有设置nonblock,如果读取的数据>=期望,则返回读取了多少字节 。如果没有则用sk_wait_data将当前进程等待 。
如下流程图所示:
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
  • 阻塞函数sk_wait_data
sk_wait_data代码-函数为:
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
该函数调用schedule_timeout进入睡眠,其进一步调用了schedule函数,首先从运行队列删除,其次加入到等待队列,最后调用和体系结构相关的switch_to宏来完成进程间的切换 。
如下图所示:
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
  • 阻塞后什么时候恢复运行呢
情况1:有对应的网络数据到来
首先我们看下网络分组到来的内核路径,网卡发起中断后调用netif_rx将事件挂入CPU的等待队列,并唤起软中断(soft_irq),再通过linux的软中断机制调用net_rx_action,如下图所示:
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
注:上图来自PLKA(<<深入Linux内核架构>>)
紧接着跟踪next_rx_action
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
紧接着tcp_v4_rcv:
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
在这里__wake_up_common将停在当前wait_queue_head_t中的进程唤醒,即状态改为task_running,等待CFS调度以进行下一步的动作,如下图所示 。
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
情况2:设定的超时时间到来
在前面调用sk_wait_event中调用了schedule_timeout
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
process_timeout函数即是将此进程重新唤醒
从 Linux 源码看 Socket 的阻塞和非阻塞

文章插图
 
原文链接:
https://my.oschina.net/alchemystar/blog/1791017

【从 Linux 源码看 Socket 的阻塞和非阻塞】


推荐阅读