信號是進程之間互傳消息的一種方法俗稱軟件中斷。很多比較重要的應用程序都需處理信號。信號提供了一種
處理異步事件的方法:終端用戶鍵入中斷鍵,則會通過信號機構停止一個程序。所以,信號可以說是進程控制的一部分。
在Redhat 7.3上kill -l 得到
CODE
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
…….
信號出現在 UNIX 的早期版本中 ,但早期的信號模型是不可靠的, 信號可能被丟失,
也很難處理關鍵段。UNIX 的兩個重要分支 BSD 和 System V 分別對早期的信號進行了擴展 ,
但這兩個系統的擴展並不兼容POSIX 統一了這兩種實現, 最終提供了可靠的信號模型。
信號的產生條件
. 當用戶按某些終端鍵時,產生信號。
. 硬件異常產生信號:除數為0、無效的存儲訪問等等。
. 進程用kill函數可將信號發送給另一個進程或進程組
. 用戶可用kill命令將信號發送給其他進程
. 當檢測到某種軟件條件已經發生,並將其通知有關進程時也產生信號。
接到信號的處理辦法
1,忽略,但是SIGKILL和SIGSTOP不能忽略。
2,捕捉。
3,執行系統的默認處理。
信號的分類
非可靠信號:早期unix下的不可靠信號主要指的是進程可能對信號做出錯誤的反應
以及信號可能丟失。
可靠信號: 信號值位於SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號
克服了信號可能丟失的問題。
信號的可靠與不可靠只與信號值有關,與信號的發送及安裝函數無關。
當然也可以稱為實時信號或者非實時信號,非實時信號都不支持排隊,都是不可靠信號;
實時信號都支持排隊,都是可靠信號。
這裡我說一下信號的生命週期,對於理解信號的分類有很大的幫助。
從信號發送到信號處理函數的執行完畢。對於一個完整的信號生命週期(從信號發送到相應的處
理函數執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:
信號產生;信號在進程中註冊完畢;信號在進程中的註銷完畢;信號處理函數執行完畢。
相鄰兩個事件的時間間隔構成信號生命週期的一個階段。當一個實時信號發送給一個進程時,不管該信號是否已經在進程中註冊,都會被再註冊一次,因此,信號不會丟失,因此,實時信號又叫做"可靠信號"。這意味著同一個實時信號可以在同一個進程的未決信號信息鏈中佔有多個sigqueue結構(進程每收到一個實時信號,都會為它分配一個結構來登記該信號信息,並把該結構添加在未決信號鏈尾,即所有誕生的實時信號都會在目標進程中註冊);
當一個非實時信號發送給一個進程時,如果該信號已經在進程中註冊,則該信號將被丟棄,造成信號丟失。
因此,非實時信號又叫做"不可靠信號"。這意味著同一個非實時信號在進程的未決信號信息鏈中,至多佔有一個
sigqueue結構(一個非實時信號產生後,1如果發現相同的信號已經在目標結構中註冊,則不再註冊,對於
進程來說,相當於不知道本次信號發生,信號丟失;2如果進程的未決信號中沒有相同信號,則在進程中註冊自己)。
需要注意的要點是:
1)信號註冊與否,與發送信號的函數(如kill()或sigqueue()等)以及信號安裝函數(signal()及sigaction())無關,
只與信號值有關(信號值小於SIGRTMIN的信號最多只註冊一次,信號值在SIGRTMIN及SIGRTMAX之間的信號,只要被進程接收到就被註冊)。
2)在信號被註銷到相應的信號處理函數執行完畢這段時間內,如果進程又收到同一信號多次,則對實時信號來說,
每一次都會在進程中註冊;而對於非實時信號來說,無論收到多少次信號,都會視為只收到一個信號,只在進程中註冊一次。
當然還有有些需要知道的概念比如 低速系統調用,中斷系統調用,可重入函數等等概念,請查閱環境高級編程。
一些概念性的東西大概就這些東西吧,下面就分類介紹一下具體的函數。
我下面介紹的,如果沒有特殊說明,都是對於linux上的實現,當然都是符合POSIX標準嘍。
二,函數詳細介紹
A:信號的發送
發送信號的主要函數有:kill(),raise(),sigqueue(),alarm(),setitimer()以及abort()。
1, int kill(pid_t pid,int signo)
用到的頭文件:
#include <sys/types.h>
#include <signal.h>
參數pid的值 信號的接收進程
pid>0 進程ID為pid的進程
pid=0 同一個進程組的進程
pid<0 pid!=-1 進程組ID為 -pid的所有進程
pid=-1 除發送進程自身外,所有進程ID大於1的進程
Sinno是信號值,當為0時(即空信號),實際不發送任何信號,但照常進行錯誤檢查,因此,可用於檢查目標進程是否存在,
以及當前進程是否具有向目標發送信號的權限(root權限的進程可以向任何進程發送信號,非root權限的進程只能向屬於同
一個session或者同一個用戶的進程發送信號)。
kill最常用於pid>0時的信號發送,調用成功返回 0; 否則,返回 -1。註:對於pid<0時的情況,對於哪些進程將接受信號,
各種版本說法不一,其實很簡單,參閱內核源碼kernal/signal.c。
2, int raise(int signo)
用到的頭文件:
#include <signal.h>
向進程本身發送信號,參數為即將發送的信號值。調用成功返回 0;否則,返回 -1。
3, int sigqueue(pid_t pid, int sig, const union sigval val)
用到的頭文件:
#include <sys/types.h>
#include <signal.h>
調用成功返回 0;否則,返回 -1。
sigqueue()是比較新的發送信號系統調用,主要是針對實時信號提出的(當然也支持前32種),支持信號帶有參數,
與函數sigaction()配合使用。sco 5.05沒有此函數。
sigqueue的第一個參數是指定接收信號的進程ID,第二個參數確定即將發送的信號,第三個參數是一個聯合數據結構union sigval,
指定了信號傳遞的參數,即通常所說的4字節值。
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。如果sig為0,
將會執行錯誤檢查,但實際上不發送任何信號,0值信號可用於檢查pid的有效性以及當前進程是否有權限向目標進程發送信號。
在調用sigqueue時,sigval_t指定的信息會拷貝到3參數信號處理函數(3參數信號處理函數指的是信號處理函數由sigaction安裝,
並設定了sa_sigaction指針,稍後將闡述)的siginfo_t結構中,這樣信號處理函數就可以處理這些信息了。由於sigqueue系統調用
支持發送帶參數信號,所以比kill()系統調用的功能要靈活和強大得多。
註:sigqueue()發送非實時信號時,第三個參數包含的信息仍然能夠傳遞給信號處理函數; sigqueue()發送非實時信號時,仍然不支持排隊,
即在信號處理函數執行過程中到來的所有相同信號,都被合併為一個信號。
4, unsigned int alarm(unsigned int seconds)
用到的頭文件:
#include <unistd.h>
專門為SIGALRM信號而設,在指定的時間seconds秒後,將向進程本身發送SIGALRM信號,又稱為鬧鐘時間。進程調用alarm後,
任何以前的alarm()調用都將無效。如果參數seconds為零,那麼進程內將不再包含任何鬧鐘時間。
函數返回是這樣的,如果調用alarm()前,進程中已經設置了鬧鐘時間,則返回上一個鬧鐘時間的剩餘時間,否則返回0。
5, int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
用到的頭文件:
#include <sys/time.h>
setitimer()比alarm功能強大,支持3種類型的定時器:
ITIMER_REAL: 設定絕對時間;經過指定的時間後,內核將發送SIGALRM信號給本進程;
ITIMER_VIRTUAL 設定程序執行時間;經過指定的時間後,內核將發送SIGVTALRM信號給本進程;
ITIMER_PROF 設定進程執行以及內核因本進程而消耗的時間和,經過指定的時間後,內核將發送ITIMER_VIRTUAL信號給本進程;
Setitimer()第一個參數which指定定時器類型(上面三種之一);第二個參數是結構itimerval的一個實例,結構itimerval。
結構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 */
};
第三個參數可不做處理。
Setitimer()調用成功返回0,否則返回-1。
這是我man setitimer時看到的
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>
向進程發送SIGABORT信號,默認情況下進程會異常退出,當然可定義自己的信號處理函數。
即使SIGABORT被進程設置為阻塞信號,調用abort()後,SIGABORT仍然能被進程接收。該函數無返回值。
B:信號的捕獲與安裝(設置信號關聯動作)
如果進程要處理某一信號,那麼就要在進程中安裝該信號。安裝信號主要用來確定信號值及進程針對該信號值
的動作之間的映射關係,
即進程將要處理哪個信號;該信號被傳遞給進程時,將執行何種操作。
linux主要有兩個函數實現信號的安裝:signal()、sigaction()。其中signal()在可靠信號系統調用的基礎上實現,
是庫函數。它只有兩個參數,不支持信號傳遞信息,主要是用於前32種非實時信號的安裝;而sigaction()是較新的
函數(由兩個系統調用實現:sys_signal以及sys_rt_sigaction),有三個參數,支持信號傳遞信息,主要用來與
sigqueue() 系統調用配合使用,當然,sigaction()同樣支持非實時信號的安裝。sigaction()優於signal()主要體現
在支持信號帶有參數。
1, void (*signal(int signum, void (*handler))(int)))(int);
#include <signal.h>
如果該函數原型不容易理解的話,可以參考下面的分解方式來理解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一個參數指定信號的值,第二個參數指定針對前面信號值的處理,
a,可以忽略該信號(參數設為SIG_IGN);
b,可以採用系統默認方式處理信號(參數設為SIG_DFL);
b,也可以自己實現處理方式(參數指定一個函數地址)。
如果signal()調用成功,返回最後一次為安裝信號signum而調用signal()時的handler值;失敗則返回SIG_ERR。
2, int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
#include <signal.h>
sigaction函數用於改變進程接收到特定信號後的行為。該函數的第一個參數為信號的值,可以為除SIGKILL及SIGSTOP外的任何
一個特定有效的信號(為這兩個信號定義自己的處理函數,將導致信號安裝錯誤)。第二個參數是指向結構sigaction的一個實例
的指針,在結構sigaction的實例中,指定了對特定信號的處理,可以為空,進程會以缺省方式對信號處理;第三個參數oldact指向
的對象用來保存原來對相應信號的處理,可指定oldact為NULL。如果把第二、第三個參數都設為NULL,那麼該函數可用於檢查信號的有效性。
第二個參數最為重要,其中包含了對指定信號的處理、信號所傳遞的信息、信號處理函數執行過程中應屏蔽掉哪些函數等等。
sigaction結構定義如下:
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,已過時,POSIX不支持它,不應再被使用。
a,聯合數據結構中的兩個元素_sa_handler以及*_sa_sigaction指定信號關聯函數,即用戶指定的信號處理函數。除了可以是用戶自
定義的處理函數外,還可以為SIG_DFL(採用缺省的處理方式),也可以為SIG_IGN(忽略信號)。
b,由_sa_handler指定的處理函數只有一個參數,即信號值,所以信號不能傳遞除信號值之外的任何信息;由_sa_sigaction是指定的
信號處理函數帶有三個參數,是為實時信號而設的(當然同樣支持非實時信號),它指定一個3參數信號處理函數。
第一個參數為信號值,第三個參數沒有使用(POSIX沒有規範使用該參數的標準),第二個參數是指向siginfo_t結構的指針,結構中包含
信號攜帶的數據值,參數所指向的結構如下:
siginfo_t {
int si_signo; /* 信號值,對所有信號有意義*/
int si_errno; /* errno值,對所有信號有意義*/
int si_code; /* 信號產生的原因,對所有信號有意義*/
union{ /* 聯合數據結構,不同成員適應不同信號 */
//確保分配足夠大的存儲空間
int _pad[SI_PAD_SIZE];
//對SIGKILL有意義的結構
struct{
...
}...
... ...
... ...
//對SIGILL, SIGFPE, SIGSEGV, SIGBUS有意義的結構
struct{
...
}...
... ...
}
}
註:為了更便於閱讀,在說明問題時常把該結構表示為如下所表示的形式。
siginfo_t {
int si_signo; /* 信號值,對所有信號有意義*/
int si_errno; /* errno值,對所有信號有意義*/
int si_code; /* 信號產生的原因,對所有信號有意義*/
pid_t si_pid; /* 發送信號的進程ID,對kill(2),實時信號以及SIGCHLD有意義 */
uid_t si_uid; /* 發送信號進程的真實用戶ID,對kill(2),實時信號以及SIGCHLD有意義 */
int si_status; /* 退出狀態,對SIGCHLD有意義*/
clock_t si_utime; /* 用戶消耗的時間,對SIGCHLD有意義 */
clock_t si_stime; /* 內核消耗的時間,對SIGCHLD有意義 */
sigval_t si_value; /* 信號值,對所有實時有意義,是一個聯合數據結構,可以為一個整數(由si_int標示,也可以為一個指針,由si_ptr標示)*/
void * si_addr; /* 觸發fault的內存地址,對SIGILL,SIGFPE,SIGSEGV,SIGBUS 信號有意義*/
int si_band; /* 對SIGPOLL信號有意義 */
int si_fd; /* 對SIGPOLL信號有意義 */
}
實際上,除了前三個元素外,其他元素組織在一個聯合結構中,在聯合數據結構中,又根據不同的信號組織成不同的結構。
註釋中提到的對某種信號有意義指的是,在該信號的處理函數中可以訪問這些域來獲得與信號相關的有意義的信息,只不過特定信號只
對特定信息感興趣而已。
siginfo_t結構中的聯合數據成員確保該結構適應所有的信號,比如對於實時信號來說,則實際採用下面的結構形式:
typedef struct {
int si_signo;
int si_errno;
int si_code;
union sigval si_value;
} siginfo_t;
結構的第四個域同樣為一個聯合數據結構:
union sigval {
int sival_int;
void *sival_ptr;
}
採用聯合數據結構,說明siginfo_t結構中的si_value要麼持有一個4字節的整數值,要麼持有一個指針,這就構成了與信號相關的數據。
在信號的處理函數中,包含這樣的信號相關數據指針,但沒有規定具體如何對這些數據進行操作,操作方法應該由程序開發人員根據具體
任務事先約定。
前面在討論系統調用sigqueue發送信號時,sigqueue的第三個參數就是sigval聯合數據結構,當調用sigqueue時,該數據結構中的數據就將
拷貝到信號處理函數的第二個參數中。這樣,在發送信號同時,就可以讓信號傳遞一些附加信息。信號可以傳遞信息對程序開發是非常有意義
的。
c,sa_mask指定在信號處理程序執行過程中,哪些信號應當被阻塞。缺省情況下當前信號本身被阻塞,防止信號的嵌套發送,除非指定SA_NODEFER或者SA_NOMASK標誌位。
註:請注意sa_mask指定的信號阻塞的前提條件,是在由sigaction()安裝信號的處理函數執行過程中由sa_mask指定的信號才被阻塞。
d,sa_flags中包含了許多標誌位,包括剛剛提到的SA_NODEFER及SA_NOMASK標誌位。另一個比較重要的標誌位是SA_SIGINFO,當設定了該標誌
位時,表示信號附帶的參數可以被傳遞到信號處理函數中,因此,應該為sigaction結構中的sa_sigaction指定處理函數,而不應該為sa_handler
指定信號處理函數,否則,設置該標誌變得毫無意義。即使為sa_sigaction指定了信號處理函數,如果不設置SA_SIGINFO,信號處理函數同樣
不能得到信號傳遞過來的數據,在信號處理函數中對這些信息的訪問都將導致段錯誤(Segmentation fault)。
註:很多文獻在闡述該標誌位時都認為,如果設置了該標誌位,就必須定義三參數信號處理函數。實際不是這樣的,驗證方法很簡單:自己實現
一個單一參數信號處理函數,並在程序中設置該標誌位,可以察看程序的運行結果。實際上,可以把該標誌位看成信號是否傳遞參數的開關,如果設置該位,
則傳遞參數;否則,不傳遞參數。
C:信號集及信號集操作函數:
信號集被定義為一種數據類型:
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t
信號集用來描述信號的集合,linux所支持的所有信號可以全部或部分的出現在信號集中,主要與信號阻塞相關函數配合使用。下面是為
信號集操作定義的相關函數:
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指定的信號集,信號集裡面的所有信號被清空;
sigfillset(sigset_t *set)調用該函數後,set指向的信號集中將包含linux支持的64種信號;
sigaddset(sigset_t *set, int signum)在set指向的信號集中加入signum信號;
sigdelset(sigset_t *set, int signum)在set指向的信號集中刪除signum信號;
sigismember(const sigset_t *set, int signum)判定信號signum是否在set指向的信號集中。
D:信號阻塞與信號未決:
每個進程都有一個用來描述哪些信號遞送到進程時將被阻塞的信號集,該信號集中的所有信號在遞送到進程後都將被阻塞。下面是與信號阻塞
相關的幾個函數:
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()函數能夠根據參數how來實現對信號集的操作,操作主要有三種:
參數how 進程當前信號集
SIG_BLOCK 在進程當前阻塞信號集中添加set指向信號集中的信號
SIG_UNBLOCK 如果進程阻塞信號集中包含set指向信號集中的信號,則解除對該信號的阻塞
SIG_SETMASK 更新進程阻塞信號集為set指向的信號集
sigpending(sigset_t *set))獲得當前已遞送到進程,卻被阻塞的所有信號,在set指向的信號集中返回結果。
sigsuspend(const sigset_t *mask))用於在接收到某個信號之前, 臨時用mask替換進程的信號掩碼, 並暫停進程執行,直到收到信號為止。
sigsuspend 返回後將恢復調用之前的信號掩碼。信號處理函數完成後,進程將繼續執行。該系統調用始終返回-1,並將errno設置為EINTR。
三,示例程序
實例一:信號發送及處理,看看函數sigaction是怎麼使用的。
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);
}
在一終端執行cc -o act act.c
$./act 8&
[1] 992
$ wait for the signal
在另一終端執行
#kill -s 8 992
看看。。
$receive signal 8
實例二:信號阻塞及信號集操作
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
$
實例三: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)
;
}
這個就是signal的用法集中展示,也是我經常用的一個方法。說實話,sco太古老了。。俺只能用這個函數了。
測試時你可以把#define damo這個註釋和不註釋看看。深刻體會signal的用法。
BTW:signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
等等忽略了這些信號後,可以讓一個進程終端無關,即使你退出這個tty.當然kill信號還是不能屏蔽的。
這種方式多在守護進程中採用。
$ cc -o si si.c
$./si
Process id is 1501
在另一個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
#
注意觀察這時候是1456
你把./si這個tty關掉。
這時候你再
#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
注意這個1和?,知道這個進程成啥了吧?成精拉。哈哈??
實例四:pause函數
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");
}
在這個例子中,程序開始執行,就像進入了死循環一樣,這是因為進程正在等待信號,
當我們按下Ctrl-C時,信號被捕捉,並且使得pause退出等待狀態。
實例五:下面是關於setitimer調用的一個簡單例子。
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, &amp;amp;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, &amp;amp;ovalue);
while(1);
}
在該例子中,每隔1秒發出一個SIGALRM,每隔1.5秒發出一個SIGVTALRM信號:
結果如下
$ ./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
ctrl+c中斷。
....
開始喜歡setitimer函數了。。。
四,補充
不得不介紹一下setjmp和longjmp的作用。
在用信號的時候,我們看到多個地方要求進程在檢查收到信號後,從原來的系統調用中直接返回,而不是等到該調用完成。
這種進程突然改變其上下文的情況,就是使用setjmp和longjmp的結果。setjmp將保存的上下文存入用戶區,並繼續在舊的
上下文中執行。這就是說,進程執行一個系統調用,當因為資源或其他原因要去睡眠時,內核為進程作了一次setjmp,如果
在睡眠中被信號喚醒,進程不能再進入睡眠時,內核為進程調用longjmp,該操作是內核為進程將原先setjmp調用保存在進程
用戶區的上下文恢復成現在的上下文,這樣就使得進程可以恢復等待資源前的狀態,而且內核為setjmp返回1,使得進程知道
該次系統調用失敗。這就是它們的作用。
有時間再man 一下waitpid吧。。都是很有用的。
沒有留言:
張貼留言