系列文章目录
进程间通信(零)——说明
进程间通信(一)——管道
进程间通信(二)——信号
进程间通信(三)——共享内存
进程间通信(四)——信号量
一、信号的概念
Linux中响应各类事件的机制
常用的信号有:
SIGINT 程序终止信号。按下Ctrl+C时通知前台进程组终止进程
SIGQUIT 类似程序错误信号,Ctrl+\控制,进程退出时会产生core文件
SIGILL 表示执行了非法指令。通常是 可执行文件 出现错误,或数据段、堆栈溢出可能产生
SIGBUS 非法地址。包括内存地址对齐出错。
二、信号调用
1.所有的信号
在ubuntu系统输入 “kill -l”来查看。
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
2.发送信号
#include<signal.h>
int kil(pid_t pid,int sig); // 发送信号
参数
pid 给谁发送信号
pid > 0 给特定的某个进程发送信号
pid = 0 给在同一进程组下的进程发送信号
pid = -1 给任意有权限的进程发送信号
pid < -1 给进程组id号为 |pid|的进程法信号
sig 发送什么信号
返回值 成功 —— 0 失败 —— -1
比如,kill(2230, 2); // 表示给pid为2230的进程发送信号2
示例,我们创建一个kill1.c的应用,实现根据输入的信号和pid号进行发送。
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#define N 3
int main(int argc,char *argv[])
{
if(argc<N)
{
printf("Please use:%s <pid> <sig>\n",argv[0]);
exit(-1);
}
kill(atoi(argv[1]),atoi(argv[2]));
return 0;
}
3.注册自定义信号
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler); // 信号注册
参数:
signum 注册信号的编号 signal(2,) 给信号2注册
handler
SIG_IGN 忽略
SIG_DFL 默认(缺省)
捕捉 -> 调用 执行相对于的自定义操作
9 和19 不能被捕捉,不能被忽略,不能被注册
示例:当函数接受到信号2后执行hello函数
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void hello(int sig)
{
printf("hello\n");
}
int main(int argc,char *argv[])
{
signal(2, hello);
while(1)
{
sleep(1);
printf("mypid:%d\n",getpid());
}
return 0;
}
测试:
-
运行应用
-
运行前面发送信号的app
-
收到信号2后,进程会打印hello
4.挂起信号
#include <unistd.h>
int pause(void); // 挂起
阻塞,直到收到信号,信号被捕捉,执行完捕捉函数后,解除阻塞,继续往下
返回值: -1
5.消息队列
三、信号——经典案例
模拟司机售票员:子进程表示售票员,父进程表示司机
(1)售票员捕捉SIGINT(2)信号(代表开车),发送SIGUSR1(10)给司机,司机捕捉到该信号后打印“move to next station”
(2)售票员捕捉SIGQUIT(3)信号(代表靠站),发送SIGUSR2(12)给司机,司机捕捉到该信号后打印“stop to bus”
(3)司机捕捉到SIGTSTP(20)信号(代表车到总站),发送SIGUSR1(10)给售票员,售票员捕捉到给信号后打印“all get off the bus”
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
static pid_t pid = -1;
void cond(int sig)
{
switch(sig)
{
case SIGINT:
printf("Cond sig:%d\n",sig);
kill(getppid(), SIGUSR1);
break;
case SIGQUIT:
printf("Cond sig:%d\n",sig);
kill(getppid(), SIGUSR2);
break;
case SIGUSR1:
printf("all get off the bus\n");
default:
break;
}
}
void driver(int sig)
{
switch(sig)
{
case SIGUSR1: // 10
printf("move to next station\n");
break;
case SIGUSR2: // 12
printf("stop to bus\n");
break;
case SIGTSTP: // 20
kill(pid, SIGUSR1);
default:
break;
}
}
int main(int argc,char *argv[])
{
pid = fork();
if(pid < -1){
perror("fork fail");
exit(1);
}else if(pid == 0){ // 子进程
signal(SIGTSTP, SIG_IGN); // 忽略
signal(SIGINT, cond); // 信号2
signal(SIGQUIT, cond); // 信号3
signal(SIGUSR1, cond); // 信号10
printf("Son %d,father %d\n",getpid(),getppid());
while(1){
pause();
}
}else { // 父进程
signal(SIGINT, SIG_IGN); // 忽略
signal(SIGQUIT, SIG_IGN);
signal(SIGUSR1, driver); // 信号10
signal(SIGUSR2, driver); // 信号12
signal(SIGTSTP, driver); // 信号20
//printf("Now %d,grand father %d\n",getpid(),getppid());
sleep(1);
while(1){
pause();
}
}
return 0;
}
测试:
可以看到子进程pid是 28874,父进程pid是28873
1、向子进程发送信号
(1)收到SIGINT
(2)收到SIGQUIT
(3)收到SIGUSR1
2、向父进程发送信号
(1)收到SIGTSTP