V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
squancher
V2EX  ›  程序员

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

  •  
  •   squancher · 2019-12-26 10:24:51 +08:00 · 7893 次点击
    这是一个创建于 1793 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    • 场景:高级编程语言中的阻塞,如 socket,sleep 等
    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 毫秒?如果不是,如何判断时间到达?

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

    第 1 条附言  ·  2019-12-26 11:21:53 +08:00
    首先感谢大家回复。我纠结的是阻塞过程中判断是否满足恢复条件这个过程,是否有循环之外的方式,不主要是针对调度、中断。另外我是做高级编程语言开发的,对底层了解不多。
    第 2 条附言  ·  2019-12-26 14:01:40 +08:00
    再次感谢大佬们,本人本科还未毕业,目前还在实习,造诣太浅,现在大致了解了,所以就结帖吧
    80 条回复    2019-12-27 14:06:52 +08:00
    robot1
        1
    robot1  
       2019-12-26 10:36:10 +08:00
    操作系统调度没有这么蠢 阻塞操作会导致任务调度器挂起进程,接下来当有中断或者信号时,再恢复进程。
    简化版的任务调度可以研究一下各语言的协程实现
    想要知道的更细可以看操作系统原理啊,大而全,专而深
    x1596357
        2
    x1596357  
       2019-12-26 10:37:51 +08:00
    可以看看 Linux 内核调度器是怎么实现的和 yield 系统调用,执行 yield 后调度器选择另一个程序进行运行,并将当前程序加入调度等待队列。阻塞有阻塞队列,失去阻塞条件的时候被入调度等待队列。 至于调度时间粒度,看调度器设置,1ms 也有 100ns 也有,甚至还有 NoHz 调度器。固定时间的调度器由时钟中断驱动。
    xiadong1994
        3
    xiadong1994  
       2019-12-26 10:40:58 +08:00 via iPhone
    schedule() 就是 call 调度器切换到其他进程,本进程就进到某个 event 的等待队列里去不会再被调度。event 发生以后 kernel 可能会唤醒一个或多个在等待的进程,唤醒其实就是把进程移到可执行的进程队列里面等待调度。
    squancher
        4
    squancher  
    OP
       2019-12-26 10:42:10 +08:00
    @robot1 主要疑惑点是如何恢复的,就算是通过信号来通知,那么如何判断何时条件满足,判断条件满足这个过程是什么,像 socket 一样,流来了就唤醒,要做到实时,他是怎么判断的,是不断循环吗?
    squancher
        5
    squancher  
    OP
       2019-12-26 10:44:27 +08:00
    @x1596357 Java 的 sleep 我测试出来结果是会多一毫秒,所以时间粒度可能是 1 毫秒吗?
    xiadong1994
        6
    xiadong1994  
       2019-12-26 10:48:08 +08:00 via iPhone   ❤️ 1
    @squancher 对于网络流这种 IO 操作是靠硬件中断来的
    Michaelssss
        7
    Michaelssss  
       2019-12-26 10:48:41 +08:00   ❤️ 1
    。。。CPU 不会停下来,只会不停的运行,只不过 CPU 的时间分配在各个进程 /线程之间,你可以认为,只要电脑开着,必然 CPU 是不断 FOR 循环
    augustheart
        8
    augustheart  
       2019-12-26 10:50:56 +08:00   ❤️ 1
    粗糙地来说,就是相当于(相当于)用 sleep 一直等待,就是我们自己经常会写的那种。
    并不是真正的不消耗资源,而是消耗的资源可以忽略不计。这也是编程中 no magic 的一个例证。
    至于时间粒度,基本不需要考虑,在 windows 下我用 GetTickcount 计时,经常做了一大堆的读取操作(而且还大量执行内核操作)后得到的时间间隔为 0,对现代 cpu 来说,1 毫秒是相当之久的,现在 cpu 比我们想象的快很多。
    squancher
        9
    squancher  
    OP
       2019-12-26 10:52:50 +08:00
    看了下调度器,感觉只是实现进程的调度,还是不理解判断条件满足这个过程谁来完成,如何判断的,除了循环有其它方法吗
    @x1596357
    @xiadong1994
    @Michaelssss
    hehheh
        10
    hehheh  
       2019-12-26 10:55:43 +08:00
    interrupt 啊,原来上微机原理课讲过。
    codehz
        11
    codehz  
       2019-12-26 10:57:10 +08:00 via Android   ❤️ 1
    其实了解一些基本硬件提供的机制就可以理解这个问题了(当然要具体实现那是另一个问题)
    首先我们知道硬件有中断,其中包括时钟中断和 IO 中断(和别的)
    于是我们可以做什么呢,时钟中断可以用来切换运行中的线程(抢占式调度)
    对应的 IO 中断就可以用来唤醒由于特定 IO 请求而睡眠的线程了!
    (所以为什么要阻塞队列?因为有可能同时有好多线程有 IO 请求,总得设计一个结构去保存吧,然后请求来了就扫描一下看看谁可以被唤醒做事)
    当然具体实现要复杂的多,比如 IO 中断怎么解析就是一个非常复杂的事情,解析好了也不是立即就唤醒的,除非是无阻塞 poll 模式,不然内核还要帮你把收到的数据填充到缓冲区(以 read 为例)
    zivyou
        12
    zivyou  
       2019-12-26 10:59:34 +08:00   ❤️ 1
    阻塞这个词是相对进程的上下文来说的,内核只是将进程挂起,不让它继续参与调度。
    lihongjie0209
        13
    lihongjie0209  
       2019-12-26 11:11:55 +08:00
    不给你调度不就行了? 至于说什么时候唤醒可以是硬件中断, 时钟, 或者是软件中断
    gamexg
        14
    gamexg  
       2019-12-26 11:16:31 +08:00
    google 搜索关键字 操作系统原理 sleep

    sleep 大体还是和进程调度在一起的。

    需要可以继续搜索 操作系统原理 进程调度。
    nevin47
        15
    nevin47  
       2019-12-26 11:20:40 +08:00 via Android   ❤️ 1
    LZ 有兴趣可以看看 CPU 流水线的概念

    你可以比较粗糙的理解,大部分硬件都是电信号的收发器和处理器,电信号的激励是由时钟来整体控制的

    对于阻塞任务,在 CPU 上只需要将这部分任务上下文切到挂起的状态,然后任务数据换到内存的某个地方。接下来时钟继续不断刷新电路,可以理解 CPU 上面执行的是其他任务。

    当挂起的条件达成的时候,下一个 cycle 刷新了挂起的寄存器,触发了中断,这个时候调度模块会重新切入这段任务,继续执行下去
    squancher
        16
    squancher  
    OP
       2019-12-26 11:26:29 +08:00
    @nevin47 我想知道“当挂起的条件达成的时候”这个过程,怎么实时判断达成条件
    nevin47
        17
    nevin47  
       2019-12-26 11:39:11 +08:00 via Android   ❤️ 7
    @squancher 举个简单的例子

    在某个神奇的单片机上,CPU 和系统共享一个硬时钟,时钟频率是 1Hz,也就是说一秒钟会从时钟刷新一次电信号

    然后我们自己实现了一个 sleep(x)函数,这个函数会把 x 写到某个寄存器 reg_sleep 上,这个寄存器的特性是,每一次电信号刷新,寄存器会减 1(某些定时器的实现和这个类似)

    然后我们还存在一个调度器,当每次刷新的时候,调度器如果发现 reg_sleep 的值从 0 变成非 0 了(某些中断的硬实现和这个类似),调度器就会把当前的任务挂起,然后引入新的任务

    然后过了 x 秒之后,reg_sleep 的值从非零归位成 0 了,这个时候调度器就把之前挂起的任务,重新切入,继续执行

    上面就是一个非常粗糙但是比较简略的 sleep 实现
    zunceng
        18
    zunceng  
       2019-12-26 11:44:36 +08:00
    看内核太费劲 建议把几种 IO 模型了解下 毕竟现在好多被吹成神的软件( nodejs,nginx 等等) 很大的亮点就是应用了 IO 模型中的最优解
    lxk11153
        19
    lxk11153  
       2019-12-26 12:09:50 +08:00
    我觉得就是( @Michaelssss #7 )说的循环,其它人说的什么 sleep,调度,中断,信号等等我也整懵了,当然他们是可能出于更加全面理解操作系统这样的目的

    @codehz #11 “扫描一下看看谁可以被唤醒做事” 我觉得楼主在问“怎么扫描” 是不是就是循环遍历一下“好多线程”所在的那个结构
    @nevin47 #15-“继续不断刷新电路” #17-“调度器如果发现 reg_sleep” 调度器怎么发现?是不是就是循环一遍所有?

    或者可以以“暴力密码破解”来理解,就是一个个试,对于系统就是 “循环接着从头循环接着从头循环接着从头循环”。至于循环什么,可能线程就对应线程 list/table(其它类似)
    或者可以这样理解: Linux 下“一切皆文件” ,对于操作系统就是循环

    ps. 以上我猜的,因为我不了解操作系统
    dongyx
        20
    dongyx  
       2019-12-26 12:21:59 +08:00 via iPhone
    我自己的理解,就是循环。其他同学说的等待标记位变为某个值再唤醒,已经是 CPU+OS 封装之后应用层看到的现象了。操作系统会让进程休眠,但是内核会不断循环读取这个标志位,直到满足条件再把数据填入缓冲区并唤醒进程。
    Michaelssss
        21
    Michaelssss  
       2019-12-26 12:29:26 +08:00
    @lxk11153
    实质上阻塞机制就是 CPU 循环+中断+特定的数据结构
    squancher
        22
    squancher  
    OP
       2019-12-26 12:31:29 +08:00
    @lxk11153 我觉得也是这么个理,好奇操作系统对那么多指令一直循环判断是怎么保持低资源的
    nevin47
        23
    nevin47  
       2019-12-26 12:32:40 +08:00
    @lxk11153 #19 可以这么去简单理解。


    @dongyx #20 内核一般不会去轮训寄存器的,那种开销太大了,大部分时候会和中断配合起来
    Michaelssss
        24
    Michaelssss  
       2019-12-26 12:32:51 +08:00
    @Michaelssss
    然后特定的数据结构以 Linux 为例,请看 https://github.com/torvalds/linux/blob/master/include/linux/sched.h
    因为 Linux 进程和线程我记得是相同的数据结构
    nevin47
        25
    nevin47  
       2019-12-26 12:37:49 +08:00
    @squancher #22 你要先认识到……CPU 的循环是 Cycle,不是上层语言的 for。
    每一次 Cycle 干的事儿可以是一样的,开销是固定的。操作系统是配合中断+轮训来实现的

    要是全靠软件循环,计算机早炸了……
    caowentao
        26
    caowentao  
       2019-12-26 12:44:12 +08:00 via iPhone
    条件满足是一个时刻,需要通过采样获得,采样频率越高,获得的时刻越精准,响应实时性越高。循环查询展开是一个时域离散采样过程。
    squancher
        27
    squancher  
    OP
       2019-12-26 12:47:41 +08:00
    @nevin47 但是中断就意味着恢复,谁来干恢复这个事,要做到达到条件就立刻恢复,不应该实时监控、不断轮询吗?
    zwhfly
        28
    zwhfly  
       2019-12-26 12:51:21 +08:00 via Android
    @squancher 让中断源达到条件时中断啊
    squancher
        29
    squancher  
    OP
       2019-12-26 12:54:06 +08:00
    @zwhfly 就是这个问题,上面问过了,怎样判断条件,能做到实时,快速反应
    x1596357
        30
    x1596357  
       2019-12-26 12:57:45 +08:00 via iPhone   ❤️ 1
    @squancher 还是看看操作系统原理处理器调度一章比较好,这样你能系统的知道究竟是怎么工作的。论坛里一人几句也只是碎片的知识。轮询是一种忙等待策略,非常浪费资源。实际使用的是中断来的时候检测条件,比如你等待网络流量,网卡电信号接受到帧,网卡中断,内核解包,发现这个包就是你在等的,唤醒你的进程,然后就处理完了一个包,继续等待其他包。所谓的等待,在用户空间,进程看来,是失去处理器时间,实际上处理器会去执行其他程序。至于中断切换恢复上下文,这也是调度器的工作,保存恢复寄存器状态等。
    GeruzoniAnsasu
        31
    GeruzoniAnsasu  
       2019-12-26 13:04:50 +08:00 via Android
    @squancher

    > 就是这个问题,上面问过了,怎样判断条件,能做到实时,快速反应





    不是“实时”的。
    协程你理解吧,协程状态变化不是“实时的”对吧,而是被等待 routine 状态改变的时候调度器把等待进程重新设为可调度而已


    进程一样的,操作系统判断被等事件状态可改变时把状态修改(为完成),同时把在等待这个事件的所有进程重新设为可调度,下次调度时就能恢复它们的执行流程



    顺带一提,正因为 linux 调度器算法机制不能做到“实时”,所以 linux 并不是一个实时操作系统。RTOS 需要有硬件机制来直接通知内核立即切换进程状态并调度目的进程
    codehz
        32
    codehz  
       2019-12-26 13:05:17 +08:00
    @squancher 中断是硬件实现,等于强行跳转到另一个执行逻辑(所谓中断处理函数,实际并不是手动调用过去的,而是硬件暴力跳转过去),软件层面无法改变也没法预测
    codehz
        33
    codehz  
       2019-12-26 13:09:19 +08:00
    判断的过程有非常多的选项,对于同步的系统调用来说,最简单了,就是遍历一个候选列表,然后一个一个判断过去(没错,就是链表)
    你以为的实时只是假象,看起来很快,但起码有个几千个时钟周期的“浪费”,根本不是实时的
    squancher
        34
    squancher  
    OP
       2019-12-26 13:13:19 +08:00
    @GeruzoniAnsasu 你说的我大概理解,我关心的是对于“操作系统判断被等事件状态”这个过程。
    GeruzoniAnsasu
        35
    GeruzoniAnsasu  
       2019-12-26 13:13:26 +08:00 via Android   ❤️ 1
    @squancher 假设我们实现一个慢悠悠的,没有优先级的,分片很长的调度方法


    那,阻塞这件事就会变成( A 等待 B 进程,系统有 ABCD4 个进程)
    1. A 进程进入等待状态,调度器把 A 关联到 B,A 变为不可调度
    2. 调度 B 进程,B 进程获得时间片,跑 1 秒,任务没结束。
    3. 调度 C 进程,C 进程获得时间片,跑 1 秒
    4. 调度 D 进程,D 进程获得时间片,跑 1 秒
    5. 调度 A 进程,A 进程不能调度,继续选下一个
    6. 调度 B 进程,B 进程获得时间片,跑 1 秒任务结束
    7. 调度器发现 B 进程结束了,遍历正在等 B 的进程,发现 A,A 设为可调度
    8. 调度 C 进程
    9. 调度 D 进程
    10. 调度 A 进程,A 进程获得时间片,取得 B 状态,跑 1 秒
    11. 调度 B 进程,B 进程所有关联的等待者已不再等待,可以卸载 B 进程
    12. 调度 C 进程

    ……
    GeruzoniAnsasu
        36
    GeruzoniAnsasu  
       2019-12-26 13:14:22 +08:00 via Android
    @squancher 内核中有专门结构,进程内会通过 api 来通知内核修改内核对象的值。
    zwhfly
        37
    zwhfly  
       2019-12-26 13:15:35 +08:00 via Android
    @squancher 什么是中断?中断是指 CPU 停止执行一段程序,转而执行另一段程序。注意后一句同样重要,中断不仅可以是一个结束,还可以是一个开始。

    比如读文件,一个典型的阻塞操作,从用户层到文件系统到驱动,一路会注册等待链,到最后给到 比如 AHCI 控制器,AHCI 控制器是硬件外设,在背后和硬盘间一通骚操作交换数据,这个过程和 CPU 无关,从用户线程到文件系统到驱动啥都不用干(死循环都不用),AHCI 控制器把数据准备好后给 CPU 发中断信号,CPU 收到中断信号就能执行“中断处理程序”,这时候这个中断处理程序就能异步恢复一些东西了,刚才注册的等待链就是响应链,一层一层最终让用户态代码继续执行。
    GeruzoniAnsasu
        38
    GeruzoniAnsasu  
       2019-12-26 13:16:26 +08:00 via Android
    @squancher 状态改变是主动的,实时发生的,但检测状态改变,不是。

    或者说 上面用户态的绝大多数情况都不需要实时感知直接触发回调
    FrankHB
        39
    FrankHB  
       2019-12-26 13:18:57 +08:00
    操作系统不需要实现如何实时反应。典型的分时系统反而有延迟,还就不是实时的。
    真正需要实时响应的场合由硬件实现保证。
    处理器的中断是可以异步触发的,中断服务例程也是硬件原生支持的异步调用。发生中断时,控制如何转移到中断服务例程根本就不用操作系统关心(操作系统看到的就是硬件自己把内部状态改了,接下来执行到了不同的位置)。操作系统需要做的,是在初始化时指定处理中断的逻辑,以及在此之上实现调度器。
    caowentao
        40
    caowentao  
       2019-12-26 13:19:49 +08:00 via iPhone
    1.实时速度有上限,不会超过工作主频。
    2.楼主给出的__wait_event 方法已经是操作系统底层实现逻辑了。
    3.软件信号事件,查询开销是必须的。
    squancher
        41
    squancher  
    OP
       2019-12-26 13:20:19 +08:00
    有点晕了,就能简单说一下定时器吗,Java 定时器 timer,我设定一个时间,操作系统怎么判断时间到了没有,是每毫秒检查一次还是?
    zjsxwc
        42
    zjsxwc  
       2019-12-26 13:23:22 +08:00
    通过 中断、事件、tick 等方式让操作系统调度呗
    squancher
        43
    squancher  
    OP
       2019-12-26 13:23:58 +08:00
    @zjsxwc 这又绕回来了
    zwhfly
        44
    zwhfly  
       2019-12-26 13:26:30 +08:00 via Android
    @squancher 算出目标时间点发给硬件时钟,硬件时钟到时间给 CPU 发中断信号啊。
    codehz
        45
    codehz  
       2019-12-26 13:26:54 +08:00 via Android
    @squanche 内核也有线程,定时器会专门开一个线程管理,然后他就可以做一个操作:依次排列各个定时器事件,循环检查第一个定时器有没有触发,然后循环内每次检查后都主动放弃执行绪切到另一个内核线程处理别的事务(
    squancher
        46
    squancher  
    OP
       2019-12-26 13:28:39 +08:00
    再拿人举个例子,我现在让另一个人一个小时后通知我一声,然后我要睡觉了,一直等待(中断),但是另一个人开始工作,要么一分钟看下时间,要么五分钟看下时间,时间差不多了就唤醒。
    wamson
        47
    wamson  
       2019-12-26 13:29:24 +08:00 via iPhone
    @squancher。。。想这么复杂干什么,其实就是 cpu 在每个时钟周期都会去检测某个引脚的电平是高电平还是低电平(这个电平是别的硬件电路传递过来的,比如 IO 事件一般是 DMA 控制器),如果是高电平则表示有中断信号到达,就会保存现场,然后跳去执行中断处理函数,完了恢复现场。
    详细请参考<操作系统>,或者看一下<ucos ii 源码>
    zwhfly
        48
    zwhfly  
       2019-12-26 13:29:55 +08:00 via Android
    @squancher 你对“中断”的理解是错误的。
    zwhfly
        49
    zwhfly  
       2019-12-26 13:31:36 +08:00 via Android
    中断是 CPU 的一个动作,而不是一个状态。中断是“状态转换”这个动作。
    squancher
        50
    squancher  
    OP
       2019-12-26 13:36:22 +08:00
    好吧,综上所述,有人能做个总结吗,这种“判断、检查”是否是快速循环检测,比如按照机器周期的频率,然后这个循环时间粒度可控吗?
    codehz
        51
    codehz  
       2019-12-26 13:36:56 +08:00   ❤️ 1
    其实不去实现操作系统也不是不能了解这个阻塞调用机制
    你可以自己实现一个协程
    然后看看不使用系统调用的情况下如何实现 timer
    (是不是只能一个死循环,最多优化优化检查策略)
    然后我这里就做了一个模仿操作系统的协程模型 https://github.com/codehz/ctxco
    其中 poller 函数可以理解为系统调用的处理函数(然后自己实现如何将请求异步提交给系统,并返回调度具体协程)
    zwhfly
        52
    zwhfly  
       2019-12-26 13:37:24 +08:00 via Android
    对于 CPU 来说,中断是跳到另一段程序执行,而不是等待。但对中断前那段代码来说,由于被跳走了,所以逻辑上这段程序在等待,等待操作系统什么时候操纵 CPU 再跳回来。但 CPU 一直是有事干的,从来不闲着,最闲的时候也是循环执行 pause 指令(x86)。
    caowentao
        53
    caowentao  
       2019-12-26 13:42:36 +08:00 via iPhone
    楼主一开始的理解就没有问题,只是对结论持怀疑态度
    justRua
        54
    justRua  
       2019-12-26 13:47:43 +08:00
    https://www.zhihu.com/question/20785028/answer/378615520
    之前在知乎上看到过类似问题,底层也是轮询
    nevin47
        55
    nevin47  
       2019-12-26 13:48:41 +08:00 via Android
    @squancher 47 楼讲的很清楚了

    另外如果真的想系统理解,最好配合计算机组成原理和现代操作系统两本书看看。只言片语得理解很容易搞成民科了,你这个问题是软硬件领域的交界,以纯软件的思维去理解是偏的
    squancher
        56
    squancher  
    OP
       2019-12-26 13:50:17 +08:00
    这个问题困扰我很久了,就算硬件时钟给 CPU 发中断信号,硬件时钟同样得做时间判断,所以一句话就是能不能不通过循环,机器周期这些方法,有特殊的算法实现
    smdbh
        57
    smdbh  
       2019-12-26 13:50:49 +08:00
    盲狙 wfe()
    ccpp132
        58
    ccpp132  
       2019-12-26 13:52:28 +08:00
    主要是硬件有中断机制,从这点出发其实没太大问题。光从软件上来理解是很难的。
    GeruzoniAnsasu
        59
    GeruzoniAnsasu  
       2019-12-26 13:54:20 +08:00 via Android
    @squancher 所以说了,没有。你看起来的“实时” 就 就 就是先把状态改了等下一个时间片而已

    下一个时间片分不到定时器就是不准的。就这样。
    ccpp132
        60
    ccpp132  
       2019-12-26 13:55:19 +08:00
    @squancher 硬件时钟可以独立于 cpu 做循环,不影响 cpu 就行了啊
    zwhfly
        61
    zwhfly  
       2019-12-26 13:57:19 +08:00 via Android
    “但是另一个人开始工作,要么一分钟看下时间,要么五分钟看下时间,时间差不多了就唤醒。”这个事不需要 CPU 上跑代码去做。有外部硬件时钟在做,比如最早的 PC 机用的是 MC146818 芯片,可编程,在规定时间向 CPU 发硬件中断信号。
    codehz
        62
    codehz  
       2019-12-26 13:59:18 +08:00
    @squancher 没有黑魔法,一切都是循环(
    pmispig
        63
    pmispig  
       2019-12-26 13:59:55 +08:00
    操作系统底层就是中断、轮询和回调咯
    zwhfly
        64
    zwhfly  
       2019-12-26 14:04:02 +08:00 via Android
    @zwhfly 更正一下,早期 PC 的 PIT 芯片是 8253,不是 MC146818。
    qiangmin
        65
    qiangmin  
       2019-12-26 14:04:26 +08:00 via Android
    信号亮,自旋锁,互斥锁等。
    qiangmin
        66
    qiangmin  
       2019-12-26 14:04:58 +08:00 via Android
    信号量,自旋锁,互斥锁等。
    zwhfly
        67
    zwhfly  
       2019-12-26 14:11:23 +08:00 via Android
    @squancher “就算硬件时钟给 CPU 发中断信号,硬件时钟同样得做时间判断,所以一句话就是能不能不通过循环,机器周期这些方法,有特殊的算法实现”
    时序逻辑电路是计算机的基石,可以说一切都是晶振驱动的。
    zwhfly
        68
    zwhfly  
       2019-12-26 14:16:31 +08:00 via Android
    对于这些外设硬件芯片来说,受高频时钟信号驱动不停工作是它的常态,是很正常的,功耗寿命等都是设计时保证的。不需要担心。
    squancher
        69
    squancher  
    OP
       2019-12-26 14:16:36 +08:00
    @zwhfly 震荡周期和指令周期类似吗?
    zivyou
        70
    zivyou  
       2019-12-26 14:17:24 +08:00   ❤️ 6
    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
        71
    squancher  
    OP
       2019-12-26 14:24:06 +08:00
    @zivyou 感谢,比较明了
    zwhfly
        72
    zwhfly  
       2019-12-26 14:31:32 +08:00 via Android
    @squancher “震荡周期和指令周期类似吗?”
    类似的。
    但 CPU 内部有倍频电路,所以指令周期短得多,纳秒级。
    8253 的工作频率是 1.2MHz 左右。
    happilylb
        73
    happilylb  
       2019-12-26 14:51:12 +08:00 via Android
    @squancher 二仙桥阻塞
    squancher
        74
    squancher  
    OP
       2019-12-26 14:57:02 +08:00
    @happilylb 这是什么算法,没谷歌到,不过成都二仙桥确实阻塞
    jsq2627
        75
    jsq2627  
       2019-12-26 15:24:19 +08:00
    @zivyou #70 补充,时钟中断只是其中一种方式,此外还有其他中断,例如 IO 中断、断电中断,也都会调起内核 handler
    caryqy
        76
    caryqy  
       2019-12-26 16:57:00 +08:00
    @happilylb 哈哈哈,你最近两天的回答
    charlie21
        77
    charlie21  
       2019-12-26 21:32:04 +08:00
    @happilylb 智慧树上智慧果 二仙桥连着你和我
    lewis89
        78
    lewis89  
       2019-12-26 22:15:50 +08:00
    你听这些 v 友讲了这么多 , 还不如读一下 深入理解计算机系统 跟 现代操作系统 的几个特定章节,说实话 这种问题别上论坛问,好好回去读书 才是正道。
    raiz
        79
    raiz  
       2019-12-27 10:00:40 +08:00
    CPU 中断机制
    imagecap
        80
    imagecap  
       2019-12-27 14:06:52 +08:00
    看看进程调度管理相关资料。个人理解:不管是 sleep 还是 wait,他们的操作都是给当前线程或者进程设置睡眠或者挂起状态,进程调度会根据状态决定哪些该唤醒
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5297 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 07:34 · PVG 15:34 · LAX 23:34 · JFK 02:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.