訊號是軟體中斷。訊號提供了一種處理一步事件的方法。
1、訊號的概念
產生訊號的條件:
(1)當使用者按某些終端鍵時,引發終端纏身的訊號。(如ctrl+c通常產生中斷訊號(sigint) ctrl+\通常產生中斷訊號(sigquit))。
(2)硬體異常產生訊號。除數為0、無效的記憶體引用等等。這些條件通常由硬體檢測到,並將其通知核心,然後核心為該條件發生時正在執行的程序產生適當的訊號。
(3)程序呼叫kill(2)函式可將訊號傳送給另乙個程序或程序組。
(4) 使用者可用kill(1)命令將訊號傳送給其他程序。(常用此命令終止乙個失控的後台程序)。
(5)當檢測到某種軟體條件已經發生,並應將其通知有關程序是也將發生訊號。l例如sigurg(在網路連線上傳來帶外資料時產生)、sigalrm(程序所設定的鬧鐘時鐘超時時產生)。
訊號的處理:
當乙個訊號出現時,要求核心對該訊號進行處理。
(1)忽略此訊號。大多數的訊號都可以使用這種處理方式,但sigkill和sigstop這兩種訊號是絕不能被忽略的。
(2)捕捉訊號。核心呼叫使用者自定義函式來處理這種訊號(這種行為稱為註冊乙個訊號處理函式)。
(3)執行系統預設動作(apue表10-1)。
2、signal函式
typedef
void
(*sighandler_t)(
int);
sighandler_t signal(
intsigno, sighandler_t handler);
先從引數看起,第乙個引數是乙個整型的訊號編號,是為哪乙個訊號設定處理動作;第乙個引數的型別是乙個指向引數為int無返回值的函式的函式指標;這個就是我們要為signo訊號註冊動作,三種處理方式分別對應的是sig_dfl(表示接到此訊號後的動作是系統預設動作) sig_ign(向核心表示忽略此訊號) 和 乙個自定義的函式位址(在訊號發生時呼叫此函式,稱為這種處理為「捕捉」該訊號)。這樣我們就改變了乙個訊號的處理動作,再看返回值,型別是乙個相同型別的函式指標,可以很容易推測出應該是signo訊號更改之前的處理動作,也就是說可以通過返回值來儲存更改之前的動作,當然如果函式執行失敗,講返回sig_err。
這裡需要注意的是,signal()僅僅是乙個註冊功能的函式,除了註冊意外不做任何其他的事情,註冊後,當訊號產生時程序會自動執行註冊的動作。整個過程是這樣的:當訊號到來時,程式從執行位置暫停,去執行該訊號的處理動作,處理動作執行完畢後(如果動作不會使程序退出)返回暫停位置繼續執行。
訊號的產生條件之前說過,那如何讓程序等待乙個訊號呢?可以使用sleep()函式,但問題是在sleep()時如果到來乙個訊號,sleep()函式就會被打斷並返回剩餘的時間,解決方法是使用迴圈,當sleep()的返回值為0時跳出迴圈 while ( (t=sleep(t)) != 0 ) 。當然這樣做也無法做到讓程式一直等待,替換函式式pause(),檢視這個函式的幫助文件可以看到這個函式式專門用來等待乙個訊號的。
下面的幾點需要清楚:
(1).多個訊號是可以註冊相同的處理函式,處理函式的整型引數就是引起該函式執行的訊號編號。
(2).在訊號處理函式執行過程中,接收到另乙個訊號,則仍會中斷去執行另乙個訊號的處理動作,執行後回來繼續執行。
(3).在訊號處理函式執行過程中,再次接受到相同的訊號,並不會立刻去執行,而是訊號處理函式執行結束後,再次執行處理函式。
(4).在訊號處理函式執行過程中,n次接受到相同的訊號,在訊號處理函式執行結束後情況分為兩類:如果是非實時訊號則只再執行一遍處理函式,如果是實時訊號則執行n次處理函式。(實時訊號講會在後面詳細介紹)
(5).子程序會繼承父程序的訊號處理方式,之後父子程序中乙個改變訊號的處理方式不再影響另乙個
(6).來自終端的訊號(如ctrl+c)將會傳送給前台程序組中的所有程序
(7).執行exec()後,訊號處理動作不會被新的程式所程序,所有的訊號的處理動作恢復為預設。(位址空間變了)
3、sigchld訊號,
程序中說到過子程序退出後需要父程序對其收屍,父程序就是通過子程序在退出時向父程序傳送的sigchld訊號得知子程序已經退出(wait()接觸阻塞)。那思考乙個問題,在程序部分編寫程式時,如果想收屍就需要使用wait()函式,但問題時 wait()會使程序阻塞而不能做其他事情,解決辦法就是在sigchld訊號上做些文章,我們可以將wait()函式放到sigchld的訊號處理函式中,這樣當乙個子程序退出時父程序就會自動執行wait(),而不用再wait()處阻塞著以至於父程序無法去做別的事情。這樣做很簡單呢,但心細的同學還會發現乙個問題是:如果說sigchld的訊號處理函式在執行過程中如果又接受到了2個sigchld(又有兩個子程序退出),那只會再執行一遍訊號處理函式(即只執行一遍wait()),這樣並不能保證所有子程序都會被收屍。既然wait()不行我們就想到了waitpid()這個函式,這個函式提供了很多wait()做不到的方法,還記得wnohang引數麼,提供了非阻塞版本。那好先回過來再思考下出現的問題,在執行一次處理函式的過程有很多子程序需要收屍(但有一些sigchld已經丟失),既然是這樣,我們就用waitpid()迴圈收屍將這些屍體全部收了,並且wnohang可以使waitpid()非阻塞,這樣在收屍完畢後waitpid()會返回0,我們就可以根據這個條件退出迴圈了。
[cpp]view plain
copy
void
sig_chld(
intsigno)
} 我們來總結下,避免出現殭屍程序的三種方法:
(1).使用wait() waitpid()這樣的函式來手動收屍。包括上面提到的waitpid()放到處理函式中。
(2).更改sigchld訊號的處理方式為忽略,這樣就不會產生殭屍程序。還可以在sigaction()中設定sa_nocldwait標誌位。
(3).利用兩次fork()的方法,子程序在第二次fork()後立即退出,這樣孫程序就成為了孤兒程序,會由init程序自動收屍。
4、可重入函式
這個概念是針對訊號的(執行緒安全是針對執行緒的,容易弄混)。函式a出現在訊號處理中時,如果處理函式為結束,又被打斷去執行另乙個處理函式,該處理函式中也呼叫了函式a,如果不會出現問題,則該函式是可重入的。不可重入的函式特點,使用了全域性變數、靜態變數等,如getpwnam()這樣的函式返回乙個靜態字串的位址就是不可重入的,這樣的函式往往提供了可重入版本getpwname_r()
給webview新增自己的訊號
比如用qtabwidget.addtab qwebview 新增了很多webview的時候,又想知道時每個webview載入網頁是否finished。connect webview,signal loadfinished tabwidget,slot slotwebloadfinished 裡面無法...
linux中的訊號1 訊號概述
以下內容源於朱有鵬 物聯網大講堂 課程的學習整理,1 訊號的目的 用來程序間通訊 ipc 程序和核心間的通訊 2 訊號是非同步的 類似於軟體中斷,對比硬體中斷,如按鍵,不知道什麼時候按下 3 訊號本質上是 int型 數字編號 每個編號有特定的含義 內容是十分有限的。1 使用者在終端按下按鍵 2 硬體...
APUE之訊號中的一些基本概念
在了解訊號機制前首先來解釋一下關於同步和非同步的區別,這裡舉乙個通俗易懂的例子就容易理解同步與非同步的概念了。同步 就是你請我吃飯,我聽到了就和你吃飯,如果沒有聽到,你就不停的叫,直到我告訴你聽到了,才一起去吃飯。非同步 就是你叫我,然後自己去吃飯,我得到訊息後可能立即走,也可能等到下班才去吃飯 訊...