時鐘換算:
1秒(s) = 1000 毫秒(ms) = 1,000,000 微秒(μs) = 1,000,000,000 納秒(ns) = 1,000,000,000,000 皮秒(ps)
程序掛起主要有以下幾種:
sleep, usleep, select, pselect, nanosleep;
它們的精度不同,在不同的應用場景下需要不同的函數;
一、用法1.1 函數名: sleep
頭文件:
#include <unistd.h> // 在gcc編譯器中,使用的頭文件因gcc版本的不同而不同
功 能: 執行掛起指定的秒數
語 法: unsigned sleep(unsigned seconds);
示例:
#include<stdio.h>
int main()
{
int a;
a=1;
printf("hello");
sleep(a);
printf("world");
return 0;
}
1.2 函數名: usleep
功 能: usleep功能把進程掛起一段時間, 單位是微秒(百萬分之一秒);
頭文件: #include <unistd.h>
語 法:
void usleep(int micro_seconds);
返回值: 無
內容說明:本函數可暫時使程序停止執行。參數 micro_seconds 為要暫停的微秒數(us)。
注 意:
用在Linux的測試環境下面。
參 見:usleep() 與sleep()類似,用於延遲掛起進程。進程被掛起放到reday queue。
是一般情況下,延遲時間數量級是秒的時候,儘可能使用sleep()函數。
如果延遲時間為幾十毫秒(1ms = 1000us),或者更小,儘可能使用usleep()函數。這樣才能最佳的利用CPU時間.
1.3 函數名:select
功能:I/O多工機制
用來等待文件描述符狀態的改變。
頭文件:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
語法:
int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval*timeout);
參數說明:
maxfdp:最大的文件描述符加1,如果我有三個文件節點1、4、6,那第一個參數就為7(6+1)
readfds:可讀文件節點集,類型為fd_set。
通過FD_ZERO(&readfd);初始化節點集;
然後通過FD_SET(fd, &readfd);把需要監聽是否可讀的節點加入節點集
readfds:可寫文件節點集中,類型為fd_set。操作方法和第二個參數一樣。
errorfds:檢查節點錯誤集。
timeout:超時參數,類型為struct timeval,用於設置超時時間,
分別可設置秒timeout.tv_sec和微秒timeout.tv_usec。
返回值:
執行成功則返回文件描述符狀態已經改變的個數,
如果返回0,則表示在描述符在狀態改變前已經超過了timeout時間。
當有錯誤發生時,返回-1, 且錯誤原因保存在errno中。
定時器用法:
//test
select tv.tv_sec = 0;
tv.tv_usec = 500000;
ret = select(0, NULL, NULL, NULL, &tv);
if (-1 == ret){
fprintf(stderr, "select error. errno = %d [%s]\n", errno, strerror(errno));
}
1.4 函數名:pselect
功能:I/O多工機制
頭文件:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
語法:
int pselect(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timespec* timeout,
const sigset_t *sigmask);
說明:
pselect()和select()作用幾乎一樣,都是用來等待文件描述符狀態的改變,
不過pselect() 還可以判斷是否有信號(signal)發生。
定時器用法:
req.tv_sec = nDelay/1000000;
req.tv_nsec = (nDelay%1000000) * 1000;
ret = pselect(0, NULL, NULL, NULL, &req, NULL);
if (-1 == ret){
fprintf(stderr, "select error. errno = %d [%s]\n", errno, strerror(errno));
}
1.5 函數名:nonasleep
功能:
nanosleep()函數會導致當前的線程將暫停執行,直到rqtp參數所指定的時間間隔。
或者在指定時間間隔內有信號傳遞到當前線程,將引起當前線程調用信號捕獲函數或終止該線程。
頭文件:
#include<time.h>
#include<sys/time.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/select.h>
語法:
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
描述:
nanosleep()函數會導致當前的線程將暫停執行,直到rqtp參數所指定的時間間隔。
或者在指定時間間隔內有信號傳遞到當前線程,將引起當前線程調用信號捕獲函數或終止該線程。
暫停時間可能超過請求時間,因為參數值是sleep粒度的整數倍數或者因為其他活動的系統調度。
但是,除了被信號中斷的情況下,暫停時間不會少於rqtp指定的時間,由系統時鐘CLOCK_REALTIME測量。
使用nanosleep()函數對其他行為沒有影響,不堵塞任何信號。
返回值:
0 :請示的時間間隔結束。
-1:信號中斷或失敗,並設置errno。
如果rmtp參數不為空,它所引用的timespec結構更新為包含剩餘時間的間隔量(請求的時間減去實際睡眠時間)。
如果rmtp參數為NULL,不返回的剩餘時間。
示例:
req.tv_sec = 0;
req.tv_nsec = 1000;
ret = nanosleep(&req, NULL);
if (-1 == ret) {
fprintf (stderr, "\t nanousleep %8u not support\n", nDelay);
}
二、精確度對比
如果用於一些 C/S 數據交互等 100ms級以上精度的程序掛起,用sleep, usleep都可以。
如果是在多線程應用場景中,如用於音視頻數據的同步,需要100ms以下的精度的程序掛起,則只能用select, 和 nanosleep.
這是因為,雖然sleep()和nanosleep()都是使進程睡眠一段時間後被喚醒,但是二者的實現完全不同。
Linux中並沒有提供系統調用sleep(),
sleep()是在庫函數中實現的,它是通過調用alarm()來設定報警時間,
調用sigsuspend()將進程掛起在信號SIGALARM上。
nanosleep()則是Linux中的系統調用,它是使用定時器來實現的,
該調用使調用進程睡眠,並往定時器隊列上加入一個timer_list型定時器,time_list結構裡包括喚醒時間以及喚醒後執行的函數,
通過nanosleep()加入的定時器的執行函數僅僅完成喚醒當前進程的功能。
系統通過一定的機制定時檢查這些隊列(比如通過系統調用陷入核心後,從核心返回用戶態前,
要檢查當前進程的時間片是否已經耗盡,如果是則調用schedule()函數重新調度,該函數中就會檢查定時器隊列,另外慢中斷返回前也會做此檢查),
如果定時時間已超過,則執行定時器指定的函數喚醒調用進程。
三、工程應用
這是FFmpeg的程序掛起,
int av_usleep(unsigned usec)
{
#if HAVE_NANOSLEEP
struct timespec ts = { usec / 1000000, usec % 1000000 * 1000 };
while (nanosleep(&ts, &ts) < 0 && errno == EINTR);
return 0;
#elif HAVE_USLEEP
return usleep(usec);
#elif HAVE_SLEEP
Sleep(usec / 1000);
return 0;
#else
return AVERROR(ENOSYS);
#endif
}