原文來自靜雅齋,轉載請註明出處。javascript
SIGCLD和SIGCHLD是兩個很類似的信號,SIGCLD是SystemV的一個信號名字,而SIGCHLD是BSD信號,可是POSIX.1標準使用了BSD的SIGCHLD信號名稱。
BSD的SIGCHLD信號是很普通的意思,就是子進程狀態改變就會產生這個信號,父進程則是調用wait函數查看子進程的狀態,而SystemV的SIGCLD信號則不一樣,基於SVR4的系統都會繼承這個狀況。java
基本就是這些,實際上這個信號可看可不看。由於在實際開發中是不可能使用這個信號的,很多平臺都不支持此信號。shell
信號是事件發生時,爲進程產生一個信號或者發送一個信號,當信號產生時,內核會在進程表中設置一個標誌。在信號產生和傳遞中間,信號是阻塞的(pending)。
進程能夠設置阻塞一個信號傳送,若是對這個進程發送了一個已經設置爲阻塞的信號,而且該信號的動做是系統默認動做或者捕捉該信號,換言之,就是不忽略該信號的處理,則爲該進程將此信號設置爲pending狀態,直到該進程對此信號接觸阻塞,或者設置該信號的動做爲忽略。app
int sigpending(sigset_t *set);
The sigpending function returns a mask of the signals pending for delivery to the calling process in the location indicated by set. Signals may be pending because they are currently masked, or transiently before delivery (although the latter case is not normally detectable).複製代碼
實際上,每一個進程都有一個信號屏蔽字(signal mask),就和權限屏蔽字同樣,這是用於記錄當前要阻塞傳遞的信號集。函數
發送信號有兩種函數ui
int kill(pid_t pid, int sig);
int raise(int sig);複製代碼
兩個函數的區別就是一個是系統函數庫,一個是ISO C函數庫,而且raise函數容許進程向自身發送信號。
kill函數很經常使用,因此在這裏講一下kill函數的參數this
If pid is greater than zero:
Sig is sent to the process whose ID is equal to pid.
If pid is zero:
Sig is sent to all processes whose group ID is equal to the process group ID of the sender, and for which the process has permission; this is a variant of killpg(2).
If pid is -1:
If the user has super-user privileges, the signal is sent to all processes excluding system processes and the process sending the signal. If the user is not the super user, the signal is sent to all processes with the same uid as the user, excluding the process sending the signal. No error is returned if any process could be signaled.
For compatibility with System V, if the process number is negative but not -1, the signal is sent to all processes whose process group ID is equal to the absolute value of the process number. This is a variant of killpg(2).複製代碼
爲了保持SystemV的兼容,當pid小於0且不等於-1的時候,信號發送給全部進程組ID等於當前進程絕對值的進程。spa
unsigned alarm(unsigned seconds);複製代碼
alarm函數就是設置一個定時器,當超時後會產生一個SIGALRM信號,若是忽略或者系統默認動做,就是進程終止,固然,通常狀況下,進程都捕捉該信號。操作系統
int pause(void);複製代碼
pause函數會強制進程暫停直到從kill函數或者setitimer函數收到一個信號。因此當看到這兩個函數,基本想法就是這兩個函數能讓進程休眠指針
#include <signal.h>
#include <unistd.h>
static void sig_alrm(int signo)
{
}
unsigned int sleep1(unsigned int seconds)
{
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
return seconds;
alarm(seconds);
pause();
return(alarm(0));
}複製代碼
固然,這樣子寫確定是有問題的,好比
爲了可以表示多個信號,系統提供了信號集的數據類型。在一般的開發中,常常會使用到二進制位來表示狀態,二進制的每一位表明一種信號,可是實際上,因爲信號的編號確定會超過一個整形量的位數,因此通常都不是用一個整形量表示信號集。POSIX.1定義了數據類型sigset_t
用以表示一個信號集,蘋果系統下其實是這麼表示的
typedef __uint32_t __darwin_sigset_t;
typedef __darwin_sigset_t sigset_t;複製代碼
除此之外,系統還定一款了下列5個處理信號集的函數
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigismember(const sigset_t *set, int signo);複製代碼
sigemptyset函數初始化set指向的信號集,清除全部信號,sigfillset初始化set指向的信號集,被設置爲包含全部信號,在使用以前,sigemptyset
或者sigfillset
必須被調用。sigaddset和sigdelset則是添加刪除一個信號,sigismember函數則返回是否一個指定的signo信號被包含在這個信號集中。
在前文中說起了信號屏蔽字指定了當前進程阻塞不能傳遞的信號集。而sigprocmask函數就是用來檢測修改信號屏蔽字的函數
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);複製代碼
若是set不是null,sigprocmask函數的行爲依賴how參數。
若是oset不是null,那麼當前的信號屏蔽字將會被設置給oset,若是set參數爲null,則不改變信號屏蔽字,how參數也沒有意義。
int sigpending(sigset_t *set);
The sigpending function returns a mask of the signals pending for delivery to the calling process in the location indicated by set. Signals may be pending because they are currently masked, or transiently before delivery (although the latter case is not normally detectable).複製代碼
sigpending函數返回當前進程阻塞不能傳遞信號的信號集。
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);複製代碼
sigaction函數用於檢查或修改與制定信號相關聯的處理動做,若是act指針非空,則修改其動做,若是oact非空,則經過oact指針返回該信號的上一個動做,是否是以爲很熟悉,這個函數很像sigprocmask函數,實際上這個函數就是替代了signal函數,在上面的參數中也能夠看出實際上多了新結構體,手冊上也寫了,因此這裏也列了出來。
struct sigaction {
union __sigaction_u __sigaction_u; /* signal handler */
sigset_t sa_mask; /* signal mask to apply */
int sa_flags; /* see signal options below */
};
union __sigaction_u {
void (*__sa_handler)(int);
void (*__sa_sigaction)(int, siginfo_t *,
void *);
};
#define sa_handler __sigaction_u.__sa_handler
#define sa_sigaction __sigaction_u.__sa_sigaction複製代碼
看起來和原著上面不同啊,其實就是一個道理,原著中有兩種handler,因此用了兩個成員存儲,而這裏實際上使用一個union來存儲,由於handler實際上只有一個的。當更改信號的時候,若是sa_handler包含一個信號捕捉函數地址,則sa_mask字段說明了一個信號集,在調用信號捕捉函數以前,信號集要加入到進程信號屏蔽字中。
siginfo_t結構體包含了信號產生緣由的有關信息,這裏就不在繼續列出。
前面講過setjmp和longjmp函數,主要用於非局部轉移。信號處理程序常常會調用longjmp函數返回到main函數,可是,當使用longjmp函數的時候,信號會自動的加到進程屏蔽字中,若是使用longjmp跳出,則會在一些平臺上致使信號屏蔽字沒法恢復,因此Unix系統提供了兩個新函數用於信號處理函數的非局部轉移。這兩個函數只在此介紹一下,不詳細講述了。
從這個函數的名字中基本也能猜出是作什麼的了,先來看看Unix系統手冊是怎麼講的
int sigsuspend(const sigset_t *sigmask);
Sigsuspend() temporarily changes the blocked signal mask to the set to which sigmask points, and then waits for a signal to arrive; on return the previous set of masked signals is restored. The signal mask set is usually empty to indicate that all signals are to be unblocked for the duration of the call.
In normal usage, a signal is blocked using sigprocmask(2) to begin a critical section, variables modified on the occurrence of the signal are examined to determine that there is no work to be done, and the process pauses awaiting work by using sigsuspend() with the previous mask returned by sigprocmask.複製代碼
sigsuspend函數臨時改變當前的阻塞信號屏蔽字爲sigmask參數指定的信號集,而後等待一個信號到來,返回的時候,先前的信號屏蔽字將被恢復。可能有朋友想問,這個函數究竟是幹嗎的,若是按照咱們以前的知識,徹底可使用sig信號集函數去除被阻塞信號,而後使用pause等待信號發生。實際上,咱們須要考慮到這是兩步操做,頗有可能在pause以前,就已經有信號傳遞了,因此這個函數只是執行了原子操做的封裝。
abort函數也沒有什麼好說的,這個函數從名字就知道是程序終止,而且是異常終止。
void abort(void);複製代碼
這是一個ISO C庫函數,函數就是發送了SIGABRT信號給調用進程。abort函數會致使不正常的程序終止,除非信號SIGABRT被捕捉而且信號處理函數沒有返回。實際上,信號處理函數不能返回的惟一方法是它調用exit、_exit、_Exit、longjmp或者siglongjmp。當執行此函數的時候,全部的stream都會被沖洗而且被關閉。其實結合上面的信息,很容易就知道了,abort函數確定是會終止程序的,可是捕捉SIGABRT的惟一意圖就是——在程序終止前由其執行所需的清理操做。
int system(const char *command);複製代碼
system函數把command參數提交給命令解釋器sh,調用的進程等待shell執行命令結束,忽略SIGINT、SIGQUIT,而且阻塞SIGCHLD信號,若是command參數是null指針,system函數將會返回非0當sh解釋器可用,若是不可用,則返回0。
爲何system函數須要考慮信號處理,實際上原著講述足夠詳細了,一句話,system函數建立的子進程不該當使用wait函數得到退出狀態而致使system函數阻塞。
unsigned int sleep(unsigned int seconds);複製代碼
sleep函數會致使調用進程掛起,當進程超過了seconds指定的時間或者收到一個信號而且從信號處理程序返回,那麼進程將會恢復。實際上,很容易就把sleep函數和alarm函數對比,並且alarm函數的信號是否會致使sleep函數的失敗,因此sleep函數並非那麼好用。
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);複製代碼
nanosleep和sleep函數差很少,可是提供了納秒級精度,並且很是尷尬的是,系統頗有可能不支持納秒級精度,這就會致使時間取整。
因爲現代化Unix操做系統有多個系統時鐘,sleep函數也衍生出了clock_nanosleep函數用於相對特定時鐘掛起,可是在蘋果系統中,好像並無存在這個函數,手冊和頭文件都沒有找到,因此這裏就不在說起。
int sigqueue(pid_t pid,int sig,const union sigval value);複製代碼
sigqueue在隊列中向指定進程發送一個信號和數據。可是好像蘋果系統也沒有提供這個函數,或者說是提供了自有函數,因此很遺憾,沒法講解,只能請各位朋友自行閱讀原著。
其實本章最後應該還有兩節,可是這兩節只是將做業控制信號和信號名編號轉換稍稍講解一下,實際上沒有什麼價值,信號在大多數的應用程序中都是很是重要的手段,因此應當掌握。