信號的產生
信號就是操作系統對用戶操作做出的反應,但它的本質就是往操作系統寫入信號,這是由操作系統的結構決定的。通過修改比特位來告訴操作系統接收信號和傳了幾號信號。
也正是因為我們身為用戶無法親自修改內核數據,所以我們需要通過操作系統提供的系統調用來讓操作系統幫我們做修改操作。
kill -l
我們可以通過kill -l來查看系統提供的命令列表
但是并非所有信號我們都能用到,一般情況下,我們只會用1-31號信號,這也是為什么我們程序正常結束是返回0了,因為0號信號不存在,也就不會報錯。
signal
如果我們想要自定義信號的轉化方式,那么必須是這樣空返回值,一個整型參數的函數
這個整型參數就是用來接收轉化前收到的信號的。
這樣當我們遇到了sigint時就會轉化成對我們handlersig的調用
那么當我們運行這個文件時,一旦我們想要ctrl+c終止時,就會被轉化成對handlersig的調用
可是新的問題來了,我們要是所有信號都自定義了,那么會不會永遠退不出來呢,其實不會的,因為操作系統為了防止惡意進程專門設置了一個kill -9信號,它無法被自定義,所以我們可以用它來終止進程。
這樣進程依舊被我們終止了。
我們也可以重寫一個直接用來刪除的文件
上面這一類我們都可以歸類于是鍵盤產生的信號。
系統調用信號
這個理解起來挺簡單的,我們調用的每一個系統函數都會有信號產生,不過我們可以通過系統調用了解一下系統的信號是怎么產生的。
raise
abort
這個系統函數會強制讓信號執行它的默認操作,這樣即使我們捕捉了對應信號想要執行自定義操作也不行了。
異常產生信號
信號的產生還有一種,就是異常。如果我們的進程有什么問題,也會觸發異常信號,比如說 /0 或者說野指針問題。
可以看到 /0的異常是8號信號,而野指針則是11號信號。
那么操作系統是怎么知道這些問題的呢。
首先操作系統管理著軟硬件,而硬件里的寄存器會記錄當前進程的狀態,那么一旦狀態出了問題,它就會通過上下文和task_struct找到進程然后發送對應的異常信號。
野指針問題也差不多,它是屬于CR3存儲的目錄地址和虛擬地址給MMU轉化后沒有對應的映射關系,所以失敗了,那么寄存器同樣記錄異常狀態然后返回給進程。
alarm以及pause
alarm就單純是個鬧鐘而已。
通過捕捉鬧鐘信號,實現個鬧鐘死循環。
pause則是只有信號返回時它才有反應,否則就一直暫停著。
這倆單看都沒啥用的感覺,但是結合起來,它就接近操作系統的本質了。
操作系統本身也是一個死循環,只有收到對應信號才會做出對應的操作,是不是沒事的時候就像上面的pause暫停,有事的時候就像接收到鬧鐘信號做出反應。不過操作系統里的鬧鐘可能是一個結構體,存儲著對應任務的觸發時間和優先級,再由堆管理起來,這樣操作系統拿堆頂的進程開始運行,這樣就知道什么時候運行上面進程又返回什么信號。
以上就是信號產生的過程。
信號的保存
信號的保存又分為三種情況 遞達、未決、阻塞
遞達
遞達就是操作系統成功接收之后的狀態。
遞達又分為自定義、默認、忽略
自定義就是我們之前的signal把捕捉的信號自定義成我們想要的方法。
默認就是不捕捉,按照信號表里的方法執行信號操作。
忽略則是雖然我接收了信號,但是無視你 你哪里涼快哪里待著。
未決
未決指的是信號產生到遞達前的狀態。
阻塞(屏蔽)
阻塞是我專門在你遞達之前卡住你,等我有空處理你的時候再讓你遞達。
它和忽略是前后者的關系,并不相同,忽略是已遞達但是不處理,阻塞是我可能暫時沒空接收你,你先別等著。
block pending handler
操作系統用三個表來標識上面的狀態
橫著看來判斷X號信號屬于什么情況。
比如一號信號已被接收未阻塞執行方法為默認。
既然有這張表那么操作系統就為我們提供了一個函數來修改它.
sigprocmask
這個函數可以修改block表內的數據
第一個是我們用什么方法修改,第二個則是根據第一個參數給,第三個是為了防止你改錯了改不回來,所以需要提供一個容器存舊數據。
第一個參數block是直接新增信號到block表里。
第二個則是通過先取反再與的操作修改信號。
第三個是我們直接提供整張修改后的block表覆蓋舊表。
實際上用起來第三種才方便。
這里的sigset_t 就是容器的類型,它是一個系統定義的結構體。我們把它初始化后作為參數傳入
sigprocmask里,這樣就設置出了一個全部不阻塞的block表,但是這些操作都只在當前作用域
所以我們需要使用sigaddset把它添加到信號集內,我們可以理解為上傳存檔。
然后我們就可以用sigpending查看它的數據變化了,一旦我們傳入任何一個信號
那么它的全0就會被改變。
最后我們輸入2號信號,我們可以看到一開始為全為0的pending表第二位變成了1,這就代表它成功接收到了我們的2號信號。
sigpending
pending表只支持查看,我們同樣傳入一個容器,它會把數據拷貝進容器然后返回,至于為什么不支持修改,因為我們的信號產生那么多種方式,我們大可以直接用鍵盤異常這樣的修改。