信號
- 2020 年 5 月 21 日
- 筆記
- c++學習, linux系統學習過程
項目 | 內容 |
---|---|
這個作業屬於哪個課程 | <班級課程的主頁鏈接 > |
這個作業的要求在哪裡 | <作業要求鏈接接地址> |
學號-姓名 | 17043211-方翔 |
作業學習目標 | 1.了解信號的概念 2.掌握信號處理的方法 |
1. 編寫一個簡單的程序並運行,然後向該進程發送不同的信號以觀察該進程對接收到信號的反應。
在終端編譯並運行該程序:
1.我們可以在當前終端通過按鍵組合向該進程發送信號 CTRL+C 、 CTRL+Z 、 CTRL+\ ,在每一次運行該程序的時候分別通過按鍵發送不同的信號來觀察進程的反應。這裡三個按鍵組合說明要發送三個信號,所以我們要運行該程序三次,然後每次使用不同的按鍵組合來觀察。
2.另外再開啟一個終端,在終端通過輸入 kill 命令來給進程發送信號,進程的 pid 在程序運行的第一行輸出,每次運行程序的時候 pid 是不同的。我們可以在終端通過輸入 kill -l 來查看當前系統當中的信號列表:
我們重新運行程序
在當前終端我們可以看到程序輸出自己的 pid 是4662,這是我們可以另開一個終端,通過 kill 命令向該進程發送信號:
這裡我們發送了信號值為9的信號給了進程2252.再切換到運行程序的終端來觀察進程接收到信號後的反應:
多次運行該程序然後發送不同的信號相應的進程來觀察進程對接收到的信號的反應。對於 kill 命令我們可以查看手冊: man kill
3.我們編寫一個簡單的程序,該程序調用 kill() 函數向某個進程發送信號
我們繼續運行之前的程序等待有信號到來:
在另外一個終端完成上面程序的輸入並編譯,運行的時候把7559作為參數。
再返回之前運行 hellosignal 的終端觀察進程接收到信號的反應:
我們可以查看手冊看看 kill() 函數: man 2 kill
2.使用 signal() 函數來捕捉信號。
通常進程在接收到某種信號後,會根據不同的信號執行默認的操作:
忽略信號
終止(殺死)進程
產生核心轉儲文件,同時 終止進程
停止進程
恢復之前被暫停的進程繼續運行
這裡我們可以 通過 signal() 來改變進程對某個信號的處置方式。
我們通過查看手冊: man 2 signal
我們輸入完上述代碼,編譯並運行,然後再給該進程發送信號,觀察進程對接收到信號的反應:
我們在另外一個終端輸入 kill 命令來向該進程發送信號:
3.通過舉例說明 alarm() 函數和 setitimer() 函數的使用。
我們先分別查看兩個函數的手冊:
man 2 alarm :
man 2 setitimer :
這裡我們通過命令 man 7 signal 可以查看當前系統信號的清單:
從上面可以看到 alarm() 函數在計時結束後會發生 SIGALRM 信號給當前進程,進程對 SIGALRM 信號的缺省動作是結束進程。
下面一個非常簡單的例子:
雖然程序中有無限循環,不斷輸出字符串 process will finish! ,由於調用了 alarm(1) 函數,alarm 函數會在1秒後給該進程發送 SIGALRM 信號,然後進程結束。
接下來繼續看一個程序設定了兩次定時炸彈,第一次設定 5 秒後爆炸,設定後過了 2 秒,再設定了一個3 秒後爆炸的定時炸彈。
這裡計時時間到了並不會結束進程,因為我們編寫了信號捕捉函數,產生 SIGALRM 信號後會輸出字符串 Bomb!! , 我們可以鍵盤按鍵組合結束進程,這裡我用了 CTRL+C 。
接下來我們用 setitimer() 函數實現 alarm() 函數
程序在運行1秒鐘後被 SIGALRM 信號結束。
可以分析一下 alarm() 函數和 setitimer() 函數的區別。
答:
alarm函數
設置定時器(鬧鐘)。可代替alarm函數。精度微秒us。在指定seconds後,內核會給當前進程發送14)SIGALRM信號。進程收到該信號,默認動作終止。
每個進程都有且只有唯一一個定時器。返回0或剩餘的秒數,無失敗。
setitimer函數
setitimer是一個比較常用的函數,可用來實現延時和定時的功能
使用時需要引入的頭文件#include <sys/time.h>
4.舉例說明信號集操作函數的使用
我們可以通過命令 man 3 sigsetops 來查看手冊:
從手冊中可以看到,這些函數都是對 sigset_t 這個數據結構進行操作的。
我們可以編寫一個打印 sigset_t 的函數
5.舉例說明對阻塞信號與未決信號的理解
在一個進程中,保存了兩個信號集(在PCB中),分別是阻塞信號集,還有一個未決信號集。當你使用sigprocmask 的時候,就會修改阻塞信號集。
當你的進程一收到信號且該信號被阻塞,它首先進入到未決信號集中(就是一個 sigset_t ),當未決信號集中的信號被信號處理函數(你自己定義的或者系統默認的)處理,就會從未決信號集中刪除。
如果一個信號加入阻塞信號集,該信號的信號處理函數就不會被調用
man sigprocmask
對於未決信號集我們不能直接操作,可以使用 sigpending 函數獲取未決信號集。
man sigpending
下面結合例子來理解,程序的功能是先把 SIGINT 、 SIGTSTP 加入到了進程阻塞信號集中去。接下來,每隔一秒打印一次未決信號集,第 10 次的時候,又把 SIGINT 信號從阻塞信號集中刪除。
6.舉例說明 sigaction() 函數的使用
不同於 signal 函數, sigaction 函數是符合 POSIX 標準的,而 signal 只是 ANSI C 定義的函數。除了上面的區別外, sigaction 提供了更多的功能。比如它可以處理帶參數的信號,在信號處理的時候,可以屏蔽其它信號等等。我們通過 man 2 sigaction 來查看手冊:
下面給出一個程序來說明 sigaction() 函數的使用,程序註冊了信號 SIGINT 和 SIGTSTP . 需要注意的一點是 sa_mask 被設置為 SIGINT ,它表示當執行信號處理函數的時候,阻塞信 SIGINT 信號。我在 handler 函數加入了一打印未決信號的功能,以驗證執行到 handler 的時候發送 SIGINT 是被阻塞住的。
1.當程序運行的時候, Ctrl+C 進入 handler ,然後立即 Ctrl+Z 發現 handler 還未執行完就被SIGTSTP 打斷.
2.當程序運行的時候, Ctrl+Z 進入 handler ,然後立即 Ctrl+C 發現並不會被 SIGINT 打斷,這是因為該 handler 註冊的時候被設置了 SA_MASK = SIGINT 。最後 handler 結束的時候打印了未決信號集,發現裡頭有 SIGINT 。所以 handler 結束後,又去繼續對 SIGINT 進行處理。