Linux 里进程阻塞原理
误区一:进程阻塞消耗 CPU 资源
我以前以为进程阻塞是不好的,比如 NIO 里 Socket socket = serverSocket.accept();
会一直阻塞直到连接到来。程序要一直等待不能往下运行,感觉上是一个不好的事情。
其实,对于 CPU 来说,在这个进程阻塞的时候完全可以去跑别的程序。而对于上面的程序来说,这个阻塞也是有益的,它提供了一个等待的时间让程序能够等待连接到来。
你可能会想,进程阻塞了之后,CPU 会立马跑别的进程吗?CPU 还会回来尝试跑这个进程吗?这些问题都会影响 CPU 性能。
你需要了解 CPU 的时间片轮转机制解决第一个问题。
CPU 的时间片轮转机制
单核 CPU 在某一时刻只能跑一个进程。但小时候用的单核 CPU 的电脑一样可以“同时”运行多个程序,为什么?这是因为操作系统提供了一种CPU时间片轮转机制。
时间片轮转调度是一种最古老、最简单、最公平且使用最广的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
如果在时间片结束时进程还在运行,则CPU使用权将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。
由于切换的时间很短(大概为5毫秒),切片时间也很短(一般为100毫秒),以人的反应结果就是感觉多个程序同时运行,且没有停顿(切换的时间和在别的切片上的时间)。当然如果我们开多了程序,也会很直观的感觉卡,玩游戏的时候会把其它软件关掉,也有这个道理。
知道了 CPU 的时间片轮转机制,你就知道了程序阻塞了它的进程之后,CPU 会立马跑别的进程。
但是你想知道CPU 还会不会回来尝试跑这个进程,你需要知道工作队列和等待队列。
工作队列和等待队列
如下图所示,Linux 内核空间里会维持一个工作队列,因为时间片轮转机制,系统会在进程A、B、C等多个进程间切换着跑。
假如现在进程 A 里跑的程序有一个对象执行了某个方法将当前进程阻塞了,内核会立刻将进程A从工作队列中移除,同时在该对象里创建等待队列,并新建一个引用指向进程A。如下图:
从图中可以看到,进程A被排在了工作队列之外,不受系统调度了,这就是我们常说的被操作系统“挂起”。
这也提现了阻塞和挂起的关系。阻塞是人为安排的,让你程序走到这里阻塞。而阻塞的实现方式是系统将进程挂起。
当这个对象受到某种“刺激”(某事件触发)之后, 操作系统将该对象等待队列上的进程重新放回到工作队列上就绪,等待时间片轮转到该进程。
所以,我们可以知道了上面第二个问题的答案,操作系统不会去尝试运行被阻塞的进程,而是由对象去等待某种“刺激”。
补充一点:这个“刺激”有的时候是说来就来的。操作系统在跑进程时,会有优先级的区别。而硬件产生的信号,CPU 收到后往往会直接中断正在执行的程序,去做出响应,执行中断程序,这个优先级是很高的。这也很好理解,我们的鼠标、键盘一有动作,计算机会立即给出反应,就是这个道理。
误区二:进程阻塞不会消耗系统资源
这两个误区对新手来说容易混淆,尤其是你在看完上面的文字之后容易进入这样的误区。
其实也很简单,系统资源不仅包括 CPU,还有内存、磁盘IO等。进程阻塞不会消耗 CPU 资源,当然不代表不会消耗系统资源。
而且进程正式系统进行资源分配的最小单位。进程虽然阻塞了,但仍然存在,存在就会占用系统资源。
原文链接:https://blog.csdn.net/weixin_44367006/article/details/101637239