Linux C中sleep函数精确休眠实现原理?

99ANYc3cd6
预计阅读时长 17 分钟
位置: 首页 C语言 正文

sleep 函数是一个非常基础且常用的函数,它的作用是让程序暂停执行指定的秒数。

linux c语言sleep函数
(图片来源网络,侵删)

函数原型

sleep 函数主要有两种常见的实现,它们位于不同的头文件中。

a) unsigned sleep(unsigned seconds); (POSIX 标准)

这是最常用、最标准的 sleep 函数,遵循 POSIX (Portable Operating System Interface) 标准,具有很好的可移植性。

  • 头文件: unistd.h
  • 参数: seconds - 一个无符号整型,表示程序需要睡眠的秒数。
  • 返回值:
    • 如果函数因睡眠时间到期而被唤醒,则返回 0
    • 如果函数被某个信号中断(SIGINT,即用户按下了 Ctrl+C),则返回剩余未睡的秒数
  • 特点: 这是最推荐使用的方式。

b) unsigned int sleep(unsigned int seconds); (C89 标准)

这是 C89 标准库中定义的 sleep 函数,在 Linux 环境下,它通常是对 POSIX 版本的一个简单封装。

  • 头文件: stdlib.h
  • 参数: seconds - 一个无符号整型,表示睡眠的秒数。
  • 返回值: 同 POSIX 版本,返回 0 或剩余秒数。
  • 特点: 虽然可用,但在编写跨平台或遵循 POSIX 标准的程序时,优先使用 unistd.h 中的版本。

重要提示: 在 Linux 环境下,#include <unistd.h> 是使用 sleep 函数的标准做法。

linux c语言sleep函数
(图片来源网络,侵删)

工作原理

sleep 函数的底层实现依赖于信号

  1. 当你调用 sleep(5) 时,程序并不会进入一个忙等待循环(不断检查时间到了没,那样会浪费 CPU 资源)。
  2. 相反,sleep 函数会向操作系统内核注册一个“闹钟”,告诉内核:“请在 5 秒后唤醒我”。
  3. 程序会主动放弃 CPU 的使用权,进入一种可被中断的休眠状态。
  4. 内核会调度其他进程来使用 CPU。
  5. 5 秒后,内核会向你的进程发送一个 SIGALRM (Alarm Signal) 信号。
  6. 进程收到这个信号后,会从休眠状态被唤醒,sleep 函数执行完毕并返回 0。

信号中断: 如果在睡眠期间,你的进程收到了其他信号(SIGINT),默认情况下信号会中断 sleep 的执行。sleep 函数会立即返回,并返回剩余的秒数,信号的默认处理通常是终止程序,所以如果你不处理 SIGINT,程序会直接退出,你可能都看不到 sleep 返回的值。


代码示例

示例 1:基本用法

这个例子展示了 sleep 的基本功能,并打印返回值。

#include <stdio.h>
#include <unistd.h> // 必须包含此头文件
int main() {
    printf("程序即将开始睡眠 3 秒...\n");
    unsigned int sleep_seconds = 3;
    unsigned int remaining = sleep(sleep_seconds);
    if (remaining == 0) {
        printf("睡眠 %d 秒已到期,程序继续执行,\n", sleep_seconds);
    } else {
        printf("睡眠被信号中断,还剩 %u 秒未睡,\n", remaining);
    }
    printf("程序执行完毕,\n");
    return 0;
}

编译和运行:

linux c语言sleep函数
(图片来源网络,侵删)
gcc sleep_example.c -o sleep_example
./sleep_example

预期输出:

程序即将开始睡眠 3 秒...
(等待 3 秒后)
睡眠 3 秒已到期,程序继续执行。
程序执行完毕。

示例 2:信号中断

这个例子演示了如何通过信号来中断 sleep 函数,我们使用 kill 命令来模拟发送信号。

#include <stdio.h>
#include <unistd.h>
#include <signal.h> // 用于信号处理
// 一个空的信号处理函数,什么都不做,只为了中断 sleep
void signal_handler(int sig) {
    // do nothing
}
int main() {
    // 设置信号 SIGINT (Ctrl+C) 的处理函数
    // 这样当按下 Ctrl+C 时,程序不会退出,而是会调用 signal_handler
    signal(SIGINT, signal_handler);
    printf("程序即将开始睡眠 10 秒...\n");
    printf("(请在 10 秒内按下 Ctrl+C 来中断睡眠)\n");
    unsigned int remaining = sleep(10);
    if (remaining == 0) {
        printf("睡眠 10 秒已到期,\n");
    } else {
        printf("睡眠被信号中断,还剩 %u 秒未睡,\n", remaining);
    }
    printf("程序执行完毕,\n");
    return 0;
}

编译和运行:

gcc sleep_interrupt.c -o sleep_interrupt
./sleep_interrupt

操作和预期输出:

  1. 程序启动,开始打印信息并进入 10 秒睡眠。
  2. 在 10 秒内,按下 Ctrl+C
  3. 你会看到 sleep 函数立即返回,并打印剩余的秒数。
    程序即将开始睡眠 10 秒...
    (请在 10 秒内按下 Ctrl+C 来中断睡眠)
    ^C睡眠被信号中断,还剩 8 秒未睡。
    程序执行完毕。

    (注意:剩余的秒数取决于你按下 Ctrl+C 的时间点)


更精确的睡眠:nanosleep

虽然 sleep 很方便,但它只精确到秒,在现代编程中,我们常常需要更高精度的休眠,比如毫秒甚至纳秒,这时,nanosleep 是更好的选择。

  • 头文件: time.h
  • 函数原型:
    int nanosleep(const struct timespec *req, struct timespec *rem);
  • 参数:
    • req: 指向一个 timespec 结构体的指针,它指定了希望睡眠的时间。
      struct timespec {
          time_t tv_sec;  // 秒
          long   tv_nsec; // 纳秒 (1秒 = 1,000,000,000 纳秒)
      };
    • rem: 一个输出参数。nanosleep 被信号中断,这个指针指向的结构体会被填充上剩余需要睡眠的时间,你可以稍后再次调用 nanosleep 并使用这个 rem 参数来完成剩余的睡眠,如果不需要,可以传 NULL
  • 返回值:
    • 成功完成睡眠,返回 0
    • 被信号中断,返回 -1,并设置 errnoEINTR,此时需要检查 rem 参数以获取剩余时间。

nanosleep 示例 (睡眠 1.5 秒)

#include <stdio.h>
#include <time.h>   // for nanosleep
#include <unistd.h> // for (optional) sleep
int main() {
    struct timespec req, rem;
    // 设置请求睡眠 1 秒 和 500,000,000 纳秒 (即 1.5 秒)
    req.tv_sec = 1;
    req.tv_nsec = 500000000;
    printf("程序即将开始睡眠 1.5 秒...\n");
    int result = nanosleep(&req, &rem);
    if (result == 0) {
        printf("睡眠 1.5 秒已成功完成,\n");
    } else {
        // 被信号中断
        if (errno == EINTR) {
            printf("睡眠被信号中断,\n");
            printf("剩余需要睡眠的时间: %ld 秒 %ld 纳秒\n", rem.tv_sec, rem.tv_nsec);
            // 在这里可以重新调用 nanosleep(&rem, NULL) 来完成剩余睡眠
        } else {
            perror("nanosleep error"); // 打印系统错误信息
        }
    }
    printf("程序执行完毕,\n");
    return 0;
}

总结与对比

特性 sleep(unsigned seconds) nanosleep(const struct timespec *req, ...)
精度 纳秒
头文件 unistd.h time.h
可移植性 POSIX 标准,广泛支持 POSIX 标准,现代 Unix/Linux 系统支持
返回值 剩余秒数 或 0 0 (成功) 或 -1 (被中断)
中断处理 返回剩余秒数 通过 rem 参数返回剩余时间
适用场景 简单、粗略的延时,对精度要求不高 需要高精度延时的场景,如多媒体、游戏、网络编程等

最佳实践建议:

  • 如果只需要秒级精度的延时,并且追求代码简洁,使用 sleep()
  • 如果需要毫秒、微秒或纳秒级的精度,或者需要处理中断后恢复睡眠,强烈推荐使用 nanosleep()
  • 避免使用 stdlib.h 中的 sleep,除非有特殊的历史遗留原因或兼容性需求,在 Linux C 编程中,unistd.h 是标准。
-- 展开阅读全文 --
头像
加快织梦生成静态html文件
« 上一篇 2025-12-22
c语言 gettickcount
下一篇 » 2025-12-22

相关文章

取消
微信二维码
支付宝二维码

目录[+]