Linux系统编程-信号(黑马笔记)

发布时间:2026/10/3 17:29:16
Linux系统编程-信号(黑马笔记) 当一个进程跟另外一个进程发送信号后接收信号的进程有以下几种行为1.执行默认处理 2.忽略 3.捕捉后特定处理1.kill函数发送信号的函数#include sys/types.h #include signal.h int kill(pid_t pid, int sig);参数说明pid指定接收信号的进程 ID进程标识符有几种特殊情况pid 0向 PID 为pid的单个进程发送信号。pid 0向当前进程所在的进程组中所有进程发送信号。pid -1向系统中所有有权限发送的进程发送信号一般用于管理员操作。pid -1向 PID 为|pid|的进程组中所有进程发送信号。sig指定要发送的信号如SIGKILL、SIGTERM、SIGALRM等常见信号SIGKILL强制终止进程无法被捕获或忽略、SIGTERM请求终止进程可被捕获处理、SIGSTOP暂停进程等。若sig 0不发送任何信号仅用于检查目标进程是否存在返回值可判断进程是否存活。返回值成功返回0。失败返回-1并设置errno表示错误原因如目标进程不存在、无权限发送信号等。这里必须在父进程中找到对应的子进程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){ //这里必须在父进程中获取子进程的id才行 res pid; } if(pid0) { break; } // 子进程跳出循环 } if (pid 0) { sleep(5); // 让子进程先运行一会 // 向子进程发送SIGTERM信号 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函数alarm函数是 Unix/Linux 系统中用于设置一次性定时器的系统调用作用是在指定时间秒数后向当前进程发送SIGALRM信号与之前提到的定时器信号相同。函数原型#include unistd.h unsigned int alarm(unsigned int seconds);参数与返回值参数seconds指定定时器的秒数。若seconds 0取消当前已设置的闹钟如果存在。若seconds 0设置一个新的闹钟seconds秒后发送SIGALRM信号。返回值如果之前没有设置过未到期的闹钟返回0。如果之前已设置过闹钟且尚未到期返回剩余的秒数即距离上一个闹钟到期还剩多少秒同时新的闹钟会覆盖旧的设置。核心功能alarm函数会让系统在seconds秒后自动向当前进程发送SIGALRM信号。默认情况下SIGALRM信号的处理行为是终止进程但可以通过signal函数注册自定义处理函数如之前代码中的myfunc从而实现 “定时执行某个操作” 的效果。需要注意的是alarm是一次性定时器信号触发后定时器自动失效若需周期性触发需在信号处理函数中再次调用alarm重置。#include stdio.h #include unistd.h #include signal.h #include sys/time.h int main(void) { int i; //自然时间 alarm(1); for(i 0; ; i) printf(%d\n, i); return 0; }3.setitimer函数等待的时间和定时器的时间内是不是都在运行主程序只是时间到了会触发中断运行一次处理函数然后继续执行主程序豆包答是的你的理解完全正确函数原型#include sys/time.h int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);参数说明which指定定时器类型有 3 种可选ITIMER_REAL实时时钟以系统真实时间计时到期后发送SIGALRM信号与alarm一致。ITIMER_VIRTUAL虚拟时钟仅计算进程在用户态运行的时间到期后发送SIGVTALRM信号。ITIMER_PROF统计时钟计算进程在用户态 内核态的总运行时间到期后发送SIGPROF信号。最常用的是ITIMER_REAL对应真实时间的定时new_value指向struct itimerval结构体的指针用于设置定时器的参数首次触发时间和周期结构体定义如下c运行struct itimerval { struct timeval it_interval; // 周期性触发的时间间隔0表示仅触发一次 struct timeval it_value; // 首次触发的延迟时间0表示关闭定时器 }; struct timeval { time_t tv_sec; // 秒数 suseconds_t tv_usec; // 微秒数0-999999 };old_value指向struct itimerval结构体的指针用于存储上一次定时器的设置若不需要可传NULL。返回值成功返回0。失败返回-1并设置errno表示错误原因如参数无效。#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:第一次定时器到期的时间间隔 it.it_value.tv_sec 1; it.it_value.tv_usec 0; //itinterval:后续定时器到期的时间间隔 sec是秒usec是微秒两个之和是最后的时间 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函数函数原型#include signal.h int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);参数说明signum类型为int表示需要处理的信号编号。在 Linux 系统中常见的信号有SIGINT通常由Ctrl C触发编号为 2 、SIGQUIT通常由Ctrl \触发编号为 3 、SIGTERM程序终止信号编号为 15 等。不同的信号有不同的默认行为通过sigaction函数可以改变这些默认行为。act类型为const struct sigaction *是一个指向sigaction结构体的指针用于指定信号signum的新处理方式。sigaction结构体通常包含以下几个重要成员sa_handler这是一个函数指针指向自定义的信号处理函数。当接收到指定信号时系统就会调用这个函数来处理信号。例如在前面的代码中act.sa_handler sig_catch;就是将sig_catch函数设置为对应信号的处理函数。sa_mask是一个信号集用于指定在执行信号处理函数时需要额外屏蔽的信号。也就是说在处理某个信号期间如果又收到了sa_mask中包含的信号这些信号会被暂时阻塞直到当前信号处理函数执行完毕。可以使用sigemptyset函数来清空信号集用sigaddset函数向信号集中添加信号。sa_flags是一些标志位用于指定信号处理的一些选项常见取值有0表示使用默认行为。⬇️SA_RESTART若信号中断了某些系统调用如read、write等在信号处理完成后会自动重启这些系统调用而不是让系统调用返回错误。oldact类型为struct sigaction *也是一个指向sigaction结构体的指针。如果它不为NULLsigaction函数会将信号signum原来的处理方式存储到oldact指向的结构体中。这样后续如果需要恢复信号的原来处理方式可以再次调用sigaction函数并传入保存的oldact信息。返回值sigaction函数调用成功时返回0。调用失败时返回-1并且会设置errno来指示错误原因常见的错误原因有无效的信号编号等。#include stdio.h #include stdlib.h #include signal.h #include string.h #include unistd.h // 包含 sleep 函数的声明 void sys_err(const char *str) { perror(str); exit(1); } //为了避免死循环默认情况下是自动屏蔽自己的信号的 void sig_catch(int signo) // 回调函数 { 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; // 设置回调函数 sigemptyset(act.sa_mask); // 清空sa_mask屏蔽字只在sig_catch工作时有效 默认初始化变成不屏蔽所有 //添加屏蔽字 sigaddset(act.sa_mask,SIGQUIT); //仅仅只是执行期间 act.sa_flags 0; // 设置信号处理的方式 默认情况下为0 int ret sigaction(SIGINT, act, oldact); // 注册SIGINT信号捕捉函数 if (ret -1) sys_err(sigaction error for SIGINT); ret sigaction(SIGQUIT, act, oldact); // 注册SIGQUIT信号捕捉函数 if (ret -1) sys_err(sigaction error for SIGQUIT); while (1); return 0; }通过sigaction函数对子进程进行回调回收#include stdio.h #include stdlib.h #include unistd.h #include signal.h #include sys/types.h #include sys/wait.h // 信号处理函数用于回收子进程 void catch_child(int signo) { pid_t pid; int status; // 循环回收所有已终止的子进程防止僵尸进程 while ((pid waitpid(-1, status, WNOHANG)) 0) { if (WIFEXITED(status)) { printf(子进程 %d 正常退出退出码%d\n, pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf(子进程 %d 被信号 %d 终止\n, pid, WTERMSIG(status)); } } } int main() { pid_t pid; sigset_t set; // 阻塞 SIGCHLD 信号 sigemptyset(set); sigaddset(set, SIGCHLD); sigprocmask(SIG_BLOCK, set, NULL); int i; for (i 0; i 15; i) { if ((pid fork()) 0) { // 子进程直接退出 break; } } if (i 15) { // 父进程逻辑 struct sigaction act; act.sa_handler catch_child; // 设置信号处理函数 sigemptyset(act.sa_mask); // 清空信号屏蔽集 act.sa_flags 0; // 默认属性 // 注册 SIGCHLD 信号的处理函数 sigaction(SIGCHLD, act, NULL); // 解除对 SIGCHLD 信号的阻塞 sigprocmask(SIG_UNBLOCK, set, NULL); printf(Im parent, pid %d\n, getpid()); // 模拟父进程后续逻辑持续运行 while (1) { sleep(1); } } else { // 子进程逻辑直接打印后退出 printf(Im child pid %d\n, getpid()); exit(0); } return 0; }