信号
信号的概念
信号是系统响应某个条件而产生的事件,进程接收到信号会执行相应的操作。
与信号有关的系统调用在“signal.h”头文件中有声明
响应信号
在键盘上按下 Ctrl+c 时,会给当前终端前台执行的进程发送 SIGINT 信号:
正常Ctrl+c 终止执行
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<signal.h>
int main(){
while(1){
printf("hello\n");
sleep(1);
}
exit(0);
}
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数含义:signum 代表信号的值;handler 是一个函数指针此处调用函数
忽略:SIG_IGN
默认:SIG_DEF
自定义:自己编写的信号函数
用 signal 修改 SIGINT 信号的响应方式示例代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<signal.h>
void sig_fun(int sig){
printf("sig=%d\n",sig);
}
int main(){
signal(SIGINT,sig_fun);//收到SIGINT信号才会执行
while(1){
printf("hello\n");
sleep(1);
}
}
此时可以看出Ctrl+c 并不能像之前一样结束程序执行.
使用signal之后,按下Ctrl+c会收到SIGINT的信号,直接会产生软中断,按照signal函数第二个参数确定此时执行的方式(此处是自定义方式),去执行signal中函数指针参数所指向的函数(此函数参数为信号的值).执行完signal()之后恢复程序执行.
修改程序使得第一次接收到SIGINT信号后打印信号值第二次接收到SIGINT信号后正常终止.
void sig_fun(int sig){
printf("sig=%d\n",sig);
signal(SIGINT,SIG_DFL);//此时重新设置了响应方式
}
发送信号
kill() 可以向指定的进程发送指定的信号:
int kill(pid_t pid, int sig);
pid > 0 指定将信号发送个那个进程
pid == 0 信号被发送到和当前进程在同一个进程组的进程
pid == -1 将信号发送给系统上有权限发送的所有的进程
pid < -1 将信号发送给进程组 id 等于 pid 绝对值,并且有权限发送的所有的进程。
sig 指定发送信号的类型。
向进程发送信号(与上面结合)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<signal.h>
//./mykill pid sig
int main(int argc,char *argv[]){
if(argc!=3){//argv[1]存放pid argv[2]存放sig
printf("mykill arg err\n");
exit(0);
}
int pid=0;
int sig=0;
sscanf(argv[1],"%d",&pid);//字符串转整型
sscanf(argv[2],"%d",&sig);
//给进程号==pid的进程发送sig信号
if(kill(pid,sig)==-1){
printf("kill err\n");
}
exit(0);
}
第一次发送信号 打印信号值 第二次发送信号终止
kill -9 pid
9号信号的响应方式不允许改变(不允许使用signal改变响应方式,改变不会成功,只能以默认方式响应).
SIGCHLD 17 子进程结束后,父进程会收到该信号.
代码演示
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<signal.h>
#include<sys/wait.h>
void sig_fun1(int sig){
printf("sig=%d ",sig);
wait(NULL);//此时不会阻塞父进程
//只有在子进程执行完成后才会发出信号执行自定义函数,获取子进程退出码
}
int main(){
int n=0;
char *s=NULL;
signal(SIGCHLD,sig_fun1);
pid_t pid= fork();
assert(pid!=-1);
if(pid==0){
s="child";
n=3;
}
else{
s="parent";
n=7;
}
for(int i=0;i<n;++i){
printf("s=%s\n",s);
sleep(1);
}
exit(0);
}
子进程结束后发出信号,父进程捕获到信号,响应信号转到此处自定义函数执行,打印信号的值.
signal(SIGCHLD,SIG_IGN)Linux特有 unix不能, 设置成忽略,和内核达成约定不关注子进程,子进程结束完该释放释放,不用保留退出码,不关心.