注:信號vs信號量:兩者沒有任何關系!
信號是什么?
Linux系統提供的,讓用戶(進程)給其他進程發送異步信息的一種方式。
進程看待信號的方式:
1.信號在沒有發生的時候,進程已經知道信號發生時該如何處理。
2.進程能夠認識進程,很早之前,有人給進程中設置了識別特定信號的方式。
3.信號到來的時候,進程正在處理更重要的事情,進程暫時不能處理到來的信號,進程必須暫時要將到來的信號進行臨時保存。
4.信號到了,可以不立即處理,可以在合適的時間處理。
5.信號是隨時產生的,進程無法準確預料,所以信號是異步發送的。
為什么要有信號?
系統要求進程要有隨時響應外部信號的能力,隨后做出反應。
信號的具體知識
以這個時間軸進行學習
信號的產生
常見信號
數組和名字都可以標識信號,名字其實就是宏。
使用kill命令查看信號。
1-31為常用信號,右下的紅框中是實時信號。
沒有0,沒有32,33 信號,共62個信號
信號的處理的方式——signal
a. 默認動作
b. 自定義處理信號——捕捉
c. 忽略了信號——是處理了信號嗎?是的,處理方式就是忽略。
實際執行信號的處理動作稱之為信號的遞達,也就是以上三種方式。
signal函數
#include <signal.h>typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
參數說明:
signum:信號編號(如 SIGINT、SIGTERM)。
handler:信號處理函數,可以是:
SIG_DFL:恢復默認行為。
SIG_IGN:忽略信號。
自定義函數指針:用戶定義的處理函數。
返回值:(類型函數指針)
成功時返回之前的處理函數的地址。
失敗時返回 SIG_ERR。
產生信號的第一種方式:kill命令
kill -9 進程pid :發送一個信號,殺死一個進程
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
int main()
{while(true){std::cout << "I am activing...,pid:" << getpid() << std::endl;sleep(1);}return 0;
}
結果:
使用命令kill -9 1121788
進程被殺死。
kill -2 進程pid :發送一個信號,默認行為是使進程自己終止。
1.默認行為——進程自己終止
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
int main()
{while(true){std::cout << "I am activing...,pid:" << getpid() << std::endl;sleep(1);}return 0;
}
結果:
在運行的進程中使用kill -2 1121490
進程自己終止。
2.自定義處理信號——捕捉
例子:
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
void handler(int signo)
{std::cout << "get a sig, number is: "<< signo << std::endl;
}
int main()
{//signal調用完了,handler方法會被立即執行嗎?不會,只是設置對應信號的處理方法。//未來我們收到對應的信號才執行handler方法。//未來進程如果一直沒有收到SIGINT,handler也就永遠不會被調用。signal(SIGINT,handler);//handler(SIGINT)while(true){std::cout << "I am activing...,pid:" << getpid() << std::endl;sleep(1);}return 0;
}
結果:
在運行的進程中使用kill -2 1120892
默認行為被更改成其他方法。
3.忽略信號
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
int main()
{signal(SIGINT,SIG_IGN);//ignore忽略while(true){std::cout << "I am activing...,pid:" << getpid() << std::endl;sleep(1);}return 0;
}
結果:
在運行的進程中使用kill -2 1120892
進程無任何反應——這條信號被忽略。
產生信號的第二種方式:鍵盤產生信號
Ctrl + c 被操作系統解釋成2號信號
Ctrl + \ 被操作系統解釋成3號信號
產生信號的第三種方式:系統調用
kill 系統調用函數
對任意進程發送任意信號。
#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);
參數說明:
pid:目標進程的進程 ID,具體行為取決于其取值:
>0:發送信號給 PID 為 pid 的進程。
=0:發送信號給當前進程組內的所有進程。
sig:要發送的信號編號(如 SIGTERM、SIGKILL)。
返回值:
成功返回 0,失敗返回 -1,并設置 errno 。
例子:自定義mykill函數
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
#include<errno.h>
#include<string.h>
using namespace std;//mykill -9 pid
int main(int argc,char* argv[])
{if(argc != 3){cout << "Usage: " << argv[0] << " -signumber pid" << endl;return 1;}int signumber = stoi(argv[1]+1);int pid = stoi(argv[2]);int n = kill(pid, signumber);if(n < 0){cerr << "kill error, " << strerror(errno) << endl;}return 0;
}
raise 系統調用函數
對當前進程發送任意信號。
#include <signal.h>int raise(int sig); // 向當前進程發送信號 sig
參數說明:
sig: 信號編號(如 SIGINT、SIGTERM)。
返回值:
成功返回 0,失敗返回非 0。
abort系統調用函數
對當前進程發送6號信號。
#include <stdlib.h> // 必須包含的頭文件void abort(void);
**參數說明:**無(void)。
**返回值:**無返回值(void)。
產生信號的第四種方式:軟件條件
alarm函數
對當前進程等待 seconds 秒后發送14號信號。
#include <unistd.h>unsigned int alarm(unsigned int seconds);
參數說明: seconds – 定時器的時間(秒)。若為 0,表示取消之前設置的定時器。
返回值:
返回之前尚未觸發的定時器的剩余秒數。
若之前沒有定時器,返回 0。
例子: 向當前進程5秒后發送14號信號,終止進程。
int main()
{alarm(5);//響一次int cnt = 0;while(true){sleep(1);cout<< "cnt: "<< cnt++ << endl;}return 0;
}
結果: 向進程發送了14號信號,終止進程
注:鬧鐘只響一次。
例: 關于alarm的返回值
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
using namespace std;int g_cnt = 0;
int ret = 0;void handler(int sig)
{std::cout << "get a sig: "<< sig <<" g_cnt: "<< g_cnt <<endl;int n = alarm(2);cout<<"剩余時間:"<< n <<endl;
}int main()
{signal( 2 ,handler);alarm(50);//響一次int cnt = 0;while(true){sleep(1);cout<< "cnt: "<< cnt++ << endl;}return 0;
}
結果: 在 cnt:3 時按下 Ctrl + C
這時獲取alarm的返回值是剩余時間。
產生信號的第五種方式:異常
- 除0錯誤
例子:
#include<iostream>
#include<unistd.h>int main()
{int a = 10;a = a/0;while(true) sleep(1);return 0;}
出現除0錯誤。
結果:發送8號信號(SIGFPE)
- 野指針問題
例子:
#include<iostream>
#include<unistd.h>int main()
{int *p = nullptr;*p = 100;//野指針while(true) sleep(1);return 0;}
結果:發送11號信號(SIGEGV)
關于信號產生的各種情況的理解
信號保存在進程的PCB中,且以位圖的方式保存在PCB中。
在PCB中以 uint32_t pending
變量進行保存。
給進程發送信號,其實就是寫入信號。向進程PCB中寫入信號數據,PCB是內核數據結構,只有操作系統有權限寫入,若用戶想寫入,操作系統提供系統調用供用戶使用。
所以以上的信號產生的5種方式,實際上最終都是交給操作系統進行最后向進程寫入信號的操作。
對于異常問題的解釋
- 除0錯誤
- 野指針