實驗七、訊號

  • 2020 年 5 月 24 日
  • 筆記

實驗七、訊號

項目 內容
這個作業屬於哪個課程 <班級課程的主頁鏈接 >
這個作業的要求在哪裡 <作業要求鏈接接地址>
學號-姓名 17043113—胡斌
作業學習目標 1.掌握系統環境c語言編程該概念 2. 學習linux系統進程概念
1.編寫一個簡單的程式並運行,然後向該進程發送不同的訊號以觀察該進程對接收到訊號的反應。

1.7

在終端編譯程式

1)我們可以在當前終端通過按鍵組合向該進程發送訊號CTRL+C 、CTRL+Z 、CTRL+\ ,大家可以試著 在每一次運行該程式的時候分別通過按鍵發送不同的訊號來觀察進程的反應。這裡三個按鍵組合說明要發送三個訊號,所以我們要運行該程式三次,然後每次使用不同的按鍵組合來觀察。

7.1

 

  1. 另外再開啟一個終端,在終端通過輸入kill 命令來給進程發送訊號,進程的pid 在程式運行的第一 行輸出,每次運行程式的時候pid 是不同的,這個是大家要注意的。我們可以在終端通過輸入kill -l 來查看當前系統當中的訊號列表:

7.2

我們重新運行程式:

7.3

在當前終端我們可以看到程式輸出自己的pid 是14465,這是我們可以另開一個終端,通過kill 命令向該 進程發送訊號:

7.5

這裡我們發送了訊號值為9的訊號給了進程14465.再切換到運行程式的終端來觀察進程接收到訊號後的反應:

7.4

對於kill 命令我們可以查看手冊: man kill

7.6

3)我們編寫一個簡單的程式,該程式調用kill()函數向某個進行發送訊號

1.8

繼續進行之前的程式等待有訊號到來:

7.7

在另一個中斷完成上面程式的輸入並且編譯,運行的時候把14650作為參數

7.8

在返回之前運行的main的終端觀察進行接收到訊號的反應:

7.7

我們可以查看手冊看啊可能kill()函數:man 2 kill

7.9

2.使用signal()函數來捕捉訊號。

通常進行在接收到某種訊號後,會根據不同的訊號執行默認的操作:

  • 忽略訊號

  • 終止(殺死)進程

  • 產生核心轉儲文件,同時終止進程

  • 停止進程

  • 恢復之前被暫停的進程繼續運行

這裡我們可以通過signal()來改變進程對某個訊號的處置方式。signal()可以通過查看手冊:man 2 signal查看

 

7.13

輸入上圖程式碼,編譯並運行,然後再給該進程發送i新年好,觀察進程對接受到訊號的反應:

7.14

我們在另外一個鍾端輸入kill命令來向該進程發送訊號:

7.15

3.通過舉例說明alarm()函數和setitimer()函數的使用。

我們先分別查看兩個函數的手冊:

man 2 alarm

7.16

man 2 setitimer:

7.17

這裡通過命令man 7 signal 可以查看當前系統訊號的清單:

7.18

從上面可以看到alarm()函數在技術結束後會發生SIGALRM訊號給當前進程,進程對SIGALRM訊號的預設動作是結束進程。

下面一個非常簡單的例子:

7.19

雖然程式中有無限循環,不斷輸出字元串process will finish!由於調用了alarm(1)函數,alarm函數會在1s後給該進程發送sigalrm訊號,然後進程結束。結果如下所示(部分截圖)

img

接下來繼續看一個程式設定了兩次定時炸彈,第一次設定5s後爆炸,設定後過了2s,在設定了一個3s後爆炸的定時炸彈。

7.20

7.21

這裡計時時間到了並不會結束進程,因為我們編寫了訊號捕捉函數,產生 SIGALRM 訊號後會輸出字元 串 Bomb!! , 我們可以鍵盤按鍵組合結束進程,這裡我用了 CTRL+C 。 接下來我們用 setitimer() 函數實現 alarm() 函數,輸入如下程式碼

7.22

7.23

效果圖:

7.24

程式在運行1秒鐘後被 SIGALRM 訊號結束。 大家可以分析一下 alarm() 函數和 setitimer() 函數的區別。

  1. 舉例說明訊號集操作函數的使用 我們可以通過命令 man 3 sigsetops 來查看手冊:

從手冊中可以看到,這些函數都是對 sigset_t 這個數據結構進行操作的。 我們可以編寫一個列印 sigset_t 的函數

7.26

7.27

7.28

7.30

5.舉例說明對阻塞訊號與未決訊號的理解 在一個進程中,保存了兩個訊號集(在PCB中),分別是阻塞訊號集,還有一個未決訊號集。當你使用 sigprocmask 的時候,就會修改阻塞訊號集。 當你的進程一收到訊號且該訊號被阻塞,它首先進入到未決訊號集中(就是一個 sigset_t ),當未決 訊號集中的訊號被訊號處理函數(你自己定義的或者系統默認的)處理,就會從未決訊號集中刪除。 如果一個訊號加入阻塞訊號集,該訊號的訊號處理函數就不會被調用。

man sigprocmask

7.31

對於未決訊號集我們不能直接操作,可以使用 sigpending 函數獲取未決訊號集。 man sigpending

1.9

 

下面結合例子來理解,程式的功能是先把 SIGINT 、SIGTSTP 加入到了進程阻塞訊號集中去。接下 來,每隔一秒列印一次未決訊號集,第 10 次的時候,又把SIGINT 訊號從阻塞訊號集中刪除。

7.32

7.33

7.34

7.35

7.36

在另一終端輸入

7.37

然後 7.38

 

  1. 舉例說明sigaction() 函數的使用 不同於 signal 函數, sigaction 函數是符合 POSIX 標準的,而 signal 只是 ANSI C 定義的函數。 除了上面的區別外, sigaction 提供了更多的功能。比如它可以處理帶參數的訊號,在訊號處理的時 候,可以屏蔽其它訊號等等。我們通過man 2 sigaction 來查看手冊

  2. 7.39

下面給出一個程式來說明sigaction() 函數的使用,程式註冊了訊號 SIGINT 和 SIGTSTP . 需要注意 的一點是 sa_mask 被設置為 SIGINT ,它表示當執行訊號處理函數的時候,阻塞信 SIGINT 訊號。我 在 handler 函數加入了一列印未決訊號的功能,以驗證執行到 handler 的時候發送 SIGINT 是被阻 塞住的。

 

 

7.41

7.42

7.43

7.44

  1. 當程式運行的時候, Ctrl+C 進入 handler ,然後立即 Ctrl+Z 發現 handler 還未執行完就被 SIGTSTP 打斷.

  2. 當程式運行的時候, Ctrl+Z 進入 handler ,然後立即Ctrl+C 發現並不會被SIGINT 打斷,這是因 為該 handler 註冊的時候被設置了 SA_MASK = SIGINT 。最後handler 結束的時候列印了未決訊號 集,發現裡頭有 SIGINT 。所以 handler 結束後,又去繼續對 SIGINT 進行處理。