我們已經在本書中多次使用了函數sleep,我們也在圖10.7以及圖10.8中展示了兩個有缺陷的sleep函數的實現。
#include <unistd.h>
unisgned int sleep(unsigned int seconds);
Returns:0 or number of unslept seconds.
該函數會造成進程掛起直到如下兩個條件中至少一個為止:
- seconds指定的系統時間到;
- 一個信號被進程捕獲並且該信號處理函數返回;
與函數alarm一樣,該函數的實際返回時間可能會比請求的時間稍微晚一些,因為其他系統活動的影響。
在上述第一種情況下,返回值是0,當因為一些信號的出現導致sleep函數提前返回(第二種情況),返回值是還沒有sleep完成的秒數。
雖然sleep函數可以使用函數alarm實現(10.10節),但是對此並不做要求.如果同時使用函數alarm與sleep,二者可能會相互影響,而POSIX.1標准對於這些影響並沒有相關要求。舉例來說,如果我們執行函數sleep(10),等待3秒鍾以后,在執行sleep(5),那么會發生什么呢?sleep函數將在5秒鍾后返回(假設其他信號並沒有在這段時間內被捕獲到),但是在2秒鍾以后是否會產生信號SIGALRM呢?這一細節與實現有關。
FreeBSD 8.0, Linux 3.2.0, Mac OS X 10.6.8以及Solaris 10使用函數nanosleep函數來實現sleep函數,nanosleep函數可以實現與alarm定時器的獨立,為了保證可移植,你不能對sleep函數的實現做任何假設,但是如果你想要混合使用函數sleep與其他任何的時間函數的話,你需要意識到可能的影響。
Example
圖10.29顯示了POSIX.1中sleep函數的實現,該函數是圖10.7中函數實現的一個修改,在圖10.7中的可靠地處理了信號,避免了之前實現中可能出現的競態條件,但是我們仍然沒有處理與早先設置的定時器的沖突(雖然這些影響在POSIX.1中並沒有定義)
#include "apue.h"
static void sig_alrm(int signo)
{
/*nothing to do,just returning wakes up sigsuspend() */
}
unsigned int sleep(unsigned int seconds)
{
struct sigaction newact, oldact;
sigset_t newmask, oldmask, suspmask;
unsigned int unslept;
/*set our handler, save previous information */
newact.sa_handler = sig_alrm;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGALRM, &newact, &oldact);
/*block SIGALRM and save current signal mask */
sigempty(&newmask);
sigaddset(&newmask, SIGALRM);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
alarm(seconds);
suspmask = oldmask;
/*make sure SIGALRM isn't blockedd */
sigdelset(&suspmask, SIGALRM);
/*wait for any signal to be canught */
sigsuspend(&suspmask);
/*some signal has been caught, SIGALRM is now blocked */
unslept = alarm(0);
/*reset previous action */
sigaction(SIGALRM, &oldact, NULL);
/*reset signal mask, which unblocks SIGALRM */
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return (unslept);
}
Figure 10.29 Reliable implementation of sleep
上述實現使用了比圖10.7中更多的代碼來實現,我們並沒有使用分支跳轉語句,因此這對於其他的被SIGALRM中斷的信號處理函數沒有任何影響。
函數nanosleep與sleep函數類似,但是提供了納秒級的時間精度;
#include <time.h>
int nanosleep(const struct timespec *reqtp, struct timespec *remtp);
Return: 0 if slept for requested time or -1 on error.
該函數將會使得調用進程處於掛起狀態,直到請求的時間到達或者是被信號中斷。參數reqtp指定了需要睡眠的秒數與納秒數,如果在睡眠中途被信號中斷,且進程沒有終止的話,timespec結構指針remtp指向的結構將保存剩余的睡眠時間,如果我們對於未睡眠時間不感興趣的話,我們可以把這一時間設置為NULL.
如果系統不支持納秒時間精度的話,請求時間將被向上取整,因為函數nanosleep並不涉及任何信號的產生,我們可以放心大膽地使用它而不用擔心與其他函數相互影響。
nanosleep函數曾經屬於the Single Unix Specification中的Timers選項,但是在SUSv4版本中被加入到了基本要求。
隨着多系統時鍾的引入,我們需要一種方法來掛起調用線程,使用一個特殊時鍾來實現一段延時。函數clock_nanosleep實現了這一功能。
#include <time.h>
int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *reqtp, struct timespec *remtp);
Returns: 0 if slept for requested time or error number on failure.
參數clock_id用於指定延時是相對於哪一種時鍾時間進行的評估。其可選數值如圖6.8所示。參數flags用於控制延時是絕對的還是相對的,當flags被設置為0的時候,睡眠時間是相對的(想要睡眠多久),當其數值被設置為TIMER_ABSTIME的時候,睡眠時間是絕對的,比如說,我們需要一直睡眠直到時鍾到達指定的時間,這個時候參數remtp是沒有被使用的。其他參數reqtp以及remtp,與函數nanosleep相同。
除了返回錯誤的情況,調用
clock_nanosleep(CLOCK_REALTIME, 0, reqtp, remtp);
與如下調用有着完全相同的作用
nanosleep(reqtp, remtp);
使用相對睡眠的問題是一些應用對於睡眠的時間要求比較精確,但是相對睡眠時間可能會導致實際睡眠時間比期望的時間更長。舉例來說,如果一個應用程序想要周期性執行一個任務,那么它可能需要獲取當前時間,然后計算到達下一次執行任務的時間間隔,然后調用函數nanosleep,因為獲取到當前時間的時刻與nanosleep函數被調用的時刻可能因為系統調度或者是搶占的原因,導致這兩個時間點間隔較長,從而導致實際設置的時間長度比期望的更長,這種情況下,使用一個絕對時間就可以很好地改善定時精度,即是時間共享的調度其可能在睡眠時間已經結束的時候無法保證立即執行我們的任務。
在早期版本的the Single UNIX Specification中,函數clock_nanosleep屬於Clock Selection選項,但是在SUSv4中,被移到了基本要求中了。
