免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Linux環(huán)境進(jìn)程間通信:信號(hào)
本系列手冊(cè)是通過(guò)參考一些網(wǎng)絡(luò)資料和本人使用經(jīng)驗(yàn)整理而寫(xiě),固然簡(jiǎn)單,但是也是花了心血而成,適用于初學(xué)
者和了解一些信號(hào)編程但始終沒(méi)有理解,沒(méi)有動(dòng)手寫(xiě)過(guò)程序的朋友們.也適用于高手們添加內(nèi)容或者糾錯(cuò)smile.gif但是不能
用于任何商業(yè)用途,可自由轉(zhuǎn)載但是必須著明出處www.loveunix.net和作者大漠孤星.

有任何意見(jiàn)和建議請(qǐng)mail to :diablo_tina@163.com或者QQ 6299745聯(lián)系.

一,前言


信號(hào)是進(jìn)程之間互傳消息的一種方法俗稱(chēng)軟件中斷。很多比較重要的應(yīng)用程序都需處理信號(hào)。信號(hào)提供了一種

處理異步事件的方法:終端用戶(hù)鍵入中斷鍵,則會(huì)通過(guò)信號(hào)機(jī)構(gòu)停止一個(gè)程序。所以,信號(hào)可以說(shuō)是進(jìn)程控制的一部分。
在Redhat 7.3上kill -l 得到

      1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
      5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
      9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
     13) SIGPIPE     14) SIGALRM     15) SIGTERM     17) SIGCHLD
     18) SIGCONT     19) SIGSTOP     20) SIGTSTP     21) SIGTTIN
     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO
     30) SIGPWR      31) SIGSYS      32) SIGRTMIN    33) SIGRTMIN+1
     34) SIGRTMIN+2  35) SIGRTMIN+3  36) SIGRTMIN+4  37) SIGRTMIN+5
     38) SIGRTMIN+6  39) SIGRTMIN+7  40) SIGRTMIN+8  41) SIGRTMIN+9
     42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13
     46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14
     50) SIGRTMAX-13 51) SIGRTMAX-12 52) SIGRTMAX-11 53) SIGRTMAX-10
     54) SIGRTMAX-9  55) SIGRTMAX-8  56) SIGRTMAX-7  57) SIGRTMAX-6
     58) SIGRTMAX-5  59) SIGRTMAX-4  60) SIGRTMAX-3  61) SIGRTMAX-2
     62) SIGRTMAX-1  63) SIGRTMAX
信號(hào)出現(xiàn)在 UNIX 的早期版本中 ,但早期的信號(hào)模型是不可靠的, 信號(hào)可能被丟失,
也很難處理關(guān)鍵段。UNIX 的兩個(gè)重要分支 BSD 和 System V 分別對(duì)早期的信號(hào)進(jìn)行了擴(kuò)展 ,
但這兩個(gè)系統(tǒng)的擴(kuò)展并不兼容POSIX 統(tǒng)一了這兩種實(shí)現(xiàn), 最終提供了可靠的信號(hào)模型。


信號(hào)的產(chǎn)生條件

. 當(dāng)用戶(hù)按某些終端鍵時(shí),產(chǎn)生信號(hào)。
. 硬件異常產(chǎn)生信號(hào):除數(shù)為0、無(wú)效的存儲(chǔ)訪(fǎng)問(wèn)等等。
. 進(jìn)程用kill函數(shù)可將信號(hào)發(fā)送給另一個(gè)進(jìn)程或進(jìn)程組
. 用戶(hù)可用kill命令將信號(hào)發(fā)送給其他進(jìn)程
. 當(dāng)檢測(cè)到某種軟件條件已經(jīng)發(fā)生,并將其通知有關(guān)進(jìn)程時(shí)也產(chǎn)生信號(hào)。

接到信號(hào)的處理辦法

1,忽略,但是SIGKILL和SIGSTOP不能忽略。
2,捕捉。
3,執(zhí)行系統(tǒng)的默認(rèn)處理。

信號(hào)的分類(lèi)

非可靠信號(hào):早期unix下的不可靠信號(hào)主要指的是進(jìn)程可能對(duì)信號(hào)做出錯(cuò)誤的反應(yīng)
以及信號(hào)可能丟失。
可靠信號(hào): 信號(hào)值位于SIGRTMIN和SIGRTMAX之間的信號(hào)都是可靠信號(hào),可靠信號(hào)
克服了信號(hào)可能丟失的問(wèn)題。

信號(hào)的可靠與不可靠只與信號(hào)值有關(guān),與信號(hào)的發(fā)送及安裝函數(shù)無(wú)關(guān)。

當(dāng)然也可以稱(chēng)為實(shí)時(shí)信號(hào)或者非實(shí)時(shí)信號(hào),非實(shí)時(shí)信號(hào)都不支持排隊(duì),都是不可靠信號(hào);

實(shí)時(shí)信號(hào)都支持排隊(duì),都是可靠信號(hào)。


這里我說(shuō)一下信號(hào)的生命周期,對(duì)于理解信號(hào)的分類(lèi)有很大的幫助。


從信號(hào)發(fā)送到信號(hào)處理函數(shù)的執(zhí)行完畢。對(duì)于一個(gè)完整的信號(hào)生命周期(從信號(hào)發(fā)送到相應(yīng)的處

理函數(shù)執(zhí)行完畢)來(lái)說(shuō),可以分為三個(gè)重要的階段,這三個(gè)階段由四個(gè)重要事件來(lái)刻畫(huà):信號(hào)產(chǎn)生;

信號(hào)在進(jìn)程中注冊(cè)完畢;信號(hào)在進(jìn)程中的注銷(xiāo)完畢;信號(hào)處理函數(shù)執(zhí)行完畢。相鄰兩個(gè)事件的時(shí)間

間隔構(gòu)成信號(hào)生命周期的一個(gè)階段。

當(dāng)一個(gè)實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),不管該信號(hào)是否已經(jīng)在進(jìn)程中注冊(cè),都會(huì)被再注冊(cè)一次,因此,

信號(hào)不會(huì)丟失,因此,實(shí)時(shí)信號(hào)又叫做"可靠信號(hào)"。這意味著同一個(gè)實(shí)時(shí)信號(hào)可以在同一個(gè)進(jìn)程的未決

信號(hào)信息鏈中占有多個(gè)sigqueue結(jié)構(gòu)(進(jìn)程每收到一個(gè)實(shí)時(shí)信號(hào),都會(huì)為它分配一個(gè)結(jié)構(gòu)來(lái)登記該信號(hào)信息,

并把該結(jié)構(gòu)添加在未決信號(hào)鏈尾,即所有誕生的實(shí)時(shí)信號(hào)都會(huì)在目標(biāo)進(jìn)程中注冊(cè));

當(dāng)一個(gè)非實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),如果該信號(hào)已經(jīng)在進(jìn)程中注冊(cè),則該信號(hào)將被丟棄,造成信號(hào)丟失。

因此,非實(shí)時(shí)信號(hào)又叫做"不可靠信號(hào)"。這意味著同一個(gè)非實(shí)時(shí)信號(hào)在進(jìn)程的未決信號(hào)信息鏈中,至多占有一個(gè)

sigqueue結(jié)構(gòu)(一個(gè)非實(shí)時(shí)信號(hào)產(chǎn)生后,1如果發(fā)現(xiàn)相同的信號(hào)已經(jīng)在目標(biāo)結(jié)構(gòu)中注冊(cè),則不再注冊(cè),對(duì)于

進(jìn)程來(lái)說(shuō),相當(dāng)于不知道本次信號(hào)發(fā)生,信號(hào)丟失;2如果進(jìn)程的未決信號(hào)中沒(méi)有相同信號(hào),則在進(jìn)程中注冊(cè)自己)。

需要注意的要點(diǎn)是:

1)信號(hào)注冊(cè)與否,與發(fā)送信號(hào)的函數(shù)(如kill()或sigqueue()等)以及信號(hào)安裝函數(shù)(signal()及sigaction())無(wú)關(guān),

只與信號(hào)值有關(guān)(信號(hào)值小于SIGRTMIN的信號(hào)最多只注冊(cè)一次,信號(hào)值在SIGRTMIN及SIGRTMAX之間的信號(hào),只要被進(jìn)程接收到就被注冊(cè))。
CODE
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void user_func(int,siginfo_t*,void*);

int main(int argc,char**argv)
{
struct sigaction act;
int sig;
sig=atoi(argv[1]);

sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=(void * )user_func;

if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}

while(1)
{
sleep(2);
printf("wait for the signal\n");
}
}
void user_func(int signum,siginfo_t *info,void *myact)
{
printf("receive signal %d\n\n\n", signum);
sleep(5);
}
在一終端執(zhí)行cc -o act act.c
$./act 8&
[1] 992
$ wait for the signal
$

在另一終端執(zhí)行

#kill -s 8 992


看看。。

$receive signal 8


實(shí)例二:信號(hào)阻塞及信號(hào)集操作
CODE
#include <signal.h>
#include <unistd.h>
void user_func(int);
main()
{
sigset_t new_mask,old_mask,pending_mask;
struct sigaction act;

sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=(void*)user_func;

if(sigaction(SIGRTMIN+10,&act,NULL))
printf("install signal SIGRTMIN+10 error\n");

sigemptyset(&new_mask);
sigaddset(&new_mask,SIGRTMIN+10);

if(sigprocmask(SIG_BLOCK, &new_mask,&old_mask))
printf("block signal SIGRTMIN+10 error\n");

sleep(20);

printf("\n\nNow begin to get pending mask and unblock SIGRTMIN+10\n\n");
if(sigpending(&pending_mask)<0)
printf("get pending mask error\n");
if(sigismember(&pending_mask,SIGRTMIN+10))
printf("signal SIGRTMIN+10 is pending\n");

if(sigprocmask(SIG_SETMASK,&old_mask,NULL)<0)
printf("unblock signal error\n");

printf("signal unblocked ,ok ... ...\n\n\n");
}
void user_func(int signum)
{
printf("receive signal %d \n",signum);
}
$cc -o sus sus.c
$./sus&
[1] 997



another console
#kill -s 42 pid

看看...

$
Now begin to get pending mask and unblock SIGRTMIN+10

signal SIGRTMIN+10 is pending
receive signal 42
signal unblocked ,ok ... ...



[1]+ Exit 31 ./d
$



實(shí)例三:signal例子
CODE
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#define damo
void user_func(int no)
{
      switch (no)
      {
              case 1:
                      printf("Get SIGHUP.\n");
                      break;
              case 2:
                      printf("Get SIGINT\n");
                      break;
              case 3:
                      printf("Get SIGQUIT \n");
                      break;
              default:
                      printf("What wan yi a \n\n");
                      break;
      }

}
int main()
{
      printf("Process id is %d\n ",getpid());

#ifdef damo
      signal(SIGHUP, user_func);
      signal(SIGINT, user_func);
      signal(SIGQUIT, user_func);
      signal(SIGBUS, user_func);
#else
      signal(SIGHUP, SIG_IGN);
      signal(SIGINT, SIG_IGN);
      signal(SIGQUIT, SIG_IGN);
      signal(SIGBUS, SIG_DFL);
#endif

      while(1)
             ;
}
這個(gè)就是signal的用法集中展示,也是我經(jīng)常用的一個(gè)方法。說(shuō)實(shí)話(huà),sco太古老了。。俺只能用這個(gè)函數(shù)了。
測(cè)試時(shí)你可以把#define damo這個(gè)注釋和不注釋看看。深刻體會(huì)signal的用法。

BTW:signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);

等等忽略了這些信號(hào)后,可以讓一個(gè)進(jìn)程終端無(wú)關(guān),即使你退出這個(gè)tty.當(dāng)然kill信號(hào)還是不能屏蔽的。
這種方式多在守護(hù)進(jìn)程中采用。

$ cc -o si si.c
$./si

Process id is 1501

在另一個(gè)tty上
#ps -ef
bank 1501 1465 51 04:07 pts/0 00:00:13 ./si
bank 1502 800 0 04:08 pts/1 00:00:00 ps -ef
#
注意觀察這時(shí)候是1456
你把./si這個(gè)tty關(guān)掉。
這時(shí)候你再
#ps -ef看看
bank 1501 1 50 04:07 ? 00:00:59 ./si
bank 1503 800 0 04:09 pts/1 00:00:00 ps -ef
注意這個(gè)1和?,知道這個(gè)進(jìn)程成啥了吧?成精拉。哈哈~~




實(shí)例四:pause函數(shù)
CODE
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void user_func()
{
      printf("\n\nCatch a signal SIGINT \n");
}

int main()
{
      printf ("pid = %d \n\n ",getpid());
      signal(SIGINT, user_func);
      pause();
      printf("receive a signal \n\n");
}
在這個(gè)例子中,程序開(kāi)始執(zhí)行,就象進(jìn)入了死循環(huán)一樣,這是因?yàn)檫M(jìn)程正在等待信號(hào),
當(dāng)我們按下Ctrl-C時(shí),信號(hào)被捕捉,并且使得pause退出等待狀態(tài)。


實(shí)例五:下面是關(guān)于setitimer調(diào)用的一個(gè)簡(jiǎn)單例子。
CODE

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>

void user_func(int sig)
{
if ( sig ==  SIGALRM)
  printf("Catch a signal  SIGALRM \n");
else if ( sig == SIGVTALRM)
  printf("Catch a signal  SIGVTALRM\n");
}

int main()
{
      struct itimerval value,ovalue,value2;

      printf("Process id is  =  %d \n",getpid());

      signal(SIGALRM, user_func);
      signal(SIGVTALRM, user_func);

      value.it_value.tv_sec = 1;
      value.it_value.tv_usec = 0;
      value.it_interval.tv_sec = 1;
      value.it_interval.tv_usec = 0;

      setitimer(ITIMER_REAL, &value, &ovalue);

      value2.it_value.tv_sec = 1;
      value2.it_value.tv_usec = 500000;
      value2.it_interval.tv_sec = 1;
      value2.it_interval.tv_usec = 500000;

      setitimer(ITIMER_VIRTUAL, &value2, &ovalue);

      while(1);
}





在該例子中,每隔1秒發(fā)出一個(gè)SIGALRM,每隔1.5秒發(fā)出一個(gè)SIGVTALRM信號(hào):

結(jié)果如下
$ ./ti
Process id is = 1734
Catch a signal SIGALRM
Catch a signal SIGVTALRM
Catch a signal SIGALRM
Catch a signal SIGALRM
Catch a signal SIGVTALRM
Catch a signal SIGALRM
Catch a signal SIGVTALRM
Catch a signal SIGALRM
Catch a signal SIGALRM
Catch a signal SIGVTALRM
Catch a signal SIGALRM
Catch a signal SIGVTALRM
Catch a signal SIGALRM
Catch a signal SIGALRM
Catch a signal SIGVTALRM


ctrl+c中斷。
....

開(kāi)始喜歡setitimer函數(shù)了。。。

四,補(bǔ)充

不得不介紹一下setjmp和longjmp的作用。

在用信號(hào)的時(shí)候,我們看到多個(gè)地方要求進(jìn)程在檢查收到信號(hào)后,從原來(lái)的系統(tǒng)調(diào)用中直接返回,而不是等到該調(diào)用完成。
這種進(jìn)程突然改變其上下文的情況,就是使用setjmp和longjmp的結(jié)果。setjmp將保存的上下文存入用戶(hù)區(qū),并繼續(xù)在舊的
上下文中執(zhí)行。這就是說(shuō),進(jìn)程執(zhí)行一個(gè)系統(tǒng)調(diào)用,當(dāng)因?yàn)橘Y源或其他原因要去睡眠時(shí),內(nèi)核為進(jìn)程作了一次setjmp,如果
在睡眠中被信號(hào)喚醒,進(jìn)程不能再進(jìn)入睡眠時(shí),內(nèi)核為進(jìn)程調(diào)用longjmp,該操作是內(nèi)核為進(jìn)程將原先setjmp調(diào)用保存在進(jìn)程
用戶(hù)區(qū)的上下文恢復(fù)成現(xiàn)在的上下文,這樣就使得進(jìn)程可以恢復(fù)等待資源前的狀態(tài),而且內(nèi)核為setjmp返回1,使得進(jìn)程知道
該次系統(tǒng)調(diào)用失敗。這就是它們的作用。

有時(shí)間再man 一下waitpid吧。。都是很有用的。

五,后記

信號(hào)的處理從以前的使用到最終文檔的成形,每次都感覺(jué)挺難,其實(shí)是挺偏的。畢竟,信號(hào)我們
一般接觸的少,用的也少。其實(shí)大家不要談信號(hào)色變,仔細(xì)研究一下,發(fā)現(xiàn)很有意思。
我接觸的東西沒(méi)有直接用信號(hào)處理一大堆東西的,都是簡(jiǎn)單的對(duì)信號(hào)的使用,所有例子比較少,很多
例子都是參考網(wǎng)絡(luò)資料的。很多資料都是參考了網(wǎng)友的資料而成,尤其是鄭彥興網(wǎng)友。

寫(xiě)本貼的目的是讓大家了解清楚函數(shù)的功能。

后面將要寫(xiě)進(jìn)程間通信這塊兒.
還是寫(xiě)進(jìn)程這塊兒好呢?

2)在信號(hào)被注銷(xiāo)到相應(yīng)的信號(hào)處理函數(shù)執(zhí)行完畢這段時(shí)間內(nèi),如果進(jìn)程又收到同一信號(hào)多次,則對(duì)實(shí)時(shí)信號(hào)來(lái)說(shuō),

每一次都會(huì)在進(jìn)程中注冊(cè);而對(duì)于非實(shí)時(shí)信號(hào)來(lái)說(shuō),無(wú)論收到多少次信號(hào),都會(huì)視為只收到一個(gè)信號(hào),只在進(jìn)程中注冊(cè)一次。

當(dāng)然還有有些需要知道的概念比如 低速系統(tǒng)調(diào)用,中斷系統(tǒng)調(diào)用,可重入函數(shù)等等概念,請(qǐng)查閱環(huán)境高級(jí)編程。

一些概念性的東西大概就這些東西吧,下面就分類(lèi)介紹一下具體的函數(shù)。

我下面介紹的,如果沒(méi)有特殊說(shuō)明,都是對(duì)于linux上的實(shí)現(xiàn),當(dāng)然都是符合POSIX標(biāo)準(zhǔn)嘍。



二,函數(shù)詳細(xì)介紹



A:信號(hào)的發(fā)送

發(fā)送信號(hào)的主要函數(shù)有:kill(),raise(),sigqueue(),alarm(),setitimer()以及abort()。



1, int kill(pid_t pid,int signo)

用到的頭文件:
#include <sys/types.h>
#include <signal.h>


參數(shù)pid的值 信號(hào)的接收進(jìn)程
pid>0 進(jìn)程ID為pid的進(jìn)程
pid=0 同一個(gè)進(jìn)程組的進(jìn)程
pid<0 pid!=-1 進(jìn)程組ID為 -pid的所有進(jìn)程
pid=-1 除發(fā)送進(jìn)程自身外,所有進(jìn)程ID大于1的進(jìn)程


Sinno是信號(hào)值,當(dāng)為0時(shí)(即空信號(hào)),實(shí)際不發(fā)送任何信號(hào),但照常進(jìn)行錯(cuò)誤檢查,因此,可用于檢查目標(biāo)進(jìn)程是否存在,

以及當(dāng)前進(jìn)程是否具有向目標(biāo)發(fā)送信號(hào)的權(quán)限(root權(quán)限的進(jìn)程可以向任何進(jìn)程發(fā)送信號(hào),非root權(quán)限的進(jìn)程只能向?qū)儆谕?

一個(gè)session或者同一個(gè)用戶(hù)的進(jìn)程發(fā)送信號(hào))。

kill最常用于pid>0時(shí)的信號(hào)發(fā)送,調(diào)用成功返回 0; 否則,返回 -1。注:對(duì)于pid<0時(shí)的情況,對(duì)于哪些進(jìn)程將接受信號(hào),

各種版本說(shuō)法不一,其實(shí)很簡(jiǎn)單,參閱內(nèi)核源碼kernal/signal.c。



2, int raise(int signo)
用到的頭文件:
#include <signal.h>

向進(jìn)程本身發(fā)送信號(hào),參數(shù)為即將發(fā)送的信號(hào)值。調(diào)用成功返回 0;否則,返回 -1。

3, int sigqueue(pid_t pid, int sig, const union sigval val)

用到的頭文件:
#include <sys/types.h>
#include <signal.h>

調(diào)用成功返回 0;否則,返回 -1。

sigqueue()是比較新的發(fā)送信號(hào)系統(tǒng)調(diào)用,主要是針對(duì)實(shí)時(shí)信號(hào)提出的(當(dāng)然也支持前32種),支持信號(hào)帶有參數(shù),

與函數(shù)sigaction()配合使用。sco 5.05沒(méi)有此函數(shù)。

sigqueue的第一個(gè)參數(shù)是指定接收信號(hào)的進(jìn)程ID,第二個(gè)參數(shù)確定即將發(fā)送的信號(hào),第三個(gè)參數(shù)是一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu)union sigval,

指定了信號(hào)傳遞的參數(shù),即通常所說(shuō)的4字節(jié)值。

typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;


sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個(gè)進(jìn)程發(fā)送信號(hào),而不能發(fā)送信號(hào)給一個(gè)進(jìn)程組。如果sig為0,

將會(huì)執(zhí)行錯(cuò)誤檢查,但實(shí)際上不發(fā)送任何信號(hào),0值信號(hào)可用于檢查pid的有效性以及當(dāng)前進(jìn)程是否有權(quán)限向目標(biāo)進(jìn)程發(fā)送信號(hào)。

在調(diào)用sigqueue時(shí),sigval_t指定的信息會(huì)拷貝到3參數(shù)信號(hào)處理函數(shù)(3參數(shù)信號(hào)處理函數(shù)指的是信號(hào)處理函數(shù)由sigaction安裝,

并設(shè)定了sa_sigaction指針,稍后將闡述)的siginfo_t結(jié)構(gòu)中,這樣信號(hào)處理函數(shù)就可以處理這些信息了。由于sigqueue系統(tǒng)調(diào)用

支持發(fā)送帶參數(shù)信號(hào),所以比kill()系統(tǒng)調(diào)用的功能要靈活和強(qiáng)大得多。

注:sigqueue()發(fā)送非實(shí)時(shí)信號(hào)時(shí),第三個(gè)參數(shù)包含的信息仍然能夠傳遞給信號(hào)處理函數(shù); sigqueue()發(fā)送非實(shí)時(shí)信號(hào)時(shí),仍然不支持排隊(duì),

即在信號(hào)處理函數(shù)執(zhí)行過(guò)程中到來(lái)的所有相同信號(hào),都被合并為一個(gè)信號(hào)。


4, unsigned int alarm(unsigned int seconds)

用到的頭文件:

#include <unistd.h>

專(zhuān)門(mén)為SIGALRM信號(hào)而設(shè),在指定的時(shí)間seconds秒后,將向進(jìn)程本身發(fā)送SIGALRM信號(hào),又稱(chēng)為鬧鐘時(shí)間。進(jìn)程調(diào)用alarm后,

任何以前的alarm()調(diào)用都將無(wú)效。如果參數(shù)seconds為零,那么進(jìn)程內(nèi)將不再包含任何鬧鐘時(shí)間。

函數(shù)返回是這樣的,如果調(diào)用alarm()前,進(jìn)程中已經(jīng)設(shè)置了鬧鐘時(shí)間,則返回上一個(gè)鬧鐘時(shí)間的剩余時(shí)間,否則返回0。

5, int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));

用到的頭文件:
#include <sys/time.h>

setitimer()比alarm功能強(qiáng)大,支持3種類(lèi)型的定時(shí)器:

ITIMER_REAL: 設(shè)定絕對(duì)時(shí)間;經(jīng)過(guò)指定的時(shí)間后,內(nèi)核將發(fā)送SIGALRM信號(hào)給本進(jìn)程;
ITIMER_VIRTUAL 設(shè)定程序執(zhí)行時(shí)間;經(jīng)過(guò)指定的時(shí)間后,內(nèi)核將發(fā)送SIGVTALRM信號(hào)給本進(jìn)程;
ITIMER_PROF 設(shè)定進(jìn)程執(zhí)行以及內(nèi)核因本進(jìn)程而消耗的時(shí)間和,經(jīng)過(guò)指定的時(shí)間后,內(nèi)核將發(fā)送ITIMER_VIRTUAL信號(hào)給本進(jìn)程;

Setitimer()第一個(gè)參數(shù)which指定定時(shí)器類(lèi)型(上面三種之一);第二個(gè)參數(shù)是結(jié)構(gòu)itimerval的一個(gè)實(shí)例,結(jié)構(gòu)itimerval。

結(jié)構(gòu)itimerval:

struct itimerval
{
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};

struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};




第三個(gè)參數(shù)可不做處理。

Setitimer()調(diào)用成功返回0,否則返回-1。


這是我man setitimer時(shí)看到的

getitimer and setitimer are not part of any currently supported standard;
they were developed at the University of California at Berkeley and are
used by permission.


6, void abort(void);

用到的頭文件:
#include <stdlib.h>


向進(jìn)程發(fā)送SIGABORT信號(hào),默認(rèn)情況下進(jìn)程會(huì)異常退出,當(dāng)然可定義自己的信號(hào)處理函數(shù)。

即使SIGABORT被進(jìn)程設(shè)置為阻塞信號(hào),調(diào)用abort()后,SIGABORT仍然能被進(jìn)程接收。該函數(shù)無(wú)返回值。




B:信號(hào)的捕獲與安裝(設(shè)置信號(hào)關(guān)聯(lián)動(dòng)作)



如果進(jìn)程要處理某一信號(hào),那么就要在進(jìn)程中安裝該信號(hào)。安裝信號(hào)主要用來(lái)確定信號(hào)值及進(jìn)程針對(duì)該信號(hào)值

的動(dòng)作之間的映射關(guān)系,

即進(jìn)程將要處理哪個(gè)信號(hào);該信號(hào)被傳遞給進(jìn)程時(shí),將執(zhí)行何種操作。

linux主要有兩個(gè)函數(shù)實(shí)現(xiàn)信號(hào)的安裝:signal()、sigaction()。其中signal()在可靠信號(hào)系統(tǒng)調(diào)用的基礎(chǔ)上實(shí)現(xiàn),

是庫(kù)函數(shù)。它只有兩個(gè)參數(shù),不支持信號(hào)傳遞信息,主要是用于前32種非實(shí)時(shí)信號(hào)的安裝;而sigaction()是較新的

函數(shù)(由兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn):sys_signal以及sys_rt_sigaction),有三個(gè)參數(shù),支持信號(hào)傳遞信息,主要用來(lái)與

sigqueue() 系統(tǒng)調(diào)用配合使用,當(dāng)然,sigaction()同樣支持非實(shí)時(shí)信號(hào)的安裝。sigaction()優(yōu)于signal()主要體現(xiàn)

在支持信號(hào)帶有參數(shù)。

1, void (*signal(int signum, void (*handler))(int)))(int);

#include <signal.h>

如果該函數(shù)原型不容易理解的話(huà),可以參考下面的分解方式來(lái)理解:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));

第一個(gè)參數(shù)指定信號(hào)的值,第二個(gè)參數(shù)指定針對(duì)前面信號(hào)值的處理,

a,可以忽略該信號(hào)(參數(shù)設(shè)為SIG_IGN);

b,可以采用系統(tǒng)默認(rèn)方式處理信號(hào)(參數(shù)設(shè)為SIG_DFL);

b,也可以自己實(shí)現(xiàn)處理方式(參數(shù)指定一個(gè)函數(shù)地址)。

如果signal()調(diào)用成功,返回最后一次為安裝信號(hào)signum而調(diào)用signal()時(shí)的handler值;失敗則返回SIG_ERR。

2, int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

#include <signal.h>


sigaction函數(shù)用于改變進(jìn)程接收到特定信號(hào)后的行為。該函數(shù)的第一個(gè)參數(shù)為信號(hào)的值,可以為除SIGKILL及SIGSTOP外的任何

一個(gè)特定有效的信號(hào)(為這兩個(gè)信號(hào)定義自己的處理函數(shù),將導(dǎo)致信號(hào)安裝錯(cuò)誤)。第二個(gè)參數(shù)是指向結(jié)構(gòu)sigaction的一個(gè)實(shí)例

的指針,在結(jié)構(gòu)sigaction的實(shí)例中,指定了對(duì)特定信號(hào)的處理,可以為空,進(jìn)程會(huì)以缺省方式對(duì)信號(hào)處理;第三個(gè)參數(shù)oldact指向

的對(duì)象用來(lái)保存原來(lái)對(duì)相應(yīng)信號(hào)的處理,可指定oldact為NULL。如果把第二、第三個(gè)參數(shù)都設(shè)為NULL,那么該函數(shù)可用于檢查信號(hào)的有效性。


第二個(gè)參數(shù)最為重要,其中包含了對(duì)指定信號(hào)的處理、信號(hào)所傳遞的信息、信號(hào)處理函數(shù)執(zhí)行過(guò)程中應(yīng)屏蔽掉哪些函數(shù)等等。

sigaction結(jié)構(gòu)定義如下:

struct sigaction
{

union
{
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int,struct siginfo *, void *);
}_u
sigset_t sa_mask;
unsigned long sa_flags;
void (*sa_restorer)(void);
}

其中,sa_restorer,已過(guò)時(shí),POSIX不支持它,不應(yīng)再被使用。

a,聯(lián)合數(shù)據(jù)結(jié)構(gòu)中的兩個(gè)元素_sa_handler以及*_sa_sigaction指定信號(hào)關(guān)聯(lián)函數(shù),即用戶(hù)指定的信號(hào)處理函數(shù)。除了可以是用戶(hù)自

定義的處理函數(shù)外,還可以為SIG_DFL(采用缺省的處理方式),也可以為SIG_IGN(忽略信號(hào))。

b,由_sa_handler指定的處理函數(shù)只有一個(gè)參數(shù),即信號(hào)值,所以信號(hào)不能傳遞除信號(hào)值之外的任何信息;由_sa_sigaction是指定的

信號(hào)處理函數(shù)帶有三個(gè)參數(shù),是為實(shí)時(shí)信號(hào)而設(shè)的(當(dāng)然同樣支持非實(shí)時(shí)信號(hào)),它指定一個(gè)3參數(shù)信號(hào)處理函數(shù)。

第一個(gè)參數(shù)為信號(hào)值,第三個(gè)參數(shù)沒(méi)有使用(POSIX沒(méi)有規(guī)范使用該參數(shù)的標(biāo)準(zhǔn)),第二個(gè)參數(shù)是指向siginfo_t結(jié)構(gòu)的指針,結(jié)構(gòu)中包含

信號(hào)攜帶的數(shù)據(jù)值,參數(shù)所指向的結(jié)構(gòu)如下:


siginfo_t {
int si_signo; /* 信號(hào)值,對(duì)所有信號(hào)有意義*/
int si_errno; /* errno值,對(duì)所有信號(hào)有意義*/
int si_code; /* 信號(hào)產(chǎn)生的原因,對(duì)所有信號(hào)有意義*/
union{ /* 聯(lián)合數(shù)據(jù)結(jié)構(gòu),不同成員適應(yīng)不同信號(hào) */
//確保分配足夠大的存儲(chǔ)空間
int _pad[SI_PAD_SIZE];
//對(duì)SIGKILL有意義的結(jié)構(gòu)
struct{
...
}...

... ...
... ...
//對(duì)SIGILL, SIGFPE, SIGSEGV, SIGBUS有意義的結(jié)構(gòu)
struct{
...
}...
... ...
}
}




注:為了更便于閱讀,在說(shuō)明問(wèn)題時(shí)常把該結(jié)構(gòu)表示為如下所表示的形式。

siginfo_t {
int si_signo; /* 信號(hào)值,對(duì)所有信號(hào)有意義*/
int si_errno; /* errno值,對(duì)所有信號(hào)有意義*/
int si_code; /* 信號(hào)產(chǎn)生的原因,對(duì)所有信號(hào)有意義*/
pid_t si_pid; /* 發(fā)送信號(hào)的進(jìn)程ID,對(duì)kill(2),實(shí)時(shí)信號(hào)以及SIGCHLD有意義 */
uid_t si_uid; /* 發(fā)送信號(hào)進(jìn)程的真實(shí)用戶(hù)ID,對(duì)kill(2),實(shí)時(shí)信號(hào)以及SIGCHLD有意義 */
int si_status; /* 退出狀態(tài),對(duì)SIGCHLD有意義*/
clock_t si_utime; /* 用戶(hù)消耗的時(shí)間,對(duì)SIGCHLD有意義 */
clock_t si_stime; /* 內(nèi)核消耗的時(shí)間,對(duì)SIGCHLD有意義 */
sigval_t si_value; /* 信號(hào)值,對(duì)所有實(shí)時(shí)有意義,是一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu),可以為一個(gè)整數(shù)(由si_int標(biāo)示,也可以為一個(gè)指針,由si_ptr標(biāo)示)*/

void * si_addr; /* 觸發(fā)fault的內(nèi)存地址,對(duì)SIGILL,SIGFPE,SIGSEGV,SIGBUS 信號(hào)有意義*/
int si_band; /* 對(duì)SIGPOLL信號(hào)有意義 */
int si_fd; /* 對(duì)SIGPOLL信號(hào)有意義 */

}


實(shí)際上,除了前三個(gè)元素外,其他元素組織在一個(gè)聯(lián)合結(jié)構(gòu)中,在聯(lián)合數(shù)據(jù)結(jié)構(gòu)中,又根據(jù)不同的信號(hào)組織成不同的結(jié)構(gòu)。

注釋中提到的對(duì)某種信號(hào)有意義指的是,在該信號(hào)的處理函數(shù)中可以訪(fǎng)問(wèn)這些域來(lái)獲得與信號(hào)相關(guān)的有意義的信息,只不過(guò)特定信號(hào)只

對(duì)特定信息感興趣而已。

siginfo_t結(jié)構(gòu)中的聯(lián)合數(shù)據(jù)成員確保該結(jié)構(gòu)適應(yīng)所有的信號(hào),比如對(duì)于實(shí)時(shí)信號(hào)來(lái)說(shuō),則實(shí)際采用下面的結(jié)構(gòu)形式:

typedef struct {
int si_signo;
int si_errno;
int si_code;
union sigval si_value;
} siginfo_t;

結(jié)構(gòu)的第四個(gè)域同樣為一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu):

union sigval {
int sival_int;
void *sival_ptr;
}

采用聯(lián)合數(shù)據(jù)結(jié)構(gòu),說(shuō)明siginfo_t結(jié)構(gòu)中的si_value要么持有一個(gè)4字節(jié)的整數(shù)值,要么持有一個(gè)指針,這就構(gòu)成了與信號(hào)相關(guān)的數(shù)據(jù)。

在信號(hào)的處理函數(shù)中,包含這樣的信號(hào)相關(guān)數(shù)據(jù)指針,但沒(méi)有規(guī)定具體如何對(duì)這些數(shù)據(jù)進(jìn)行操作,操作方法應(yīng)該由程序開(kāi)發(fā)人員根據(jù)具體

任務(wù)事先約定。

前面在討論系統(tǒng)調(diào)用sigqueue發(fā)送信號(hào)時(shí),sigqueue的第三個(gè)參數(shù)就是sigval聯(lián)合數(shù)據(jù)結(jié)構(gòu),當(dāng)調(diào)用sigqueue時(shí),該數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)就將

拷貝到信號(hào)處理函數(shù)的第二個(gè)參數(shù)中。這樣,在發(fā)送信號(hào)同時(shí),就可以讓信號(hào)傳遞一些附加信息。信號(hào)可以傳遞信息對(duì)程序開(kāi)發(fā)是非常有意義

的。


c,sa_mask指定在信號(hào)處理程序執(zhí)行過(guò)程中,哪些信號(hào)應(yīng)當(dāng)被阻塞。缺省情況下當(dāng)前信號(hào)本身被阻塞,防止信號(hào)的嵌套發(fā)送,除非指定

SA_NODEFER或者SA_NOMASK標(biāo)志位。

注:請(qǐng)注意sa_mask指定的信號(hào)阻塞的前提條件,是在由sigaction()安裝信號(hào)的處理函數(shù)執(zhí)行過(guò)程中由sa_mask指定的信號(hào)才被阻塞。

d,sa_flags中包含了許多標(biāo)志位,包括剛剛提到的SA_NODEFER及SA_NOMASK標(biāo)志位。另一個(gè)比較重要的標(biāo)志位是SA_SIGINFO,當(dāng)設(shè)定了該標(biāo)志

位時(shí),表示信號(hào)附帶的參數(shù)可以被傳遞到信號(hào)處理函數(shù)中,因此,應(yīng)該為sigaction結(jié)構(gòu)中的sa_sigaction指定處理函數(shù),而不應(yīng)該為sa_handler

指定信號(hào)處理函數(shù),否則,設(shè)置該標(biāo)志變得毫無(wú)意義。即使為sa_sigaction指定了信號(hào)處理函數(shù),如果不設(shè)置SA_SIGINFO,信號(hào)處理函數(shù)同樣

不能得到信號(hào)傳遞過(guò)來(lái)的數(shù)據(jù),在信號(hào)處理函數(shù)中對(duì)這些信息的訪(fǎng)問(wèn)都將導(dǎo)致段錯(cuò)誤(Segmentation fault)。

注:很多文獻(xiàn)在闡述該標(biāo)志位時(shí)都認(rèn)為,如果設(shè)置了該標(biāo)志位,就必須定義三參數(shù)信號(hào)處理函數(shù)。實(shí)際不是這樣的,驗(yàn)證方法很簡(jiǎn)單:自己實(shí)現(xiàn)

一個(gè)單一參數(shù)信號(hào)處理函數(shù),并在程序中設(shè)置該標(biāo)志位,可以察看程序的運(yùn)行結(jié)果。實(shí)際上,可以把該標(biāo)志位看成信號(hào)是否傳遞參數(shù)的開(kāi)關(guān),如果設(shè)置該位,

則傳遞參數(shù);否則,不傳遞參數(shù)。


C:信號(hào)集及信號(hào)集操作函數(shù):

信號(hào)集被定義為一種數(shù)據(jù)類(lèi)型:
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t




信號(hào)集用來(lái)描述信號(hào)的集合,linux所支持的所有信號(hào)可以全部或部分的出現(xiàn)在信號(hào)集中,主要與信號(hào)阻塞相關(guān)函數(shù)配合使用。下面是為

信號(hào)集操作定義的相關(guān)函數(shù):



int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum)
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);

頭文件
#include <signal.h>

sigemptyset(sigset_t *set)初始化由set指定的信號(hào)集,信號(hào)集里面的所有信號(hào)被清空;
sigfillset(sigset_t *set)調(diào)用該函數(shù)后,set指向的信號(hào)集中將包含linux支持的64種信號(hào);
sigaddset(sigset_t *set, int signum)在set指向的信號(hào)集中加入signum信號(hào);
sigdelset(sigset_t *set, int signum)在set指向的信號(hào)集中刪除signum信號(hào);
sigismember(const sigset_t *set, int signum)判定信號(hào)signum是否在set指向的信號(hào)集中。



D:信號(hào)阻塞與信號(hào)未決:

每個(gè)進(jìn)程都有一個(gè)用來(lái)描述哪些信號(hào)遞送到進(jìn)程時(shí)將被阻塞的信號(hào)集,該信號(hào)集中的所有信號(hào)在遞送到進(jìn)程后都將被阻塞。下面是與信號(hào)阻塞

相關(guān)的幾個(gè)函數(shù):

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));
int sigpending(sigset_t *set));
int sigsuspend(const sigset_t *mask));

頭文件:
#include <signal.h>

sigprocmask()函數(shù)能夠根據(jù)參數(shù)how來(lái)實(shí)現(xiàn)對(duì)信號(hào)集的操作,操作主要有三種:

參數(shù)how 進(jìn)程當(dāng)前信號(hào)集
SIG_BLOCK 在進(jìn)程當(dāng)前阻塞信號(hào)集中添加set指向信號(hào)集中的信號(hào)
SIG_UNBLOCK 如果進(jìn)程阻塞信號(hào)集中包含set指向信號(hào)集中的信號(hào),則解除對(duì)該信號(hào)的阻塞
SIG_SETMASK 更新進(jìn)程阻塞信號(hào)集為set指向的信號(hào)集

sigpending(sigset_t *set))獲得當(dāng)前已遞送到進(jìn)程,卻被阻塞的所有信號(hào),在set指向的信號(hào)集中返回結(jié)果。

sigsuspend(const sigset_t *mask))用于在接收到某個(gè)信號(hào)之前, 臨時(shí)用mask替換進(jìn)程的信號(hào)掩碼, 并暫停進(jìn)程執(zhí)行,直到收到信號(hào)為止。

sigsuspend 返回后將恢復(fù)調(diào)用之前的信號(hào)掩碼。信號(hào)處理函數(shù)完成后,進(jìn)程將繼續(xù)執(zhí)行。該系統(tǒng)調(diào)用始終返回-1,并將errno設(shè)置為EINTR。
 
 
 
 
 | | | |
12月15日

Linux環(huán)境進(jìn)程間通信-信號(hào)(下)

在信號(hào)(上)中,討論了linux信號(hào)種類(lèi)、來(lái)源、如何安裝一個(gè)信號(hào)以及對(duì)信號(hào)集的操作。本部分則首先討論從信號(hào)的生命周期上認(rèn)識(shí)信號(hào),或者宏觀上看似簡(jiǎn)單的信號(hào)機(jī)制(進(jìn)程收到信號(hào)后,作相應(yīng)的處理,看上去再簡(jiǎn)單不過(guò)了),在微觀上究竟是如何實(shí)現(xiàn)的,也是在更深層次上理解信號(hào)。接下來(lái)還討論了信號(hào)編程的一些注意事項(xiàng),最后給出了信號(hào)編程的一些實(shí)例。

一、信號(hào)生命周期



從信號(hào)發(fā)送到信號(hào)處理函數(shù)的執(zhí)行完畢

對(duì)于一個(gè)完整的信號(hào)生命周期(從信號(hào)發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來(lái)說(shuō),可以分為三個(gè)重要的階段,這三個(gè)階段由四個(gè)重要事件來(lái)刻畫(huà):信號(hào)誕生;信號(hào)在進(jìn)程中注冊(cè)完畢;信號(hào)在進(jìn)程中的注銷(xiāo)完畢;信號(hào)處理函數(shù)執(zhí)行完畢。相鄰兩個(gè)事件的時(shí)間間隔構(gòu)成信號(hào)生命周期的一個(gè)階段。

下面闡述四個(gè)事件的實(shí)際意義:

  • 信號(hào)"誕生"。信號(hào)的誕生指的是觸發(fā)信號(hào)的事件發(fā)生(如檢測(cè)到硬件異常、定時(shí)器超時(shí)以及調(diào)用信號(hào)發(fā)送函數(shù)kill()或sigqueue()等)。

  • 信號(hào)在目標(biāo)進(jìn)程中"注冊(cè)";進(jìn)程的task_struct結(jié)構(gòu)中有關(guān)于本進(jìn)程中未決信號(hào)的數(shù)據(jù)成員:

    struct sigpending pending:    struct sigpending{    struct sigqueue *head, **tail;    sigset_t signal;    };    

    第三個(gè)成員是進(jìn)程中所有未決信號(hào)集,第一、第二個(gè)成員分別指向一個(gè)sigqueue類(lèi)型的結(jié)構(gòu)鏈(稱(chēng)之為"未決信號(hào)信息鏈")的首尾,信息鏈中的每個(gè)sigqueue結(jié)構(gòu)刻畫(huà)一個(gè)特定信號(hào)所攜帶的信息,并指向下一個(gè)sigqueue結(jié)構(gòu):

struct sigqueue{
           struct sigqueue *next;
           siginfo_t info;
          }
信號(hào)在進(jìn)程中注冊(cè)指的就是信號(hào)值加入到進(jìn)程的未決信號(hào)集中(sigpending結(jié)構(gòu)的第二個(gè)成員sigset_t signal),并且信號(hào)所攜帶的信息被?!      ×舻轿礇Q信號(hào)信息鏈的某個(gè)sigqueue結(jié)構(gòu)中。只要信號(hào)在進(jìn)程的未決信號(hào)集中,表明進(jìn)程已經(jīng)知道這些信號(hào)的存在,但還沒(méi)來(lái)得及處理,或者該信號(hào)被進(jìn)程阻塞。 

注:
當(dāng)一個(gè)實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),不管該信號(hào)是否已經(jīng)在進(jìn)程中注冊(cè),都會(huì)被再注冊(cè)一次,因此,信號(hào)不會(huì)丟失,因此,實(shí)時(shí)信號(hào)又叫做"可靠信號(hào)"。這意味著同一個(gè)實(shí)時(shí)信號(hào)可以在同一個(gè)進(jìn)程的未決信號(hào)信息鏈中占有多個(gè)sigqueue結(jié)構(gòu)(進(jìn)程每收到一個(gè)實(shí)時(shí)信號(hào),都會(huì)為它分配一個(gè)結(jié)構(gòu)來(lái)登記該信號(hào)信息,并把該結(jié)構(gòu)添加在未決信號(hào)鏈尾,即所有誕生的實(shí)時(shí)信號(hào)都會(huì)在目標(biāo)進(jìn)程中注冊(cè));
當(dāng)一個(gè)非實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),如果該信號(hào)已經(jīng)在進(jìn)程中注冊(cè),則該信號(hào)將被丟棄,造成信號(hào)丟失。因此,非實(shí)時(shí)信號(hào)又叫做"不可靠信號(hào)"。這意味著同一個(gè)非實(shí)時(shí)信號(hào)在進(jìn)程的未決信號(hào)信息鏈中,至多占有一個(gè)sigqueue結(jié)構(gòu)(一個(gè)非實(shí)時(shí)信號(hào)誕生后,(1)、如果發(fā)現(xiàn)相同的信號(hào)已經(jīng)在目標(biāo)結(jié)構(gòu)中注冊(cè),則不再注冊(cè),對(duì)于進(jìn)程來(lái)說(shuō),相當(dāng)于不知道本次信號(hào)發(fā)生,信號(hào)丟失;(2)、如果進(jìn)程的未決信號(hào)中沒(méi)有相同信號(hào),則在進(jìn)程中注冊(cè)自己)。

  • 信號(hào)在進(jìn)程中的注銷(xiāo)。

在目標(biāo)進(jìn)程執(zhí)行過(guò)程中,會(huì)檢測(cè)是否有信號(hào)等待處理(每次從系統(tǒng)空間返回到用戶(hù)空間時(shí)都做這樣的檢查)。如果存在未決信號(hào)等待處理且該信號(hào)沒(méi)有被進(jìn)程阻塞,則在運(yùn)行相應(yīng)的信號(hào)處理函數(shù)前,進(jìn)程會(huì)把信號(hào)在未決信號(hào)鏈中占有的結(jié)構(gòu)卸掉。是否將信號(hào)從進(jìn)程未決信號(hào)集中刪除對(duì)于實(shí)時(shí)與非實(shí)時(shí)信號(hào)是不同的。對(duì)于非實(shí)時(shí)信號(hào)來(lái)說(shuō),由于在未決信號(hào)信息鏈中最多只占用一個(gè)sigqueue結(jié)構(gòu),因此該結(jié)構(gòu)被釋放后,應(yīng)該把信號(hào)在進(jìn)程未決信號(hào)集中刪除(信號(hào)注銷(xiāo)完畢);而對(duì)于實(shí)時(shí)信號(hào)來(lái)說(shuō),可能在未決信號(hào)信息鏈中占用多個(gè)sigqueue結(jié)構(gòu),因此應(yīng)該針對(duì)占用sigqueue結(jié)構(gòu)的數(shù)目區(qū)別對(duì)待:如果只占用一個(gè)sigqueue結(jié)構(gòu)(進(jìn)程只收到該信號(hào)一次),則應(yīng)該把信號(hào)在進(jìn)程的未決信號(hào)集中刪除(信號(hào)注銷(xiāo)完畢)。否則,不應(yīng)該在進(jìn)程的未決信號(hào)集中刪除該信號(hào)(信號(hào)注銷(xiāo)完畢)。
進(jìn)程在執(zhí)行信號(hào)相應(yīng)處理函數(shù)之前,首先要把信號(hào)在進(jìn)程中注銷(xiāo)。

  • 信號(hào)生命終止。

進(jìn)程注銷(xiāo)信號(hào)后,立即執(zhí)行相應(yīng)的信號(hào)處理函數(shù),執(zhí)行完畢后,信號(hào)的本次發(fā)送對(duì)進(jìn)程的影響徹底結(jié)束。

注:
1)信號(hào)注冊(cè)與否,與發(fā)送信號(hào)的函數(shù)(如kill()或sigqueue()等)以及信號(hào)安裝函數(shù)(signal()及sigaction())無(wú)關(guān),只與信號(hào)值有關(guān)(信號(hào)值小于SIGRTMIN的信號(hào)最多只注冊(cè)一次,信號(hào)值在SIGRTMIN及SIGRTMAX之間的信號(hào),只要被進(jìn)程接收到就被注冊(cè))。
2)在信號(hào)被注銷(xiāo)到相應(yīng)的信號(hào)處理函數(shù)執(zhí)行完畢這段時(shí)間內(nèi),如果進(jìn)程又收到同一信號(hào)多次,則對(duì)實(shí)時(shí)信號(hào)來(lái)說(shuō),每一次都會(huì)在進(jìn)程中注冊(cè);而對(duì)于非實(shí)時(shí)信號(hào)來(lái)說(shuō),無(wú)論收到多少次信號(hào),都會(huì)視為只收到一個(gè)信號(hào),只在進(jìn)程中注冊(cè)一次。

二、信號(hào)編程注意事項(xiàng)


  1. 防止不該丟失的信號(hào)丟失。如果對(duì)八中所提到的信號(hào)生命周期理解深刻的話(huà),很容易知道信號(hào)會(huì)不會(huì)丟失,以及在哪里丟失。

  2. 程序的可移植性
    考慮到程序的可移植性,應(yīng)該盡量采用POSIX信號(hào)函數(shù),POSIX信號(hào)函數(shù)主要分為兩類(lèi):

    • POSIX 1003.1信號(hào)函數(shù): Kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、sigpending()、sigprocmask()、sigsuspend()。

    • POSIX 1003.1b信號(hào)函數(shù)。POSIX 1003.1b在信號(hào)的實(shí)時(shí)性方面對(duì)POSIX 1003.1做了擴(kuò)展,包括以下三個(gè)函數(shù): sigqueue()、sigtimedwait()、sigwaitinfo()。其中,sigqueue主要針對(duì)信號(hào)發(fā)送,而sigtimedwait及sigwaitinfo()主要用于取代sigsuspend()函數(shù),后面有相應(yīng)實(shí)例。

      #i nclude <signal.h>        int sigwaitinfo(sigset_t *set, siginfo_t *info).        

      該函數(shù)與sigsuspend()類(lèi)似,阻塞一個(gè)進(jìn)程直到特定信號(hào)發(fā)生,但信號(hào)到來(lái)時(shí)不執(zhí)行信號(hào)處理函數(shù),而是返回信號(hào)值。因此為了避免執(zhí)行相應(yīng)的信號(hào)處理函數(shù),必須在調(diào)用該函數(shù)前,使進(jìn)程屏蔽掉set指向的信號(hào),因此調(diào)用該函數(shù)的典型代碼是:

      sigset_t newmask;        int rcvd_sig;        siginfo_t info;        sigemptyset(&newmask);        sigaddset(&newmask, SIGRTMIN);        sigprocmask(SIG_BLOCK, &newmask, NULL);        rcvd_sig = sigwaitinfo(&newmask, &info)        if (rcvd_sig == -1) {        ..        }        
      調(diào)用成功返回信號(hào)值,否則返回-1。sigtimedwait()功能相似,只不過(guò)增加了一個(gè)進(jìn)程等待的時(shí)間。
  3. 程序的穩(wěn)定性。
    為了增強(qiáng)程序的穩(wěn)定性,在信號(hào)處理函數(shù)中應(yīng)使用可重入函數(shù)。

    信號(hào)處理程序中應(yīng)當(dāng)使用可再入(可重入)函數(shù)(注:所謂可重入函數(shù)是指一個(gè)可以被多個(gè)任務(wù)調(diào)用的過(guò)程,任務(wù)在調(diào)用時(shí)不必?fù)?dān)心數(shù)據(jù)是否會(huì)出錯(cuò))。因?yàn)檫M(jìn)程在收到信號(hào)后,就將跳轉(zhuǎn)到信號(hào)處理函數(shù)去接著執(zhí)行。如果信號(hào)處理函數(shù)中使用了不可重入函數(shù),那么信號(hào)處理函數(shù)可能會(huì)修改原來(lái)進(jìn)程中不應(yīng)該被修改的數(shù)據(jù),這樣進(jìn)程從信號(hào)處理函數(shù)中返回接著執(zhí)行時(shí),可能會(huì)出現(xiàn)不可預(yù)料的后果。不可再入函數(shù)在信號(hào)處理函數(shù)中被視為不安全函數(shù)。

    滿(mǎn)足下列條件的函數(shù)多數(shù)是不可再入的:(1)使用靜態(tài)的數(shù)據(jù)結(jié)構(gòu),如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;(2)函數(shù)實(shí)現(xiàn)時(shí),調(diào)用了malloc()或者free()函數(shù);(3)實(shí)現(xiàn)時(shí)使用了標(biāo)準(zhǔn)I/O函數(shù)的。The Open Group視下列函數(shù)為可再入的:

    _exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown()、close()、creat()、dup()、dup2()、execle()、execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、kill()、link()、lseek()、mkdir()、mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid()、setpgid()、setsid()、setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、signal()、sigpending()、sigprocmask()、sigsuspend()、sleep()、stat()、sysconf()、tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、 umask()、uname()、unlink()、utime()、wait()、waitpid()、write()。

    即使信號(hào)處理函數(shù)使用的都是"安全函數(shù)",同樣要注意進(jìn)入處理函數(shù)時(shí),首先要保存errno的值,結(jié)束時(shí),再恢復(fù)原值。因?yàn)?,信?hào)處理過(guò)程中,errno值隨時(shí)可能被改變。另外,longjmp()以及siglongjmp()沒(méi)有被列為可再入函數(shù),因?yàn)椴荒鼙WC緊接著兩個(gè)函數(shù)的其它調(diào)用是安全的。

三、深入淺出:信號(hào)應(yīng)用實(shí)例


linux下的信號(hào)應(yīng)用并沒(méi)有想象的那么恐怖,程序員所要做的最多只有三件事情:

  1. 安裝信號(hào)(推薦使用sigaction());

  2. 實(shí)現(xiàn)三參數(shù)信號(hào)處理函數(shù),handler(int signal,struct siginfo *info, void *);

  3. 發(fā)送信號(hào),推薦使用sigqueue()。

實(shí)際上,對(duì)有些信號(hào)來(lái)說(shuō),只要安裝信號(hào)就足夠了(信號(hào)處理方式采用缺省或忽略)。其他可能要做的無(wú)非是與信號(hào)集相關(guān)的幾種操作。

實(shí)例一:信號(hào)發(fā)送及處理
實(shí)現(xiàn)一個(gè)信號(hào)接收程序sigreceive(其中信號(hào)安裝由sigaction())。

#i nclude <signal.h>#i nclude <sys/types.h>#i nclude <unistd.h>void new_op(int,siginfo_t*,void*);int main(int argc,char**argv){struct sigaction act;int sig;sig=atoi(argv[1]);sigemptyset(&act.sa_mask);act.sa_flags=SA_SIGINFO;act.sa_sigaction=new_op;if(sigaction(sig,&act,NULL) < 0){printf("install sigal error\n");}while(1){sleep(2);printf("wait for the signal\n");}}void new_op(int signum,siginfo_t *info,void *myact){printf("receive signal %d", signum);sleep(5);}

說(shuō)明,命令行參數(shù)為信號(hào)值,后臺(tái)運(yùn)行sigreceive signo &,可獲得該進(jìn)程的ID,假設(shè)為pid,然后再另一終端上運(yùn)行kill -s signo pid驗(yàn)證信號(hào)的發(fā)送接收及處理。同時(shí),可驗(yàn)證信號(hào)的排隊(duì)問(wèn)題。
注:可以用sigqueue實(shí)現(xiàn)一個(gè)命令行信號(hào)發(fā)送程序sigqueuesend,見(jiàn)
附錄1。

實(shí)例二:信號(hào)傳遞附加信息
主要包括兩個(gè)實(shí)例:

  1. 向進(jìn)程本身發(fā)送信號(hào),并傳遞指針參數(shù);

    #i nclude <signal.h>    #i nclude <sys/types.h>    #i nclude <unistd.h>    void new_op(int,siginfo_t*,void*);    int main(int argc,char**argv)    {    struct sigaction act;    union sigval mysigval;    int i;    int sig;    pid_t pid;    char data[10];    memset(data,0,sizeof(data));    for(i=0;i < 5;i++)    data[i]='2';    mysigval.sival_ptr=data;    sig=atoi(argv[1]);    pid=getpid();    sigemptyset(&act.sa_mask);    act.sa_sigaction=new_op;//三參數(shù)信號(hào)處理函數(shù)    act.sa_flags=SA_SIGINFO;//信息傳遞開(kāi)關(guān)    if(sigaction(sig,&act,NULL) < 0)    {    printf("install sigal error\n");    }    while(1)    {    sleep(2);    printf("wait for the signal\n");    sigqueue(pid,sig,mysigval);//向本進(jìn)程發(fā)送信號(hào),并傳遞附加信息    }    }    void new_op(int signum,siginfo_t *info,void *myact)//三參數(shù)信號(hào)處理函數(shù)的實(shí)現(xiàn)    {    int i;    for(i=0;i<10;i++)    {    printf("%c\n ",(*( (char*)((*info).si_ptr)+i)));    }    printf("handle signal %d over;",signum);    }    

    這個(gè)例子中,信號(hào)實(shí)現(xiàn)了附加信息的傳遞,信號(hào)究竟如何對(duì)這些信息進(jìn)行處理則取決于具體的應(yīng)用。

  2. 2、 不同進(jìn)程間傳遞整型參數(shù):把1中的信號(hào)發(fā)送和接收放在兩個(gè)程序中,并且在發(fā)送過(guò)程中傳遞整型參數(shù)。
    信號(hào)接收程序:

    #i nclude <signal.h>    #i nclude <sys/types.h>    #i nclude <unistd.h>    void new_op(int,siginfo_t*,void*);    int main(int argc,char**argv)    {    struct sigaction act;    int sig;    pid_t pid;    pid=getpid();    sig=atoi(argv[1]);    sigemptyset(&act.sa_mask);    act.sa_sigaction=new_op;    act.sa_flags=SA_SIGINFO;    if(sigaction(sig,&act,NULL)<0)    {    printf("install sigal error\n");    }    while(1)    {    sleep(2);    printf("wait for the signal\n");    }    }    void new_op(int signum,siginfo_t *info,void *myact)    {    printf("the int value is %d \n",info->si_int);    }    

    信號(hào)發(fā)送程序:命令行第二個(gè)參數(shù)為信號(hào)值,第三個(gè)參數(shù)為接收進(jìn)程ID。

    #i nclude <signal.h>    #i nclude <sys/time.h>    #i nclude <unistd.h>    #i nclude <sys/types.h>    main(int argc,char**argv)    {    pid_t pid;    int signum;    union sigval mysigval;    signum=atoi(argv[1]);    pid=(pid_t)atoi(argv[2]);    mysigval.sival_int=8;//不代表具體含義,只用于說(shuō)明問(wèn)題    if(sigqueue(pid,signum,mysigval)==-1)    printf("send error\n");    sleep(2);    }    

    注:實(shí)例2的兩個(gè)例子側(cè)重點(diǎn)在于用信號(hào)來(lái)傳遞信息,目前關(guān)于在linux下通過(guò)信號(hào)傳遞信息的實(shí)例非常少,倒是Unix下有一些,但傳遞的基本上都是關(guān)于傳遞一個(gè)整數(shù),傳遞指針的我還沒(méi)看到。我一直沒(méi)有實(shí)現(xiàn)不同進(jìn)程間的指針傳遞(實(shí)際上更有意義),也許在實(shí)現(xiàn)方法上存在問(wèn)題吧,請(qǐng)實(shí)現(xiàn)者email我。

實(shí)例三:信號(hào)阻塞及信號(hào)集操作

#i nclude "signal.h"#i nclude "unistd.h"static void my_op(int);main(){sigset_t new_mask,old_mask,pending_mask;struct sigaction act;sigemptyset(&act.sa_mask);act.sa_flags=SA_SIGINFO;act.sa_sigaction=(void*)my_op;if(sigaction(SIGRTMIN+10,&act,NULL))printf("install signal SIGRTMIN+10 error\n");sigemptyset(&new_mask);sigaddset(&new_mask,SIGRTMIN+10);if(sigprocmask(SIG_BLOCK, &new_mask,&old_mask))printf("block signal SIGRTMIN+10 error\n");sleep(10);printf("now begin to get pending mask and unblock SIGRTMIN+10\n");if(sigpending(&pending_mask)<0)printf("get pending mask error\n");if(sigismember(&pending_mask,SIGRTMIN+10))printf("signal SIGRTMIN+10 is pending\n");if(sigprocmask(SIG_SETMASK,&old_mask,NULL)<0)printf("unblock signal error\n");printf("signal unblocked\n");sleep(10);}static void my_op(int signum){printf("receive signal %d \n",signum);}

編譯該程序,并以后臺(tái)方式運(yùn)行。在另一終端向該進(jìn)程發(fā)送信號(hào)(運(yùn)行kill -s 42 pid,SIGRTMIN+10為42),查看結(jié)果可以看出幾個(gè)關(guān)鍵函數(shù)的運(yùn)行機(jī)制,信號(hào)集相關(guān)操作比較簡(jiǎn)單。

注:在上面幾個(gè)實(shí)例中,使用了printf()函數(shù),只是作為診斷工具,pringf()函數(shù)是不可重入的,不應(yīng)在信號(hào)處理函數(shù)中使用。

結(jié)束語(yǔ):

系統(tǒng)地對(duì)linux信號(hào)機(jī)制進(jìn)行分析、總結(jié)使我受益匪淺!感謝王小樂(lè)等網(wǎng)友的支持!
Comments and suggestions are greatly welcome!

附錄1:

用sigqueue實(shí)現(xiàn)的命令行信號(hào)發(fā)送程序sigqueuesend,命令行第二個(gè)參數(shù)是發(fā)送的信號(hào)值,第三個(gè)參數(shù)是接收該信號(hào)的進(jìn)程ID,可以配合實(shí)例一使用:

#i nclude <signal.h>#i nclude <sys/types.h>#i nclude <unistd.h>int main(int argc,char**argv){pid_t pid;int sig;sig=atoi(argv[1]);pid=atoi(argv[2]);sigqueue(pid,sig,NULL);sleep(2);}
 | | |

Linux環(huán)境進(jìn)程間通信(二):信號(hào)(上)

linux信號(hào)機(jī)制遠(yuǎn)遠(yuǎn)比想象的復(fù)雜,本文力爭(zhēng)用最短的篇幅,對(duì)該機(jī)制做了深入細(xì)致的分析。信號(hào)應(yīng)用實(shí)例將在信號(hào)(下)中給出。

一、信號(hào)及信號(hào)來(lái)源

信號(hào)本質(zhì)

信號(hào)是在軟件層次上對(duì)中斷機(jī)制的一種模擬,在原理上,一個(gè)進(jìn)程收到一個(gè)信號(hào)與處理器收到一個(gè)中斷請(qǐng)求可以說(shuō)是一樣的。信號(hào)是異步的,一個(gè)進(jìn)程不必通過(guò)任何操作來(lái)等待信號(hào)的到達(dá),事實(shí)上,進(jìn)程也不知道信號(hào)到底什么時(shí)候到達(dá)。

信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,可以看作是異步通知,通知接收信號(hào)的進(jìn)程有哪些事情發(fā)生了。信號(hào)機(jī)制經(jīng)過(guò)POSIX實(shí)時(shí)擴(kuò)展后,功能更加強(qiáng)大,除了基本通知功能外,還可以傳遞附加信息。

信號(hào)來(lái)源

信號(hào)事件的發(fā)生有兩個(gè)來(lái)源:硬件來(lái)源(比如我們按下了鍵盤(pán)或者其它硬件故障);軟件來(lái)源,最常用發(fā)送信號(hào)的系統(tǒng)函數(shù)是kill, raise, alarm和setitimer以及sigqueue函數(shù),軟件來(lái)源還包括一些非法運(yùn)算等操作。

二、信號(hào)的種類(lèi)

可以從兩個(gè)不同的分類(lèi)角度對(duì)信號(hào)進(jìn)行分類(lèi):(1)可靠性方面:可靠信號(hào)與不可靠信號(hào);(2)與時(shí)間的關(guān)系上:實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)。在《Linux環(huán)境進(jìn)程間通信(一):管道及有名管道》的附1中列出了系統(tǒng)所支持的所有信號(hào)。

1、可靠信號(hào)與不可靠信號(hào)

"不可靠信號(hào)"

Linux信號(hào)機(jī)制基本上是從Unix系統(tǒng)中繼承過(guò)來(lái)的。早期Unix系統(tǒng)中的信號(hào)機(jī)制比較簡(jiǎn)單和原始,后來(lái)在實(shí)踐中暴露出一些問(wèn)題,因此,把那些建立在早期機(jī)制上的信號(hào)叫做"不可靠信號(hào)",信號(hào)值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號(hào)都是不可靠信號(hào)。這就是"不可靠信號(hào)"的來(lái)源。它的主要問(wèn)題是:

  • 進(jìn)程每次處理信號(hào)后,就將對(duì)信號(hào)的響應(yīng)設(shè)置為默認(rèn)動(dòng)作。在某些情況下,將導(dǎo)致對(duì)信號(hào)的錯(cuò)誤處理;因此,用戶(hù)如果不希望這樣的操作,那么就要在信號(hào)處理函數(shù)結(jié)尾再一次調(diào)用signal(),重新安裝該信號(hào)。

  • 信號(hào)可能丟失,后面將對(duì)此詳細(xì)闡述。
    因此,早期unix下的不可靠信號(hào)主要指的是進(jìn)程可能對(duì)信號(hào)做出錯(cuò)誤的反應(yīng)以及信號(hào)可能丟失。

Linux支持不可靠信號(hào),但是對(duì)不可靠信號(hào)機(jī)制做了改進(jìn):在調(diào)用完信號(hào)處理函數(shù)后,不必重新調(diào)用該信號(hào)的安裝函數(shù)(信號(hào)安裝函數(shù)是在可靠機(jī)制上的實(shí)現(xiàn))。因此,Linux下的不可靠信號(hào)問(wèn)題主要指的是信號(hào)可能丟失。

"可靠信號(hào)"

隨著時(shí)間的發(fā)展,實(shí)踐證明了有必要對(duì)信號(hào)的原始機(jī)制加以改進(jìn)和擴(kuò)充。所以,后來(lái)出現(xiàn)的各種Unix版本分別在這方面進(jìn)行了研究,力圖實(shí)現(xiàn)"可靠信號(hào)"。由于原來(lái)定義的信號(hào)已有許多應(yīng)用,不好再做改動(dòng),最終只好又新增加了一些信號(hào),并在一開(kāi)始就把它們定義為可靠信號(hào),這些信號(hào)支持排隊(duì),不會(huì)丟失。同時(shí),信號(hào)的發(fā)送和安裝也出現(xiàn)了新版本:信號(hào)發(fā)送函數(shù)sigqueue()及信號(hào)安裝函數(shù)sigaction()。POSIX.4對(duì)可靠信號(hào)機(jī)制做了標(biāo)準(zhǔn)化。但是,POSIX只對(duì)可靠信號(hào)機(jī)制應(yīng)具有的功能以及信號(hào)機(jī)制的對(duì)外接口做了標(biāo)準(zhǔn)化,對(duì)信號(hào)機(jī)制的實(shí)現(xiàn)沒(méi)有作具體的規(guī)定。

信號(hào)值位于SIGRTMIN和SIGRTMAX之間的信號(hào)都是可靠信號(hào),可靠信號(hào)克服了信號(hào)可能丟失的問(wèn)題。Linux在支持新版本的信號(hào)安裝函數(shù)sigation()以及信號(hào)發(fā)送函數(shù)sigqueue()的同時(shí),仍然支持早期的signal()信號(hào)安裝函數(shù),支持信號(hào)發(fā)送函數(shù)kill()。

注:不要有這樣的誤解:由sigqueue()發(fā)送、sigaction安裝的信號(hào)就是可靠的。事實(shí)上,可靠信號(hào)是指后來(lái)添加的新信號(hào)(信號(hào)值位于SIGRTMIN及SIGRTMAX之間);不可靠信號(hào)是信號(hào)值小于SIGRTMIN的信號(hào)。信號(hào)的可靠與不可靠只與信號(hào)值有關(guān),與信號(hào)的發(fā)送及安裝函數(shù)無(wú)關(guān)。目前l(fā)inux中的signal()是通過(guò)sigation()函數(shù)實(shí)現(xiàn)的,因此,即使通過(guò)signal()安裝的信號(hào),在信號(hào)處理函數(shù)的結(jié)尾也不必再調(diào)用一次信號(hào)安裝函數(shù)。同時(shí),由signal()安裝的實(shí)時(shí)信號(hào)支持排隊(duì),同樣不會(huì)丟失。

對(duì)于目前l(fā)inux的兩個(gè)信號(hào)安裝函數(shù):signal()及sigaction()來(lái)說(shuō),它們都不能把SIGRTMIN以前的信號(hào)變成可靠信號(hào)(都不支持排隊(duì),仍有可能丟失,仍然是不可靠信號(hào)),而且對(duì)SIGRTMIN以后的信號(hào)都支持排隊(duì)。這兩個(gè)函數(shù)的最大區(qū)別在于,經(jīng)過(guò)sigaction安裝的信號(hào)都能傳遞信息給信號(hào)處理函數(shù)(對(duì)所有信號(hào)這一點(diǎn)都成立),而經(jīng)過(guò)signal安裝的信號(hào)卻不能向信號(hào)處理函數(shù)傳遞信息。對(duì)于信號(hào)發(fā)送函數(shù)來(lái)說(shuō)也是一樣的。

2、實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)

早期Unix系統(tǒng)只定義了32種信號(hào),Ret hat7.2支持64種信號(hào),編號(hào)0-63(SIGRTMIN=31,SIGRTMAX=63),將來(lái)可能進(jìn)一步增加,這需要得到內(nèi)核的支持。前32種信號(hào)已經(jīng)有了預(yù)定義值,每個(gè)信號(hào)有了確定的用途及含義,并且每種信號(hào)都有各自的缺省動(dòng)作。如按鍵盤(pán)的CTRL ^C時(shí),會(huì)產(chǎn)生SIGINT信號(hào),對(duì)該信號(hào)的默認(rèn)反應(yīng)就是進(jìn)程終止。后32個(gè)信號(hào)表示實(shí)時(shí)信號(hào),等同于前面闡述的可靠信號(hào)。這保證了發(fā)送的多個(gè)實(shí)時(shí)信號(hào)都被接收。實(shí)時(shí)信號(hào)是POSIX標(biāo)準(zhǔn)的一部分,可用于應(yīng)用進(jìn)程。

非實(shí)時(shí)信號(hào)都不支持排隊(duì),都是不可靠信號(hào);實(shí)時(shí)信號(hào)都支持排隊(duì),都是可靠信號(hào)。

三、進(jìn)程對(duì)信號(hào)的響應(yīng)

進(jìn)程可以通過(guò)三種方式來(lái)響應(yīng)一個(gè)信號(hào):(1)忽略信號(hào),即對(duì)信號(hào)不做任何處理,其中,有兩個(gè)信號(hào)不能忽略:SIGKILL及SIGSTOP;(2)捕捉信號(hào)。定義信號(hào)處理函數(shù),當(dāng)信號(hào)發(fā)生時(shí),執(zhí)行相應(yīng)的處理函數(shù);(3)執(zhí)行缺省操作,Linux對(duì)每種信號(hào)都規(guī)定了默認(rèn)操作,詳細(xì)情況請(qǐng)參考[2]以及其它資料。注意,進(jìn)程對(duì)實(shí)時(shí)信號(hào)的缺省反應(yīng)是進(jìn)程終止。

Linux究竟采用上述三種方式的哪一個(gè)來(lái)響應(yīng)信號(hào),取決于傳遞給相應(yīng)API函數(shù)的參數(shù)。

四、信號(hào)的發(fā)送

發(fā)送信號(hào)的主要函數(shù)有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。

1、kill()
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int signo)

參數(shù)pid的值信號(hào)的接收進(jìn)程
pid>0進(jìn)程ID為pid的進(jìn)程
pid=0同一個(gè)進(jìn)程組的進(jìn)程
pid<0 pid!=-1進(jìn)程組ID為 -pid的所有進(jìn)程
pid=-1除發(fā)送進(jìn)程自身外,所有進(jìn)程ID大于1的進(jìn)程

Sinno是信號(hào)值,當(dāng)為0時(shí)(即空信號(hào)),實(shí)際不發(fā)送任何信號(hào),但照常進(jìn)行錯(cuò)誤檢查,因此,可用于檢查目標(biāo)進(jìn)程是否存在,以及當(dāng)前進(jìn)程是否具有向目標(biāo)發(fā)送信號(hào)的權(quán)限(root權(quán)限的進(jìn)程可以向任何進(jìn)程發(fā)送信號(hào),非root權(quán)限的進(jìn)程只能向?qū)儆谕粋€(gè)session或者同一個(gè)用戶(hù)的進(jìn)程發(fā)送信號(hào))。

Kill()最常用于pid>0時(shí)的信號(hào)發(fā)送,調(diào)用成功返回 0; 否則,返回 -1。注:對(duì)于pid<0時(shí)的情況,對(duì)于哪些進(jìn)程將接受信號(hào),各種版本說(shuō)法不一,其實(shí)很簡(jiǎn)單,參閱內(nèi)核源碼kernal/signal.c即可,上表中的規(guī)則是參考red hat 7.2。

2、raise()
#include <signal.h>
int raise(int signo)
向進(jìn)程本身發(fā)送信號(hào),參數(shù)為即將發(fā)送的信號(hào)值。調(diào)用成功返回 0;否則,返回 -1。

3、sigqueue()
#include <sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval val)
調(diào)用成功返回 0;否則,返回 -1。

sigqueue()是比較新的發(fā)送信號(hào)系統(tǒng)調(diào)用,主要是針對(duì)實(shí)時(shí)信號(hào)提出的(當(dāng)然也支持前32種),支持信號(hào)帶有參數(shù),與函數(shù)sigaction()配合使用。

sigqueue的第一個(gè)參數(shù)是指定接收信號(hào)的進(jìn)程ID,第二個(gè)參數(shù)確定即將發(fā)送的信號(hào),第三個(gè)參數(shù)是一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu)union sigval,指定了信號(hào)傳遞的參數(shù),即通常所說(shuō)的4字節(jié)值。

                                    typedef union sigval {                                    int  sival_int;                                    void *sival_ptr;                                    }sigval_t;                                    

sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個(gè)進(jìn)程發(fā)送信號(hào),而不能發(fā)送信號(hào)給一個(gè)進(jìn)程組。如果signo=0,將會(huì)執(zhí)行錯(cuò)誤檢查,但實(shí)際上不發(fā)送任何信號(hào),0值信號(hào)可用于檢查pid的有效性以及當(dāng)前進(jìn)程是否有權(quán)限向目標(biāo)進(jìn)程發(fā)送信號(hào)。

在調(diào)用sigqueue時(shí),sigval_t指定的信息會(huì)拷貝到3參數(shù)信號(hào)處理函數(shù)(3參數(shù)信號(hào)處理函數(shù)指的是信號(hào)處理函數(shù)由sigaction安裝,并設(shè)定了sa_sigaction指針,稍后將闡述)的siginfo_t結(jié)構(gòu)中,這樣信號(hào)處理函數(shù)就可以處理這些信息了。由于sigqueue系統(tǒng)調(diào)用支持發(fā)送帶參數(shù)信號(hào),所以比kill()系統(tǒng)調(diào)用的功能要靈活和強(qiáng)大得多。

注:sigqueue()發(fā)送非實(shí)時(shí)信號(hào)時(shí),第三個(gè)參數(shù)包含的信息仍然能夠傳遞給信號(hào)處理函數(shù); sigqueue()發(fā)送非實(shí)時(shí)信號(hào)時(shí),仍然不支持排隊(duì),即在信號(hào)處理函數(shù)執(zhí)行過(guò)程中到來(lái)的所有相同信號(hào),都被合并為一個(gè)信號(hào)。

4、alarm()
#include <unistd.h>
unsigned int alarm(unsigned int seconds)
專(zhuān)門(mén)為SIGALRM信號(hào)而設(shè),在指定的時(shí)間seconds秒后,將向進(jìn)程本身發(fā)送SIGALRM信號(hào),又稱(chēng)為鬧鐘時(shí)間。進(jìn)程調(diào)用alarm后,任何以前的alarm()調(diào)用都將無(wú)效。如果參數(shù)seconds為零,那么進(jìn)程內(nèi)將不再包含任何鬧鐘時(shí)間。
返回值,如果調(diào)用alarm()前,進(jìn)程中已經(jīng)設(shè)置了鬧鐘時(shí)間,則返回上一個(gè)鬧鐘時(shí)間的剩余時(shí)間,否則返回0。

5、setitimer()
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比alarm功能強(qiáng)大,支持3種類(lèi)型的定時(shí)器:

  • ITIMER_REAL: 設(shè)定絕對(duì)時(shí)間;經(jīng)過(guò)指定的時(shí)間后,內(nèi)核將發(fā)送SIGALRM信號(hào)給本進(jìn)程;

  • ITIMER_VIRTUAL 設(shè)定程序執(zhí)行時(shí)間;經(jīng)過(guò)指定的時(shí)間后,內(nèi)核將發(fā)送SIGVTALRM信號(hào)給本進(jìn)程;

  • ITIMER_PROF 設(shè)定進(jìn)程執(zhí)行以及內(nèi)核因本進(jìn)程而消耗的時(shí)間和,經(jīng)過(guò)指定的時(shí)間后,內(nèi)核將發(fā)送ITIMER_VIRTUAL信號(hào)給本進(jìn)程;

Setitimer()第一個(gè)參數(shù)which指定定時(shí)器類(lèi)型(上面三種之一);第二個(gè)參數(shù)是結(jié)構(gòu)itimerval的一個(gè)實(shí)例,結(jié)構(gòu)itimerval形式見(jiàn)附錄1。第三個(gè)參數(shù)可不做處理。

Setitimer()調(diào)用成功返回0,否則返回-1。

6、abort()
#include <stdlib.h>
void abort(void);

向進(jìn)程發(fā)送SIGABORT信號(hào),默認(rèn)情況下進(jìn)程會(huì)異常退出,當(dāng)然可定義自己的信號(hào)處理函數(shù)。即使SIGABORT被進(jìn)程設(shè)置為阻塞信號(hào),調(diào)用abort()后,SIGABORT仍然能被進(jìn)程接收。該函數(shù)無(wú)返回值。

五、信號(hào)的安裝(設(shè)置信號(hào)關(guān)聯(lián)動(dòng)作)

如果進(jìn)程要處理某一信號(hào),那么就要在進(jìn)程中安裝該信號(hào)。安裝信號(hào)主要用來(lái)確定信號(hào)值及進(jìn)程針對(duì)該信號(hào)值的動(dòng)作之間的映射關(guān)系,即進(jìn)程將要處理哪個(gè)信號(hào);該信號(hào)被傳遞給進(jìn)程時(shí),將執(zhí)行何種操作。

linux主要有兩個(gè)函數(shù)實(shí)現(xiàn)信號(hào)的安裝:signal()、sigaction()。其中signal()在可靠信號(hào)系統(tǒng)調(diào)用的基礎(chǔ)上實(shí)現(xiàn), 是庫(kù)函數(shù)。它只有兩個(gè)參數(shù),不支持信號(hào)傳遞信息,主要是用于前32種非實(shí)時(shí)信號(hào)的安裝;而sigaction()是較新的函數(shù)(由兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn):sys_signal以及sys_rt_sigaction),有三個(gè)參數(shù),支持信號(hào)傳遞信息,主要用來(lái)與 sigqueue() 系統(tǒng)調(diào)用配合使用,當(dāng)然,sigaction()同樣支持非實(shí)時(shí)信號(hào)的安裝。sigaction()優(yōu)于signal()主要體現(xiàn)在支持信號(hào)帶有參數(shù)。

1、signal()
#include <signal.h>
void (*signal(int signum, void (*handler))(int)))(int);
如果該函數(shù)原型不容易理解的話(huà),可以參考下面的分解方式來(lái)理解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一個(gè)參數(shù)指定信號(hào)的值,第二個(gè)參數(shù)指定針對(duì)前面信號(hào)值的處理,可以忽略該信號(hào)(參數(shù)設(shè)為SIG_IGN);可以采用系統(tǒng)默認(rèn)方式處理信號(hào)(參數(shù)設(shè)為SIG_DFL);也可以自己實(shí)現(xiàn)處理方式(參數(shù)指定一個(gè)函數(shù)地址)。
如果signal()調(diào)用成功,返回最后一次為安裝信號(hào)signum而調(diào)用signal()時(shí)的handler值;失敗則返回SIG_ERR。

2、sigaction()
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

sigaction函數(shù)用于改變進(jìn)程接收到特定信號(hào)后的行為。該函數(shù)的第一個(gè)參數(shù)為信號(hào)的值,可以為除SIGKILL及SIGSTOP外的任何一個(gè)特定有效的信號(hào)(為這兩個(gè)信號(hào)定義自己的處理函數(shù),將導(dǎo)致信號(hào)安裝錯(cuò)誤)。第二個(gè)參數(shù)是指向結(jié)構(gòu)sigaction的一個(gè)實(shí)例的指針,在結(jié)構(gòu)sigaction的實(shí)例中,指定了對(duì)特定信號(hào)的處理,可以為空,進(jìn)程會(huì)以缺省方式對(duì)信號(hào)處理;第三個(gè)參數(shù)oldact指向的對(duì)象用來(lái)保存原來(lái)對(duì)相應(yīng)信號(hào)的處理,可指定oldact為NULL。如果把第二、第三個(gè)參數(shù)都設(shè)為NULL,那么該函數(shù)可用于檢查信號(hào)的有效性。

第二個(gè)參數(shù)最為重要,其中包含了對(duì)指定信號(hào)的處理、信號(hào)所傳遞的信息、信號(hào)處理函數(shù)執(zhí)行過(guò)程中應(yīng)屏蔽掉哪些函數(shù)等等。

sigaction結(jié)構(gòu)定義如下:

                                    struct sigaction {                                    union{                                    __sighandler_t _sa_handler;                                    void (*_sa_sigaction)(int,struct siginfo *, void *);                                    }_u                                    sigset_t sa_mask;                                    unsigned long sa_flags;                                    void (*sa_restorer)(void);                                    }                                    

其中,sa_restorer,已過(guò)時(shí),POSIX不支持它,不應(yīng)再被使用。

1、聯(lián)合數(shù)據(jù)結(jié)構(gòu)中的兩個(gè)元素_sa_handler以及*_sa_sigaction指定信號(hào)關(guān)聯(lián)函數(shù),即用戶(hù)指定的信號(hào)處理函數(shù)。除了可以是用戶(hù)自定義的處理函數(shù)外,還可以為SIG_DFL(采用缺省的處理方式),也可以為SIG_IGN(忽略信號(hào))。

2、由_sa_handler指定的處理函數(shù)只有一個(gè)參數(shù),即信號(hào)值,所以信號(hào)不能傳遞除信號(hào)值之外的任何信息;由_sa_sigaction是指定的信號(hào)處理函數(shù)帶有三個(gè)參數(shù),是為實(shí)時(shí)信號(hào)而設(shè)的(當(dāng)然同樣支持非實(shí)時(shí)信號(hào)),它指定一個(gè)3參數(shù)信號(hào)處理函數(shù)。第一個(gè)參數(shù)為信號(hào)值,第三個(gè)參數(shù)沒(méi)有使用(posix沒(méi)有規(guī)范使用該參數(shù)的標(biāo)準(zhǔn)),第二個(gè)參數(shù)是指向siginfo_t結(jié)構(gòu)的指針,結(jié)構(gòu)中包含信號(hào)攜帶的數(shù)據(jù)值,參數(shù)所指向的結(jié)構(gòu)如下:

                                    siginfo_t {                                    int      si_signo;  /* 信號(hào)值,對(duì)所有信號(hào)有意義*/                                    int      si_errno;  /* errno值,對(duì)所有信號(hào)有意義*/                                    int      si_code;   /* 信號(hào)產(chǎn)生的原因,對(duì)所有信號(hào)有意義*/                                    union{				  /* 聯(lián)合數(shù)據(jù)結(jié)構(gòu),不同成員適應(yīng)不同信號(hào) */                                    //確保分配足夠大的存儲(chǔ)空間                                    int _pad[SI_PAD_SIZE];                                    //對(duì)SIGKILL有意義的結(jié)構(gòu)                                    struct{                                    ...                                    }...                                    ... ...                                    ... ...                                    //對(duì)SIGILL, SIGFPE, SIGSEGV, SIGBUS有意義的結(jié)構(gòu)                                    struct{                                    ...                                    }...                                    ... ...                                    }                                    }                                    

注:為了更便于閱讀,在說(shuō)明問(wèn)題時(shí)常把該結(jié)構(gòu)表示為附錄2所表示的形式。

siginfo_t結(jié)構(gòu)中的聯(lián)合數(shù)據(jù)成員確保該結(jié)構(gòu)適應(yīng)所有的信號(hào),比如對(duì)于實(shí)時(shí)信號(hào)來(lái)說(shuō),則實(shí)際采用下面的結(jié)構(gòu)形式:

                                    typedef struct {                                    int si_signo;                                    int si_errno;                                    int si_code;                                    union sigval si_value;                                    } siginfo_t;                                    

結(jié)構(gòu)的第四個(gè)域同樣為一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu):

                                    union sigval {                                    int sival_int;                                    void *sival_ptr;                                    }                                    

采用聯(lián)合數(shù)據(jù)結(jié)構(gòu),說(shuō)明siginfo_t結(jié)構(gòu)中的si_value要么持有一個(gè)4字節(jié)的整數(shù)值,要么持有一個(gè)指針,這就構(gòu)成了與信號(hào)相關(guān)的數(shù)據(jù)。在信號(hào)的處理函數(shù)中,包含這樣的信號(hào)相關(guān)數(shù)據(jù)指針,但沒(méi)有規(guī)定具體如何對(duì)這些數(shù)據(jù)進(jìn)行操作,操作方法應(yīng)該由程序開(kāi)發(fā)人員根據(jù)具體任務(wù)事先約定。

前面在討論系統(tǒng)調(diào)用sigqueue發(fā)送信號(hào)時(shí),sigqueue的第三個(gè)參數(shù)就是sigval聯(lián)合數(shù)據(jù)結(jié)構(gòu),當(dāng)調(diào)用sigqueue時(shí),該數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)就將拷貝到信號(hào)處理函數(shù)的第二個(gè)參數(shù)中。這樣,在發(fā)送信號(hào)同時(shí),就可以讓信號(hào)傳遞一些附加信息。信號(hào)可以傳遞信息對(duì)程序開(kāi)發(fā)是非常有意義的。

信號(hào)參數(shù)的傳遞過(guò)程可圖示如下:


3、sa_mask指定在信號(hào)處理程序執(zhí)行過(guò)程中,哪些信號(hào)應(yīng)當(dāng)被阻塞。缺省情況下當(dāng)前信號(hào)本身被阻塞,防止信號(hào)的嵌套發(fā)送,除非指定SA_NODEFER或者SA_NOMASK標(biāo)志位。

注:請(qǐng)注意sa_mask指定的信號(hào)阻塞的前提條件,是在由sigaction()安裝信號(hào)的處理函數(shù)執(zhí)行過(guò)程中由sa_mask指定的信號(hào)才被阻塞。

4、sa_flags中包含了許多標(biāo)志位,包括剛剛提到的SA_NODEFER及SA_NOMASK標(biāo)志位。另一個(gè)比較重要的標(biāo)志位是SA_SIGINFO,當(dāng)設(shè)定了該標(biāo)志位時(shí),表示信號(hào)附帶的參數(shù)可以被傳遞到信號(hào)處理函數(shù)中,因此,應(yīng)該為sigaction結(jié)構(gòu)中的sa_sigaction指定處理函數(shù),而不應(yīng)該為sa_handler指定信號(hào)處理函數(shù),否則,設(shè)置該標(biāo)志變得毫無(wú)意義。即使為sa_sigaction指定了信號(hào)處理函數(shù),如果不設(shè)置SA_SIGINFO,信號(hào)處理函數(shù)同樣不能得到信號(hào)傳遞過(guò)來(lái)的數(shù)據(jù),在信號(hào)處理函數(shù)中對(duì)這些信息的訪(fǎng)問(wèn)都將導(dǎo)致段錯(cuò)誤(Segmentation fault)。

注:很多文獻(xiàn)在闡述該標(biāo)志位時(shí)都認(rèn)為,如果設(shè)置了該標(biāo)志位,就必須定義三參數(shù)信號(hào)處理函數(shù)。實(shí)際不是這樣的,驗(yàn)證方法很簡(jiǎn)單:自己實(shí)現(xiàn)一個(gè)單一參數(shù)信號(hào)處理函數(shù),并在程序中設(shè)置該標(biāo)志位,可以察看程序的運(yùn)行結(jié)果。實(shí)際上,可以把該標(biāo)志位看成信號(hào)是否傳遞參數(shù)的開(kāi)關(guān),如果設(shè)置該位,則傳遞參數(shù);否則,不傳遞參數(shù)。

六、信號(hào)集及信號(hào)集操作函數(shù):

信號(hào)集被定義為一種數(shù)據(jù)類(lèi)型:

                                    typedef struct {                                    unsigned long sig[_NSIG_WORDS];                                    } sigset_t                                    

信號(hào)集用來(lái)描述信號(hào)的集合,linux所支持的所有信號(hào)可以全部或部分的出現(xiàn)在信號(hào)集中,主要與信號(hào)阻塞相關(guān)函數(shù)配合使用。下面是為信號(hào)集操作定義的相關(guān)函數(shù):

                                    #include <signal.h>                                    int sigemptyset(sigset_t *set);                                    int sigfillset(sigset_t *set);                                    int sigaddset(sigset_t *set, int signum)                                    int sigdelset(sigset_t *set, int signum);                                    int sigismember(const sigset_t *set, int signum);                                    sigemptyset(sigset_t *set)初始化由set指定的信號(hào)集,信號(hào)集里面的所有信號(hào)被清空;                                    sigfillset(sigset_t *set)調(diào)用該函數(shù)后,set指向的信號(hào)集中將包含linux支持的64種信號(hào);                                    sigaddset(sigset_t *set, int signum)在set指向的信號(hào)集中加入signum信號(hào);                                    sigdelset(sigset_t *set, int signum)在set指向的信號(hào)集中刪除signum信號(hào);                                    sigismember(const sigset_t *set, int signum)判定信號(hào)signum是否在set指向的信號(hào)集中。                                    

七、信號(hào)阻塞與信號(hào)未決:

每個(gè)進(jìn)程都有一個(gè)用來(lái)描述哪些信號(hào)遞送到進(jìn)程時(shí)將被阻塞的信號(hào)集,該信號(hào)集中的所有信號(hào)在遞送到進(jìn)程后都將被阻塞。下面是與信號(hào)阻塞相關(guān)的幾個(gè)函數(shù):

                                    #include <signal.h>                                    int  sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset));                                    int sigpending(sigset_t *set));                                    int sigsuspend(const sigset_t *mask));                                    

sigprocmask()函數(shù)能夠根據(jù)參數(shù)how來(lái)實(shí)現(xiàn)對(duì)信號(hào)集的操作,操作主要有三種:

參數(shù)how進(jìn)程當(dāng)前信號(hào)集
SIG_BLOCK在進(jìn)程當(dāng)前阻塞信號(hào)集中添加set指向信號(hào)集中的信號(hào)
SIG_UNBLOCK如果進(jìn)程阻塞信號(hào)集中包含set指向信號(hào)集中的信號(hào),則解除對(duì)該信號(hào)的阻塞
SIG_SETMASK更新進(jìn)程阻塞信號(hào)集為set指向的信號(hào)集

sigpending(sigset_t *set))獲得當(dāng)前已遞送到進(jìn)程,卻被阻塞的所有信號(hào),在set指向的信號(hào)集中返回結(jié)果。

sigsuspend(const sigset_t *mask))用于在接收到某個(gè)信號(hào)之前, 臨時(shí)用mask替換進(jìn)程的信號(hào)掩碼, 并暫停進(jìn)程執(zhí)行,直到收到信號(hào)為止。sigsuspend 返回后將恢復(fù)調(diào)用之前的信號(hào)掩碼。信號(hào)處理函數(shù)完成后,進(jìn)程將繼續(xù)執(zhí)行。該系統(tǒng)調(diào)用始終返回-1,并將errno設(shè)置為EINTR。

附錄1:結(jié)構(gòu)itimerval:

                                    struct itimerval {                                    struct timeval it_interval; /* next value */                                    struct timeval it_value;    /* current value */                                    };                                    struct timeval {                                    long tv_sec;                /* seconds */                                    long tv_usec;               /* microseconds */                                    };                                    

附錄2:三參數(shù)信號(hào)處理函數(shù)中第二個(gè)參數(shù)的說(shuō)明性描述:

                                    siginfo_t {                                    int      si_signo;  /* 信號(hào)值,對(duì)所有信號(hào)有意義*/                                    int      si_errno;  /* errno值,對(duì)所有信號(hào)有意義*/                                    int      si_code;   /* 信號(hào)產(chǎn)生的原因,對(duì)所有信號(hào)有意義*/                                    pid_t    si_pid;    /* 發(fā)送信號(hào)的進(jìn)程ID,對(duì)kill(2),實(shí)時(shí)信號(hào)以及SIGCHLD有意義 */                                    uid_t    si_uid;    /* 發(fā)送信號(hào)進(jìn)程的真實(shí)用戶(hù)ID,對(duì)kill(2),實(shí)時(shí)信號(hào)以及SIGCHLD有意義 */                                    int      si_status; /* 退出狀態(tài),對(duì)SIGCHLD有意義*/                                    clock_t  si_utime;  /* 用戶(hù)消耗的時(shí)間,對(duì)SIGCHLD有意義 */                                    clock_t  si_stime;  /* 內(nèi)核消耗的時(shí)間,對(duì)SIGCHLD有意義 */                                    sigval_t si_value;  /* 信號(hào)值,對(duì)所有實(shí)時(shí)有意義,是一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu),可以為一個(gè)整數(shù)(由si_int標(biāo)示,也可以為一個(gè)指針,由si_ptr標(biāo)示)*/                                    void *   si_addr;   /* 觸發(fā)fault的內(nèi)存地址,對(duì)SIGILL,SIGFPE,SIGSEGV,SIGBUS 信號(hào)有意義*/                                    int      si_band;   /* 對(duì)SIGPOLL信號(hào)有意義 */                                    int      si_fd;     /* 對(duì)SIGPOLL信號(hào)有意義 */                                    
}

實(shí)際上,除了前三個(gè)元素外,其他元素組織在一個(gè)聯(lián)合結(jié)構(gòu)中,在聯(lián)合數(shù)據(jù)結(jié)構(gòu)中,又根據(jù)不同的信號(hào)組織成不同的結(jié)構(gòu)。注釋中提到的對(duì)某種信號(hào)有意義指的是,在該信號(hào)的處理函數(shù)中可以訪(fǎng)問(wèn)這些域來(lái)獲得與信號(hào)相關(guān)的有意義的信息,只不過(guò)特定信號(hào)只對(duì)特定信息感興趣而已。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Linux環(huán)境進(jìn)程間通信(二): 信號(hào)(上)
sigqueue
Linux信號(hào)(signal) 機(jī)制分析
Linux | 一篇文章徹底搞定信號(hào)!
linux的信號(hào)機(jī)制
信號(hào)集
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服