簡 述: 此處指 Linux 中系統內核發出的信號;而不是之前 Qt 學習的信號。
編程環境:
💻: uos20
📎 gcc/g++ 8.3
📎 gdb8.0
💻: MacOS 10.14
📎 gcc/g++ 9.2
📎 gdb8.3
信號初識:
-
特點:
- 簡單
- 攜帶信息量很少
- 用在某個特定的場景中
-
型號的狀態:
- 產生原因:
- 鍵盤 Ctrl + C
- 命令:kill
- 系統函數:kill()
- 軟條件:定時器
- 硬件:段錯誤,除 0 錯誤
- 未決狀態 – 沒有被處理
- 遞達狀態 – 信號被處理了
- 產生原因:
-
處理方式:
- 忽略
- 捕捉,然後自定義動作
- 執行了默認動作
-
信號的四要素:
-
信號名、信號編號、信號默認動作、事件描述
-
"動作(Action)"欄 的 字母 有 下列 含義:
A 缺省動作是結束進程. B 缺省動作是忽略這個信號. C 缺省動作是結束進程, 並且核心轉儲. D 缺省動作是停止進程. E 信號不能被捕獲. F 信號不能被忽略. 譯註: 這裏 “結束” 指 進程 終止 並 釋放資源, “停止” 指 進程 停止 運行, 但是 資源 沒有 釋放,有可能 繼續 運行。
-
-
通過 man 文檔查看信號
- 執行
man 7 signal
- 注意(man 手冊中有寫):
SIGKILL
、SIGSTOP
這兩個信號不能夠被捕捉,阻塞,忽略的
The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
- 執行
-
概念:阻塞信號集,未決信號集
- 是在 PCB 中
- 阻塞信號集:讓信號處於一個未決的狀態
- 未決信號集:如果信號被阻塞了,該信號集會對阻塞的信號做記錄
-
圖解進程產生和處理:
kill() 函數:
作用: 發射信號給指定進程、或者同組的信號。
int kill(pid_t pid, int sig);
-
參數:
- pid:
- pid > 0; 發送信號給指定的進程
- pid = 0; 發送信號給 調用 kill 函數進程屬於同一個組的所有進程
- pid = -1;如果用戶擁有超級用戶權限,則信號將被髮送到所有進程
- pid < -1;取 |pid| 發給對應進程組
- sig:
- 推薦使用完整的宏名稱而非數字,在少數發行版下,可能指定宏對應的數值有變化
- pid:
-
返回值:
- 成功: 0
- 失敗:-1(ID 非法,信號非法,普通信號殺 init進程等權限級別問題,設置 errno)
寫一個小的例子驗證 kill 信號的使用,子進程在 5S 後,通知系統內核發送 SIGKILL 給它的父進程,然後系統將父進程殺死。
int main(int argc, char *argv[])
{
pid_t pid = fork();
if (pid > 0) {
while (true) {
printf("this is a parent process = %d\n", getpid());
sleep(1);
}
} else if (pid == 0) {
sleep(5);
kill(getppid(), SIGKILL);
}
return 0;
}
運行效果:
raise() 函數:
作用: 自己給自己發射信號。在單線程程序中,它相當於 kill(getpid(), sig);
在一個多線程程序中,它等同於 pthread_kill(pthread_self(), sig)。
int raise(int sig);
比較簡單,但依舊寫一個例子使用一下:子進程給機子發射終止進程的信號。然後父進程在回收子進程的時候,答應其死亡的信號值
int main(int argc, char *argv[])
{
pid_t pid = fork();
if (pid > 0) {
int s;
pid_t wpid = wait(&s);
printf("this is a child process dide pid = %d\n", wpid);
if (WIFSIGNALED(s))
printf("dide by signal: %d\n", WTERMSIG(s));
} else if (pid == 0) {
raise(SIGINT); //給自己發射信號
}
return 0;
}
代碼中 19 行,選中的 SIGINT
信號,默認動作是終止進程。其對應的事件爲 當用戶按下<ctrl + c>組合鍵時候,用戶終端向正在運行中的 (由該終端啓動的)程序 發出此信號。
運行截圖:
abort() 函數:
作用: 給自己發送異常終止的信號。該函數沒有參數和返回值,也永遠不會調用失敗。
void abort(void);
定時器:
alarm() 函數:
unsigned int alarm(unsigned int seconds);
-
設置定時器(每一個進程只有一個定時器)
-
使用的是自然定時法則;不受進程狀態的影響
-
當時間到達之後,函數發出一個信號
SIGALRM
- SIGALRM – 調用 abort() 函數時候產生該信號 – 終止進程併產生 core 文件
-
返回值:始終是返回上一次調用此函數,還剩些的時間。
-
寫一個例子:
int main(int argc, char *argv[]) { int ret = alarm(5); printf("ret = %d\n", ret); sleep(2); ret = alarm(10); //重新設定定時器,返回值是返回之前鬧鐘的剩餘的時間 printf("ret = %d\n", ret); while (true) { printf("-------test-----\n"); sleep(1); } return 0; }
-
分析:
這裏 ret = 0 ,是因爲第一次調用,它的上一次的剩餘時間爲 0;然後 ret = 3 是因爲 5 - 2 s,然後定時器被充重置了新的 10 s。故後面會打印 10 次輸出語句,然後收到系統的終止信號,殺死本進程。因爲是執行的
alarm()
的默認動作,爲終止進程,且沒有捕捉該函數發射的對應的信號,也沒有定義自定義動作,所以本進程會被終止。 -
運行結果:
分析程序運行的損耗:
實際耗時 == 用戶 + 系統 + 損耗
-
寫一個例子,檢測一下計算機在 1s 內可以計算多少個數字?
-
代碼如下:
int main(int argc, char *argv[]) { alarm(1); int n = 0; while (true) { printf("%d\n", n++); } return 0; }
-
運行演示:
分析:其中損耗文件來自文件 IO 操作
輸出到終端: 1.012 = 0.31 + 0.34 + 損耗 (約 70w)
輸出到文件: 1.012 = 0.93 + 0.07 + 損耗 (約 800w)
//執行 time ./myAlarmCount(輸出到終端) 0.31s user 0.34s system 64% cpu 1.012 total (輸出到 692977) //time ./myAlarmCount > file(輸出到文本) 0.93s user 0.07s system 98% cpu 1.012 total (輸出到 8686215)
setitimer() 函數:
- 作用: 定時器,並且實現週期性 定時。
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
struct itimerval {
struct timeval it_interval; /* 定時器的循環週期 */
struct timeval it_value; /* 第一次觸發定時器的時間 */
};
struct timeval {
time_t tv_sec; /* 秒 */
suseconds_t tv_usec; /* 微秒 */
};
-
參數:
- which:
- ITIMER_REAL: 自然定時法,會發出信號
SIGALRM
- ITIMER_VIRTUAL:虛擬定時法,只會計算 用戶時間 ,對應信號
SIGVTALRM
- ITIMER_PROF: 只計算 用戶時間 + 系統時間 ,對應發射
ITIMER_VIRTUAL
信號
- ITIMER_REAL: 自然定時法,會發出信號
- new_value:我們需要設置的參數,總的定時時間是
tv_sec + tv_usec
之和 - old_value:傳出參數,傳出上一次定時器的設置,一般用不到,用 NULL
- which:
-
寫一個例子:
int main(int argc, char *argv[]) { itimerval time; // time.it_interval = 3; //每隔 3s 一次循環定時 //第一次觸發定時器的時間爲 5s + 3ms time.it_value.tv_sec = 5; //5s time.it_value.tv_usec = 3; //3 ms setitimer(ITIMER_REAL, &time, NULL); while (true) { printf("-----printf()-----\n"); sleep(1); } return 0; }
-
運行效果:
下載地址:
歡迎 star 和 fork 這個系列的 linux 學習,附學習由淺入深的目錄。