目录
一、信号的概念
二、信号的处理
三、信号的发送
四、通过信号处理僵死进程
一、信号的概念
在Linux系统中,信号是一种进程间通信的机制,用于通知进程发生了某个事件。信号可以由内核、其他进程或进程自身发送。信号的本质就是用软件来模拟中断的行为。例如,当用户按下Ctrl+C键发送中断信号给一个运行中的进程时,该进程会收到SIGINT信号,通常会导致进程终止。
Linux系统提供了多种信号,每个信号都用一个唯一的整数值标识。有一些常见的信号,比如SIGINT(中断)、SIGKILL(强制终止)、SIGTERM(正常终止)、SIGCHLD(子进程状态改变)等。通过发送不同的信号,可以实现进程的控制、通知和同步。
二、信号的处理
在Linux中,进程可以通过系统调用signal()
或sigaction()
来注册信号处理函数,以处理接收到的信号。处理函数可以是系统提供的默认处理方式,也可以是自定义的处理方式。通过信号的机制,可以实现进程之间的通信、协作和错误处理等功能。
进程可以对信号进行以下几种处理:
默认处理:系统为每种信号定义了默认行为。例如,SIGINT 的默认行为是终止进程。
忽略信号:进程可以选择忽略某些信号(除了 SIGKILL 和 SIGSTOP)。收到信号但不执行
捕获信号:进程可以通过信号处理函数捕获并处理信号。用户捕捉某个或某些信号,并对它们的功能进行修改
信号处理函数是一个用户自定义的函数,当进程接收到信号时,会调用该函数。可以通过 signal 或 sigaction 系统调用来设置信号处理函数。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>void fun(int sig)
{printf("sig=%d\n",sig);//2
}int main()
{signal(SIGINT,fun);//设置信号的响应方式,达成约定,如果接收到信号,就调用fun函数while(1){printf("hello pid=%d\n",getpid());sleep(1);}
}
运行结果:
运行程序会无限循环打印"hello的pid号",按Ctrl+C时,程序不会终止,而是会捕获SIGINT信号,并调用fun函数打印出信号的编号。只有按Ctrl+\ 时才会终止程序。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>void fun(int sig)
{printf("sig=%d\n",sig);//2signal(sig,SIG_DFL);
}int main()
{signal(SIGINT,fun);//设置信号的响应方式while(1){printf("hello pid=%d\n",getpid());sleep(1);}
}
运行结果:
和上次不同的是调用fun函数后会执行默认的代码,然后接收到默认的信号,按Ctrl+C会打印出信号的编号,在下一次执行的时候按Ctrl+C就会结束程序。
三、信号的发送
使用kill命令,他需要的头文件和函数形式如下:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
kill函数的两个参数中,第一个参数pid代表的是给哪个进程发送,这个进程的pid号;第二个参数是信号的代号。发送成功与否我们要看它的返回值,如果返回值是-1,那么就发送失败了。
第一种方式:atoi
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>//argc 参数个数
//argv 参数内容
int main(int argc,char* argv[])
{if(argc!=3){printf("argc err\n");exit(1);}int pid=atoi(argv[1]);int sig=atoi(argv[2]);if(kill(pid,sig)==-1){printf("kill err\n");exit(1);}exit(0);//printf("argc=%d\n",argc);//for(int i=0;i<argc;i++)//{// printf("argv[%d]=%s\n",i,argv[i]);//}
}
运行结果:
第二种方式:sscanf
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>//argc 参数个数
//argv 参数内容
int main(int argc,char* argv[])
{if(argc!=3){printf("argc err\n");exit(1);}int pid=0;int sig=0;sscanf(argv[1],"%d",&pid);sscanf(argv[2],"%d",&sig);if(kill(pid,sig)==-1){printf("kill err\n");exit(1);}exit(0);
}
运行结果:
四、通过信号处理僵死进程
通过以下代码可以看出子进程先结束,父进程没有获取子进程的退出码,使进程变成僵死进程。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
#include<sys/wait.h>void fun(int sig)
{printf("sig=%d\n",sig);
}
int main()
{int n=0;char*s=NULL;signal(SIGCHLD,fun);pid_t pid=fork();if(pid==-1){exit(1);}if(pid==0){n=2;s="child";}else{n=7;s="parent";}for(int i=0;i<n;i++){printf("s=%s\n",s);sleep(1);}exit(0);
}
运行结果:
那么我们就可以利用信号处理僵死进程:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>// 信号处理函数,用于打印接收到的信号编号,并等待子进程结束
void fun(int sig)
{wait(NULL);printf("sig=%d\n",sig);
}int main()
{int n = 0;char* s = NULL;signal(SIGCHLD,fun); // 注册信号处理函数fun,用于处理SIGCHLD信号pid_t pid = fork();if (pid == -1) {exit(1); }if (pid == 0) {n = 3;s = "child";}else{n = 7;s = "parent";}// 根据进程类型打印相应次数的标识字符串for(int i = 0; i < n; i++){printf("%s\n",s);sleep(1); }exit(0);
}
运行结果: