溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C語言的sleep、usleep、nanosleep等休眠函數如何使用

發(fā)布時間:2023-03-09 10:30:39 來源:億速云 閱讀:193 作者:iii 欄目:開發(fā)技術

這篇文章主要講解了“C語言的sleep、usleep、nanosleep等休眠函數如何使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C語言的sleep、usleep、nanosleep等休眠函數如何使用”吧!

引子

一個無聊的死循環(huán)小代碼:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    for (char c = 0; c < 128; c++) {
                printf("cool\n");
    }
    return 0;
}

以及 運行過程 展示版:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    struct timespec n_sleep;
    n_sleep.tv_sec = 0; //secondes, integer part sleep duration
    n_sleep.tv_nsec = 5e8L; //nanoseconds, decimal part sleep duration

    char c;
    for (c = 0; c < 128; c++) {
        printf("char of c :%c\n", c);
        printf("ASCII num of c :%d\n", c);
        sleep(1); // 1 s
        usleep(900000); // 0.9 s 
        nanosleep(&n_sleep, NULL); // 0 + 0.5 s
    }
    return 0;
}

另外,推薦一下 clang 這款編譯器,
它的(1)錯誤、(2)警告 提示非常直觀、準確、體貼。
比如,上面的死循環(huán)代碼,編譯之后,它就貼心地顯示了一個警告:

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

強烈推薦?。。?!
當然,如果還是喜歡或者必須使用 gcc 的話,建議可以將 clang 作為一個輔助選項。

(一) sleep 函數

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

/* Make the process sleep for SECONDS seconds, or until a signal arrives
   and is not ignored.  The function returns the number of seconds less
   than SECONDS which it actually slept (thus zero if it slept the full time).
   If a signal handler does a `longjmp' or modifies the handling of the
   SIGALRM signal while inside `sleep' call, the handling of the SIGALRM
   signal afterwards is undefined.  There is no return value to indicate
   error, but if `sleep' returns SECONDS, it probably didn't work.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern unsigned int sleep (unsigned int __seconds);

通過debug的方式,進入 sleep 函數本體內部,可以反向查找到 sleep 函數所在的具體文件是 /glibc-2.23/sysdeps/posix/sleep.c 。

(根據gcc版本的不同,上面的庫函數版本號 glibc-2.23 有所不同。)

源文件 sleep.c

sleep 函數的原型代碼如下:

#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sys/param.h>

/* Make the process sleep for SECONDS seconds, or until a signal arrives
   and is not ignored.  The function returns the number of seconds less
   than SECONDS which it actually slept (zero if it slept the full time).
   If a signal handler does a `longjmp' or modifies the handling of the
   SIGALRM signal while inside `sleep' call, the handling of the SIGALRM
   signal afterwards is undefined.  There is no return value to indicate
   error, but if `sleep' returns SECONDS, it probably didn't work.  */
unsigned int __sleep(unsigned int seconds)
{
    int save_errno = errno;

    const unsigned int max =
        (unsigned int)(((unsigned long int)(~((time_t)0))) >> 1);
    struct timespec ts = { 0, 0 };
    do {
        if (sizeof(ts.tv_sec) <= sizeof(seconds)) {
            /* Since SECONDS is unsigned assigning the value to .tv_sec can
             overflow it.  In this case we have to wait in steps.  */
            ts.tv_sec += MIN(seconds, max);
            seconds -= (unsigned int)ts.tv_sec;
        } else {
            ts.tv_sec = (time_t)seconds;
            seconds = 0;
        }

        if (__nanosleep(&ts, &ts) < 0)
            /* We were interrupted.
           Return the number of (whole) seconds we have not yet slept.  */
            return seconds + ts.tv_sec;
    } while (seconds > 0);

    __set_errno(save_errno);

    return 0;
}
weak_alias(__sleep, sleep)

sleep 函數的用法

簡單地說, sleep 函數實現的功能是 讓程序休眠若干秒鐘,時間的最小刻度是「秒」。

extern unsigned int sleep (unsigned int __seconds);

sleep 函數的返回值

  • (1)如果 sleep 函數順利執(zhí)行了的話,返回值是實際休眠的時間數,

  • (2)如果實際休眠的時間和設定的休眠時間一致的話,返回值是0,

  • (3)不會返回表示 錯誤 信息的值,但是如果返回的值與設定的休眠時間的值一樣的話,很可能 sleep 函數其實并沒有執(zhí)行,

  • (4)返回值類型是 unsigned int 型,也就是說,是一個 非負數 。

sleep 函數的參數

  • (1)參數的類型是 unsigned int 型,也就是說,是一個 非負數 ,

  • (2)參數的時間單位是 秒 。

所以, sleep 函數,使 進程/process 休眠的最短時間段,是一秒鐘。

(二) usleep 函數

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

/* Sleep USECONDS microseconds, or until a signal arrives that is not blocked
   or ignored.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
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 函數的原型代碼如下:

#include <time.h>
#include <unistd.h>

int
usleep (useconds_t useconds)
{
  struct timespec ts = { .tv_sec = (long int) (useconds / 1000000),
             .tv_nsec = (long int) (useconds % 1000000) * 1000ul };

  /* Note the usleep() is a cancellation point.  But since we call
     nanosleep() which itself is a cancellation point we do not have
     to do anything here.  */
  return __nanosleep (&ts, NULL);
}

名稱 usleep 的第一個字母 u 代表的是時間單位 微秒 的第一個字母。
雖然實際上是希臘字母的 &mu; ,但英語鍵盤里不方便敲出這個字母,所以就用了樣子相似的英文字母 u 。
時間單位 微秒 的英文單詞是 microsecond ,是由詞根 micro 和 second 組合而成的單詞。
微秒 是 10的負6次方 秒。

另外,還有一個以字母 m 開頭的時間單位的英文單詞 millisecond ,意思是 毫秒 ,也就是 千分之一秒。
注意,區(qū)分 micro 和 milli ,一個是 微 ,一個是 毫 。

usleep 函數的用法

簡單地說, usleep 函數實現的功能是 讓程序休眠若干「微秒」,時間的最小刻度是「微秒」,10的負6次方 秒。

/* Sleep USECONDS microseconds, or until a signal arrives that is not blocked
   or ignored.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
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 中的原文如下:

/* Pause execution for a number of nanoseconds.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int nanosleep (const struct timespec *__requested_time,
              struct timespec *__remaining);

函數名稱的 nano 是 納米、納秒 等計量單位的開頭字母,一納秒是10的負9次方 秒,是10的負6次方 毫秒,是10的負3次方 微秒。

源文件 nanosleep.c

下面是這個路徑 /glibc/posix/nanosleep.c 的文件內容。

在C語言自帶的函數庫中搜索 nanosleep ,出來的結果同樣有好幾個,暫時分不清到底是哪個,先采用了這個。

nanosleep 函數的原型代碼如下:

#include <errno.h>
#include <time.h>


/* Pause execution for a number of nanoseconds.  */
int
__nanosleep (const struct timespec *requested_time,
         struct timespec *remaining)
{
  __set_errno (ENOSYS);
  return -1;
}
stub_warning (nanosleep)

hidden_def (__nanosleep)
weak_alias (__nanosleep, nanosleep)

nanosleep 函數的用法

簡單地說, nanosleep 函數實現的功能是 讓程序休眠若干「納秒」,時間的最小刻度是「納秒」,10的負9次方 秒。

/* Pause execution for a number of nanoseconds.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int nanosleep (const struct timespec *__requested_time,
              struct timespec *__remaining);

nanosleep 函數的返回值

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

這個函數功能是暫停某個進程直到你規(guī)定的時間后恢復,參數 req 就是你要暫停的時間,其中 req->tv_sec 是以秒為單位,而 tv_nsec 以納秒為單位(10的-9次方秒)。

由于調用 nanosleep 是進程進入 TASK_INTERRUPTIBLE ,這種狀態(tài)是會相應信號而進入 TASK_RUNNING 狀態(tài)的,這就意味著有可能會沒有等到你規(guī)定的時間就因為其它信號而喚醒,此時函數返回 -1 ,切換剩余的時間會被記錄在 rem 中。

return值: 若進程暫停到參數 *req 所指定的時間,成功則返回0;若有信號中斷則返回-1,并且將剩余微秒數記錄在 *rem 中。

nanosleep 函數的參數

第一個參數 *__requested_time 的數據類型是 struct timespec ,定義在 time.h 文件中,

具體是定義在 /glibc/time/bits/types/struct_timespec.h 文件中的。
文件內容如下:

#include <bits/types.h>

/* POSIX.1b structure for a time value.  This is like a `struct timeval' but
   has nanoseconds instead of microseconds.  */
struct timespec
{
  __time_t tv_sec;        /* Seconds.  */
  __syscall_slong_t tv_nsec;    /* Nanoseconds.  */
};

對于 struct timespec 數據類型的詳細說明如下:
(昨天晚上上網查資料、自行測試的時候,自己寫的英文筆記,請忽略語法疏漏~)

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

struct timespec; (since C11)

struct timespec {
    time_t tv_sec; // seconds
    long tv_nsec; // nanoseconds
};

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 決定 休眠持續(xù)時間段的 整數部分:

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 決定 休眠持續(xù)時間段的 小數部分:

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 系統(tǒng)標準頭文件,用于系統(tǒng)調用,相當于 win32 中的 windows.h , unistd.h 定義的函數只能用于 UNIX 環(huán)境中,而不能用于 windows 。

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

  • nalosleep 和 其它時間日期操作函數 一樣,都是定義在 time.h 中的,所以都適用。

  • 使用 clang 編譯c程序文件的時候,提示「警告」說, usleep 和 nanosleep 在 C99 中是非法的。不過因為實際采用的是 C11 標準,所以還是編譯通過了,也能正常執(zhí)行。這里只是 clang 的一個善意的提醒吧。

感謝各位的閱讀,以上就是“C語言的sleep、usleep、nanosleep等休眠函數如何使用”的內容了,經過本文的學習后,相信大家對C語言的sleep、usleep、nanosleep等休眠函數如何使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI