转载|网络编程中阻塞式函数的底层逻辑

逛知乎看到的,觉得写的挺透彻的,转载一下,原文链接:Unix网络编程里的阻塞是在操作系统的内核态创建一个线程来死循环吗?
原文以阻塞式的recv函数作为讲解,但是所有阻塞式的api底层逻辑基本相通。
下面是正文:

作者:张彦飞
链接:https://www.zhihu.com/question/492983429/answer/2236327954
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

大家天天都在说阻塞,实际上95%的程序员并没有真正理解阻塞是啥。这里并没有循环的事情,我们来从内核视角详细剖析一下阻塞到底是啥,它是如何工作的。把问题再具体一下,recv 接收数据阻塞的原理是啥? 理解了这个就能真正理解所有的阻塞了。用一段大家都熟悉的代码来举例!

int main()
{int sk = socket(AF_INET, SOCK_STREAM, 0);connect(sk, ...)recv(sk, ...)
}

在上面的 demo 中虽然只是简单的两三行代码,但实际上用户进程和内核配合做了非常多的工作。大致的工作流程如下:
看到这里,你可能还没看着阻塞的原理。别着急,往下看。我们来看 recv 函数依赖的底层实现。首先通过 strace 命令跟踪,可以看到 clib 库函数 recv 会执行到 recvfrom 系统调用。进入系统调用后,用户进程就进入到了内核态,通过执行一系列的内核协议层函数,然后到 socket 对象的接收队列中查看是否有数据,没有的话就把自己添加到 socket 对应的等待队列里。最后让出CPU,操作系统会选择下一个就绪状态的进程来执行。
整个流程图如下:

以上这个流程图是我根据 Linux 内核源码的执行过程总结后画出来的。注意上面的第四步和第五步。第四步中是在访问 sock 对象下面的接收队列,如果接收队列中还没有数据到达,那么就会进入第五步,把当前进程阻塞掉。但是在把自己阻塞掉之前,进程干了一件事, 给 socket 上留了个标记。告诉内核,如果这个 socket 上数据好了,记得叫我起来哈!就是源码 prepare_to_wait 函数中的 __add_wait_queue 这一句。

//file: kernel/wait.c
void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{unsigned long flags;wait->flags &= ~WQ_FLAG_EXCLUSIVE;spin_lock_irqsave(&q->lock, flags);if (list_empty(&wait->task_list))__add_wait_queue(q, wait);set_current_state(state);spin_unlock_irqrestore(&q->lock, flags);
}

接下来 Linux 就会选择下一个就绪状态的进程来执行。这就是阻塞原理的上半段,就是进程修改自己的状态,主动交出 CPU 的执行权。当有数据到达的时候,内核首先将数据包放到该 socket 的接收队列中。然后扫描一下 socket 等待队列,然后发现:“呦呵,有进程阻塞在这个 socket 上面哎,好唤醒它”。

具体到代码里就是 __wake_up_common 这个函数会访问 socket 的等待队列。

//file: kernel/sched/core.c
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, int wake_flags, void *key)
{wait_queue_t *curr, *next;list_for_each_entry_safe(curr, next, &q->task_list, task_list) {unsigned flags = curr->flags;if (curr->func(curr, mode, wake_flags, key) &&(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)break;}
}

__wake_up_common 中找出一个等待队列项 curr,然后调用其回调函数 curr->func,来完成进程的唤醒。不过,要注意的是,这个唤醒只是把相应的进程放到可运行队列里而已。真正的执行还得等其它进程主动释放 CPU 或者是时间片到了之后,内核把其它进程拿下以后才能真正获得 CPU 并开始执行。
参考:图解 | 深入理解高性能网络开发路上的绊脚石 - 同步阻塞网络 IO说到这里,你可能还会问了。内核是如何接收包的,毕竟唤醒用户进程是它干的。难道它不是一个死循环么?是的,并不是。 网卡上收到数据包的时候,是通过硬中断唤醒内核进程处理,硬中断会触发软中断。有了软中断请求以后,ksoftirqd 内核线程才开始执行。来从网卡上取包,处理,放到接收队列,然后唤醒用户进程。
参见:图解Linux网络包接收过程
究其根源,是由网卡的硬中断来触发的。如果一段时间内没有网络包处理,那么没有死循环来消耗 CPU 的。对网络底层还有啥不理解的,来看看我的公众号「开发内功修炼」 或许可以帮你解开一些困惑。

Github: GitHub - yanfeizhang/coder-kung-fu: 开发内功修炼
哦对了,想理解多路复用,来看看我的这一篇吧,也是从源码角度深入分析的。图解 | 深入揭秘 epoll 是如何实现 IO 多路复用的!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/376782.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树的存储结构2 - 数据结构和算法42

树的存储结构 让编程改变世界 Change the world by program 孩子表示法 我们这次换个角度来考虑,由于树中每个结点可能有多棵子树,可以考虑用多重链表来实现。 就像我们虽然有计划生育,但我们还是无法确保每个家庭只养育一个孩子的冲动&a…

Sharepoint 2013 发布功能(Publishing features)

一、默认情况下,在创建网站集时,只有选择的模板为‘ Publishing Portal(发布门户)’与‘ Enterprise Wiki(企业 Wiki)’时才默认启用发布功能,如下图所示: 二、发布功能包含两块&…

【草稿】windows + vscode 远程开发

主要分为三个步骤: 1、开启openssh服务 2、通过ssh命令连接到远程服务器 3、通过vscode连接远程服务器进行开发调试 ssh概念 SSH是较可靠,专为远程登陆会话和其他网络服务提供安全性得协议,利用ssh协议可以有效防止远程管理过程中得信息…

POJ3185(简单BFS,主要做测试使用)

没事做水了一道POJ的简单BFS的题目 这道题的数据范围是20,所以状态总数就是&#xff08;1<<20&#xff09; 第一次提交使用STL的queue&#xff0c;并且是在队首判断是否达到终点&#xff0c;达到终点就退出&#xff0c;超时&#xff1a;&#xff08;其实这里我是很不明白…

sql server根据表中数据生成insert语句

几个收藏的根据数据库生成Insert语句的存储过程[修正版]----根据表中数据生成insert语句的存储过程--建立存储过程&#xff0c;执行spGenInsertSQL 表名--感谢playyuer----感谢szyicol--CREATEproc[dbo].[spGenInsertSQL](tablenamevarchar(256))asbegindeclaresqlvarchar(8000…

杂感无题|

今天中午和组里面的人吃饭&#xff0c;聊起了科兴跳楼的事情。这事其实前几天我华为的mentor就转给我了&#xff0c;当时也没太在意&#xff0c;在脉脉上看了看&#xff0c;也不知晓是谁&#xff0c;想着可能又是抑郁症吧。 饭后依旧绕着食堂散步&#xff0c;ly说那个人好像还是…

uva1366_Martian Mining_简单DP

题目不难&#xff0c;却想了好长时间&#xff0c;目测自己DP还是很水。。。囧 思路&#xff1a;舍f[i][j]为前i行j列的最大矿总量不难推出状态转移方程为f[i][j]max(f[i-1][j]line[i][j],f[i][j-1]row[j][i]) 其中line[i][j]为第i行前j个A矿的和&#xff08;a[i][1]a[i][2]...a…

数学图形之Boy surface

这是一个姓Boy的人发现的,所以取名为Boy surface.该图形与罗马图形有点相似,都是三分的图形.它甚至可以说是由罗马曲面变化而成的. 本文将展示几种Boy曲面的生成算法和切图,使用自己定义语法的脚本代码生成数学图形.相关软件参见:数学图形可视化工具,该软件免费开源.QQ交流群: …

开个定时器给echarts组件配置定时更新

我在js文件中开了个定时器&#xff0c;每1s从后端获取数据并解析&#xff0c;然后用异步方法就渲染不出来&#xff0c;改成同步就可以了。 这个解决方法来自于这篇文章&#xff0c;我出的问题和他一样&#xff1a;关于ajax中readyState的值一直为1的问题 这里将ajax参数修改为f…

关于Oracle Insert 语句的子查询 和 with check option的用法

今日睇ocp教程 发现 insert语句还可以子查询例如&#xff1a;INSERT INTO (SELECT employee_id, last_name, email, hire_date, job_id, salary, department_id FROM employees where department_id 50 )VALUES (9999…

ORACLE基本SQL语句-查询篇

一、普通查询 /*查询表数据*/select * from STU /*取出前3行数据*/select * from stu where ROWNUM<3 /*模糊查询*/select * from stu where stu_id like stu001% 说明&#xff1a;通配符“%”代表一个或者多个字符&#xff0c;通配符“_”代表一个字符。 /*别名*/select S…

三次握手建立失败的几种情况以及三次握手的理解

上面的图是阻塞式socket进行通信的过程&#xff0c;阻塞的时候是操作系统内核网络协议栈在工作 调用 connect 函数将激发 TCP 的三次握手过程&#xff0c;而且仅在连接建立成功或出错时才返回。其中出错返回可能有以下几种情况&#xff1a; 1、三次握手无法建立&#xff0c;客…

在熟练使用2B铅笔前,请不要打开Axure

在互联网产品领域&#xff0c;Axure已成为产品经理、产品设计师以及交互设计师的必备工具&#xff0c;从某种程度讲&#xff0c;Axure帮助我们建立低保真模型&#xff0c;便于与用户的需求验证&#xff0c;也帮助我们构思交互细节&#xff0c;使前端和开发人员更容易理解我们的…

启用isqlplus

iSQL*Plus是sqlplus基于web方式发布的&#xff0c;要使用它只要在服务器上开启即可&#xff1a; [oraclelocalhost ~]$ isqlplusctl start perl: warning: Setting locale failed. perl: warning: Please check that your locale settings: LANGUAGE (unset), LC_ALL (unset)…

echarts 怎么知道鼠标点击的哪根柱子

有个需求&#xff0c;点击柱子&#xff0c;然后得到该柱子的信息&#xff0c;然后展示这个机房的时序图。 第一步卡住了&#xff0c;就是不知道如何获取柱子的序号。后参考&#xff1a;https://blog.csdn.net/zt_fucker/article/details/72461572?utm_sourceblogxgwz1 得到思路…

codeforces 261 D

题目链接&#xff1a; 解题报告&#xff1a;给出一个序列a1,a2,a3.........an&#xff0c;f(i , j ,x) ak 等于x的个数(i < k < j)&#xff0c;令i < j&#xff0c;求有多少对 i 和 j 使得 f(1,i,ai) > f(j,n,aj)。 从左往右扫一遍这个序列&#xff0c;num1[i] 等于…

不使用物理引擎,自己动手做真实物理的模拟投篮游戏

最近打算做一个2D投篮游戏&#xff0c;由于对于BOX2D等物理引擎并不熟悉&#xff0c;加之一开始低估了游戏所需要的碰撞检测复杂度&#xff0c;认为仅仅涉及4面墙&#xff0c;篮球&#xff0c;篮板&#xff0c;篮筐&#xff0c;篮网的碰撞检测并不复杂。因此决定自己实现所需要…

nyist 488 素数环

有一个整数n&#xff0c;把从1到n的数字无重复的排列成环&#xff0c;且使每相邻两个数&#xff08;包括首尾&#xff09;的和都为素数&#xff0c;称为素数环。 为了简便起见&#xff0c;我们规定每个素数环都从1开始。例如&#xff0c;下图就是6的一个素数环。 这题在进行判断…

.Net入门-部署问题

学习一门新的语言难免会遇到各种各样的问题&#xff0c;总结一下。 测试环境&#xff1a;windows2008serverIIS7 开发环境: vs2010 问题1&#xff1a;"Unrecognized attribute targetFramework. Note that attribute names are case-sensitive. " 分析&#xff1a; 开…

WINDOWS系统Eclipse+NDK+Android + OpenCv

WINDOWS系统EclipseNDKAndroid OpenCv 参考文档博客 1 NDK环境搭建 http://jingyan.baidu.com/article/5d6edee22d908799eadeec9f.html 2 官方文档 Android.mk与Application.mk如何编写&#xff0c;OpenCV库如何调用 http://docs.opencv.org/trunk/doc/tutorials/introduction…