信号章节 -- 信号章节整体概要异步
信号基本概念函数
信号是异步事件,发送信号的线程能够继续向下执行而不阻塞。spa
信号无优先级。线程
1到31号信号是非实时信号,发送的信号可能会丢失,不支持信号排队。3d
31号信号到64是实时信号, 发送的信号都会被接收, 支持信号排队。blog
信号在Linux内核头文件中的宏定义进程
信号的处理事件
因为进程启动时,SIGUSR1和SIGUSR2被忽略,通常咱们能够在有须要时,去捕获这两个信号,进而调用本身的处理函数。相应的,咱们的程序其余地方去发送相应的信号。资源
signal函数原型 以及使用时所要包含的头文件get
和下面的是等价的:
实验1 signal基本使用
实验1.1
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//定义信号处理函数
//signo: 进程捕获到的信号
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
}
int main(){
#if 1 // 屏蔽这块代码,就是不捕获这俩信号
//向内核登记信号处理函数以及信号值
if(signal(SIGTSTP, sig_handler) == SIG_ERR){
perror("signal error");
}
if(signal(SIGINT, sig_handler) == SIG_ERR){
perror("signal sigint error");
}
#endif
while(1){
sleep(1);
printf("- hello -\n");
};
return 0;
}
编译运行
同时,根据这里的打印也能够看出,
SIGINT信号就是2号信号, 咱们在键盘上按下CTRL+C就能够发送该信号了。
SIGTSTP信号就是20号信号,咱们在键盘上按下CTRL+Z就能够发送该信号了。20号信号的备注就是 Keyboard stop, 即经过键盘发信号让进程中止。
实验1.2
若是屏蔽实验1内捕获这俩信号的代码块,运行效果以下
经常使用知识点补充:
19) SIGSTOP 20) SIGTSTP
19号信号和29号信号的相同点: 均可以使得进程暂停,而且收到SIGCONT信号后可让进程从新运行。
19号信号和29号信号的不一样点: SIGSTOP不能够捕获(即便用信号处理函数)。
那么,咱们来让刚才中止的a.out继续运行吧:
先查看a.out的pid
可见a.out的pid是8349
咱们经过kill来发SIGCONT信号(18号信号)让a.out继续运行
可见,a.out又继续运行起来了,
然而,须要注意的是,经过18号信号被继续执行的进程:当终端内按下CTRL+C,则不能使得该进程终止了;且按下CTRL+Z,终端内也毫无迹象;可是能够经过kill -9被杀死。
根据实测,是这样的,事实胜于雄辩。这个问题的缘由以及背后隐藏的暂时咱们还不知的相应知识点,能够留待之后探索,咱们先暂且知道这么一回事就好了。
实验2 SIG_DFL 和 SIG_IGN 使用
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//定义信号处理函数
//signo: 进程捕获到的信号
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
}
int main(){
printf("pid: %d \n", getpid());
#if 1 // 屏蔽这块代码,就是不捕获这俩信号
//向内核登记信号处理函数以及信号值
if(signal(SIGTSTP, SIG_IGN) == SIG_ERR){
perror("signal error");
}
if(signal(SIGINT, SIG_DFL) == SIG_ERR){
perror("signal sigint error");
}
#endif
while(1){
sleep(1);
printf("- hello -\n");
};
return 0;
}
此时,按下CTRL+Z对程序运行将毫无影响,而CTRL+C则采用默认方式,即结束进程。
实验3 SIGUSR1 和 SIGUSR2 使用
注意,这两个信号在进程启动时默认是被忽略的。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//定义信号处理函数
//signo: 进程捕获到的信号
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
}
int main(){
printf("pid: %d \n", getpid());
#if 1 // 屏蔽这块代码,就是不捕获这俩信号
//向内核登记信号处理函数以及信号值
if(signal(SIGUSR1, sig_handler) == SIG_ERR){
perror("signal error");
}
if(signal(SIGUSR2, sig_handler) == SIG_ERR){
perror("signal sigint error");
}
#endif
while(1){
sleep(1);
printf("- hello -\n");
};
return 0;
}
编译运行,同时在另外一个终端内发送信号 kill -SIGUSR1 10452 、 kill -SIGUSR2 10452
实验4
知识点:SIGKILL 和 SIGSTOP不能被忽略,也不能被捕获。
本实验将尝试捕获SIGKILL和SIGSTOP,并以SIG_IGN的方式进程处理。
核心代码展现:
编译运行将返回SIG_ERR,以下图所示
实验5 SIGCHLD
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
//定义信号处理函数
//signo: 进程捕获到的信号
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
wait(NULL);
}
int main(void)
{
pid_t pid;
if(signal(SIGCHLD, sig_handler) == SIG_ERR){
perror("signal error");
}
pid = fork();
if (pid < 0)
{
printf("fork error");
}
else if (pid == 0) /* first child : 子进程 */
{
sleep(2);
printf("pid: child =%ld\n", (long)getpid());
exit(0);
}
// 使用信号的方式,父进程没必要在此处阻塞调用wait,能够继续向下执行本身的任务。
while(1){
sleep(1);
printf("father can does his own things \n");
}
}
编译运行:
实验中可见,父进程收到了子进程的17号信号,17号信号就是SIGCHLD信号(或写做SIGCLD)
使用信号来回收子进程后,父进程没必要在阻塞调用wait,能够继续向下执行本身的任务。这个例子充分体现出了信号是一个异步事件。
在父进程还存活的期间,子进程退出将不会产生僵尸进程。
PS:父进程死后,确定不会有其子进程还仍然是僵尸进程,由于一个子进程们会在其父进程死后被1号进程领养,进而被1号进程回收掉所占用的系统资源。
.