请问编程语言中阻塞机制在操作系统最底层是如何实现的?

2019-12-26 10:24:51 +08:00
 squancher

首先大致问题如上,有以下两个疑问

  1. 有一种说法是程序阻塞不占用 CPU 资源。根据我所查阅到的资料,JAVA 为例,阻塞机制最后都到了 native 方法,也就是 C 语言实现,调用到了操作系统的方法(这个说法有点不准确),然后是关于 Linux 阻塞队列的原理,有一个 __wait_event 方法,具体如下:
#define __wait_event(wq, condition)
       do {
               DEFINE_WAIT(__wait);

               for (;;) {
                       prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);
                       if (condition)
                               break;
                       schedule();
               }
               finish_wait(&wq, &__wait);
       } while (0)

然后就没有其它资料了,可以看到里面有一个死循环,所以底层是会用循环来判断是否满足条件?(感觉并没有到底层)那么这也是变相消耗系统资源了啊,有点像回调
2. 如果底层是使用循环来判断,那么例如定时器,操作系统是如何管理时间的?如果也是有循环,那么时间粒度是多少,1 毫秒?如果不是,如何判断时间到达?

感觉最底层一定不是我所猜测的这样,应该是有特殊的数据结构和方法。最后,不要说什么操作系统内核完成,我就是想学习一下原理,就算是汇编也想研究下,有懂的大佬能解下惑吗?或者给几个相关技术名词。

8279 次点击
所在节点    程序员
80 条回复
zwhfly
2019-12-26 13:57:19 +08:00
“但是另一个人开始工作,要么一分钟看下时间,要么五分钟看下时间,时间差不多了就唤醒。”这个事不需要 CPU 上跑代码去做。有外部硬件时钟在做,比如最早的 PC 机用的是 MC146818 芯片,可编程,在规定时间向 CPU 发硬件中断信号。
codehz
2019-12-26 13:59:18 +08:00
@squancher 没有黑魔法,一切都是循环(
pmispig
2019-12-26 13:59:55 +08:00
操作系统底层就是中断、轮询和回调咯
zwhfly
2019-12-26 14:04:02 +08:00
@zwhfly 更正一下,早期 PC 的 PIT 芯片是 8253,不是 MC146818。
qiangmin
2019-12-26 14:04:26 +08:00
信号亮,自旋锁,互斥锁等。
qiangmin
2019-12-26 14:04:58 +08:00
信号量,自旋锁,互斥锁等。
zwhfly
2019-12-26 14:11:23 +08:00
@squancher “就算硬件时钟给 CPU 发中断信号,硬件时钟同样得做时间判断,所以一句话就是能不能不通过循环,机器周期这些方法,有特殊的算法实现”
时序逻辑电路是计算机的基石,可以说一切都是晶振驱动的。
zwhfly
2019-12-26 14:16:31 +08:00
对于这些外设硬件芯片来说,受高频时钟信号驱动不停工作是它的常态,是很正常的,功耗寿命等都是设计时保证的。不需要担心。
squancher
2019-12-26 14:16:36 +08:00
@zwhfly 震荡周期和指令周期类似吗?
zivyou
2019-12-26 14:17:24 +08:00
1. linux 内核的 0 号进程,主体就是 C 语言的 for(;;){ schedule()}; 可以参见 linux-0.12 的源码。 同样,内核的调度核心 schedule()函数,是去遍历就绪进程队列(链表的遍历,但是若 list head 就满足,直接就可以返回)。
2. CPU 有一个叫时钟中断的东西。有一个硬件每隔一定时间(非常短,纳秒级)触发上升沿高电平,内核用 jiffies 计数。当然不是每次上升沿都引起时钟中断,通常是 1000 次触发一次时钟中断。也就是说,大约 1ms 一次时钟中断。
3. 在时钟中断的处理逻辑中,内核会调用 schedule()函数,这个函数会取就绪队列的一个进程,交出 CPU。
4. LZ 给出的代码,核心是内核的 workqueue 机制,workqueue 可以接受调度的、线程级:
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);
这个函数的意思是,将自己的 task struct 放入一个等待队列中(就是数据结构放入链表头的操作),这个函数有个参数自己在放入等待队列的时候,将自己的进程状态设置成 TASK_UNINTERRUPTIBLE。进入该状态后,这个进程就不会被 schedule() 函数唤醒了。
后续想要解除这个进程(称为 a) 的阻塞,必须在另一个进程(称为 b)中,b 在一顿操作之后,发现满足了该 workqueue 的 condition, b 就会取挂在该 workqueue 等待队列上的一个进程 task_struct,修改其进程状态,然后调用 schedule()。
5. 比较绕是因为 schedule()函数、workqueue、中断这些概念缠在一起。这几个东西的设计上是非常棒的

大概就是这样,凭我已有的理解写的,细节上可能有偏颇
squancher
2019-12-26 14:24:06 +08:00
@zivyou 感谢,比较明了
zwhfly
2019-12-26 14:31:32 +08:00
@squancher “震荡周期和指令周期类似吗?”
类似的。
但 CPU 内部有倍频电路,所以指令周期短得多,纳秒级。
8253 的工作频率是 1.2MHz 左右。
happilylb
2019-12-26 14:51:12 +08:00
@squancher 二仙桥阻塞
squancher
2019-12-26 14:57:02 +08:00
@happilylb 这是什么算法,没谷歌到,不过成都二仙桥确实阻塞
jsq2627
2019-12-26 15:24:19 +08:00
@zivyou #70 补充,时钟中断只是其中一种方式,此外还有其他中断,例如 IO 中断、断电中断,也都会调起内核 handler
caryqy
2019-12-26 16:57:00 +08:00
@happilylb 哈哈哈,你最近两天的回答
charlie21
2019-12-26 21:32:04 +08:00
@happilylb 智慧树上智慧果 二仙桥连着你和我
lewis89
2019-12-26 22:15:50 +08:00
你听这些 v 友讲了这么多 , 还不如读一下 深入理解计算机系统 跟 现代操作系统 的几个特定章节,说实话 这种问题别上论坛问,好好回去读书 才是正道。
raiz
2019-12-27 10:00:40 +08:00
CPU 中断机制
imagecap
2019-12-27 14:06:52 +08:00
看看进程调度管理相关资料。个人理解:不管是 sleep 还是 wait,他们的操作都是给当前线程或者进程设置睡眠或者挂起状态,进程调度会根据状态决定哪些该唤醒

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/632449

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX