Linux C: 信号及异常和捕捉函数原理
| #define SIGHUP 1 | 終端掛起或控制進程終止 |
| #define SIGINT 2 | 終端中斷(Ctrl+C 組合鍵) |
| #define SIGQUIT 3 | 終端退出(Ctrl+\組合鍵) |
| #define SIGILL 4 | 非法指令 |
| #define SIGTRAP 5 | debug 使用,有斷點指令產(chǎn)生 |
| #define SIGABRT 6 | 由 abort(3)發(fā)出的退出指令 |
| #define SIGIOT 6 | IOT 指令 |
| #define SIGBUS 7 | 總線錯誤 |
| #define SIGFPE 8 | 浮點運算錯誤 |
| #define SIGKILL 9 | 殺死、終止進程 |
| #define SIGUSR1 10 | 用戶自定義信號 1 |
| #define SIGSEGV 11 | 段錯誤(無效的內(nèi)存段) |
| #define SIGUSR2 12 | 用戶自定義信號 2 |
| #define SIGPIPE 13 | 向非讀管道寫入數(shù)據(jù) |
| #define SIGALRM 14 | 鬧鐘 |
| #define SIGTERM 15 | 軟件終止 |
| #define SIGSTKFLT 16 | 棧異常 |
| #define SIGCHLD 17 | 子進程結(jié)束 |
| #define SIGCONT 18 | 進程繼續(xù) |
| #define SIGSTOP 19 | 停止進程的執(zhí)行,只是暫停 |
| #define SIGTSTP 20 | 停止進程的運行(Ctrl+Z 組合鍵) |
| #define SIGTTIN 21 | 后臺進程需要從終端讀取數(shù)據(jù) |
| #define SIGTTOU 22 | 后臺進程需要向終端寫數(shù)據(jù) |
| #define SIGURG 23 | 有"緊急"數(shù)據(jù) |
| #define SIGXCPU 24 | 超過 CPU 資源限制 |
| #define SIGXFSZ 25 | 文件大小超額 |
| #define SIGVTALRM 26 | 虛擬時鐘信號 |
| #define SIGPROF 27 | 時鐘信號描述 |
| #define SIGWINCH 28 | 窗口大小改變 |
| #define SIGIO 29 | 可以進行輸入/輸出操作 |
| #define SIGPOLL | SIGIO |
| #define SIGPWR 30 | 斷點重啟 |
| #define SIGSYS 31 | 非法的系統(tǒng)調(diào)用 |
| #define SIGUNUSED 32 | 未使用信號 |
轉(zhuǎn)載:「一只青木呀」的原創(chuàng)文章,原文鏈接:https://blog.csdn.net/weixin_45309916/article/details/111939072
一、信號和中斷
?
? ? ? ?信號是進程間通信的重要內(nèi)容之一。它可以來源于硬件,例如鍵盤的 Ctrl+C 組合鍵,間隔定時器,IO錯誤等硬件錯誤;也可以是來源于自己,例如自己的代碼除0,指針越界等執(zhí)行報錯;其中最需要了解的是來自于其他進程例如 kill命令。信號可以被捕捉從而觸發(fā)信號處理函數(shù)。信號處理函數(shù)可以被重寫,信號也可以被屏蔽。
? ? ? ? 在進程結(jié)構(gòu)體PROC 中,都有一個信號處理數(shù)組 int sig[32] ; 其中值為0 代表默認處理,1代表忽略,其他非零值表示用戶模式下預(yù)先設(shè)定好的信號處理函數(shù)地址。除了信號處理數(shù)組每個PROC都有一個32位向量(信號位向量) 和Mask(屏蔽)位向量? 。 bits向量用來指明哪些信號被signal? , masks 用來指明哪些信號被Block 。當信號位為1時,且屏蔽位為0時,信號才會生效并傳遞給進程。如果進程發(fā)現(xiàn)了個未被阻塞的信號,則會將信號位清0。
二、信號的捕捉和捕捉函數(shù)的設(shè)置?
? ? ? ? 信號處理內(nèi)容可以被修改,除了 SIGKILL(9)? 和 SIGSTOP(19)? 。 為了處理信號捕捉可能造成的死循環(huán),這兩個信號9和19作為終止進程的最后手段,規(guī)定了不能被修改。
? ? ? ?進程可以使用系統(tǒng)調(diào)用來修改捕捉到信號時的信號處理函數(shù):
? ? ? int r = signal (int signal_number , void *handler) ;?
? ? ?但是signal 函數(shù)有幾個個缺點:
? ? 1。如果信號觸發(fā)頻率過快,可能導(dǎo)致下一個信號和信號處理函數(shù)重新設(shè)置會出現(xiàn)競態(tài)條件。相同的,signal是線程不安全的,可能不適用于多線程。
? ? 2.? signal不能阻塞其他信號,只能通過sigprocmask()來顯示屏蔽或者接觸屏蔽信號
? ? 3.signal 只能傳輸一個信號編號,不能夠傳輸關(guān)于信號的其他信息?
因此,現(xiàn)在大多數(shù)都用sigaction()? 來代替 signal:
sigaction()? 的系統(tǒng)調(diào)用和 sigaction 結(jié)構(gòu)體如下:
int sigaction(int sigid ,const struct sigaction *act , struct sigaction *oldact );struct sigaction{ void (*sa_handler)(int); void (*sa_sigaction)(int , siginfo_t *,void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(int); }sa_handler : 指向處理函數(shù)的指針
sa_sigaction :另一種方法,指向處理函數(shù)的指針 , 外加上了兩個額外的參數(shù)。其中siginfo_t 接收信號的更多信息。
sa_mask? ?: 在處理函數(shù)執(zhí)行時,設(shè)置要阻塞的信號
sa_flags ;? ? 設(shè)置信號處理過程的行為,如果用sa_sigaction處理函數(shù),sa_flags 需設(shè)置為 SA_SIGINFO.
sigaction:的示例:
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <string.h> void handler (int sig ,siginfo_t *siginfo , void * context){printf("handler:sig = %d from PID=%d UID=%d \n",sig,siginfo->si_pid,siginfo->si_uid); }int main(int argc, char * argv[]){struct sigaction act ;memset(&act ,0 ,sizeof(act));act.sa_sigaction = &handler ;act.sa_flags =SA_SIGINFO ;sigaction(SIGTERM,&act ,NULL);printf("looping\n");printf("enter kill PID=%d to send SIGTERM signal to it \n" , getpid());while(1){sleep(10);} }上面的代碼利用sigaction 重新設(shè)置SIGTERM (15) 信號,當收到 15信號時就會執(zhí)行 handler內(nèi)容。開啟另一個會話對進程發(fā)出kill命令,則會有上面的輸出結(jié)果。最后發(fā)出的8信號對應(yīng)是浮點異常信號,雖然上述程序并不會出現(xiàn)浮點異常,但是由于程序收到了該信號,就執(zhí)行默認的信號8處理函數(shù)了.
三、利用sigaction和管道實現(xiàn)消息IPC
Linux 管道和和文件描述符的相關(guān)內(nèi)容:可看
https://blog.csdn.net/superSmart_Dong/article/details/118641774
/****sigaction*****/ #include <stdio.h> #include <fcntl.h> #include <string.h> #include <signal.h>#define LEN 64 int ppipe[2]; //管道-文件描述符 int pid ; char line[LEN]; int parent(){printf("parent %d running \n",getpid());close(ppipe[0]); // 關(guān)閉標準輸入文件描述符while(1){printf("parent %d : input a line : \n" ,getpid());fgets(line ,LEN,stdin); // line[LEN]得到標準輸入line[strlen(line) -1 ] = 0 ;printf("parent %d write to pipe",getpid());write(ppipe[1],line,LEN); //向管道寫入內(nèi)容printf("parent %d send signal 10 to %d",getpid(),pid);kill(pid,SIGUSR1);} } void chandler(int sig){printf ("\n child %d got an interrupt sig=%d \n ",getpid(),sig);read(ppipe[0] ,line ,LEN); //向管道讀出內(nèi)容 printf("child %d get a message = %s \n" ,getpid(),line); } int child(){char msg[LEN];int parent =getppid();printf("child %d running \n",getpid());close(ppipe[1]);signal(SIGUSR1,chandler);while(1); } int main(int argc, char * argv[]){pipe(ppipe);pid = fork();if (pid){parent();}else{child();} }?
?
?
總結(jié)
以上是生活随笔為你收集整理的Linux C: 信号及异常和捕捉函数原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux C: 定时器及时钟服务
- 下一篇: 转:ext2文件系统详解