編程-信號(hào)(黑馬筆記))
當(dāng)一個(gè)進(jìn)程跟另外一個(gè)進(jìn)程發(fā)送信號(hào)后接收信號(hào)的進(jìn)程有以下幾種行為1.執(zhí)行默認(rèn)處理 2.忽略 3.捕捉后特定處理1.kill函數(shù)發(fā)送信號(hào)的函數(shù)#include sys/types.h #include signal.h int kill(pid_t pid, int sig);參數(shù)說明pid指定接收信號(hào)的進(jìn)程 ID進(jìn)程標(biāo)識(shí)符有幾種特殊情況pid 0向 PID 為pid的單個(gè)進(jìn)程發(fā)送信號(hào)。pid 0向當(dāng)前進(jìn)程所在的進(jìn)程組中所有進(jìn)程發(fā)送信號(hào)。pid -1向系統(tǒng)中所有有權(quán)限發(fā)送的進(jìn)程發(fā)送信號(hào)一般用于管理員操作。pid -1向 PID 為|pid|的進(jìn)程組中所有進(jìn)程發(fā)送信號(hào)。sig指定要發(fā)送的信號(hào)如SIGKILL、SIGTERM、SIGALRM等常見信號(hào)SIGKILL強(qiáng)制終止進(jìn)程無法被捕獲或忽略、SIGTERM請(qǐng)求終止進(jìn)程可被捕獲處理、SIGSTOP暫停進(jìn)程等。若sig 0不發(fā)送任何信號(hào)僅用于檢查目標(biāo)進(jìn)程是否存在返回值可判斷進(jìn)程是否存活。返回值成功返回0。失敗返回-1并設(shè)置errno表示錯(cuò)誤原因如目標(biāo)進(jìn)程不存在、無權(quán)限發(fā)送信號(hào)等。這里必須在父進(jìn)程中找到對(duì)應(yīng)的子進(jìn)程id#include stdio.h #include stdlib.h #include sys/types.h #include signal.h #include unistd.h void sys_err(const char *str) { perror(str); exit(1); } int main() { pid_t pid; pid_t res; int i; for(i0;i5;i){ pid fork(); if(i2){ //這里必須在父進(jìn)程中獲取子進(jìn)程的id才行 res pid; } if(pid0) { break; } // 子進(jìn)程跳出循環(huán) } if (pid 0) { sleep(5); // 讓子進(jìn)程先運(yùn)行一會(huì) // 向子進(jìn)程發(fā)送SIGTERM信號(hào) if (kill(res, SIGTERM) -1) { sys_err(kill error); } printf(parent process send SIGTERM to child process\n); } else if (pid 0) { while (1) { printf(child process is running, my %dth\n, i1); sleep(1); } } else { sys_err(fork error); } return 0; }2.alarm函數(shù)alarm函數(shù)是 Unix/Linux 系統(tǒng)中用于設(shè)置一次性定時(shí)器的系統(tǒng)調(diào)用作用是在指定時(shí)間秒數(shù)后向當(dāng)前進(jìn)程發(fā)送SIGALRM信號(hào)與之前提到的定時(shí)器信號(hào)相同。函數(shù)原型#include unistd.h unsigned int alarm(unsigned int seconds);參數(shù)與返回值參數(shù)seconds指定定時(shí)器的秒數(shù)。若seconds 0取消當(dāng)前已設(shè)置的鬧鐘如果存在。若seconds 0設(shè)置一個(gè)新的鬧鐘seconds秒后發(fā)送SIGALRM信號(hào)。返回值如果之前沒有設(shè)置過未到期的鬧鐘返回0。如果之前已設(shè)置過鬧鐘且尚未到期返回剩余的秒數(shù)即距離上一個(gè)鬧鐘到期還剩多少秒同時(shí)新的鬧鐘會(huì)覆蓋舊的設(shè)置。核心功能alarm函數(shù)會(huì)讓系統(tǒng)在seconds秒后自動(dòng)向當(dāng)前進(jìn)程發(fā)送SIGALRM信號(hào)。默認(rèn)情況下SIGALRM信號(hào)的處理行為是終止進(jìn)程但可以通過signal函數(shù)注冊(cè)自定義處理函數(shù)如之前代碼中的myfunc從而實(shí)現(xiàn) “定時(shí)執(zhí)行某個(gè)操作” 的效果。需要注意的是alarm是一次性定時(shí)器信號(hào)觸發(fā)后定時(shí)器自動(dòng)失效若需周期性觸發(fā)需在信號(hào)處理函數(shù)中再次調(diào)用alarm重置。#include stdio.h #include unistd.h #include signal.h #include sys/time.h int main(void) { int i; //自然時(shí)間 alarm(1); for(i 0; ; i) printf(%d\n, i); return 0; }3.setitimer函數(shù)等待的時(shí)間和定時(shí)器的時(shí)間內(nèi)是不是都在運(yùn)行主程序只是時(shí)間到了會(huì)觸發(fā)中斷運(yùn)行一次處理函數(shù)然后繼續(xù)執(zhí)行主程序豆包答是的你的理解完全正確函數(shù)原型#include sys/time.h int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);參數(shù)說明which指定定時(shí)器類型有 3 種可選ITIMER_REAL實(shí)時(shí)時(shí)鐘以系統(tǒng)真實(shí)時(shí)間計(jì)時(shí)到期后發(fā)送SIGALRM信號(hào)與alarm一致。ITIMER_VIRTUAL虛擬時(shí)鐘僅計(jì)算進(jìn)程在用戶態(tài)運(yùn)行的時(shí)間到期后發(fā)送SIGVTALRM信號(hào)。ITIMER_PROF統(tǒng)計(jì)時(shí)鐘計(jì)算進(jìn)程在用戶態(tài) 內(nèi)核態(tài)的總運(yùn)行時(shí)間到期后發(fā)送SIGPROF信號(hào)。最常用的是ITIMER_REAL對(duì)應(yīng)真實(shí)時(shí)間的定時(shí)new_value指向struct itimerval結(jié)構(gòu)體的指針用于設(shè)置定時(shí)器的參數(shù)首次觸發(fā)時(shí)間和周期結(jié)構(gòu)體定義如下c運(yùn)行struct itimerval { struct timeval it_interval; // 周期性觸發(fā)的時(shí)間間隔0表示僅觸發(fā)一次 struct timeval it_value; // 首次觸發(fā)的延遲時(shí)間0表示關(guān)閉定時(shí)器 }; struct timeval { time_t tv_sec; // 秒數(shù) suseconds_t tv_usec; // 微秒數(shù)0-999999 };old_value指向struct itimerval結(jié)構(gòu)體的指針用于存儲(chǔ)上一次定時(shí)器的設(shè)置若不需要可傳NULL。返回值成功返回0。失敗返回-1并設(shè)置errno表示錯(cuò)誤原因如參數(shù)無效。#include stdio.h #include signal.h #include sys/time.h #include stdlib.h int count0; void myfunc(int signo) { printf(count %d\n,count); count0; } int main(void) { struct itimerval it, oldit; signal(SIGALRM, myfunc); //itvalue:第一次定時(shí)器到期的時(shí)間間隔 it.it_value.tv_sec 1; it.it_value.tv_usec 0; //itinterval:后續(xù)定時(shí)器到期的時(shí)間間隔 sec是秒usec是微秒兩個(gè)之和是最后的時(shí)間 it.it_interval.tv_sec 2; it.it_interval.tv_usec 0; if (setitimer(ITIMER_REAL, it, oldit) -1) { perror(setitimer error); return -1; } while (1){ count; } return 0; }4.sigaction函數(shù)函數(shù)原型#include signal.h int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);參數(shù)說明signum類型為int表示需要處理的信號(hào)編號(hào)。在 Linux 系統(tǒng)中常見的信號(hào)有SIGINT通常由Ctrl C觸發(fā)編號(hào)為 2 、SIGQUIT通常由Ctrl \觸發(fā)編號(hào)為 3 、SIGTERM程序終止信號(hào)編號(hào)為 15 等。不同的信號(hào)有不同的默認(rèn)行為通過sigaction函數(shù)可以改變這些默認(rèn)行為。act類型為const struct sigaction *是一個(gè)指向sigaction結(jié)構(gòu)體的指針用于指定信號(hào)signum的新處理方式。sigaction結(jié)構(gòu)體通常包含以下幾個(gè)重要成員sa_handler這是一個(gè)函數(shù)指針指向自定義的信號(hào)處理函數(shù)。當(dāng)接收到指定信號(hào)時(shí)系統(tǒng)就會(huì)調(diào)用這個(gè)函數(shù)來處理信號(hào)。例如在前面的代碼中act.sa_handler sig_catch;就是將sig_catch函數(shù)設(shè)置為對(duì)應(yīng)信號(hào)的處理函數(shù)。sa_mask是一個(gè)信號(hào)集用于指定在執(zhí)行信號(hào)處理函數(shù)時(shí)需要額外屏蔽的信號(hào)。也就是說在處理某個(gè)信號(hào)期間如果又收到了sa_mask中包含的信號(hào)這些信號(hào)會(huì)被暫時(shí)阻塞直到當(dāng)前信號(hào)處理函數(shù)執(zhí)行完畢。可以使用sigemptyset函數(shù)來清空信號(hào)集用sigaddset函數(shù)向信號(hào)集中添加信號(hào)。sa_flags是一些標(biāo)志位用于指定信號(hào)處理的一些選項(xiàng)常見取值有0表示使用默認(rèn)行為。??SA_RESTART若信號(hào)中斷了某些系統(tǒng)調(diào)用如read、write等在信號(hào)處理完成后會(huì)自動(dòng)重啟這些系統(tǒng)調(diào)用而不是讓系統(tǒng)調(diào)用返回錯(cuò)誤。oldact類型為struct sigaction *也是一個(gè)指向sigaction結(jié)構(gòu)體的指針。如果它不為NULLsigaction函數(shù)會(huì)將信號(hào)signum原來的處理方式存儲(chǔ)到oldact指向的結(jié)構(gòu)體中。這樣后續(xù)如果需要恢復(fù)信號(hào)的原來處理方式可以再次調(diào)用sigaction函數(shù)并傳入保存的oldact信息。返回值sigaction函數(shù)調(diào)用成功時(shí)返回0。調(diào)用失敗時(shí)返回-1并且會(huì)設(shè)置errno來指示錯(cuò)誤原因常見的錯(cuò)誤原因有無效的信號(hào)編號(hào)等。#include stdio.h #include stdlib.h #include signal.h #include string.h #include unistd.h // 包含 sleep 函數(shù)的聲明 void sys_err(const char *str) { perror(str); exit(1); } //為了避免死循環(huán)默認(rèn)情況下是自動(dòng)屏蔽自己的信號(hào)的 void sig_catch(int signo) // 回調(diào)函數(shù) { if (signo SIGINT) { sleep(10); printf(catch you!! %d\n, signo); } else if (signo SIGQUIT) printf(catch SIGQUIT!! %d\n, signo); return; } int main(int argc, char *argv[]) { struct sigaction act, oldact; act.sa_handler sig_catch; // 設(shè)置回調(diào)函數(shù) sigemptyset(act.sa_mask); // 清空sa_mask屏蔽字只在sig_catch工作時(shí)有效 默認(rèn)初始化變成不屏蔽所有 //添加屏蔽字 sigaddset(act.sa_mask,SIGQUIT); //僅僅只是執(zhí)行期間 act.sa_flags 0; // 設(shè)置信號(hào)處理的方式 默認(rèn)情況下為0 int ret sigaction(SIGINT, act, oldact); // 注冊(cè)SIGINT信號(hào)捕捉函數(shù) if (ret -1) sys_err(sigaction error for SIGINT); ret sigaction(SIGQUIT, act, oldact); // 注冊(cè)SIGQUIT信號(hào)捕捉函數(shù) if (ret -1) sys_err(sigaction error for SIGQUIT); while (1); return 0; }通過sigaction函數(shù)對(duì)子進(jìn)程進(jìn)行回調(diào)回收#include stdio.h #include stdlib.h #include unistd.h #include signal.h #include sys/types.h #include sys/wait.h // 信號(hào)處理函數(shù)用于回收子進(jìn)程 void catch_child(int signo) { pid_t pid; int status; // 循環(huán)回收所有已終止的子進(jìn)程防止僵尸進(jìn)程 while ((pid waitpid(-1, status, WNOHANG)) 0) { if (WIFEXITED(status)) { printf(子進(jìn)程 %d 正常退出退出碼%d\n, pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf(子進(jìn)程 %d 被信號(hào) %d 終止\n, pid, WTERMSIG(status)); } } } int main() { pid_t pid; sigset_t set; // 阻塞 SIGCHLD 信號(hào) sigemptyset(set); sigaddset(set, SIGCHLD); sigprocmask(SIG_BLOCK, set, NULL); int i; for (i 0; i 15; i) { if ((pid fork()) 0) { // 子進(jìn)程直接退出 break; } } if (i 15) { // 父進(jìn)程邏輯 struct sigaction act; act.sa_handler catch_child; // 設(shè)置信號(hào)處理函數(shù) sigemptyset(act.sa_mask); // 清空信號(hào)屏蔽集 act.sa_flags 0; // 默認(rèn)屬性 // 注冊(cè) SIGCHLD 信號(hào)的處理函數(shù) sigaction(SIGCHLD, act, NULL); // 解除對(duì) SIGCHLD 信號(hào)的阻塞 sigprocmask(SIG_UNBLOCK, set, NULL); printf(Im parent, pid %d\n, getpid()); // 模擬父進(jìn)程后續(xù)邏輯持續(xù)運(yùn)行 while (1) { sleep(1); } } else { // 子進(jìn)程邏輯直接打印后退出 printf(Im child pid %d\n, getpid()); exit(0); } return 0; }