C语言的sleep、usleep、nanosleep等休眠函数如何使用

其他教程   发布日期:2023年08月10日   浏览次数:1195

这篇文章主要讲解了“C语言的sleep、usleep、nanosleep等休眠函数如何使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言的sleep、usleep、nanosleep等休眠函数如何使用”吧!

引子

一个无聊的死循环小代码:

  1. #include <stdio.h>
  2. int main(int argc, char const *argv[])
  3. {
  4. for (char c = 0; c < 128; c++) {
  5. printf("cool
  6. ");
  7. }
  8. return 0;
  9. }

以及 运行过程 展示版:

  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. int main(int argc, char const *argv[])
  6. {
  7. struct timespec n_sleep;
  8. n_sleep.tv_sec = 0; //secondes, integer part sleep duration
  9. n_sleep.tv_nsec = 5e8L; //nanoseconds, decimal part sleep duration
  10. char c;
  11. for (c = 0; c < 128; c++) {
  12. printf("char of c :%c
  13. ", c);
  14. printf("ASCII num of c :%d
  15. ", c);
  16. sleep(1); // 1 s
  17. usleep(900000); // 0.9 s
  18. nanosleep(&n_sleep, NULL); // 0 + 0.5 s
  19. }
  20. return 0;
  21. }

另外,推荐一下 clang 这款编译器,
它的(1)错误、(2)警告 提示非常直观、准确、体贴。
比如,上面的死循环代码,编译之后,它就贴心地显示了一个警告:

  1. result of comparison of constant 128 with expression of type 'char' is always true
  2. [-Wtautological-constant-out-of-range-compare]
  3. for (c = 0; c < 128; c++) {
  4. ~ ^ ~~~

强烈推荐啊!!!
当然,如果还是喜欢或者必须使用 gcc 的话,建议可以将 clang 作为一个辅助选项。

(一) sleep 函数

头文件 unistd.h
头文件 unistd.h 中的原文如下:

  1. /* Make the process sleep for SECONDS seconds, or until a signal arrives
  2. and is not ignored. The function returns the number of seconds less
  3. than SECONDS which it actually slept (thus zero if it slept the full time).
  4. If a signal handler does a `longjmp' or modifies the handling of the
  5. SIGALRM signal while inside `sleep' call, the handling of the SIGALRM
  6. signal afterwards is undefined. There is no return value to indicate
  7. error, but if `sleep' returns SECONDS, it probably didn't work.
  8. This function is a cancellation point and therefore not marked with
  9. __THROW. */
  10. extern unsigned int sleep (unsigned int __seconds);

通过debug的方式,进入 sleep 函数本体内部,可以反向查找到 sleep 函数所在的具体文件是 /glibc-2.23/sysdeps/posix/sleep.c 。

(根据gcc版本的不同,上面的库函数版本号 glibc-2.23 有所不同。)

源文件 sleep.c

sleep 函数的原型代码如下:

  1. #include <time.h>
  2. #include <unistd.h>
  3. #include <errno.h>
  4. #include <sys/param.h>
  5. /* Make the process sleep for SECONDS seconds, or until a signal arrives
  6. and is not ignored. The function returns the number of seconds less
  7. than SECONDS which it actually slept (zero if it slept the full time).
  8. If a signal handler does a `longjmp' or modifies the handling of the
  9. SIGALRM signal while inside `sleep' call, the handling of the SIGALRM
  10. signal afterwards is undefined. There is no return value to indicate
  11. error, but if `sleep' returns SECONDS, it probably didn't work. */
  12. unsigned int __sleep(unsigned int seconds)
  13. {
  14. int save_errno = errno;
  15. const unsigned int max =
  16. (unsigned int)(((unsigned long int)(~((time_t)0))) >> 1);
  17. struct timespec ts = { 0, 0 };
  18. do {
  19. if (sizeof(ts.tv_sec) <= sizeof(seconds)) {
  20. /* Since SECONDS is unsigned assigning the value to .tv_sec can
  21. overflow it. In this case we have to wait in steps. */
  22. ts.tv_sec += MIN(seconds, max);
  23. seconds -= (unsigned int)ts.tv_sec;
  24. } else {
  25. ts.tv_sec = (time_t)seconds;
  26. seconds = 0;
  27. }
  28. if (__nanosleep(&ts, &ts) < 0)
  29. /* We were interrupted.
  30. Return the number of (whole) seconds we have not yet slept. */
  31. return seconds + ts.tv_sec;
  32. } while (seconds > 0);
  33. __set_errno(save_errno);
  34. return 0;
  35. }
  36. weak_alias(__sleep, sleep)

sleep 函数的用法

简单地说, sleep 函数实现的功能是 让程序休眠若干秒钟,时间的最小刻度是「秒」。

  1. extern unsigned int sleep (unsigned int __seconds);

sleep 函数的返回值

  • (1)如果 sleep 函数顺利执行了的话,返回值是实际休眠的时间数,

  • (2)如果实际休眠的时间和设定的休眠时间一致的话,返回值是0,

  • (3)不会返回表示 错误 信息的值,但是如果返回的值与设定的休眠时间的值一样的话,很可能 sleep 函数其实并没有执行,

  • (4)返回值类型是 unsigned int 型,也就是说,是一个 非负数 。

sleep 函数的参数

  • (1)参数的类型是 unsigned int 型,也就是说,是一个 非负数 ,

  • (2)参数的时间单位是 秒 。

所以, sleep 函数,使 进程/process 休眠的最短时间段,是一秒钟。

(二) usleep 函数

头文件 unistd.h
头文件 unistd.h 中的原文如下:

  1. /* Sleep USECONDS microseconds, or until a signal arrives that is not blocked
  2. or ignored.
  3. This function is a cancellation point and therefore not marked with
  4. __THROW. */
  5. extern int usleep (__useconds_t __useconds);

查找上面的 sleep.c 文件的时候,在 find 命令的结果中看到了 usleep.c 文件和 sleep.c 文件位于同一个文件夹:
/glibc-2.23/sysdeps/posix/sleep.c 。

(根据gcc版本的不同,上面的库函数版本号 glibc-2.23 有所不同。)

源文件 usleep.c

usleep 函数的原型代码如下:

  1. #include <time.h>
  2. #include <unistd.h>
  3. int
  4. usleep (useconds_t useconds)
  5. {
  6. struct timespec ts = { .tv_sec = (long int) (useconds / 1000000),
  7. .tv_nsec = (long int) (useconds % 1000000) * 1000ul };
  8. /* Note the usleep() is a cancellation point. But since we call
  9. nanosleep() which itself is a cancellation point we do not have
  10. to do anything here. */
  11. return __nanosleep (&ts, NULL);
  12. }

名称 usleep 的第一个字母 u 代表的是时间单位 微秒 的第一个字母。
虽然实际上是希腊字母的 &mu; ,但英语键盘里不方便敲出这个字母,所以就用了样子相似的英文字母 u 。
时间单位 微秒 的英文单词是 microsecond ,是由词根 micro 和 second 组合而成的单词。
微秒 是 10的负6次方 秒。

另外,还有一个以字母 m 开头的时间单位的英文单词 millisecond ,意思是 毫秒 ,也就是 千分之一秒。
注意,区分 micro 和 milli ,一个是 微 ,一个是 毫 。

usleep 函数的用法

简单地说, usleep 函数实现的功能是 让程序休眠若干「微秒」,时间的最小刻度是「微秒」,10的负6次方 秒。

  1. /* Sleep USECONDS microseconds, or until a signal arrives that is not blocked
  2. or ignored.
  3. This function is a cancellation point and therefore not marked with
  4. __THROW. */
  5. extern int usleep (__useconds_t __useconds);

usleep 函数的返回值

网上查到的是:成功返回0,出错返回-1。

usleep 函数的参数

  • (1) 参数的类型是 __useconds_t ,这个类型的定义要查找好几个文件才找得到,

  • (2) 首先是找到了头文件 types.h ,具体路径是 /glibc/include/sys/types.h ,可惜这里面没有明确、具体的定义,

  • (3) 然后还得找到头文件 typesizes.h ,具体路径是 ==/glibc/sysdeps/mach/hurd/bits/typesizes.h ==,这里终于有了,

  • (4) 第一个宏, #define __USECONDS_T_TYPE __U32_TYPE ,在 typesizes.h 文件里,

  • (5) 第二个宏, #define __U32_TYPE unsigned int ,在 types.h 文件里,

  • (6) 真是折腾啊!这下总算知道 __useconds_t 就是 unsigned int 类型了,也就是 非负整数。

  • (7) 参数的时间单位是 微秒 。

所以, usleep 函数,使 进程/process 休眠的最短时间段,是一微秒。

(三) nanosleep 函数

头文件 time.h
这个 time.h 头文件的路径是 /glibc/time/time.h 。
在C语言自带的库目录里面,有好几个不同目录下的 time.h 文件,只有这个才是定义了 nanosleep 函数的。
也不知道,编译器在预处理阶段去获取的 time.h 到底是哪个? 或者说,全部都获取过来了?

头文件 time.h 中的原文如下:

  1. /* Pause execution for a number of nanoseconds.
  2. This function is a cancellation point and therefore not marked with
  3. __THROW. */
  4. extern int nanosleep (const struct timespec *__requested_time,
  5. struct timespec *__remaining);

函数名称的 nano 是 纳米、纳秒 等计量单位的开头字母,一纳秒是10的负9次方 秒,是10的负6次方 毫秒,是10的负3次方 微秒。

源文件 nanosleep.c

下面是这个路径 /glibc/posix/nanosleep.c 的文件内容。

在C语言自带的函数库中搜索 nanosleep ,出来的结果同样有好几个,暂时分不清到底是哪个,先采用了这个。

nanosleep 函数的原型代码如下:

  1. #include <errno.h>
  2. #include <time.h>
  3. /* Pause execution for a number of nanoseconds. */
  4. int
  5. __nanosleep (const struct timespec *requested_time,
  6. struct timespec *remaining)
  7. {
  8. __set_errno (ENOSYS);
  9. return -1;
  10. }
  11. stub_warning (nanosleep)
  12. hidden_def (__nanosleep)
  13. weak_alias (__nanosleep, nanosleep)

nanosleep 函数的用法

简单地说, nanosleep 函数实现的功能是 让程序休眠若干「纳秒」,时间的最小刻度是「纳秒」,10的负9次方 秒。

  1. /* Pause execution for a number of nanoseconds.
  2. This function is a cancellation point and therefore not marked with
  3. __THROW. */
  4. extern int nanosleep (const struct timespec *__requested_time,
  5. struct timespec *__remaining);

nanosleep 函数的返回值

网上查到的是:成功返回0,出错返回-1。

这个函数功能是暂停某个进程直到你规定的时间后恢复,参数 req 就是你要暂停的时间,其中 req->tv_sec 是以秒为单位,而 tv_nsec 以纳秒为单位(10的-9次方秒)。

由于调用 nanosleep 是进程进入 TASK_INTERRUPTIBLE ,这种状态是会相应信号而进入 TASK_RUNNING 状态的,这就意味着有可能会没有等到你规定的时间就因为其它信号而唤醒,此时函数返回 -1 ,切换剩余的时间会被记录在 rem 中。

return值: 若进程暂停到参数 *req 所指定的时间,成功则返回0;若有信号中断则返回-1,并且将剩余微秒数记录在 *rem 中。

nanosleep 函数的参数

第一个参数 *__requested_time 的数据类型是 struct timespec ,定义在 time.h 文件中,

具体是定义在 /glibc/time/bits/types/struct_timespec.h 文件中的。
文件内容如下:

  1. #include <bits/types.h>
  2. /* POSIX.1b structure for a time value. This is like a `struct timeval' but
  3. has nanoseconds instead of microseconds. */
  4. struct timespec
  5. {
  6. __time_t tv_sec; /* Seconds. */
  7. __syscall_slong_t tv_nsec; /* Nanoseconds. */
  8. };

对于 struct timespec 数据类型的详细说明如下:
(昨天晚上上网查资料、自行测试的时候,自己写的英文笔记,请忽略语法疏漏~)

in header file of C lib, time.h, declaration as below:

struct timespec; (since C11)

  1. struct timespec {
  2. time_t tv_sec; // seconds
  3. long tv_nsec; // nanoseconds
  4. };

Member objects / 结构体成员:

time_t tv_sec whole seconds (valid values are >= 0)
long tv_nsec nanoseconds (valid values are [0, 999999999])

time_t 类型是 long 型,成员 tv_sec 决定 休眠持续时间段的 整数部分:

member of 「tv_sec」 at the type of 「time_t」,
aka (also know as) 「long」 type,
determines the integer part of sleep duration;

time_t 类型是 long 型,成员 tv_nsec 决定 休眠持续时间段的 小数部分:

member of 「tv_nsec」 at the type of 「long」,
and in the range of [0, 999999999] or [0,1e9),
that means tv_nsec is forever less than 1 second,
determines the decimal part of sleep duration.

就算赋值给成员 tv_nsec 的数值直接计算下来超过了一秒,成员 tv_nsec 获得的实际的值也是被取余后的结果。

even if a value more than 999999999 ns given to tv_nsec,
actually what tv_nsec will get is a value cut down extra part,
eg. 1e9L or 100000000 will be cut the high bits and leave 0 to tv_nsec.

总而言之,整个的休眠时间段是 整数部分的 tv_sec 加上 小数部分的 tv_nsec 组成的。

so, the whole sleep duration is tv_sec + tv_nsec,
just as 「integer part + decimal part」 of the whole sleep duration.

所以, nanosleep 函数,使 进程/process 休眠的最短时间段,是一纳秒。

注意

  • unistd.h 是 unix 系统标准头文件,用于系统调用,相当于 win32 中的 windows.h , unistd.h 定义的函数只能用于 UNIX 环境中,而不能用于 windows 。

  • 所以 sleep 和 usleep 只能用于 Linux / Unix 下,而不能用于 windows 。

  • nalosleep 和 其它时间日期操作函数 一样,都是定义在 time.h 中的,所以都适用。

  • 使用 clang 编译c程序文件的时候,提示「警告」说, usleep 和 nanosleep 在 C99 中是非法的。不过因为实际采用的是 C11 标准,所以还是编译通过了,也能正常执行。这里只是 clang 的一个善意的提醒吧。

以上就是C语言的sleep、usleep、nanosleep等休眠函数如何使用的详细内容,更多关于C语言的sleep、usleep、nanosleep等休眠函数如何使用的资料请关注九品源码其它相关文章!