文章目錄
- 前言
- 一、相關函數/系統調用
- 1. signal
- 2. kill
- 3. abort (庫函數)
- 4. raise (庫函數)
- 5. alarm
前言
現實生活中, 存在著諸多信號, 比如紅綠燈, 上下課鈴聲…我們在接收到信號時, 就會做出相應的動作. 對于進程也是如此的, 進程也會收到來自 OS 發出的信號, 根據信號的不同也會做出不同的動作, 進程在收到信號時也并不一定會立即執行, 也可以在適當的時候在執行該信號對應的動作, 一般信號常見處理方式有如下三種:
- 忽略此信號.
- 執行該信號的默認處理動作.
- 提供一個信號處理函數, 要求內核在處理該信號時切換到用戶態執行這個處理函數, 這種方式稱為捕捉一個信號.
而在進程中用以保存信號的容器可以是一個位圖, 通過 0, 1 來表示是否收到某信號.
在 Linux 中, 可以通過指令:
kill -l
來查看系統定義的信號列表:
通過指令:
man 7 signal
可以查看關于信號的詳細說明:
在 Linux 中可以通過指令:
kill -信號編號 進程pid
來對指定進程發送指定信號.
一、相關函數/系統調用
1. signal
頭文件: #include <signal.h>
函數聲明: sighandler_t signal(int signum, sighandler_t handler);
- signum: 被設置的信號編號.
- handler: 被設置的信號的新的處理函數, 是一個回調函數, 通過用戶傳遞.
- sighandler_t: 是一個函數指針, 可以指向一個返回值為 void, 參數為 int 的函數, 以下是系統中的 typedef:
typedef void (*sighandler_t)(int);
功能: 將指定的信號的處理函數覆蓋為 handler.
示例代碼:
#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;void sighandler(int signo)
{cout << "void sighandler(int signo): " << signo << endl;
}int main()
{signal(2, sighandler);while(1){cout << "Hello" << endl;sleep(1);}return 0;
}
運行結果:
實際 Ctrl + C 就是編號為 2 的信號, 平常通過 Ctrl + C 向進程發送 SIGINT(2) 號信號, 可以終止進程, 但是把信號 2 的處理函數換成了自定義的, 所以在終端按下 Ctrl + C 時執行我們自定義的函數.
PS: 9, 18, 19 號信號即時被重定向了新的處理函數也沒用, 該信號仍然會執行原本的處理函數.
2. kill
頭文件:
#include <sys/types.h>
#include <signal.h>
函數聲明: int kill(pid_t pid, int sig);
- pid: 目標進程pid.
- sig: 向目標發送的信號編號.
功能: 向指定進程發送指定信號.
示例代碼:
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
using namespace std;int main()
{for(int i = 0; i < 10; ++i){if(i == 5){kill(getpid(), 9);}cout << i << ":Hello" << endl;sleep(1);}return 0;
}
運行結果:
在輸出 5 條語句后, 向自己發送 9 號信號, 直接終止自己了.
3. abort (庫函數)
頭文件: #include <stdlib.h>
函數聲明: void abort(void);
功能: 向調用進程發送終止信號.
示例代碼:
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;int main()
{for(int i = 0; i < 10; ++i){if(i == 5){abort();}cout << i << ":Hello" << endl;sleep(1);}return 0;
}
運行結果:
4. raise (庫函數)
頭文件: #include <signal.h>
函數聲明: int raise(int sig);
- 返回值: 成功調用返回 0, 失敗返回非零整數.
- sig: 信號編號.
功能: 向調用進程發送指定信號.
示例代碼:
#include <iostream>
#include <csignal>
#include <unistd.h>
using namespace std;int main()
{for(int i = 0; i < 10; ++i){if(i == 5){raise(9);}cout << i << ":Hello" << endl;sleep(1);}return 0;
}
運行結果:
5. alarm
頭文件: #include <unistd.h>
函數聲明: unsigned int alarm(unsigned int seconds);
- 返回值: 返回一個無符號整數, 表示前一個鬧鐘剩于的秒數, 打個比方, 鬧鐘設置為 30s 后響, 但是在 20s 的時候就收到了 SIGALRM(14) 信號, 此時鬧鐘會提前響, 返回值就為 30 - 20 = 10.
- seconds: 多少秒后響鈴.
功能: 在過了 seconds 秒以后終止調用進程.
示例代碼:
#include <iostream>
#include <unistd.h>
using namespace std;int main()
{alarm(1);for(int i = 0; ; i++){cout << i << ":Hello" << endl;}return 0;
}
運行結果:
可以看到, 在 1 秒鐘后鬧鐘響了, 進程也就被終止了.
接下來通過另一段代碼查看返回值:
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void sighandler(int signo)
{cout << "void sighandler(int signo): " << signo << endl;int n = alarm(10);cout << "n: " << n << endl;
}int main()
{cout << "pid:" << getpid() << endl;signal(SIGALRM, sighandler);alarm(10);while(1);return 0;
}
運行結果:
可以看到, 在鬧鐘設定后, 以我最快的速度給調用鬧鐘的進程發送 14 號信號之后, 返回的剩于秒數為 8s, 也就是說鬧鐘只跑了 2s, 而后又設置了一個 10s 后響的鬧鐘, 這次沒有提前發送 14 號信號, 它正常跑完, 返回的剩于秒數為 0s, 合理.