Linux系統程式設計(24)——訊號的生命週期
訊號生命週期為從訊號傳送到訊號處理函式的執行完畢。
對於一個完整的訊號生命週期(從訊號傳送到相應的處理函式執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:訊號誕生;訊號在程式中註冊完畢;訊號在程式中的登出完畢;訊號處理函式執行完畢。相鄰兩個事件的時間間隔構成訊號生命週期的一個階段。
下面闡述四個事件的實際意義:
1、訊號"誕生"。訊號的誕生指的是觸發訊號的事件發生(如檢測到硬體異常、定時器超時以及呼叫訊號傳送函式kill()或sigqueue()等)。
2、訊號在目標程式中"註冊";程式的task_struct結構中有關於本程式中未決訊號的資料成員:
struct sigpending pending:
struct sigpending{
structsigqueue *head, **tail;
sigset_tsignal;
};
第三個成員是程式中所有未決訊號集,第一、第二個成員分別指向一個sigqueue型別的結構鏈(稱之為"未決訊號資訊鏈")的首尾,資訊鏈中的每個sigqueue結構刻畫一個特定訊號所攜帶的資訊,並指向下一個sigqueue結構:
struct sigqueue{
structsigqueue *next;
siginfo_tinfo;
}
訊號在程式中註冊指的就是訊號值加入到程式的未決訊號集中(sigpending結構的第二個成員sigset_t signal),並且訊號所攜帶的資訊被保留到未決訊號資訊鏈的某個sigqueue結構中。只要訊號在程式的未決訊號集中,表明程式已經知道這些訊號的存在,但還沒來得及處理,或者該訊號被程式阻塞。
注意:
當一個實時訊號傳送給一個程式時,不管該訊號是否已經在程式中註冊,都會被再註冊一次,因此,訊號不會丟失,因此,實時訊號又叫做"可靠訊號"。這意味著同一個實時訊號可以在同一個程式的未決訊號資訊鏈中佔有多個sigqueue結構(程式每收到一個實時訊號,都會為它分配一個結構來登記該訊號資訊,並把該結構新增在未決訊號鏈尾,即所有誕生的實時訊號都會在目標程式中註冊);
當一個非實時訊號傳送給一個程式時,如果該訊號已經在程式中註冊,則該訊號將被丟棄,造成訊號丟失。因此,非實時訊號又叫做"不可靠訊號"。這意味著同一個非實時訊號在程式的未決訊號資訊鏈中,至多佔有一個sigqueue結構(一個非實時訊號誕生後,(1)、如果發現相同的訊號已經在目標結構中註冊,則不再註冊,對於程式來說,相當於不知道本次訊號發生,訊號丟失;(2)、如果程式的未決訊號中沒有相同訊號,則在程式中註冊自己)。
3、訊號在程式中的登出。在目標程式執行過程中,會檢測是否有訊號等待處理(每次從系統空間返回到使用者空間時都做這樣的檢查)。如果存在未決訊號等待處理且該訊號沒有被程式阻塞,則在執行相應的訊號處理函式前,程式會把訊號在未決訊號鏈中佔有的結構卸掉。是否將訊號從程式未決訊號集中刪除對於實時與非實時訊號是不同的。對於非實時訊號來說,由於在未決訊號資訊鏈中最多隻佔用一個sigqueue結構,因此該結構被釋放後,應該把訊號在程式未決訊號集中刪除(訊號登出完畢);而對於實時訊號來說,可能在未決訊號資訊鏈中佔用多個sigqueue結構,因此應該針對佔用sigqueue結構的數目區別對待:如果只佔用一個sigqueue結構(程式只收到該訊號一次),則應該把訊號在程式的未決訊號集中刪除(訊號登出完畢)。否則,不應該在程式的未決訊號集中刪除該訊號(訊號登出完畢)。
程式在執行訊號相應處理函式之前,首先要把訊號在程式中登出。
4、訊號生命終止。程式登出訊號後,立即執行相應的訊號處理函式,執行完畢後,訊號的本次傳送對程式的影響徹底結束。
注意:
訊號註冊與否,與傳送訊號的函式(如kill()或sigqueue()等)以及訊號安裝函式(signal()及sigaction())無關,只與訊號值有關(訊號值小於SIGRTMIN的訊號最多隻註冊一次,訊號值在SIGRTMIN及SIGRTMAX之間的訊號,只要被程式接收到就被註冊)。
在訊號被登出到相應的訊號處理函式執行完畢這段時間內,如果程式又收到同一訊號多次,則對實時訊號來說,每一次都會在程式中註冊;而對於非實時訊號來說,無論收到多少次訊號,都會視為只收到一個訊號,只在程式中註冊一次。
linux下的訊號應用並沒有想象的那麼恐怖,我們所要做的最多隻有三件事情:
1、安裝訊號(推薦使用sigaction());
2、實現三引數訊號處理函式,handler(intsignal,struct siginfo *info, void *);
3、傳送訊號,推薦使用sigqueue()。
實際上,對有些訊號來說,只要安裝訊號就足夠了(訊號處理方式採用預設或忽略)。其他可能要做的無非是與訊號集相關的幾種操作。
下面的例子完整實現了訊號傳送及處理:
實現一個訊號接收程式sigreceive(其中訊號安裝由sigaction())。
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
structsigaction act;
intsig;
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("installsigal error\n");
}
while(1)
{
sleep(2);
printf("waitfor the signal\n");
}
}
void new_op(int signum,siginfo_t *info,void*myact)
{
printf("receivesignal %d", signum);
sleep(5);
}
說明,命令列引數為訊號值,後臺執行sigreceive signo &,可獲得該程式的ID,假設為pid,然後再另一終端上執行kill -s signo pid驗證訊號的傳送接收及處理。同時,可驗證訊號的排隊問題。
注:可以用sigqueue實現一個命令列訊號傳送程式sigqueuesend。
下面我們實現訊號傳遞附加資訊。向程式本身傳送訊號,並傳遞指標引數:
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
structsigaction act;
unionsigval mysigval;
inti;
intsig;
pid_tpid;
chardata[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;//三引數訊號處理函式
act.sa_flags=SA_SIGINFO;//資訊傳遞開關
if(sigaction(sig,&act,NULL)< 0)
{
printf("installsigal error\n");
}
while(1)
{
sleep(2);
printf("waitfor the signal\n");
sigqueue(pid,sig,mysigval);//向本程式傳送訊號,並傳遞附加資訊
}
}
void new_op(int signum,siginfo_t *info,void*myact)//三引數訊號處理函式的實現
{
inti;
for(i=0;i<10;i++)
{
printf("%c\n",(*( (char*)((*info).si_ptr)+i)));
}
printf("handlesignal %d over;",signum);
}
這個例子中,訊號實現了附加資訊的傳遞,訊號究竟如何對這些資訊進行處理則取決於具體的應用。
相關文章
- linux系統程式設計之程式(二):程式生命週期與PCB(程式控制塊)Linux程式設計
- Linux系統程式設計—訊號捕捉Linux程式設計
- Linux系統程式設計:訊號捕捉Linux程式設計
- 【Linux系統程式設計】Linux訊號列表Linux程式設計
- 【系統設計】系統發展生命週期(System Development Life Cycle)dev
- 《一個程式設計師的生命週期》——有感程式設計師
- Linux系統程式設計(21)——訊號的產生Linux程式設計
- linux系統程式設計之訊號(一):中斷與訊號Linux程式設計
- Linux系統程式設計(22)——響應訊號Linux程式設計
- Rust 程式設計視訊教程(進階)——004_5 生命週期的例子Rust程式設計
- 【linux】系統程式設計-1-程式、管道和訊號Linux程式設計
- Linux系統程式設計(20)——訊號基本概念Linux程式設計
- Rust 程式設計視訊教程(進階)——004_3 生命週期省略Rust程式設計
- Rust 程式設計視訊教程(進階)——004_4 方法定義中的生命週期註解和靜態生命週期Rust程式設計
- 《一個程式設計師的生命週期》讀後感程式設計師
- Rust 程式設計視訊教程(進階)——004_1 生命週期介紹Rust程式設計
- 設計專案全生命週期管理系統構建與實踐
- linux系統程式設計之訊號(五):訊號集操作函式,訊號阻塞與未決Linux程式設計函式
- Linux系統程式設計之訊號中斷處理(下)Linux程式設計
- Linux系統程式設計之訊號中斷處理(上)Linux程式設計
- linux系統程式設計之訊號(七):被訊號中斷的系統呼叫和庫函式處理方式Linux程式設計函式
- 微信小程式生命週期微信小程式
- 微信小程式--生命週期微信小程式
- linux系統程式設計之訊號(三):訊號安裝、signal、kill,arise講解Linux程式設計
- View生命週期與Activity生命週期的關係View
- ASP.NET頁面生命週期與應用程式生命週期ASP.NET
- Linux 核心測試的生命週期Linux
- Rust 程式設計影片教程(進階)——004_4 方法定義中的生命週期註解和靜態生命週期Rust程式設計
- 生命週期
- Rust 程式設計影片教程(進階)——004_5 生命週期的例子Rust程式設計
- TiPLM---產品全生命週期管理系統
- linux系統程式設計之訊號(四):alarm和可重入函式Linux程式設計函式
- Flutter 的生命週期Flutter
- SQL的生命週期SQL
- Laravel的生命週期Laravel
- vue的生命週期Vue
- Fragment的生命週期Fragment
- App的生命週期APP