Linux系統程式設計之訊號中斷處理(下)
訊號集與訊號阻塞集
訊號集
為了方便對多個訊號進行處理,一個使用者程式常常需要對多個訊號做出處理,在 Linux 系統中引入了訊號集(訊號的集合)。這個訊號集有點類似於我們的 QQ 群,一個個的訊號相當於 QQ 群裡的一個個好友。
訊號集是用來表示多個訊號的資料型別(sigset_t ),其定義路徑為: /usr/include/i386-linux-gnu/bits/sigset.h 。
訊號集相關的操作主要有如下幾個函式:
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigismember(const sigset_t *set, int signum);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
透過例子來檢視他的使用方法:
#include <signal.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
sigset_t set; // 定義一個訊號集變數
int ret = 0;
sigemptyset(&set); // 清空訊號集的內容
// 判斷 SIGINT 是否在訊號集 set 裡
// 在返回 1, 不在返回 0
ret = sigismember(&set, SIGINT);
if(ret == 0){
printf("SIGINT is not a member of set \nret = %d\n", ret);
}
sigaddset(&set, SIGINT); // 把 SIGINT 新增到訊號集 set
sigaddset(&set, SIGQUIT);// 把 SIGQUIT 新增到訊號集 set
// 判斷 SIGINT 是否在訊號集 set 裡
// 在返回 1, 不在返回 0
ret = sigismember(&set, SIGINT);
if(ret == 1){
printf("SIGINT is a member of set \nret = %d\n", ret);
}
sigdelset(&set, SIGQUIT); // 把 SIGQUIT 從訊號集 set 移除
// 判斷 SIGQUIT 是否在訊號集 set 裡
// 在返回 1, 不在返回 0
ret = sigismember(&set, SIGQUIT);
if(ret == 0){
printf("SIGQUIT is not a member of set \nret = %d\n", ret);
}
return 0;
}
執行結果:
訊號阻塞集( 遮蔽集、掩碼 )
訊號阻塞集也稱訊號遮蔽集、訊號掩碼。每個程式都有一個阻塞集,建立子程式時子程式將繼承父程式的阻塞集。訊號阻塞集用來描述哪些訊號遞送到該程式的時候被阻塞(在訊號發生時記住它,直到程式準備好時再將訊號通知程式)。
所謂阻塞並不是禁止傳送訊號, 而是暫緩訊號的傳送。若將被阻塞的訊號從訊號阻塞集中刪除,且對應的訊號在被阻塞時發生了,程式將會收到相應的訊號。
我們可以透過 sigprocmask() 修改當前的訊號掩碼來改變訊號的阻塞情況。
所需標頭檔案:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:
檢查或修改訊號阻塞集,根據 how 指定的方法對程式的阻塞集合進行修改,新的訊號阻塞集由 set 指定,而原先的訊號阻塞集合由 oldset 儲存。
引數:
how: 訊號阻塞集合的修改方法,有 3 種情況:
SIG_BLOCK :向訊號阻塞集合中新增 set 訊號集,新的訊號掩碼是 set 和舊訊號掩碼的並集。
SIG_UNBLOCK :從訊號阻塞集合中刪除 set 訊號集,從當前訊號掩碼中去除 set 中的訊號。
SIG_SETMASK :將訊號阻塞集合設為 set 訊號集,相當於原來訊號阻塞集的內容清空,然後按照 set 中的訊號重新設定訊號阻塞集。
set: 要操作的訊號集地址
若 set 為 NULL ,則不改變訊號阻塞集合,函式只把當前訊號阻塞集合儲存到 oldset 中。
oldset: 儲存原先訊號阻塞集地址
返回值 :
成功:0 ,
失敗:-1 ,失敗時錯誤程式碼只可能是 EINVAL ,表示引數 how 不合法。
注意:不能阻塞 SIGKILL 和 SIGSTOP 等訊號,但是當 set 引數包含這些訊號時 sigprocmask() 不返回錯誤,只是忽略它們。另外,阻塞 SIGFPE 這樣的訊號可能導致不可挽回的結果,因為這些訊號是由程式錯誤產生的,忽略它們只能導致程式無法執行而被終止。
示例程式碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char *argv[])
{
sigset_t set; // 訊號集合
int i = 0;
sigemptyset(&set); // 清空訊號集合
sigaddset(&set, SIGINT); // SIGINT 加入 set 集合
while(1)
{
// set 集合加入阻塞集,在沒有移除前,SIGINT 會被阻塞
sigprocmask(SIG_BLOCK, &set, NULL);
for(i=0; i<5; i++)
{
printf("SIGINT signal is blocked\n");
sleep(1);
}
// set 集合從阻塞集中移除
// 假如 SIGINT 訊號在被阻塞時發生了
// 此刻,SIGINT 訊號立馬生效,中斷當前程式
sigprocmask(SIG_UNBLOCK, &set, NULL);
for(i=0; i<5; i++)
{
printf("SIGINT signal unblocked\n");
sleep(1);
}
}
return 0;
}
執行結果:
可靠訊號的操作
從 UNIX 系統繼承過來的訊號( SIGHUP ~ SIGSYS ,前 32 個)都是不可靠訊號,不支援排隊(多次傳送相同的訊號 , 程式可能只能收到一次,可能會丟失)。
SIGRTMIN 至 SIGRTMAX 的訊號支援排隊(發多少次 , 就可以收到多少次 , 不會丟失),故稱為可靠訊號。
可靠訊號就是實時訊號,非可靠訊號就是非實時訊號。
signal() 函式只能提供簡單的訊號安裝操作,使用 signal() 函式處理訊號比較簡單,只要把要處理的訊號和處理函式列出即可。
signal() 函式主要用於前面 32 種不可靠、非實時訊號的處理,並且不支援訊號傳遞資訊。
Linux 提供了功能更強大的 sigaction() 函式,此函式可以用來檢查和更改訊號處理操作,可以支援可靠、實時訊號的處理,並且支援訊號傳遞資訊。
下面我們一起學習其相關函式的使用。
所需標頭檔案:
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
功能:給指定程式傳送訊號。
引數:
pid: 程式號。
sig: 訊號的編號,這裡可以填數字編號,也可以填訊號的宏定義,可以透過命令 kill -l ("l" 為字母 ) 進行相應檢視。
value: 透過訊號傳遞的引數。
union sigval 型別如下:
union sigval
{
int sival_int;
void *sival_ptr;
};
返回值:
成功:0
失敗:-1
int sigaction(int signum,const struct sigaction *act, struct sigaction *oldact );
功能:
檢查或修改指定訊號的設定(或同時執行這兩種操作)。
引數:
signum :要操作的訊號。
act : 要設定的對訊號的新處理方式(設定)。
oldact :原來對訊號的處理方式(設定)。
如果 act 指標非空,則要改變指定訊號的處理方式(設定),如果 oldact 指標非空,則系統將此前指定訊號的處理方式(設定)存入 oldact 。
返回值:
成功:0
失敗:-1
訊號設定結構體:
struct sigaction
{
/*舊的訊號處理函式指標*/
void (*sa_handler)(int signum) ;
/*新的訊號處理函式指標*/
void (*sa_sigaction)(int signum, siginfo_t *info, void *context);
sigset_t sa_mask;/*訊號阻塞集*/
int sa_flags;/*訊號處理的方式*/
};
sa_handler 、 sa_sigaction :訊號處理函式指標,和 signal() 裡的函式指標用法一樣,應根據情況給 sa_sigaction 、 sa_handler 兩者之一賦值,其取值如下:
SIG_IGN :忽略該訊號
SIG_DFL :執行系統預設動作
處理函式名:自定義訊號處理函式
sa_mask :訊號阻塞集
sa_flags :用於指定訊號處理的行為,它可以是一下值的 “ 按位或 ” 組合:
SA_RESTART :使被訊號打斷的系統呼叫自動重新發起(已經廢棄)
SA_NOCLDSTOP :使父程式在它的子程式暫停或繼續執行時不會收到 SIGCHLD 訊號。
SA_NOCLDWAIT :使父程式在它的子程式退出時不會收到 SIGCHLD 訊號,這時子程式如果退出也不會成為殭屍程式。
SA_NODEFER :使對訊號的遮蔽無效,即在訊號處理函式執行期間仍能發出這個訊號。
SA_RESETHAND :訊號處理之後重新設定為預設的處理方式。
SA_SIGINFO :使用 sa_sigaction 成員而不是 sa_handler 作為訊號處理函式。
訊號處理函式:
void (*sa_sigaction)( int signum, siginfo_t *info, void *context );
引數說明:
signum :訊號的編號。
info :記錄訊號傳送程式資訊的結構體,程式資訊結構體路徑: /usr/include/i386-linux-gnu/bits/siginfo.h ,其結構體詳情請點此連結。
context :可以賦給指向 ucontext_t 型別的一個物件的指標,以引用在傳遞訊號時被中斷的接收程式或執行緒的上下文
下面我們做這麼一個例子,一個程式在傳送訊號,一個程式在接收訊號的傳送。
傳送訊號示例程式碼:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
/*******************************************************
*功能: 發 SIGINT 訊號及訊號攜帶的值給指定的程式
*引數: argv[1]:程式號
argv[2]:待傳送的值(預設為100)
*返回值: 0
********************************************************/
int main(int argc, char *argv[])
{
if(argc >= 2)
{
pid_t pid,pid_self;
union sigval tmp;
pid = atoi(argv[1]); // 程式號
if( argc >= 3 )
{
tmp.sival_int = atoi(argv[2]);
}
else
{
tmp.sival_int = 100;
}
// 給程式 pid,傳送 SIGINT 訊號,並把 tmp 傳遞過去
sigqueue(pid, SIGINT, tmp);
pid_self = getpid(); // 程式號
printf("pid = %d, pid_self = %d\n", pid, pid_self);
}
return 0;
}
接收訊號示例程式碼如下:
[cpp] view plaincopy
#include <signal.h>
#include <stdio.h>
// 訊號處理回電函式
void signal_handler(int signum, siginfo_t *info, void *ptr)
{
printf("signum = %d\n", signum); // 訊號編號
printf("info->si_pid = %d\n", info->si_pid); // 對方的程式號
printf("info->si_sigval = %d\n", info->si_value.sival_int); // 對方傳遞過來的資訊
}
int main(int argc, char *argv[])
{
struct sigaction act, oact;
act.sa_sigaction = signal_handler; //指定訊號處理回撥函式
sigemptyset(&act.sa_mask); // 阻塞集為空
act.sa_flags = SA_SIGINFO; // 指定呼叫 signal_handler
// 註冊訊號 SIGINT
sigaction(SIGINT, &act, &oact);
while(1)
{
printf("pid is %d\n", getpid()); // 程式號
pause(); // 捕獲訊號,此函式會阻塞
}
return 0;
}
兩個終端分別編譯程式碼,一個程式接收,一個程式傳送,執行結果如下:
最後:
關注回覆“物聯網”即可獲取物聯網全套影片教程
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69914734/viewspace-2656155/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Linux系統程式設計之訊號中斷處理(上)Linux程式設計
- linux系統程式設計之訊號(一):中斷與訊號Linux程式設計
- linux系統程式設計之訊號(七):被訊號中斷的系統呼叫和庫函式處理方式Linux程式設計函式
- linux系統程式設計之訊號(二):訊號處理流程(產生、註冊、登出、執行)Linux程式設計
- Linux系統程式設計—訊號捕捉Linux程式設計
- Linux系統程式設計:訊號捕捉Linux程式設計
- Linux核心模組程式設計--中斷處理程式(轉)Linux程式設計
- 【Linux系統程式設計】Linux訊號列表Linux程式設計
- linux系統程式設計之訊號(五):訊號集操作函式,訊號阻塞與未決Linux程式設計函式
- PHP 訊號中斷系統PHP
- Unix環境程式設計之定時、訊號與中斷程式設計
- Linux系統程式設計(33)—— socket程式設計之TCP程式的錯誤處理Linux程式設計TCP
- Linux系統程式設計(22)——響應訊號Linux程式設計
- linux系統程式設計之訊號(三):訊號安裝、signal、kill,arise講解Linux程式設計
- 【linux】系統程式設計-1-程式、管道和訊號Linux程式設計
- 《Linux核心設計與實現》——中斷和中斷處理Linux
- Linux系統程式設計(20)——訊號基本概念Linux程式設計
- Linux系統程式設計(21)——訊號的產生Linux程式設計
- linux系統程式設計之錯誤處理:perror,strerror和errnoLinux程式設計Error
- linux系統程式設計之訊號(四):alarm和可重入函式Linux程式設計函式
- 訊號、系統與訊號處理邊角雜談
- Linux系統程式設計(35)—— socket程式設計之TCP伺服器的併發處理Linux程式設計TCP伺服器
- Linux系統程式設計(24)——訊號的生命週期Linux程式設計
- linux中的訊號處理與SROPLinux
- Linux下多執行緒程式設計與訊號處理易疏忽的一個例子(轉)Linux執行緒程式設計
- linux 訊號與處理Linux
- 中斷和中斷處理程式
- Linux訊號機制與訊號處理Linux
- Linux系統中對中斷的處理(學習筆記)Linux筆記
- Linux系統程式設計之程式間通訊方式:訊息佇列Linux程式設計佇列
- 大牛講解訊號與系統以及數字訊號處理
- 【linux】系統程式設計-6-POSIX標準下的訊號量與互斥鎖Linux程式設計
- 【原創】Linux中斷子系統(二)-通用框架處理Linux框架
- Linux系統程式設計之程式間通訊方式:管道(二)Linux程式設計
- Linux系統程式設計之程式間通訊方式:管道(一)Linux程式設計
- Linux SIGCHLD訊號處理LinuxGC
- Linux訊號處理機制Linux
- linux核心分析筆記----中斷和中斷處理程式【轉】Linux筆記