信號:當一個進程產生異常、中斷等時,操作系統則給進程發送一個信號,即向進程PCB中寫入一個信息(此處即修改一個比特位(位圖實現),表示進程是否收到該信號),當進程確認收到該信號則會在恰當的時間去執行其對應的相關處理動作。
三種處理動作:
1.忽略此信號;
2. 執行該信號的默認處理動作(一般都爲終止此進程);
3. 提供一個用戶自定義信號處理函數,內核處理時切換到用戶態執行,即爲信號的捕捉。
信號捕捉函數:
#include <signal.h>
typedef void (*sighandler_t)(int); //函數指針,信號處理函數,參數爲信號編號
sighandler_t signal(int signum,sighandler_t handler);
參數:
signum:信號編號;
handler:自定義的信號處理函數指針
可用以下命令查看當前系統下的信號:
kill -l
其中1~31是常用信號,其中9號信號不可以被捕捉,34~64則成爲實時信號。
產生信號的四種方式:
1.通過終端按鍵產生信號:
例如:Ctrl-C產生(2號)SIGINT信號,Ctrl-\產生(3號)SIGQUIT信號,Ctrl-Z產生(20號)SIGTSTP信號,SIGINT的默認處理動作是終止進程,SIGQUIT的默認處理動作是終止進程並且Core Dump,SIGTSTP信號使前臺進程停止;
2. 硬件異常產生信號:
這些條件由硬件檢測並通知內核,然後內核向當前進程發送適當的信號。例如當前進程執行了除以0的指令,CPU的運算單元會產生異常,內核將這個異常解釋爲SIGFPE信號發送給進程。再比如當前進程訪問了非法內存地址,MMU會產生異常,內核將這個異常解釋爲SIGSEGV信號發送給進程;
此處引入Core Dump:
Core Dump:稱爲核心轉儲,即當一個進程異常退出之前,將進程的用戶空間內存數據全部以文件方式保存到磁盤上,文件名則稱作是core,方便gdb的調試。
Post-mortem Debug(事後調試):進程異常終止通常是因爲有Bug,比如非法內存訪問導致段錯誤,事後可以用調試器檢查core文件以查清錯誤原因。
一個進程允許產生多大的core文件取決於進程的Resource Limit(這個信息保存在PCB中),如下:
可用命令:
ulimit -a //查看當前shell下軟硬件資源的上限
可知默認下系統分配core文件大小爲0,是不允許產生core文件的,因爲core文件中可能包含用戶密碼等敏感信息,不安全,並且若一個較大程序每次運行都要產生一個core文件,很可能將硬盤空間佔用完。
可以用以下命令更改:
ulimit -c 1024 //設置core文件大小上限爲1024字節
此處寫一個代碼(指針非法操作)運用core文件在gdb中調試:
此處第9行指針非法操作,會產生異常,運行此代碼:
運用gdb調試如圖:最後則直接定位到錯誤行:第9行,並顯示出操作系統發送的信號。
3.調用系統函數向進程發信號(也可使用命令向進程發信號:kill 信號 進程號):
int kill(pid_t id,int signo); //向進程id發送signo信號
int raise(int signo); //自己給自己發送signo信號
返回值:成功返回0,錯誤返回-1;
#include <stdlib.h>
int abort(void); //使當前進程接收到信號而異常終止
此函數可以捕捉但最後必定使進程退出;
4.由軟件條件產生信號:
#include <unisted.h>
unsigned int alarm(unsigned int seconds); //告訴操作系統seconds時間後給自己發送一個SIGALRM信號
返回值:0或以前設定鬧鐘時間還餘下的秒數.
代碼模擬:
mykill.c:模擬實現自定義發送信號給某進程
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void usage(char* argv)
{
printf("%s pid sig\n",argv);
exit(0);
}
//自定義實現給一個進程發送信號:./mykill pid sig
int main(int argc,char*argv[])
{
if(argc!=3)
{
usage(argv[0]);
}
int pid=atoi(argv[1]);
int sig=atoi(argv[2]);
kill(pid,sig); //系統接口函數向pid進程發送sig信號
return 0;
}
mysig.c:此代碼可以實現:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int value=0;
void myhander1(int sig) //自定義信號處理動作:信號遞達
{
printf("pid: %d sig: %d\n",getpid(),sig);
}
void myhander2(int sig)
{
printf("pid:%d sig: %d\n",getpid(),sig);
printf("count==%d\n",value);
}
int main()
{
signal(2,myhander1); //信號捕捉函數:給2號信號定義動作
signal(14,myhander2); //給14號信號定義動作
//統計1s變量可以累加到多大
/*alarm(1); //告訴操作系統1s後給自己發送一個14號信號
while(1)
{
++value;
}*/
int count=0;
while(1)
{
printf("count==%d\n",count);
if(count%5==0)
{
raise(2); //當count%5==0時,自己給自己發送2號信號
}
++count;
sleep(1);
}
return 0;
}
1.count累加無限循環中當%5==0時自己給自己發送2號信號,也可以鍵盤輸入發送2號信號:2.調用alarm(1),告訴操作系統1s後給自己發送一個14號信號,從而調用函數統計1s變量value可以累加到多大:
3.可以調用mykill.c程序結束此無限循環程序: