信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。
信号是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。
信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
SIGALRM
信号,进程执行的CPU时间超限,或者该进程的某个子进程退出。kill
命令调用 kill 函数服务器程序必须处理(或至少忽略)一些常见的信号,以免异常终止。
查看系统定义的信号列表:kill -l
(前31个信号为常规信号,其余为实时信号)
查看信号的详细信息:man 7 signal
列出所有的参数选项:ulimit -a
信号的几种状态:产生、未决、抵达。
信号的5种默认处理动作 | |
---|---|
1. Term | 终止进程 |
2. Ign | 当前进程忽略此信号 |
3. Core | 终止进程,并生成一个Core文件 |
4. Stop | 暂停当前进程 |
5. Cont | 继续执行当前被暂停的进程 |
int kill (pid_t pid, int sig);
kill(getppid(), 9); // 给父进程发送9号信号 kill(getpid(), 0); // 给本进程发送9号信号
int raise (int sig);
void abort (void);
信号捕捉
sighandler_t signal (int signum, sighandler_t handler);
注意:信号 SIGKILL 和 SIGSTOP 不能被捕获或忽略。
unsigned int alarm (unsigned int seconds);
int setitimer (int which, const struct itimerval *new_value, struct itimerval *old_value);
许多信号相关的系统调用都需要能表示一组不同的信号,Linux使用数据结构信号集sigset_t来表示一组信号。
在PCB中有两个非常重要的信号集:“阻塞信号集”、“未决信号集”。“未决”是一种状态,指的是从信号产生到信号被处理前的这一段时间。“阻塞”是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。
头文件: #include <signal.h>
int sigfillset (sigset_t *_set)
int sigemptyset (sigset_t *set);
int sigaddset (sigset_t *set, int signum);
int sigdelset (sigset_t *set, int signum);
int sigismember (const sigset_t *set, int signum);
int sigprocmask (int how, const sigset_t *set, sigset_t *oldset);
int sigpending (sigset_t *set);
案例:
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { int num = 0; // 计数 // 1、创建信号集 sigset_t set; // 2、将2 3 号信号添加到信号集中 sigemptyset(&set); // 清空信号集 sigaddset(&set, SIGINT); sigaddset(&set, SIGQUIT); // 3、修改内核中信号集的阻塞状态 sigprocmask(SIG_BLOCK, &set, NULL); while (num < 10) { num++; // 获取当前未决信号集数据 sigset_t pendingset; sigemptyset(&pendingset); // 清空信号集 // 将未决信号集中的数据放入pendingset中 sigpending(&pendingset); // 遍历前32位 for (int i = 1; i <= 31; i++) { if (sigismember(&pendingset, i) == 1) { // i号信号在未决信号集中 printf("1"); } else if (sigismember(&pendingset, i) == 0) { printf("0"); } else { perror("sigismember"); exit(-1); } } printf("\n"); sleep(1); // if(num == 10) { // // 解除阻塞 // sigprocmask(SIG_UNBLOCK, &set, NULL); // } } return 0; }
运行结果:
无输入时,内核中前31个信号标志位均位0
输入Ctrl + C,信号SIGINT处于未决状态
输入Ctrl + \,信号SIGQUIT处于未决状态
添加计数变量num后,当num=10,sigprocmask()
函数解除信号集set阻塞,Ctrl + C产生的信号SIGINT结束当前进程。
int sigaction(int signum, 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; // 使用哪一个信号处理对捕捉到的信号进行处理 // 0,表示使用sa_handler; SA_SIGINFO表示使用sa_sigaction void (*sa_restorer)(void); // 被废弃掉了 };
案例:
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/time.h> void myalarm(int num) { printf("捕捉到了信号的编号是:%d\n", num); printf("xxxxxxx\n"); } // 过3秒以后,每隔2秒钟定时一次 int main() { struct sigaction act; act.sa_flags = 0; // 使用sa_handler处理 act.sa_handler = myalarm; // 使用回调函数 sigemptyset(&act.sa_mask); // 清空默认临时阻塞信号集 // 捕捉 SIGALRM 信号 sigaction(SIGALRM, &act, NULL); struct itimerval new_value; // 设置延迟的时间, 即3秒之后开始第一次定时 new_value.it_value.tv_sec = 3; new_value.it_value.tv_usec = 0; // 设置间隔的时间, 即每次倒计时2秒 new_value.it_interval.tv_sec = 2; new_value.it_interval.tv_usec = 0; int ret = setitimer(ITIMER_REAL, &new_value, NULL); // 真实时间、非阻塞的 printf("定时器开始了...\n"); if (ret == -1) { perror("setitimer"); exit(0); } while (1) ; return 0; }
运行结果:
SIGCHLD信号产生的3个条件:
1. 子进程结束
2. 子进程暂停
3. 子进程继续运行
都会给父进程发送该信号,父进程默认忽略该信号。
可以使用SIGCHLD信号解决子进程退出产生僵尸进程的问题。
案例:
#include <signal.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> void myFun(int num) { printf("捕捉到的信号 :%d\n", num); // 回收子进程PCB的资源,彻底结束子进程 while (1) { // 回收所有子进程资源,设置为非阻塞 int ret = waitpid(-1, NULL, WNOHANG); if (ret > 0) { // 返回值 >0, 已回收ret子进程资源 printf("chiled die, pid = %d\n", ret); } else if (ret == 0) { // options=WNOHANG, 表示还有子进程存在 break; } else if (ret == -1) { // 调用失败 break; } } } int main() { // 防止出现子进程运行结束,父进程还没有注册完信号捕捉 // 提前设置好阻塞信号集,阻塞SIGCHLD sigset_t set; sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, NULL); // 创建一些子进程 pid_t pid; for (int i = 0; i < 5; i++) { pid = fork(); if (pid == 0) { // 子进程 // break防止子进程再去进入循环生成子进程,保证只有5个子进程 break; } } if (pid > 0) { // 父进程 // 捕捉子进程死亡时发出的SIGCHLD信号 struct sigaction act; act.sa_flags = 0; act.sa_handler = myFun; sigemptyset(&act.sa_mask); sigaction(SIGCHLD, &act, NULL); // 信号捕捉注册完成,解除SIGCHLD阻塞 sigprocmask(SIG_UNBLOCK, &set, NULL); while (1) { printf("parent process pid = %d\n", getpid()); sleep(1); } } else if (pid == 0) { // 子进程 printf("child process pid = %d\n", getpid()); } return 0; }
运行结果: