信號: 產生->保存->處理
一、預備知識
信號vs信號量->沒有任何關系
什么叫做信號?
中斷我們正在做的事情,是一種事件的異步通知機制。
同步和異步理解:
同步指事件發生具有一定的順序性(如命名管道中服務端讀方式打開會阻塞,直到客戶端的寫入打開),異步指事情發生沒有順序性(如共享內存的讀取和寫入沒有順序性)。
信號的產生相對于進程的運行是異步的。
信號是發送給進程的。
基本結論:
1.信號處理,進程在信號沒有產生的時候,早就知道信號該如何處理了。
2.信號的處理,不是立即處理,而是可以等一會在處理,合適的時候,進行信號的處理。
3.人能識別信號,是被“提前教育”過的,進程也是如此,OS程序員設計的進程,進程早已內置對于信號的識別和處理方式。
4.信號源非常多->給進程產生信號的,信號源,也非常多。
SIGINT:鍵盤產生信號。
收到信號,處理信號(進程收到信號,合適的時候處理信號):
1.默認處理動作
2.自定義信號處理動作
3.忽略處理
自定義捕捉系統調用:
sighandler_t 實質上等同于void(*)(int)
sighandler_t signal(int signum, sighandler_t handler);
參數:
signum:信號編號
handler: 自定義的信號處理方式,當收到對應的信號,就回調執行handler方法。
返回值:成功返回handler,失敗返回SIG_ERR(一個宏,通常定義為 (void (*)(int))-1,表示一個無效的函數指針),錯誤碼被設置。
二、信號的產生
1.鍵盤產生信號
ctrl+C向前臺進程發送2號信號。
相當一部分信號的處理動作就是讓自己終止。
前臺進程和后臺進程:
./xxx 前臺進程 ->鍵盤產生的信號,只能發送給前臺進程。
./xxx & 后臺進程
前臺進程只能有一個,本質上前臺進程就是要從標準輸入獲取數據的。
后臺進程可以有多個。
前后臺移動命令:
jobs-l 查看當前終端會話中通過?
&
?或?Ctrl+Z
?掛起的后臺任務
fg 任務號? 特定的進程,提到前臺
ctrl+z 進程切換到后臺,并且暫停bg 任務號? 讓指定的后臺進程進行回復運行
什么叫做給進程發送信號?
信號產生之后,不是立即處理的,所以要求進程把信號記錄下來。
發送信號本質:向目標進程寫信號,修改位圖!
信號記錄在task_struct中,是一個位圖結構->屬于操作系統的數據結構對象->修改位圖本質就是修改內核的數據->不管信號怎么產生,發送信號,在底層,必須讓OS發送。
特別注意:不是所有信號都能被自定義捕捉。
2.系統調用產生信號
int kill(pid_t pid, int sig);? ? //給目標進程發送信號
raise給自己發送信號,abort給自己進程發送終止信號(發送6號新號,要求進程必須處理,默認終止)。
3.系統指令kill產生信號
4.產生信號的方式(硬件異常)
常見的硬件異常:
除0,野指針。
信號全部都是操作系統發送的!
操作系統怎么知道犯錯了?->操作系統是軟硬件資源的管理者->CPU,寄存器,狀態寄存器(標志寄存器:EFLAGS)可以判斷溢出
野指針異常->cpu里的MMU將虛擬地址轉化成物理地址失敗了->硬件報錯
5.產生信號的方式---軟件條件
進程--管道--進程,在匿名管道,如果讀端關閉,寫端繼續,操作系統就會發送SIGPIPE信號來終止寫端進程,OS不做沒有意義的事情。
調用alarm函數可以設定?個鬧鐘,也就是告訴內核在 seconds 秒之后給當前進程發SIGALRM 信號,該信號的默認處理動作是終止當前進程。這個函數的返回值是0或者是以前設定的鬧鐘時間還余下的秒數。例如:alarm(5)->過5秒終止,返回值是0alarm(5)->過3秒,設alarm(10)->過10秒終止了,返回值為2,即以前設定鬧鐘剩余的秒數pause函數作用:暫停,等待信號到來,直到信號處理動作結束,pause函數才會返回。操作系統本質:死循環pause,等待信號到來,每隔一段時間,CPU向操作系統發送硬件中斷。如何理解鬧鐘:時間片本質就是一個計數器。鬧鐘理解:鬧鐘的結構里存有過期時間,組織方式可以理解成最小堆,將當前操作系統記錄的時間戳與鬧鐘堆頂的時間戳進行比較,如果堆頂的時間戳大于等于操作系統當前記錄的,就向對應的進程發送SIGALRM信號,同時把堆頂刪除。