信号:是一种特殊的IPC(进程间通讯),它是系统里面已经设计好了的,我们只能去使用它,且是一种异步通信方式
关于linux下的信号:
(1).一共有64个信号值;可以用:kill -l 命令查询所有的信号值
(2).前面1-31种信号值是不可靠信号值(非实时信号),同时发送多个一样的信号只会响应第一个信号,后面的会忽略掉;
(3).后面32-64是后来添加的是可靠的信号值(实时信号值) ,无论发送多少个一样的信号都会一一响应
(4).比较常用的信号有:
2号SIGINT:终止一个进程(ctrl + c);
9号SIGKILL:强制杀死一个进程;
18号SIGCONT:回复一个进程的运行(之前已经执行了 SIGSTOP 这个暂停信号);
19号SIGSTOP:暂停一个进程的运行,并没有结束掉
5.信号间可以嵌套,但是会按接收到的顺序逐一响应,接收到多个相同的不可靠信号(1-31号信号)只会执行一个
一、信号中的kill和killall
(1)命令使用kill和killall指令发送信号给某一进程:
(1)kill -信号值 进程的pid号
(注:可先使用ps -ef查看目前系统正在运行的所有进行及其进程号等进程信息)
(2)killall -信号值 进程的名字
(2)代码使用kill函数发送信号给某一进程:
函数作用:
给一个进程发送信号
函数原型:
#include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);
函数形参:
pid:想要用于接收发送出去的信号的进程pid号
sig:需要发送的信号值
函数返回值:
成功返回0
失败返回-1
测试代码:
/**测试kill代码实现*/#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <signal.h>int main(){//创建一个子进程pid_t id = fork();if(id<0){perror("fork failed");return -1;}else if(id==0){printf("hello world\n");//把自己杀掉kill(getpid(),9);printf("i am child\n");}printf("hello world\n");//退出程序并且刷新缓冲区exit(0);}
运行结果:
(1)因为子进程的中的kill(getpid(),9);,把自己pid获取到后把自己杀掉了,因此下面那一句”i am child“没有输出
一、信号中的signal等函数
进程对信号的操作主要有:
1、缺省 —》执行默认动作
2、捕捉 —》接收到信号后,执行自己规定的动作
3、忽略 —》接收到信号后,不执行任何操作
4、阻塞 —》接收到信号后,不立即响应,等该信号被解除阻塞,再响应(延迟响应)
(注:9,18,19这几个信号不可捕捉,忽略,阻塞;否则所有进程都忽略了这几个函数所有进程将变成精灵进程,当进程创建到达最大数时将会导致系统奔溃)
1.捕捉与忽略信号函数signal
函数原型:
#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
函数形参:
typedef void (*sighandler_t)(int):宏定义了一个函数指针,返回值类型为void,形参类型为:int
signum:需要捕捉的信号值
handler:本质是一个指针函数,接受接收到相应信号执行的函数,也可使用:
SIG_IGN —》忽略
SIG_DFL —》执行默认动作
函数返回值:
成功:返回前一个这个信号处理的回调函数的指针
出错:出现错误时,返回SIG_ERR
测试代码:
/*信号相关函数测试*/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <signal.h>void handel(int num){if(num == 2){printf("catch the 2 signal\n");}else if(num == 3){printf("catch the 3 signal\n");}else if(num == 4){printf("catch the 4 signal\n");}}int main(){//捕捉2,3,4号信号,并且执行相对应的函数signal(2,handel);signal(3,handel);signal(4,handel);//创建一个子进程while(1){printf("hello world\n");sleep(1);}//退出程序并且刷新缓冲区exit(0);}
运行结果:
(1)当捕捉到2,3,4号信号时将执行相对应的代码,且不再执行系统默认动作
(2)当signal(int signum, sighandler_t handler);的handler设置SIG_IGN时接收到相对应的信号时将不执行任何动作
(3)当signal(int signum, sighandler_t handler);的handler设置SIG_DFL时接收到相对应的信号时将执行系统默认的动作(和正常使用效果一样)
2.阻塞相关信号函数:
阻塞时,该信号不执行,等待解除阻塞再执行该信号,并且如果是1-31号的信号,发送多个相同信号也只会执行一次
常用的阻塞信号函数原型:
//一个信号列表(集合)sigset_t set;//信号值int signum;//清空一个信号列表(集合)int sigemptyset(sigset_t *set);//把所有的信号都加入一个信号列表(集合)int sigfillset(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);//把一个信号列表(集合)设置为阻塞状态或解除阻塞//how:(1)SIG_BLOCK:在原有的阻塞信号中再添加信号列表(集合)中的信号// (2)SIG_UNBLOCK:在原有阻塞信号基础上(在阻塞信号中找)解除信号列表(集合)中的信号// (3)SIG_SETMASK:把原有的阻塞信号全部替换成信号列表(集合)中的信号////const sigset_t *set:新的信号列表(集合)//sigset_t *oldset:原有的信号列表(集合)int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
函数的返回值:
成功:sigismember返回1,其他函数均为返回0
失败:sigismember返回0,其他函数均为返回-1
测试代码:
/**信号阻塞测试代码*/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <signal.h>int main(){//定义一个信号列表(集合)sigset_t set;//清空一下信号列表(集合)sigemptyset(&set);//添加信号到信号列表sigaddset(&set,2);sigaddset(&set,3);sigaddset(&set,4);//设置信号列表为阻塞sigprocmask(SIG_SETMASK, &set, NULL);int i=0;while(1){//判断i是否为20,如果是就解除阻塞if(i==20){//解除信号阻塞sigprocmask(SIG_UNBLOCK, &set, NULL);}//最后看会不会打印这行printf("hello world\n");sleep(1);//每次加1i++;}//退出程序并且刷新缓冲区exit(0);}
运行结果:
(1)虽然一开始从代码开始运行就发送了信号过去,当前为阻塞为阻塞状态,收到的信号会等解除阻塞再去执行
(2)其实只执行了第一个信号,因为第一个信号就把进程终结掉了,后面的并没有执行,以至于最后打印了一个找不到这个进程