1. 程式人生 > >linux 訊號阻塞和訊號未決

linux 訊號阻塞和訊號未決

訊號的“未決”是一種狀態,指的是從訊號的產生到訊號被處理前的這一段時間;
訊號的“阻塞”是一個開關動作,指的是阻止訊號被處理,但不是阻止信 號產生。

訊號的阻塞就是讓系統暫時保留訊號留待以後傳送。由於另外有辦法讓系統忽略訊號,所以一般情況下訊號的阻塞只是暫時的,只是為了 防止訊號打斷敏感的操作。
* 當你需要修改某些全域性變數時,你可以通過sigprocmask()函式阻塞處理函式中也使用該變數的訊號。
* 在某些訊號處理函式中,為了阻止同類訊號的到來,可以使用sigaction()函式的sa_mask阻塞特定的訊號。
11.7.1 阻塞訊號的作用
使用函式sigprocmask()阻塞訊號的傳遞,只是延遲訊號的到達。訊號會在解除阻塞後繼續傳遞。這種情況往往需要在信 號程式和其它程式共享全域性變數時,如果全域性變數的型別不是sig_atomic_t型別,當一部分程式恰好讀、寫到變數的一半發生訊號,而訊號程式裡會改 變該訊號,那麼就會產生混亂。為了避免這種混亂,提供程式的可靠性,你必須在操作這類變數前阻塞訊號,操作完成後恢復訊號的傳遞。

訊號阻塞也 用來處理必須保證連續操作的完整性方面。比如,你需要檢測一個標誌(可以是sig_atomic_t型別),該標誌在訊號程式中設定,當標誌沒有設定時可 以執行某個操作。假如恰好在檢測標誌後發生訊號,那麼訊號返回後,程式也會執行這個操作,即使已經設定了標誌。這顯然會引起程式的不穩定。最好的方法就是 在檢測標誌到執行操作之間阻塞訊號的發生。
11.7.2 訊號集
所有的訊號阻塞函式都使用稱作訊號集的資料結構來表明受到影響的訊號。每一個操作都包括兩個階段:建立訊號集,傳遞訊號集給特定的庫函式。下面說明訊號集 和相關的資料型別:
sigset_t:這個資料型別用來代表訊號的集合,有兩種方法對它進行初始化。一種是通過函式 sigemptyset()使之不包含任何訊號,然後用sigaddset()函式加入需要的訊號。另一種方法是通過函式sigfillset()使之包 含所以訊號,然後通過sigdelset()函式刪除我們不需要的訊號。注意,千萬不用試圖通過手工方式直接操作這種型別變數,否則會帶來嚴重的錯誤。下 面介紹相關的函式。

int sigemptyset(sigset_t *set):初始化訊號集set使之不包含任何訊號,這個函式總是返回0。
int sigfillset(sigset_t *set):初始化訊號集set使之包含所有的訊號,這個函式也是總返回0。
int sigaddset(sigset_t *set, intsignum):該函式把訊號signum加入到訊號集set中,需要注意的是這個函式只是修改了set變數本身,並不作其它操作。該函式成功操作 返回0,失敗返回-1,錯誤程式碼設定成EINVAL,表示signum不是有效的訊號程式碼。
int sigdelset(sigset_t *set, int signum):該函式從訊號集set中刪除訊號signum,其它方面和sigaddset()函式類似,不再贅述。

int sigismember(const sigset_t *set,intsignum):這個函式測試訊號signum是否包含在訊號集合set中,如果包含返回1,不包含返回0,出錯返回-1。錯誤程式碼也只 有一個EINVAL,表示signum不是有效的訊號程式碼。
11.7.3 程序的訊號掩碼
我們稱正在阻塞的訊號的集合為訊號掩碼(signal mask)。每個程序都有自己的訊號掩碼,建立子程序時子程序將繼承父程序的訊號掩碼。我們可以通過修改當前的訊號掩碼來改變訊號的阻塞情況。
int sigprocmask(int how, const sigset_t *set,sigset_t *oldset),該函式用來檢查和改變呼叫程序的訊號掩碼,其中的how引數指出訊號掩碼改變的方式,必須是下面的值之一:
SIG_BLOCK,阻塞set中包含的訊號。意思是說把set中的訊號加到當前的訊號掩碼中去,新的訊號掩碼是set和舊訊號掩碼的並集。
SIG_UNBLOCK,解除set中訊號的阻塞,從當前訊號掩碼中去除set中的訊號。
SIG_SETMASK,設定訊號掩碼,既按照set中的訊號重新設定訊號掩碼。
最後一個引數是程序原來的訊號集。如果你只需要改變訊號的阻塞情況而不需要關心原來的值,可以傳遞NULL指標給函式。如果你希望什麼也不改變,只是想獲 得當前訊號掩碼的資訊,那麼把set設定成NULL,old中返回當前的設定。
sigprocmask()函式成功返回0,失敗返回-1。失敗時錯誤程式碼只可能是EINVAL,表示引數how不合法。
不能阻塞 SIGKILL和SIGSTOP等訊號,但是當set引數包含這些訊號時sigprocmask()不返回錯誤,只是忽略它們。另外,阻塞SIGFPE這 樣的訊號可能導致不可挽回的結果,因為這些訊號是由程式錯誤產生的,忽略它們只能導致程式無法執行而被終止。
11.7.4 舉例:禁止關鍵程式碼時訊號到達
假定你建立訊號SIGALRM的處理函式,在其中設定一個標誌。主程式中檢查標誌並清除,使用函式sigprocmask()控制訊號到達:
#include
volatile sig_atomic_t flag=0;

int
main(void)
{
sigset_t block_alarm;
... ...
sigemptyset(&block_alarm);
sigaddset(&block_alarm,SIGALRM);
while(1)
{
sigprocmask(SIG_BLOCK,&block_alarm,NULL);
if(flag)
{
... ...
flag=0;
}
sigprocmask(SIG_UNBLOCK,&block_alarm,NULL);
... ...
}
}
11.7.5 在訊號控制代碼中阻塞訊號
訊號控制代碼執行時,如果你希望從頭至尾不會被其它訊號打擾,那麼你必須阻塞其它訊號。當一個訊號控制代碼啟用時,相同的訊號自動被阻塞,但是其它訊號並不會阻 塞,最可靠的方法是使用sigaction結構的sa_mask成員。例如:
#include
#include

void catch_stop();

void
install_handler(void)
{
struct sigaction setup_action;
sigset_t block_mask;
sigemptyset(&block_mask);
sigaddset(&block_mask,SIGINT);
sigaddset(&block_mask,SIGQUIT);
setup_action.sa_handler=catch_stop;
setup_action.sa_mask=block_mask;
setup_action.sa_flag=0;
sigaction(SIGINT,&setup_action,NULL);
}
這個方法比在訊號函式中使用 sigprocmask()函式可靠,因為使用sigprocmask()函式起碼無法避免在訊號函式開始的訊號不被阻塞的間隙。使用這個機制你不能從當 前訊號掩碼中刪除訊號,但是,在訊號函式當中,你可以使用sigprocmask()阻塞訊號。在任何情況下,訊號控制代碼返回時,系統重新裝入進入訊號控制代碼 之前的訊號掩碼。當訊號控制代碼一返回,訊號控制代碼執行時被阻塞的訊號就會立即到達,甚至在返回被中斷的程式碼前就要進入新的訊號處理控制代碼。
11.7.6 查詢阻塞的訊號
你能夠使用函式sigpending()來查詢系統中何種訊號有正在被阻塞的訊號。
int sigpending(sigset_t *set),該函式把存在阻塞訊號的訊號型別放置到set中,可以使用sigismenber()函式判斷某種訊號是否是set的成員。該函式成功返回 0,失敗返回-1。
下面舉例說明:
#include
#include

sigset_t base_mask,wait_mask;

sigemptyset(&base_mask);
sigaddset(&base_mask,SIGINT);
sigaddset(&base_mask,SIGTSTP);
sigprocmask(SIG_SETMASK,&base_mask,NULL);
... ...
sigpending(&waiting_mask);
if(sigismember(&waiting_mask,SIGINT))
{
//do something, if user try killing the process
}
else if(sigismember(&waiting_mask,SIGTSTP))
{
//user tring to stop process
}
對於同一種訊號,如果有被阻塞的訊號存在,那麼其它到來的訊號就會被丟棄,而不是被阻塞。
11.7.7 訊號阻塞的代替方法
我們可以採用在訊號處理程式中讀取其它程式進入關鍵程式碼前設定的標誌來判斷是否進行處理,如果不能進行處理,則在一個公共變數中設定不能進行處理的訊號的 值,關鍵程式碼執行完成後再重新發送該訊號,請看下面的例子:
volatile sig_atomic_t signal_pending;
volatile sig_atomic_t defer_signal;

void
handler(int signum)
{
if(defer_signal)
signal_pending=signum;
else
...
}

...
void
update_number(int frob)
{
defer_signal++;
...
defer_signal--;
if(defer_signal==0&&signal_pending!=0)
raise(signal_pending);
}

訊號是unix處理非同步事件的經典方法。產生訊號的方法一般有:
        使用者按中斷鍵、硬體異常訊號、軟體異常訊號、使用者的kill命令等。系統也可以有多種方式處理這些非同步 事件,比如:忽略訊號、捕捉訊號或者執行預設動作。
        大多數unix程式都是用信core檔案來檢查程序終止時候的狀態的。 core檔案就是對於該檔案的程序儲存映像進行復制。

一定要記住這兩種情況:
   1)當程序啟動的時候,所有訊號的狀態就是系統預設或者忽略。除非呼叫exec動作。
     因為exec動作,將原先設定為要捕捉的訊號為預設動作,道理很簡單,就是exec要用新的程序地址空間覆蓋舊的,自然原先的訊號就已經被遮蔽了。
   2)開啟一個子程序的時候,子程序繼承父程序的訊號處理方式。因為子程序一開始啟動就複製了父程序的儲存映像,訊號捕捉函式地址在父子程序中都是有意義 的。

signal的訊號處理方式帶有好多的缺陷,比如,在產生訊號和呼叫signal訊號處理程式之間有一個時 間視窗,如果再來一個訊號可能丟失。等等。

在執行慢速的系統呼叫的時候,程序很可能阻塞幾個小時或者數天,如果程序在執行一個低速系統呼叫期間捕捉到 一個訊號的時候,那麼該系統呼叫就被中斷不在繼續執行。因為一個訊號發生了,這就意味著發生了某種事情,所以這是喚醒被阻塞的程序的好機會。
   為了幫助應用程序不必處理被總段的系統呼叫,linux系統引入了某些被中斷系統呼叫的自動重啟函 數,包括:Ioctl,read,readv,write,writev,wait和waitpid。前5個只對低速裝置呼叫才有效,而後兩個只對捕捉到 程序退出訊號時才有效。可重入函式主要用於多工環境中,一個函式是可重入與否,簡單的說就是否可以被中斷。假如一個連結串列,如果被中斷則會發生莫名的錯誤,其執行序列是不可預測的。
    訊號發生的時候到遞送之間的時間間隔,我們稱為訊號時未決的。在未決的訊號期間,如果我們阻塞了某些訊號,那麼,即使我們產生訊號,這些都會被阻塞。除非 等解除阻塞以後,系統才會處理這些訊號。而且,阻塞期間多次產生的同一個訊號,解除阻塞後,系統處理且只處理一次。

引用原文:
1. http://www.hadoopor.com/redirect.php?tid=1156&goto=lastpost
2. http://www.groad.net/bbs/read.php?tid-920.html

相關推薦

linux 訊號阻塞訊號未決

訊號的“未決”是一種狀態,指的是從訊號的產生到訊號被處理前的這一段時間;訊號的“阻塞”是一個開關動作,指的是阻止訊號被處理,但不是阻止信 號產生。訊號的阻塞就是讓系統暫時保留訊號留待以後傳送。由於另外有辦法讓系統忽略訊號,所以一般情況下訊號的阻塞只是暫時的,只是為了 防止訊

三十四、Linux 程序與訊號——訊號特點、訊號訊號遮蔽函式

34.1 訊號特點 訊號的發生是隨機的,但訊號在何種條件下發生是可預測的 程序槓開始啟動時,所有訊號的處理方式要麼預設,要麼忽略;忽略是 SIGUSR1 和 SIGUSR2 兩個訊號,其他都採取預設方式(大多數是終止程序)。 程序在呼叫 exec 函式後,原有訊號的捕捉函式失效 子程序的誕

linux系統——程序訊號

一、程序 ** 1、啟動新執行緒 ** #include int system(const char *string); 比如:system(“pa ax &”) , 相當於在shell內呼叫ps sx &(&是後臺執行的意思) 函式返回

linux阻塞阻塞的區別

所謂阻塞方式block,顧名思義,就是程序或是執行緒執行到這些函式時必須等待某個事件的發生,如果事件沒有發生,程序或執行緒就被阻塞,函式不能立即返回)。 所謂非阻塞方式non-block,就是程序或執行緒執行此函式時不必非要等待事件的發生,一旦執行肯定返回,以返回值的不同來

linux訊號阻塞未決

執行訊號的處理動作稱為訊號遞達(Delivery),訊號從產生到遞達之間的狀態,稱為訊號未決(Pending)。 程序可以選擇阻塞(Block)某個訊號。被阻塞的訊號產生時將保持在未決狀態,直到程序解除對此訊號的阻塞,才執行遞達的動作。注意,阻塞和忽略是不同,只要訊號被阻塞

linux系統程式設計之訊號(三):訊號阻塞未決

/*************************************************************************     > File Name: process_.c     > Author: Simba     > Mail: [email 

Linux多執行緒程式設計---執行緒間同步(互斥鎖、條件變數、訊號讀寫鎖)

本篇博文轉自http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數、訊號量和讀寫鎖。  下面是思維導

linux 詳細訊號列表及Linux訊號訊號

轉自: http://blog.51cto.com/vabc1314/1844888   SIGHUP     終止程序     終端線路結束通話[喝小酒的網摘]http://blog.hehehe

linux核心態使用者態的訊號

在Linux的核心態和使用者態都有訊號量,使用也不同,簡單記錄一下。 1> 核心訊號量,由核心控制路徑使用。 核心訊號量是struct semaphore型別的物件,它在<asm/semaphore.h>中定義 struct semaphore {    

linux中使用訊號--sigwait pthread sigmask

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

linux 訊號signalsigaction理解

今天看到unp時發現之前對signal到理解實在淺顯,今天拿來單獨學習討論下。   signal,此函式相對簡單一些,給定一個訊號,給出訊號處理函式則可,當然,函式簡單,其功能也相對簡單許多,簡單給出個函式例子如下:     1 #incl

linux訊號處理 (訊號產生 訊號阻塞 訊號集)

1.0 定義 訊號(signal)是Linux程序間通訊的一種機制,全稱為軟中斷訊號,也被稱為軟中斷。訊號本質上是在軟體層次上對硬體中斷機制的一種模擬。它提供了一種處理非同步事件的方法,也是程序間惟一的非同步通訊方式。體現為作業系統修改了目標程序的PCB內容,即為對其傳送了訊號。 2.0 訊

linux中的訊號簡介trap命令

1.訊號 linux通過訊號來在執行在系統上的程序之間通訊,也可以通過訊號來控制shell指令碼的執行 主要有一下訊號 1 ##程序重新載入配置 2 ##刪除程序在記憶體中的資料 3 ##刪除滑鼠在記憶體中的資料 9

Linux】程序間通訊之訊息佇列、訊號共享儲存

訊息佇列、訊號量和共享儲存是IPC(程序間通訊)的三種形式,它們功能不同,但有相似之處,下面先介紹它們的相似點,然後再逐一說明。 1、相似點 每個核心中的IPC結構(訊息佇列、訊號量和共享儲存)都用一個非負整數的識別符號加以引用,與檔案描述符不同,當一個

linux高階訊號傳送接收(附帶訊息)

高階訊號傳送和接收  sigaction   1.可以讓訊號處理函式遮蔽訊號集   2.可以讓訊號處理函式在訊號響應的同時接收訊號源程序資訊和附帶訊息。  sigqueue   可以傳送訊號並同時傳送附帶訊息。   使用kill函式傳送訊號,sigaction可以接收到訊號

Linux Signal (6): 傳送訊號的killraise 函式

1. 函式說明: kill和raise是用來發送訊號的: kill把訊號傳送給程序或程序組,它不僅可以中止程序,也可以向程序傳送其他訊號; raise把訊號傳送給(程序)自身. 它們的原型如下: #include <signal.h> #include <s

Linux 程序通訊之 ——訊號訊號量總結

現在最常用的程序間通訊的方式有:訊號,訊號量,訊息佇列,共享記憶體。       所謂程序通訊,就是不同程序之間進行一些"接觸",這種接觸有簡單,也有複雜。機制不同,複雜度也不一樣。通訊是一個廣義上的意義,不僅僅指傳遞一些massege。他們的使用方法是基本相同的,所以只要

Linux核心同步機制之訊號互斥體

訊號量:訊號量(semaphore)是程序間通訊處理同步互斥的機制。是在多執行緒環境下使用的一種措施,它負責協調各個程序,以保證他們能夠正確、合理的使用公共資源。 它和spin lock最大的不同之處就是:無法獲取訊號量的程序可以睡眠,因此會導致系統排程。原理訊號量一般可以用

Linux訊號機制分析訊號處理函式

【摘要】本文分析了Linux核心對於訊號的實現機制和應用層的相關處理。首先介紹了軟中斷訊號的本質及訊號的兩種不同分類方法尤其是不可靠訊號的原理。接著分析了核心對於訊號的處理流程包括訊號的觸發/註冊/執行及登出等。最後介紹了應用層的相關處理,主要包括訊號處理函式的安裝、訊號

linux中訊息佇列kfifo訊號量sem_t的用法

使用kfifo和sem_t配合來實現訊息佇列:由sem來管理目前可以傳送和接收的總的訊息數,由kfifo來儲存訊息。具體實現起來就是定義訊號量sem_t_send和sem_t_recv,sem_t_send設為max_num,sem_t_recv設為0。