C語(yǔ)言的sleep、usleep、nanosleep等休眠函數(shù)的使用
昨天晚上,無(wú)聊中搗鼓「死循環(huán)」小代碼的時(shí)候,想用 休眠 函數(shù)來(lái)慢慢顯示輸出結(jié)果,免得輸出結(jié)果閃得太快,看都看不清。
但是,使用 sleep 函數(shù)的話,最短的休眠時(shí)間段是一秒鐘,要想看到比較大的輸出結(jié)果的話,要等好久,于是就查了一下有沒(méi)有休眠時(shí)間段更小的函數(shù)。很容易地就找到了兩個(gè),一個(gè)是 usleep ,一個(gè)是 nanosleep 函數(shù)。
因?yàn)槭堑谝淮问褂?,尤其?nanosleep 函數(shù)的第一個(gè)參數(shù)是一個(gè)沒(méi)見過(guò)的結(jié)構(gòu)體數(shù)據(jù)類型,所以花了一點(diǎn)點(diǎn)時(shí)間去學(xué)習(xí)和探索背后的細(xì)節(jié),當(dāng)然了,對(duì)于俺目前的水平而言,也只是了解了函數(shù)本身層面的淺顯的細(xì)節(jié)而已。
搗鼓下來(lái),覺(jué)得內(nèi)容也不少,而且還是挺有用的,就整理了一下,組合成文章,發(fā)布在這里,也方便自己以后的回顧學(xué)習(xí)。
引子
一個(gè)無(wú)聊的死循環(huán)小代碼:
#include <stdio.h> int main(int argc, char const *argv[]) { ?? ?for (char c = 0; c < 128; c++) { ? ? ? ? ? ? ? ? printf("cool\n"); ?? ?} ?? ?return 0; }
以及 運(yùn)行過(guò)程 展示版:
#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)錯(cuò)誤、(2)警告 提示非常直觀、準(zhǔn)確、體貼。
比如,上面的死循環(huán)代碼,編譯之后,它就貼心地顯示了一個(gè)警告:
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++) { ~ ^ ~~~
強(qiáng)烈推薦?。。?!
當(dāng)然,如果還是喜歡或者必須使用 gcc 的話,建議可以將 clang 作為一個(gè)輔助選項(xiàng)。
(一) sleep 函數(shù)
頭文件 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);
通過(guò)debug的方式,進(jìn)入 sleep 函數(shù)本體內(nèi)部,可以反向查找到 sleep 函數(shù)所在的具體文件是 /glibc-2.23/sysdeps/posix/sleep.c 。
(根據(jù)gcc版本的不同,上面的庫(kù)函數(shù)版本號(hào) glibc-2.23 有所不同。)
源文件 sleep.c
sleep 函數(shù)的原型代碼如下:
#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 函數(shù)的用法
簡(jiǎn)單地說(shuō), sleep 函數(shù)實(shí)現(xiàn)的功能是 讓程序休眠若干秒鐘,時(shí)間的最小刻度是「秒」。
extern unsigned int sleep (unsigned int __seconds);
sleep 函數(shù)的返回值
- (1)如果 sleep 函數(shù)順利執(zhí)行了的話,返回值是實(shí)際休眠的時(shí)間數(shù),
- (2)如果實(shí)際休眠的時(shí)間和設(shè)定的休眠時(shí)間一致的話,返回值是0,
- (3)不會(huì)返回表示 錯(cuò)誤 信息的值,但是如果返回的值與設(shè)定的休眠時(shí)間的值一樣的話,很可能 sleep 函數(shù)其實(shí)并沒(méi)有執(zhí)行,
- (4)返回值類型是 unsigned int 型,也就是說(shuō),是一個(gè) 非負(fù)數(shù) 。
sleep 函數(shù)的參數(shù)
- (1)參數(shù)的類型是 unsigned int 型,也就是說(shuō),是一個(gè) 非負(fù)數(shù) ,
- (2)參數(shù)的時(shí)間單位是 秒 。
所以, sleep 函數(shù),使 進(jìn)程/process 休眠的最短時(shí)間段,是一秒鐘。
(二) usleep 函數(shù)
頭文件 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 文件的時(shí)候,在 find 命令的結(jié)果中看到了 usleep.c 文件和 sleep.c 文件位于同一個(gè)文件夾:
/glibc-2.23/sysdeps/posix/sleep.c 。
(根據(jù)gcc版本的不同,上面的庫(kù)函數(shù)版本號(hào) glibc-2.23 有所不同。)
源文件 usleep.c
usleep 函數(shù)的原型代碼如下:
#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 的第一個(gè)字母 u 代表的是時(shí)間單位 微秒 的第一個(gè)字母。
雖然實(shí)際上是希臘字母的 μ ,但英語(yǔ)鍵盤里不方便敲出這個(gè)字母,所以就用了樣子相似的英文字母 u 。
時(shí)間單位 微秒 的英文單詞是 microsecond ,是由詞根 micro 和 second 組合而成的單詞。
微秒 是 10的負(fù)6次方 秒。
另外,還有一個(gè)以字母 m 開頭的時(shí)間單位的英文單詞 millisecond ,意思是 毫秒 ,也就是 千分之一秒。
注意,區(qū)分 micro 和 milli ,一個(gè)是 微 ,一個(gè)是 毫 。
usleep 函數(shù)的用法
簡(jiǎn)單地說(shuō), usleep 函數(shù)實(shí)現(xiàn)的功能是 讓程序休眠若干「微秒」,時(shí)間的最小刻度是「微秒」,10的負(fù)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 函數(shù)的返回值
網(wǎng)上查到的是:成功返回0,出錯(cuò)返回-1。
usleep 函數(shù)的參數(shù)
- (1) 參數(shù)的類型是 __useconds_t ,這個(gè)類型的定義要查找好幾個(gè)文件才找得到,
- (2) 首先是找到了頭文件 types.h ,具體路徑是 /glibc/include/sys/types.h ,可惜這里面沒(méi)有明確、具體的定義,
- (3) 然后還得找到頭文件 typesizes.h ,具體路徑是 ==/glibc/sysdeps/mach/hurd/bits/typesizes.h ==,這里終于有了,
- (4) 第一個(gè)宏, #define __USECONDS_T_TYPE __U32_TYPE ,在 typesizes.h 文件里,
- (5) 第二個(gè)宏, #define __U32_TYPE unsigned int ,在 types.h 文件里,
- (6) 真是折騰??!這下總算知道 __useconds_t 就是 unsigned int 類型了,也就是 非負(fù)整數(shù)。
- (7) 參數(shù)的時(shí)間單位是 微秒 。
所以, usleep 函數(shù),使 進(jìn)程/process 休眠的最短時(shí)間段,是一微秒。
(三) nanosleep 函數(shù)
頭文件 time.h
這個(gè) time.h 頭文件的路徑是 /glibc/time/time.h 。
在C語(yǔ)言自帶的庫(kù)目錄里面,有好幾個(gè)不同目錄下的 time.h 文件,只有這個(gè)才是定義了 nanosleep 函數(shù)的。
也不知道,編譯器在預(yù)處理階段去獲取的 time.h 到底是哪個(gè)? 或者說(shuō),全部都獲取過(guò)來(lái)了?
頭文件 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);
函數(shù)名稱的 nano 是 納米、納秒 等計(jì)量單位的開頭字母,一納秒是10的負(fù)9次方 秒,是10的負(fù)6次方 毫秒,是10的負(fù)3次方 微秒。
源文件 nanosleep.c
下面是這個(gè)路徑 /glibc/posix/nanosleep.c 的文件內(nèi)容。
在C語(yǔ)言自帶的函數(shù)庫(kù)中搜索 nanosleep ,出來(lái)的結(jié)果同樣有好幾個(gè),暫時(shí)分不清到底是哪個(gè),先采用了這個(gè)。
nanosleep 函數(shù)的原型代碼如下:
#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 函數(shù)的用法
簡(jiǎn)單地說(shuō), nanosleep 函數(shù)實(shí)現(xiàn)的功能是 讓程序休眠若干「納秒」,時(shí)間的最小刻度是「納秒」,10的負(fù)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 函數(shù)的返回值
網(wǎng)上查到的是:成功返回0,出錯(cuò)返回-1。
這個(gè)函數(shù)功能是暫停某個(gè)進(jìn)程直到你規(guī)定的時(shí)間后恢復(fù),參數(shù) req 就是你要暫停的時(shí)間,其中 req->tv_sec 是以秒為單位,而 tv_nsec 以納秒為單位(10的-9次方秒)。
由于調(diào)用 nanosleep 是進(jìn)程進(jìn)入 TASK_INTERRUPTIBLE ,這種狀態(tài)是會(huì)相應(yīng)信號(hào)而進(jìn)入 TASK_RUNNING 狀態(tài)的,這就意味著有可能會(huì)沒(méi)有等到你規(guī)定的時(shí)間就因?yàn)槠渌盘?hào)而喚醒,此時(shí)函數(shù)返回 -1 ,切換剩余的時(shí)間會(huì)被記錄在 rem 中。
return值: 若進(jìn)程暫停到參數(shù) *req 所指定的時(shí)間,成功則返回0;若有信號(hào)中斷則返回-1,并且將剩余微秒數(shù)記錄在 *rem 中。
nanosleep 函數(shù)的參數(shù)
第一個(gè)參數(shù) *__requested_time 的數(shù)據(jù)類型是 struct timespec ,定義在 time.h 文件中,
具體是定義在 /glibc/time/bits/types/struct_timespec.h 文件中的。
文件內(nèi)容如下:
#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. ?*/ };
對(duì)于 struct timespec 數(shù)據(jù)類型的詳細(xì)說(shuō)明如下:
(昨天晚上上網(wǎng)查資料、自行測(cè)試的時(shí)候,自己寫的英文筆記,請(qǐng)忽略語(yǔ)法疏漏~)
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 / 結(jié)構(gòu)體成員:
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ù)時(shí)間段的 整數(shù)部分:
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ù)時(shí)間段的 小數(shù)部分:
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 的數(shù)值直接計(jì)算下來(lái)超過(guò)了一秒,成員 tv_nsec 獲得的實(shí)際的值也是被取余后的結(jié)果。
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.
總而言之,整個(gè)的休眠時(shí)間段是 整數(shù)部分的 tv_sec 加上 小數(shù)部分的 tv_nsec 組成的。
so, the whole sleep duration is tv_sec + tv_nsec,
just as 「integer part + decimal part」 of the whole sleep duration.
所以, nanosleep 函數(shù),使 進(jìn)程/process 休眠的最短時(shí)間段,是一納秒。
注意
- unistd.h 是 unix 系統(tǒng)標(biāo)準(zhǔn)頭文件,用于系統(tǒng)調(diào)用,相當(dāng)于 win32 中的 windows.h , unistd.h 定義的函數(shù)只能用于 UNIX 環(huán)境中,而不能用于 windows 。
- 所以 sleep 和 usleep 只能用于 Linux / Unix 下,而不能用于 windows 。
- nalosleep 和 其它時(shí)間日期操作函數(shù) 一樣,都是定義在 time.h 中的,所以都適用。
- 使用 clang 編譯c程序文件的時(shí)候,提示「警告」說(shuō), usleep 和 nanosleep 在 C99 中是非法的。不過(guò)因?yàn)閷?shí)際采用的是 C11 標(biāo)準(zhǔn),所以還是編譯通過(guò)了,也能正常執(zhí)行。這里只是 clang 的一個(gè)善意的提醒吧。
到此這篇關(guān)于C語(yǔ)言的sleep、usleep、nanosleep等休眠函數(shù)的使用的文章就介紹到這了,更多相關(guān)C語(yǔ)言sleep,usleep,nanosleep內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++中的內(nèi)聯(lián)函數(shù)和函數(shù)重載
這篇文章主要介紹了詳解C++中的內(nèi)聯(lián)函數(shù)和函數(shù)重載,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09C語(yǔ)言函數(shù)棧幀的創(chuàng)建和銷毀詳解
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言函數(shù)棧幀的創(chuàng)建和銷毀,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-02-02Qt串口通信開發(fā)之Qt串口通信模塊QSerialPort開發(fā)完整實(shí)例(串口助手開發(fā))
這篇文章主要介紹了Qt串口通信開發(fā)之Qt串口通信模塊QSerialPort開發(fā)完整實(shí)例(串口助手開發(fā)),需要的朋友可以參考下2020-03-03C++ 隨機(jī)數(shù)字以及隨機(jī)數(shù)字加字母生成的案例
這篇文章主要介紹了C++ 隨機(jī)數(shù)字以及隨機(jī)數(shù)字加字母生成的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12C語(yǔ)言開發(fā)實(shí)現(xiàn)井字棋及電腦落子優(yōu)化示例詳解
以前上課經(jīng)常和同桌玩起井字棋,那么我們就當(dāng)我們回憶童年,現(xiàn)在也用C語(yǔ)言來(lái)實(shí)現(xiàn)井字棋,本次代碼相對(duì)于初階的井字棋,在電腦下棋代碼部分做了優(yōu)化,使得電腦更加具有威脅2021-11-11