进程间通信 — 信号
信号机制是Linux系统内核管理任务的一种重要机制,信号传递的信息有限,所以都是表达一些特定意义,大部分的信号的默认功能都是让目标进程退出,暂停(SIGSTOP),继续(SIGCONT)。
1)Linux下的信号可以通过命令 kill -l 查看
一共是 62个信号。
前面的31个信号, 1~31
1、这些信号被称为非实时信号,也叫作不可靠信号
2、信号不会排队,但是会嵌套,如果有新的信号到达,但是原来的信号没有及时响应,前面的信号会被丢弃掉。
3、每一个信号对应一个事件,当这个事件发送的时候,系统就会给进程发送信号
4、如果进程挂起的信号中同时存在实时信号与非实时信号,进程会优先响应实时信号,实时信号的优先级从大到小。
后面31个信号,34~64
1、实时信号,也叫可靠信号
2、接受排队,不接受嵌套
3、如果同时发生多个相同的实时信号,只响应一次
4、实时信号没有具体的事件对应。
–》信号9 (SIGKILL)、 19(SIGSTOP)不允许被捕捉,忽略,阻塞的。
2)信号的发送
==> kill ==> kill -信号值 进程ID
==> killall ==> killall -信号值 进程名
==> kill() 发送信号的函数
函数的API
SYNOPSIS
#include
#include
int kill(pid_t pid, int sig);
==> pid : 目标进程的进程ID
==> sig : 信号值
返回值: 成功返回0,失败返回-1
练习1:父进程创建2个子进程,父进程3s之后发送3号信号给子进程1,5s之后发送1号信号给子进程2,父进程等待两个子进程都结束之后,父进程再结束。
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*父进程给两个子进程分别发送信号*/
int main(int argc, char *argv[])
{
//1,父进程创建两个子进程
pid_t pid1, pid2;
pid1 = fork(); //创建进程
if(pid1 > 0) //父进程执行部分
{
pid2 = fork(); //创建进程
if(pid2 > 0) //父进程执行部分
{
sleep(3); // 等待3S
kill(pid1, 3); //发送3号信号给子进程1
printf("发送信号3给子进程1\n");
sleep(2);
kill(pid2, 1); //发送1号信号给子进程2
printf("发送信号1给子进程2\n");
wait(NULL);
wait(NULL);
exit(0); //退出进程
}
if(0 == pid2) //子进程2执行部分
{
int time;
for(time = 0; ; time++)
{
printf("child2: time:%d\n", time);
sleep(1);
}
}
}
if(0 == pid1) //子进程1执行部分
{
while(1);
}
return 0;
}
关于参数pid的补充:
1、Pid > 0; 信号发送给进程号为pid的进程
2、Pid == 0; 信号发送给同一组内其他所有进程
3、Pid == -1; 信号发送给所有进程(当前进程具有权限发送的进程)
4、Pid < -1; 信号发送给进程组ID为pid绝对值的进程
发送成功返回0,失败返回-1,并且错误被设置
EINVAL : 信号无效或不支持
EPERM : 没有权限发送
ESRCH :目标进程不存在
3、信号的捕捉 signal ()
==> 进程在接收到信号之后,如果没有特殊设置,会响应信号的默认动作,但是一些时候我们希望接收到信号之后不去响应退出,此时可以通过signal函数对信号进行捕捉,忽略。
·函数原型 signal
SYNOPSIS
#include
typedef void (*sighandler_t)(int); //参数为int,返回值为void函数指针
sighandler_t signal(int signum, sighandler_t handler);
==> signum : 要捕捉的信号值 (SIGKILL,SIGSTOP不能被捕捉)
==> handler : 捕捉到信号之后执行的动作
SIG_IGN : 忽略这个信号
SIG_DFL : 执行默认动作
Fun : 自定义的函数指针 --> 捕捉到信号之后执行规定的动作
==> 例子: 设计程序。使用signal函数捕捉信号 ctrl + c (SIGINT),每次捕捉到信号,就打印一句话 :”有话好好说!”
==> 练习2: 设计程序,父进程创建一个子进程,父进程使用signal函数捕捉信号1,每次捕捉到信号1就打印 “小样!”, 父进程循环打印时间,子进程3s之后给父进程发送一个信号1, 再等2s发送一个信号2,然后子进程退出。
4)alarm --> 给自己发送一个闹钟信号 SIGALRM
SYNOPSIS
#include
unsigned int alarm(unsigned int seconds);
==> seconds : 时间(单位 秒)
==> 例子: 设计程序,每5秒钟打印一次helloworld.
1) 信号集的概念
Signal函数的功能是将信号捕捉,然后去执行特定的事件,或者是忽略,或者执行默认动作。而信号集的作用是把信号阻塞。
每一个进程都会有一个描述将来哪些信号会被阻塞的信号集,这个信号集也被成为信号掩码,当某一个信号在这个被阻塞的信号集 (sigset_t) 中,那么传送到该进程的这种信号会被阻塞。
创建子进程时,子进程会继承父进程的信号集。
2)信号忽略与信号阻塞的区别
阻塞: 操作系统在进程解除信号阻塞之前不会把信号传递出去,被阻塞的信号也不会影响进程的行为。
忽略:当进程忽略一个信号时,信号会被传递出去,但进程会把信号丢弃。
3)相关函数
SYNOPSIS 信号集操作概念函数
#include
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); //判断信号是否在信号集中
SYNOPSIS 设置对信号集内的信号的处理方式
#include
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
==> how : 对信号集中的信号的处理方式
SIG_BLOCK : 在原来的基础上,添加参数set信号集中的信号
SIG_SETMASK :将set中信号替换掉原来的信号
SIG_UNBLOCK :在原来的基础上解除set里面的信号
==> set : 信号集变量的地址
==> oldset : 原来的信号集地址
返回值: 成功返回0,失败返回-1.
==》例子: 设计一个程序,把信号集添加 SIGHUP, SIGINT, SIGQUIT 信号添加阻塞,10s做自己的事情,10s之后解除阻塞。其他的进程这个过程中给这个进程发送 SIGHUP信号,
1, System V IPC对象
System V IPC对象是Linux从Unix中继承过来的进程间通信方式。
最初进程间的通信主要是依赖信号与管道,但是这两种通信方式各有不足之处,随着系统发展,这些通信方式不太能满足通信需求。例如:无名管道只能实现亲缘进程之间的通信,有名管道传递的数据接收方只能有一个,不知道发送方是谁,信号传递的信息量太少…
为了解决这些问题,人们发明了IPC对象
2,IPC对象
IPC对象主要是指: 消息队列, 共享内存, 信号量
IPC对象不存在与Linux的文件系统中,它的唯一标识符是它的key值。通过key值来识别IPC对象,IPC对象除了key值之外,还有对应的ID,这个ID是我们操作IPC对象的句柄。
==> 注意: 每一个IPC对象的key值是唯一的,ID不是惟一的。
==> IPC对象会一直存在于Linux系统中,不会随着进程的结束而消失。
1)相关命令
Ipcs -a 查看当前系统中所有的IPC对象
------ Message Queues -------- //消息队列
key msqid owner perms used-bytes messages
------ Shared Memory Segments -------- //共享内存
key shmid owner perms bytes nattch status
0x00000000 393216 gec 600 524288 2 dest
0x00000000 1245185 gec 600 524288 2 dest
0x00000000 589826 gec 600 16777216 2
0x00000000 622595 gec 600 524288 2 dest
0x00000000 884740 gec 600 524288 2 dest
0x00000000 983045 gec 600 524288 2 dest
0x00000000 1081350 gec 600 524288 2 dest
0x00000000 1146887 gec 600 16777216 2 dest
0x00000000 1343496 gec 600 524288 2 dest
0x00000000 1605641 gec 600 524288 2 dest
0x00000000 1835019 gec 600 67108864 2 dest
------ Semaphore Arrays -------- //信号量
key semid owner perms nsems
==> 删除IPC对象 ipcrm
==> 删除消息队列 : ipcrm -q msgid
==> 删除共享内存 : ipcrm -m shmid
==> 删除信号量 : pcrm -s semid
2)相关函数
(1)获取IPC对象键值 key – ftok()
SYNOPSIS
#include
#include
key_t ftok(const char *pathname, int proj_id);
==> pathname : 一个合法的路径 (路径要存在)
==> proj_id : 一个大于0的整数
==> 返回值: 成功返回键值,失败返回-1;
(2)获取消息队列的ID --> msgget()
SYNOPSIS
#include
#include
#include
int msgget(key_t key, int msgflg);
==> key : IPC对象的key值
==> msgflg : 标志位
IPC_CREAT | 0666 ; 如果对象不存在则创建
IPC_EXCL 如果对象存在则报错
返回值: 成功返回消息队列的ID,失败返回-1
(3)收发消息 msgsnd, msgrcv,
SYNOPSIS
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //发送消息
==> msqid : 消息队列的ID
==> msgp : 需要发送的消息
==> msgsz : 消息的大小
==> msgflg : 消息的标志,默认是0
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); //接收
==> msqid : 消息队列的ID
==> msgp : 接收消息的缓冲区地址
==> msgsz : 消息的大小
==> msgtyp : 消息的类型
==> msgflg : 消息的标志,默认是0
消息结构体:
struct msgbuf {
long mtype; /* message type, must be > 0 / 消息类型
char mtext[1]; / message data */ //消息数据 ==> 自行设计大小
};
(4)消息队列的删除 msgctl
SYNOPSIS
#include
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
==> msqid : 消息队列ID
==> cmd : 操作命令 (IPC_RMID:删除IPC对象)
==> buf : 如果第二个参数选择IPC_RMID ,那第三个参数不要了
练习
1,理解消息队列单向通信功能代码,实现使用一个消息队列实现双向通信。
进程A 进程B
父进程A写入类型1的消息 子进程b读取类型1的消息
子进程a读取类型2的消息 父进程B写入类型2的消息
进程A端代码
#include
#include
#include
#include
#include
#include
#define MSQ_PATH "."
#define PROJ_ID 10
#define MTYPE_1 1 //消息类型1
#define MTYPE_2 2 //消息类型2
#define BUF_SIZE 1024
struct msgbuf {
long mtype; /* message type, must be > 0 */ //消息类型
char mtext[BUF_SIZE]; /* message data */ //消息数据 ==> 自行设计大小
};
/*进程A*/
int main(int argc, char *argv[])
{
struct msgbuf msgp; //消息缓冲区
//1, 获取消息队列key值
key_t key = ftok(MSQ_PATH, PROJ_ID);
if(-1 == key)
{
perror("ftok failed");
return -1;
}
//2, 获取消息队列操作ID
int msqid = msgget(key, IPC_CREAT | 0666);
if(-1 == msqid)
{
perror("msgget failed");
return -1;
}
//3, 创建子进程 --> fork
pid_t pid = fork();
if(-1 == pid)
{
perror("fork failed");
return -1;
}
//父进程循环往消息队列发送类型为1的消息
if(pid > 0)
{
for( ; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgp.mtype = MTYPE_1;
fgets(msgp.mtext, sizeof(msgp.mtext), stdin); //scanf
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送消息
}
}
//子进程循环从消息队列中获取类型2的消息
if(pid == 0)
{
for(; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_2, 0); //接收消息
printf("recv from rose : %s", msgp.mtext);
}
}
return 0;
}
进程B端代码
#include
#include
#include
#include
#include
#include
#define MSQ_PATH "."
#define PROJ_ID 10
#define MTYPE_1 1 //消息类型1
#define MTYPE_2 2 //消息类型2
#define BUF_SIZE 1024
struct msgbuf {
long mtype; /* message type, must be > 0 */ //消息类型
char mtext[BUF_SIZE]; /* message data */ //消息数据 ==> 自行设计大小
};
/*进程B*/
int main(int argc, char *argv[])
{
struct msgbuf msgp; //消息缓冲区
//1, 获取消息队列key值
key_t key = ftok(MSQ_PATH, PROJ_ID);
if(-1 == key)
{
perror("ftok failed");
return -1;
}
//2, 获取消息队列操作ID
int msqid = msgget(key, IPC_CREAT | 0666);
if(-1 == msqid)
{
perror("msgget failed");
return -1;
}
//3, 创建子进程 --> fork
pid_t pid = fork();
if(-1 == pid)
{
perror("fork failed");
return -1;
}
//父进程循环往消息队列发送类型为2的消息
if(pid > 0)
{
for( ; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgp.mtype = MTYPE_2;
fgets(msgp.mtext, sizeof(msgp.mtext), stdin); //scanf
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送消息
}
}
//子进程循环从消息队列中获取类型1的消息
if(pid == 0)
{
for(; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_1, 0); //接收消息
printf("recv from jack : %s", msgp.mtext);
}
}
return 0;
}
2,利用相关函数实现以下功能
父进程创建2个子进程。父进程循环打印时间。 alarm
父进程每隔5s打印”开饭了!”,并且给子进程发送信号,子进程接收到信号之后打印 “Child 1 process 吃饱了!”
“Child 2 process 吃饱了!”
或者
“Child 2 process 吃饱了!”
“Child 1 process 吃饱了!”
不断循环实现这个效果。
#include
#include
#include
#include
#include
pid_t pid1, pid2;
void fun(int arg)
{
printf("开饭了!\n");
kill(pid1, SIGQUIT);
kill(pid2, SIGQUIT);
alarm(5);
}
void fun1(int arg)
{
printf("Child 1 process 吃饱了!\n");
}
void fun2(int arg)
{
printf("Child 2 process 吃饱了!\n");
}
/*父进程循环给子进程喂饭*/
int main(int argc, char *argv[])
{
//1,创建2条子进程
pid1 = fork();
if(-1 == pid1)
{
perror("fork 1 failed");
return -1;
}
if(0 == pid1) //子进程1 子进程1与子进程2循环等待父进程的信号
{
signal(SIGQUIT, fun1);
while(1)
pause();
}
if(0 < pid1) //父进程
{
pid2 = fork();
if(-1 == pid2)
{
perror("fork 2 failed");
return -1;
}
if(0 == pid2) //子进程2
{
signal(SIGQUIT, fun2);
while(1)
pause();
}
if(0 < pid2) //父进程 //2,父进程注册捕捉信号 SIGALRM //3,父进程循环打印时间
{
signal(SIGALRM, fun);
alarm(5);
for(int i = 0; ; i++)
{
printf("time : %d\n", i);
sleep(1);
}
wait(NULL);
wait(NULL);
}
}
return 0;
}
3,-- 使用消息队列实现一个服务器转发功能
实现思路: 服务器循环从消息队列中接收类型1的消息。接收到消息就输出。
进程A的父进程A循环发送类型为1的消息,子进程a循环接收类型为2的消息,接收到就打印。
进程B的父进程B循环发送类型为1的消息,子进程B循环接收类型为3的消息,接收到就打印。
进程C的父进程C循环发送类型为1的消息,子进程c循环接收类型为4的消息,接收到就打印。
进程A,B,C任何一个想要发送广播数据(服务器转发给其他进程),那就给服务器发送类型为”BoardCast:xxxxx”;的数据,如果服务器接收到这种数据,那就把这个数据转发给进程A, B, C
进程A
#include
#include
#include
#include
#include
#include
#define MSQ_PATH "."
#define PROJ_ID 100
#define MTYPE_1 1 //消息类型1
#define MTYPE_2 2 //消息类型2
#define MTYPE_3 3 //消息类型3
#define MTYPE_4 4 //消息类型4
#define BUF_SIZE 1024
struct msgbuf {
long mtype; /* message type, must be > 0 */ //消息类型
char mtext[BUF_SIZE]; /* message data */ //消息数据 ==> 自行设计大小
};
/*进程A*/
int main(int argc, char *argv[])
{
struct msgbuf msgp; //消息缓冲区
//1, 获取消息队列key值
key_t key = ftok(MSQ_PATH, PROJ_ID);
if(-1 == key)
{
perror("ftok failed");
return -1;
}
//2, 获取消息队列操作ID
int msqid = msgget(key, IPC_CREAT | 0666);
if(-1 == msqid)
{
perror("msgget failed");
return -1;
}
//3, 创建子进程 --> fork
pid_t pid = fork();
if(-1 == pid)
{
perror("fork failed");
return -1;
}
//父进程循环往消息队列发送类型为1的消息
if(pid > 0)
{
for( ; ; )
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgp.mtype = MTYPE_1;
fgets(msgp.mtext, sizeof(msgp.mtext), stdin); //scanf
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送消息
}
}
//子进程循环从消息队列中获取类型2的消息
if(pid == 0)
{
for(; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_2, 0); //接收消息
printf("%s", msgp.mtext);
}
}
return 0;
}
进程B
#include
#include
#include
#include
#include
#include
#define MSQ_PATH "."
#define PROJ_ID 100
#define MTYPE_1 1 //消息类型1
#define MTYPE_2 2 //消息类型2
#define MTYPE_3 3 //消息类型3
#define MTYPE_4 4 //消息类型4
#define BUF_SIZE 1024
struct msgbuf {
long mtype; /* message type, must be > 0 */ //消息类型
char mtext[BUF_SIZE]; /* message data */ //消息数据 ==> 自行设计大小
};
/*进程B*/
int main(int argc, char *argv[])
{
struct msgbuf msgp; //消息缓冲区
//1, 获取消息队列key值
key_t key = ftok(MSQ_PATH, PROJ_ID);
if(-1 == key)
{
perror("ftok failed");
return -1;
}
//2, 获取消息队列操作ID
int msqid = msgget(key, IPC_CREAT | 0666);
if(-1 == msqid)
{
perror("msgget failed");
return -1;
}
//3, 创建子进程 --> fork
pid_t pid = fork();
if(-1 == pid)
{
perror("fork failed");
return -1;
}
//父进程循环往消息队列发送类型为1的消息
if(pid > 0)
{
for( ; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgp.mtype = MTYPE_1;
fgets(msgp.mtext, sizeof(msgp.mtext), stdin); //scanf
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送消息
}
}
//子进程循环从消息队列中获取类型3的消息
if(pid == 0)
{
for(; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_3, 0); //接收消息
printf("%s", msgp.mtext);
}
}
return 0;
}
进程C
#include
#include
#include
#include
#include
#include
#define MSQ_PATH "."
#define PROJ_ID 100
#define MTYPE_1 1 //消息类型1
#define MTYPE_2 2 //消息类型2
#define MTYPE_3 3 //消息类型3
#define MTYPE_4 4 //消息类型4
#define BUF_SIZE 1024
struct msgbuf {
long mtype; /* message type, must be > 0 */ //消息类型
char mtext[BUF_SIZE]; /* message data */ //消息数据 ==> 自行设计大小
};
/*进程C*/
int main(int argc, char *argv[])
{
struct msgbuf msgp; //消息缓冲区
//1, 获取消息队列key值
key_t key = ftok(MSQ_PATH, PROJ_ID);
if(-1 == key)
{
perror("ftok failed");
return -1;
}
//2, 获取消息队列操作ID
int msqid = msgget(key, IPC_CREAT | 0666);
if(-1 == msqid)
{
perror("msgget failed");
return -1;
}
//3, 创建子进程 --> fork
pid_t pid = fork();
if(-1 == pid)
{
perror("fork failed");
return -1;
}
//父进程循环往消息队列发送类型为1的消息
if(pid > 0)
{
for( ; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgp.mtype = MTYPE_1;
fgets(msgp.mtext, sizeof(msgp.mtext), stdin); //scanf
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送消息
}
}
//子进程循环从消息队列中获取类型4的消息
if(pid == 0)
{
for(; ;)
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_4, 0); //接收消息
printf("%s", msgp.mtext);
}
}
return 0;
}
消息队列服务器
#include
#include
#include
#include
#include
#include
#define MSQ_PATH "."
#define PROJ_ID 100
#define MTYPE_1 1 //消息类型1
#define MTYPE_2 2 //消息类型2
#define MTYPE_3 3 //消息类型3
#define MTYPE_4 4 //消息类型4
#define BUF_SIZE 1024
struct msgbuf {
long mtype; /* message type, must be > 0 */ //消息类型
char mtext[BUF_SIZE]; /* message data */ //消息数据 ==> 自行设计大小
};
/*消息队列服务器*/
int main(int argc, char *argv[])
{
struct msgbuf msgp; //消息缓冲区
//1, 获取消息队列key值
key_t key = ftok(MSQ_PATH, PROJ_ID);
if(-1 == key)
{
perror("ftok failed");
return -1;
}
//2, 获取消息队列操作ID
int msqid = msgget(key, IPC_CREAT | 0666);
if(-1 == msqid)
{
perror("msgget failed");
return -1;
}
//3,循环接收类型为1的消息,如果接收到以"BoardCast:"开头的消息,就转发给所有客户端
for( ; ; )
{
memset(msgp.mtext, 0, sizeof(msgp.mtext));
msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_1, 0); //接收消息
printf("recv msg : %s", msgp.mtext);
if( strncmp("BoardCast:", msgp.mtext, 10) == 0) //如果按照广播格式发送消息到服务器,那就转发
{
msgp.mtype = MTYPE_2;
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送类型2消息
msgp.mtype = MTYPE_3;
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送类型3消息
msgp.mtype = MTYPE_4;
msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0); //发送类型4消息
}
}
return 0;
}
例程:父进程给两个子进程分别发送信号
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*父进程给两个子进程分别发送信号*/
int main(int argc, char *argv[])
{
//1,父进程创建两个子进程
pid_t pid1, pid2;
pid1 = fork(); //创建进程
if(pid1 > 0) //父进程执行部分
{
pid2 = fork(); //创建进程
if(pid2 > 0) //父进程执行部分
{
sleep(3); // 等待3S
kill(pid1, 3); //发送3号信号给子进程1
printf("发送信号3给子进程1\n");
sleep(2);
kill(pid2, 1); //发送1号信号给子进程2
printf("发送信号1给子进程2\n");
wait(NULL);
wait(NULL);
exit(0); //退出进程
}
if(0 == pid2) //子进程2执行部分
{
int time;
for(time = 0; ; time++)
{
printf("child2: time:%d\n", time);
sleep(1);
}
}
}
if(0 == pid1) //子进程1执行部分
{
while(1);
}
return 0;
}
例程:创建两个进程,父进程等待一个信号,如果父进程接收到信号就触发函数
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
pid_t pid1, pid2;
void fun(int arg) //父进程信号触发函数
{
kill(pid1, SIGHUP); //给子进程1发送信号
kill(pid2, SIGHUP); //给子进程2发送信号
}
void fun1(int arg)
{
printf("Child process 1 is killed by parent!\n");
}
void fun2(int arg)
{
printf("Child process 2 is killed by parent!\n");
}
int main(int argc, char *argv[])
{
//1,父进程创建两个子进程
pid1 = fork();
if(pid1 > 0) //父进程
{
pid2 = fork();
if(pid2 > 0) //父进程
{
signal(SIGINT, fun);
pause(); //挂起等待一个信号
wait(NULL);
wait(NULL);
printf("Parent process exit!\n");
}
if(pid2 == 0) //子进程2
{
signal(SIGHUP, fun2);
pause();
}
}
if(pid1 == 0) //子进程1
{
signal(SIGHUP, fun1);
pause();
}
return 0;
}