| 專案 | 內容 |
|---|---|
| 作業所屬課程 | 2021春季Linux系統與應用 (南昌航空大學 - 資訊工程學院) |
| 作業要求 | https://edu.cnblogs.com/campus/nchu/2021-spring-Linux/homework/11957 |
| 學號-姓名 | 18041520-李國明 |
| 作業學習目標: | 1.了解信號的概念 2.掌握信號處理的方法 |
1.撰寫一個簡單的程式并運行,然后向該行程發送不同的信號以觀察該行程對接收到信號的反應,
#include <stdio.h>
#include <unistd.h>
int main() {
printf("hello signal! I'm %d\n", getpid());
while(1) {
write(STDOUT_FILENO, ".", 1);
sleep(10);
}
return 0;
}

在終端編譯并運行該程式:

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

按下 CTRL+z :

按下 CTRL+\ :

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

我們重新運行程式:

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

這里我們發送了信號值為9的信號給了行程30094.再切換到運行程式的終端來觀察行程接收到信號后的反應:

2.使用 signal() 函式來捕捉信號,
通常行程在接收到某種信號后,會根據不同的信號執行默認的操作:
忽略信號
終止(殺死)行程
產生核心轉儲檔案,同時 終止行程
停止行程
恢復之前被暫停的行程繼續運行
這里我們可以 通過 signal() 來改變行程對某個信號的處置方式, signal() 可能是很多同學目前為止
見過最復雜的函式,我們通過查看手冊: man 2 signal

#include <unistd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> void sighandler(int sig) { switch(sig) { case SIGUSR1://10 printf("hello SIGUSR1\n");break; case SIGUSR2://12 printf("hello SIGUSR2\n");break; case SIGINT://2 CTRL+C printf("休想干掉我!\n");break; case SIGTSTP://20 CTRL+Z printf("不要停止我!\n");break; case SIGQUIT://3 CTRL+\ printf("就是不退出!\n");break; case SIGSEGV://11 printf("呃!程式出 bug 了!\n");break; default: printf("hello, who are you %d?\n", sig); }sleep(2); // 洗掉這一行,再給程式發信號,看看 main 函式打點的情況, }int main() { printf("I'm %d\n", getpid()); if (SIG_ERR == signal(SIGUSR1, sighandler)) { perror("signal SIGUSR1"); }if (SIG_ERR == signal(SIGUSR2, sighandler)) { perror("signal SIGUSR2"); }if (SIG_ERR == signal(SIGINT, sighandler)) { perror("signal SIGINT"); }if (SIG_ERR == signal(SIGTSTP, sighandler)) { perror("signal SIGTSTP"); }if (SIG_ERR == signal(SIGQUIT, sighandler)) { perror("signal SIGQUTI");
我們輸入完上述代碼,編譯并運行,然后再給該行程發送信號,觀察行程對接收到信號的反應:

我們在另外一個終端輸入 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 秒后爆炸的定時炸彈,
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig) {
if (sig == SIGALRM)
printf("Bomb!!!!!!!!\n"); }int main() { if(SIG_ERR == signal(SIGALRM, handler)) { perror("signal SIGALRM"); }unsigned int remain = 0; remain = alarm(5); // 設定 5 秒后爆炸 printf("the previous alarm remain %d seconds\n", remain); sleep(3); // 等待 3 秒 remain = alarm(3); // 設定 3 秒后爆炸,同時會取消前面那個定時炸彈 printf("the previous alarm remain %d seconds\n", remain); while(1) { write(STDOUT_FILENO, ".", 1); pause(); } }


這里計時時間到了并不會結束行程,因為我們撰寫了信號捕捉函式,產生 SIGALRM 信號后會輸出字串 Bomb!! , 我們可以鍵盤按鍵組合結束行程,這里我用了 CTRL+C ,
接下來我們用 setitimer() 函式實作 alarm() 函式


程式在運行1秒鐘后被 SIGALRM 信號結束,
試分析一下 alarm() 函式和 setitimer() 函式的區別?
alarm()函式,當定時時間到了,就會產生SIGALRM信號結束行程,
setitimer()函式從開始計時到時間結束,會產生SIGALRM信號,
若it_interval的值為0,則不會重新啟動該定時器,呼叫 setitimer()失敗,回傳-1,結束行程,
4. 舉例說明信號集操作函式的使用
我們可以通過命令 man 3 sigsetops 來查看手冊:
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/288092.html
標籤:Linux
