訊號是告知某個程序發生了某個事件的通知,也叫軟體中斷,通常是非同步發生的。
訊號的傳遞:a程序→b程序或者核心→某個程序。
當程序收到某一訊號,需要有個相應的處置(disposition),一般通過呼叫sigaction函式來設定對某個特定訊號的處置,並有三種選擇:
(1)為訊號提供乙個訊號處理函式,這種行為稱為捕獲訊號。但是這種行為無法捕獲sigkill和sigstop訊號。處理函式一般形式為:
void handler(int signo);函式名和引數名可以由自己指定。
(2)把某個訊號的處置設為sig_ign來忽略它。sigkill和sigstop無法忽略。
(3)把某個訊號的處置設為sig_dfl來啟用它的預設處置。比如sigchld和sigurg的預設處置是忽略。
由於sigaction函式呼叫複雜,unp卷一中將其進行改進,通過signal函式去呼叫完善它。
signal函式原型:void (*signal(int signo,void(*func)(int)))(int);
typedef簡化:typedef void sigfunc(int);
變為:sigfunc *signal(int signo,sigfunc *func);
signal函式對posix訊號處理可以總結為以下幾點:
(a)一旦設定了訊號處理函式 sigfunc*func,它便一直安裝著。
(b)在乙個訊號處理函式執行期間,正被遞交的訊號是阻塞的。
(c)同一訊號在遞交時預設不排隊,即使它在阻塞期間產生了多次,等到訊號被解阻塞之後也只被遞交一次。
(d)利用sigprocmask函式選擇性地阻塞或解阻塞一組訊號是可能的。
處理sigchld訊號:
當乙個子程序退出後,核心釋放該程序所有的資源,包括開啟的檔案,占用的記憶體等,並且會向它的父程序傳遞乙個sigchld訊號。
子程序雖然退出了,但核心依然為其保留一些狀態資訊(程序id,終止狀態,執行時間等),父程序可以通過wait和waitpid函式去獲取這些狀態資訊。
#includepid_t wait(int *statloc) ;
pid_t waitpid(pid_t pid,int *statloc,int options);
//均返回:成功返回程序id,失敗返回0或-1;
注意點:
wait和waitpid函式均返回兩個值:已終止程序的id,以及通過statloc指標返回的子程序終止狀態。
子程序終止狀態包括:正常終止,由某個訊號殺死,作業控制停止。
(a)當呼叫wait的程序沒有已終止的子程序,有乙個或多個子程序仍在執行,wait將阻塞到現有子程序第乙個終止為止;而waitpid可以通過設定引數options來控制是否要阻塞,當options=wnohang時,waitpid不會在沒有已終止子程序時候阻塞。
(b)waitpid的pid引數允許我們指定想等待的程序id,值-1表示等待第乙個終止的子程式。
有了wait和waitpid函式,就可以簡單的編寫sigchld訊號處理函式了
#include "unp.h"
void sig_chld(int signo)
wait和waitpid函式存在不僅僅是為了捕獲sigchld訊號,更是為了防止殭屍程序的產生。
來看兩個概念:孤兒程序與殭屍程序。
孤兒程序:故名思議,就是沒了爹的程序。準確的說,在子程序終止前,父程序先一步終止,子程序沒了父程序後,核心將其交由init程序,於是init程序變成了該子程序的新的父程序,這類子程序被稱為孤兒程序。
殭屍程序:子程序終止後,它的父程序沒有呼叫相應處置函式**子程序的狀態資訊(程序id,終止狀態,執行時間等),該類子程序沒有完全釋放資源就成了殭屍程序,init程序也不會成為殭屍程序的父程序,殭屍程序就像孤魂野鬼一樣存在核心中。
相比於孤兒程序,殭屍程序的危害更大。殭屍程序不僅浪費記憶體空間,而且會使可用的程序id越來越少(系統中的程序id是有限制的,殭屍程序保持自己的程序id不放,那麼該程序id就不能被其他新程序使用)。因此,為了防止殭屍程序的產生,需要父程序呼叫wait和waitpid函式去獲取子程序終止後的資訊,讓子程序徹底釋放資源。
考慮這麼一種情況:有多個子程序同時終止並且向父程序傳送sigchld訊號,父程序收到sigchld訊號後僅僅呼叫一次signal函式,相當於僅執行一次sig_chld函式,如果採用上述sig_chld函式的做法,只能清理這些終止子程序中某乙個程序,其他子程序就會變為殭屍程序,
所以為了防止這類現象產生,應該在sig_chld使用迴圈:
#include "unp.h"
void sig_chld(int signo)
迴圈內不用wait而用waitpid的原因是,waitpid可以通過wnohang設定為非阻塞,而wait在子程序尚未終止前,會一直阻塞到第乙個子程序終止為止,這樣會浪費時間。
處理被中斷的系統呼叫:
慢系統呼叫:可能永遠阻塞的系統呼叫(呼叫有可能永遠無法返回,比如多數網路支援函式)。當阻塞於某個系統呼叫的乙個程序捕獲某個訊號且相應訊號處理函式返回時,該系統呼叫可能返回乙個eintr錯誤。
例如處理accept函式:
for(;;)
重啟被中斷的系統呼叫適合:accept,read,write,select和open之類函式。不適合:connect函式。
最後總結下在網路程式設計過程中可能遇到的三個情況:
(1)當子程序終止時,必須捕獲sigchld訊號;
(2)當捕獲訊號時,必須處理被中斷的系統呼叫;
(3)sigchld的訊號處理函式必須正確編寫,使用waitpid避免留下殭屍程序。
UNP學習筆記
1.一般情況下,伺服器程序在呼叫accpet函式後處於睡眠狀態,它等待客戶的連線和核心對它的接受。tcp連線使用三路握手來建立 tcp三次握手發生在呼叫accept函式後 當握手完畢時,accept函式返回。2.三次握手的全過程實現 下面步驟建立乙個tcp連線 1 伺服器必須準備好接受外來的連線。這...
UNP卷一chapter20 廣播
型別ipv4 ipv6 tcpudp 所標識介面數 遞送到介面數單播y yyy乙個 乙個任播 y 尚沒有y 一組一組中的乙個 多播可選yy 一組一組中的全體廣播y y全體全體表中要點 i 多播支援在ipv4中是可選的,在ipv6中卻是必需的 ii ipv6不支援廣播。使用廣播的任何ipv4應用程式一...
UNP卷1 第一章(簡介)
1.時間獲取程式 服務端 srv.c include include include include include include include define sa struct sockaddr define maxline 1024 int main void if waitpid pid,...