Linux进程的软中断通信
一、软中断信号
软中断信号(
s
i
g
n
a
l
signal
signal,又简称为信号)用来通知进程发生了事件。进程之间可以通过调用kill
函数发送软中断信号。
L
i
n
u
x
Linux
Linux 内核也可能给进程发送信号,通知进程发生了某个事件(例如内存越界)。
注意:信号只是用来通知某进程发生了什么事件,无法给进程传递任何数据,进程对信号的处理方法有三种:
① 忽略某个信号,对该信号不做任何处理,就像未发生过一样。
② 设置中断的处理函数,收到信号后,由该函数来处理。
③ 对该信号的处理采用系统的默认操作,大部分的信号的默认操作是终止进程。
常用信号
信号名 | 信号值 | 发出信号的原因 |
---|---|---|
SIGINT | 2 | 键盘中断 ctrl + c |
SIGTERM | 15 | 采用kill 进程编号 或killall 程序名 通知程序 |
SIGKILL | 9 | 采用kill -9 进程编号 强制杀死程序 |
常用信号处理方法
信号处理方法 | 作用 |
---|---|
SIG_IGN | 忽视信号 |
SIG_DFL | 默认的信号处理程序 |
✔ 更具体内容下文会提及
二、函数介绍
(1)wait函数
#include<sys/types.h>
#include<wait.h> //头文件
pid_t wait(int *status) //函数原型
(pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中)
wait
函数作用是阻塞父进程,由wait
自动分析是否当前进程的某个子进程已经退出,如果让它找到了子进程,即调用成功,wait
就会收集这个子进程的信息,并把它彻底销毁后返回被收集的子进程的进程ID;如果调用进程没有子进程,调用就会失败,此时wait
返回
−
1
-1
−1。
因此wait
函数有三个功能:
① 阻塞父进程,等待子进程退出。
② 回收子进程残留资源。
③ 获取子进程结束状态(即返回值)。
参数
s
t
a
t
u
s
status
status 用来保存被收集进程退出时的一些状态,它是一个指向
i
n
t
int
int 类型的指针。如果只是把子进程消灭掉,则设定这个参数为
N
U
L
L
NULL
NULL 或
0
0
0 就可以了,即wait(NULL)
或wait(0)
。
✔ wait
函数一旦被调用,就会一直阻塞在这里,直到有一个子进程退出出现为止。
(2)signal函数
#include<signal.h> //头文件
void (*signal(int sig, void (*func)(int)))(int) //函数原型
sighandler_t signal(int signum, sighandler_t handler) //用typedef处理过的signal函数
signal
函数可以设置程序对信号的处理方式。
(对
t
y
p
e
d
e
f
typedef
typedef 处理过的函数进行说明)
参数
s
i
g
n
u
m
signum
signum 表示信号的编号。
参数
h
a
n
d
l
e
r
handler
handler 表示信号的处理方式,有三种情况:
① SIG_IGN:忽略参数
s
i
g
n
u
m
signum
signum所指的信号。
② 一个自定义的处理信号的函数,信号的编号为这个自定义函数的参数。
③ SIG_DFL:恢复参数
s
i
g
n
u
m
signum
signum所指信号的处理方法为默认值。
这里不解释其返回值,它的返回值对本次内容关系不大。
(3)sleep函数
#include<unistd.h> //头文件
unsigned int sleep (unsigned int seconds) //函数原型
进程执行挂起一段时间,也就是等待一段时间在继续执行。(参数 s e c o n d s seconds seconds单位为秒)
(4)kill函数
#include<sys/types.h>
#include<signal.h> //头文件
int kill(pid_t pid, int sig) //函数原型
kill
函数用于在程序中向其它进程或者线程发送信号。
参数
p
i
d
pid
pid 有几种情况:
①
p
i
d
>
0
pid > 0
pid>0 将信号传给进程号为
p
i
d
pid
pid 的进程。
②
p
i
d
=
0
pid=0
pid=0将信号传给和目前进程相同进程组的所有进程,常用于父进程给子进程发送信号,注意,发送信号者进程也会收到自己发出的信号。
③
p
i
d
=
−
1
pid=-1
pid=−1 将信号广播传送给系统内所有的进程,例如系统关机时,会向所有的登录窗口广播关机信息。
参数
s
i
g
sig
sig 用作准备发送的信号代码。
返回值: 成功执行时,返回
0
0
0;失败时返回
−
1
-1
−1。
对于pid的值的理解可以参考:
https://blog.csdn.net/weixin_45920385/article/details/109555124
三、示例
(1)
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>
int wait_flag;
void stop();
int main()
{
int pid1,pid2;
wait_flag = 1;
signal(2,stop);
while(wait_flag == 1);
while((pid1 = fork()) == -1);
if(pid1>0)
{
while((pid2 = fork()) == -1);
if(pid2 > 0)
{
wait_flag = 1;
sleep(5);
kill(pid1,16);
kill(pid2,17);
wait(0);
wait(0);
printf("Parent process is killed !!\n");
exit(0);
}
else
{
wait_flag = 1;
signal(17,stop);
while(wait_flag == 1);
printf("Child process 2 is killed by parent !!\n");
exit(0);
}
}
else
{
wait_flag = 1;
signal(16,stop);
while(wait_flag == 1);
printf("Child process 1 is killed by parent !!\n");
exit(0);
}
}
void stop()
{
wait_flag = 0;
}
运行结果:
看运行结果可以看出“Child process 2 is killed by parent !!”和“Child process 1 is killed by parent !!”的次序会变化,而“Parent process is killed !!”次序不会变,总是在最后输出。
分析:
signal(2,stop)
的作用是键盘输入ctrl + c这个信号后,会调用stop
函数,因此 wait_flag 赋予为
0
0
0,因此可退出
16
16
16行的while
这个循环,下一步创建子进程(称作子进程①);首先看向父进程①,父进程①再创建一个子进程(称作子进程②);先看父进程②,因
p
i
d
2
>
0
pid2>0
pid2>0,因此从
23
23
23行开始执行,运行sleep(5)
等待
5
5
5秒后 (也可以输入ctrl + c跳过) ,执行两个kill
语句,把信号码
16
16
16和
17
17
17分别传给
I
D
ID
ID 为
p
i
d
1
pid1
pid1 的进程(即子进程①)和
I
D
ID
ID 为
p
i
d
2
pid2
pid2 的进程(即子进程②),然后遇到
27
27
27行的wait(0)
,父进程②进入等待,需要等待子进程2执行完毕;看向子进程②,因
p
i
d
2
=
0
pid2=0
pid2=0,因此从
34
34
34行开始执行,因为收到kill
发来的信号码
17
17
17而执行singal(17,stop)
函数后调用stop
函数,使得wait_flag
=
0
=0
=0,跳出
36
36
36行的while
循环,打印出“Child process 2 is killed by parent !!”语句;子进程②执行完回到父进程②,执行
28
28
28行的wait(0)
进入等待,需要先执行完子进程①;对于子进程①,从
43
43
43行开始执行,因为收到kill
发来的信号码
16
16
16,同理可以知道最后会打印“Child process 1 is killed by parent !!”;再次回到父程序②,执行
29
29
29行打印“Parent process is killed !!”。因此我们从这个过程可以知道“Parent process is killed !!”语句肯定是最后打印出来的,另外两条打印部分的语句是同时进行,运行多次的次序是不同的。
(2)
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>
int wait_flag;
void stop();
int main()
{
int pid1,pid2,pid3;
wait_flag = 1;
signal(2,stop);
while(wait_flag == 1);
while((pid1 = fork()) == -1);
if(pid1>0)
{
while((pid2 = fork()) == -1);
if(pid2 > 0)
{
while((pid3 = fork()) == -1);
if(pid3 > 0)
{
wait_flag = 1;
sleep(5);
kill(pid1,16);
kill(pid2,17);
kill(pid3,18);
wait(0);
wait(0);
wait(0);
printf("Parent process is killed !!\n");
exit(0);
}
else
{
wait_flag = 1;
signal(18,stop);
while(wait_flag == 1);
printf("Child process 3 is killed by parent !!\n");
exit(0);
}
else
{
wait_flag = 1;
signal(17,stop);
while(wait_flag == 1);
printf("Child process 2 is killed by parent !!\n");
exit(0);
}
}
else
{
wait_flag = 1;
signal(16,stop);
while(wait_flag == 1);
printf("Child process 1 is killed by parent !!\n");
exit(0);
}
}
void stop()
{
wait_flag = 0;
}
运行结果:
看运行结果可以看出“Child process 2 is killed by parent !!”、“Child process 1 is killed by parent !!”和“Child process 3 is killed by parent !!”的次序会变化,而“Parent process is killed !!”次序不会变,总是在最后输出,和上面第一个的代码示例相似。
分析:
对于第一个代码示例多了一个进程组,稍微变得复杂一点,不过具体思路也是一样的。
需要转载请标明出处