C 什么';sleep()背后的算法是什么?

C 什么';sleep()背后的算法是什么?,c,algorithm,sleep,C,Algorithm,Sleep,现在我一直在想:sleep()是如何实现的 如果全部都是关于使用操作系统的API,那么API是如何制作的 这一切都归结为在CPU上使用特殊的机器代码吗?该CPU是否需要一个特殊的协处理器或其他小发明,如果没有它,您将无法使用sleep() sleep()最著名的体现是在C语言中(更准确地说,在C编译器附带的库中,如GNU的libc),尽管现在几乎每种语言都有它的等价物,但在某些语言中实现sleep(想想Bash)并不是我们在这个问题中要看的 编辑:在阅读了一些答案之后,我看到进程被放置在等待队列

现在我一直在想:sleep()是如何实现的

如果全部都是关于使用操作系统的API,那么API是如何制作的

这一切都归结为在CPU上使用特殊的机器代码吗?该CPU是否需要一个特殊的协处理器或其他小发明,如果没有它,您将无法使用sleep()

sleep()最著名的体现是在C语言中(更准确地说,在C编译器附带的库中,如GNU的libc),尽管现在几乎每种语言都有它的等价物,但在某些语言中实现sleep(想想Bash)并不是我们在这个问题中要看的

编辑:在阅读了一些答案之后,我看到进程被放置在等待队列中。从那里,我可以猜到两个选择

  • 设置计时器,以便内核在适当的时间唤醒进程,或者
  • 每当允许内核使用时间片时,它都会轮询时钟以检查是否到了唤醒进程的时间

  • 答案只提到备选方案1。因此,我问:这个计时器的行为如何?如果只是一个简单的中断让内核唤醒进程,那么内核怎么能要求计时器“在140毫秒内唤醒我,这样我就可以让进程处于运行状态”

    有一个内核数据结构称为睡眠队列。这是一个优先队列。每当将进程添加到睡眠队列时,都会计算最快被唤醒的进程的过期时间,并设置计时器。此时,过期的作业将从队列中取出,进程将继续执行

    (有趣的琐事:在旧的unix实现中,有一个队列,用于调用fork()但尚未创建子进程的进程。当然,它被称为fork队列


    多任务操作系统有一个名为调度器的组件,该组件负责为线程分配CPU时间,调用sleep命令操作系统在一段时间内不要给该线程分配CPU时间


    有关完整的详细信息,请参阅。

    我对Linux一无所知,但我可以告诉您在Windows上会发生什么

    Sleep()使进程的时间片立即结束,以将控制权返回操作系统。然后,操作系统设置一个计时器内核对象,该对象在时间流逝后收到信号。在内核对象收到信号之前,操作系统将不再给该进程任何时间。即使如此,如果其他进程具有更高或同等的优先级,它仍可能等待一段时间,然后让该进程继续


    操作系统使用特殊的CPU机器代码进行进程切换。用户模式代码无法访问这些函数,因此只能通过对操作系统的API调用来访问它们。

    至少有两个不同的级别可以回答这个问题。(还有很多其他与之混淆的东西,我不会碰它们)

  • 在应用程序级别,这是C库所做的。这是一个简单的操作系统调用,它只是告诉操作系统在时间过去之前不要给这个进程分配CPU时间。操作系统有一个挂起的应用程序队列,以及一些关于它们在等待什么的信息(通常是时间,或者一些数据会出现在某处)

  • 内核级。当操作系统现在没有任何事情要做时,它会执行“hlt”指令。这条指令没有任何作用,但它永远不会自行完成。当然,硬件中断是正常的。简单地说,操作系统的主循环如下(从很远的地方):

    允许中断(); while(true){ hlt; 检查待办事项队列(); } 中断处理程序simpy向todo队列添加内容。实时时钟被编程为周期性地(以固定速率)产生中断,或者在未来某个固定时间下一个进程想要被唤醒时产生中断


  • 也许操作系统的主要工作是向应用程序编写者隐藏真实硬件的复杂性。因此,任何对操作系统工作方式的描述都有可能变得非常复杂、非常迅速。因此,我不打算讨论真正的操作系统需要处理的所有“如果”和“是的,但是”。我只想从较高的概念层次上描述什么是进程,调度器做什么,计时器队列如何工作。希望这是有帮助的

    什么是过程:

    把一个进程——我们先谈谈进程,稍后再讨论线程——想象成“操作系统调度的东西”。一个进程有一个ID——想象成一个整数——你可以把这个整数想象成一个包含该进程所有上下文的表的索引

    上下文是硬件信息——寄存器、内存管理单元内容、其他硬件状态——当加载到机器中时,将允许进程“运行”。上下文还有其他组件——打开的文件列表、信号处理程序的状态,最重要的是,这里还有进程等待的内容

    进程花费大量时间睡眠(又称等待)

    进程花费大量时间等待。例如,读取或写入磁盘的进程将花费大量时间等待数据到达或确认数据已在磁盘上。操作系统人员使用术语“等待”和“睡眠”(和“阻塞”)在某种程度上是可以互换的——这一切都意味着进程正在等待某些事情发生,然后才能继续它的快乐之路。令人困惑的是,OS API sleep()恰好使用底层操作系统机制来睡眠进程

    进程可以等待其他事情:例如,网络数据包到达、窗口选择事件或计时器过期

    流程和日程安排

    正在等待的进程被认为是不可运行的。它们不会进入操作系统的运行队列。但是当进程正在等待的事件发生时,它会 allow_interrupts (); while (true) { hlt; check_todo_queues (); }
    git ls-files | grep sleep
    
    sysdeps/unix/sysv/linux/sleep.c
    
    sysdeps/unix/sysv/linux/
    
    /* We are going to use the `nanosleep' syscall of the kernel.  But the
       kernel does not implement the stupid SysV SIGCHLD vs. SIG_IGN
       behaviour for this syscall.  Therefore we have to emulate it here.  */
    unsigned int
    __sleep (unsigned int seconds)
    
     weak_alias (__sleep, sleep)
    
    result = __nanosleep (&ts, &ts);
    
    git grep nanosleep | grep -v abilist
    
    sysdeps/unix/sysv/linux/syscalls.list 
    
    nanosleep   -   nanosleep   Ci:pp   __nanosleep nanosleep
    
    sysdeps/unix/make-syscalls.sh
    
    grep -r __nanosleep
    
    #### CALL=nanosleep NUMBER=35 ARGS=i:pp SOURCE=-
    ifeq (,$(filter nanosleep,$(unix-syscalls)))
    unix-syscalls += nanosleep
    $(foreach p,$(sysd-rules-targets),$(foreach o,$(object-suffixes),$(objpfx)$(patsubst %,$p,nanosleep)$o)): \
            $(..)sysdeps/unix/make-syscalls.sh
        $(make-target-directory)
        (echo '#define SYSCALL_NAME nanosleep'; \
         echo '#define SYSCALL_NARGS 2'; \
         echo '#define SYSCALL_SYMBOL __nanosleep'; \
         echo '#define SYSCALL_CANCELLABLE 1'; \
         echo '#include <syscall-template.S>'; \
         echo 'weak_alias (__nanosleep, nanosleep)'; \
         echo 'libc_hidden_weak (nanosleep)'; \
        ) | $(compile-syscall) $(foreach p,$(patsubst %nanosleep,%,$(basename $(@F))),$($(p)CPPFLAGS))
    endif
    
    sysdeps/unix/Makefile:23:-include $(common-objpfx)sysd-syscalls 
    
    # This is the end of the pipeline for compiling the syscall stubs.
    # The stdin is assembler with cpp using sysdep.h macros.
    compile-syscall = $(COMPILE.S) -o $@ -x assembler-with-cpp - \
                       $(compile-mkdep-flags)
    
    #define SYSCALL_NAME nanosleep
    
    #include <syscall-template.S>
    
    sys_nanosleep
    
    SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,