进程间通讯
引入
我们要如何将A程序中的数据传入B程序中呢?
这时候就要使用进程间通讯
进程间通讯
别名:IPC(inter processes communication)
作用
数据传输
一个进程需要将他的数据发送给另一个进程
资源共享
多个进程之间共享同样的资源
事件通知
一个进程向另一个或另一组进程通知一个事件
进程控制
有些进程希望完全控制另一个进程的执行,此时控制进程希望拦截另一个进程的所有操作,并能够及时知道他的状态改变
Linux间进程通讯机制
![](https://img-blog.csdnimg.cn/direct/1091bdcd2a9a46f79e800f9a73906d29.png)
分类
信号量
管道
消息队列
内存共享
套接字
信号
概述
信号是Linux最古老的通信方式
特点
简单
不能携带大量信息
满足某个特设条件才发出
注意
信号是一种异步通信方式
信号是软件中断,他是在软件层次上对中断机制的一种模拟
信号可以导致某个进程被另一个异步进程中断,转而处理某个突发案件
进程不必等待信号的到达,也不知道信号什么时候到达
信号可以直接进行用户空间进程和内核空间进程的转换,内核空间进程可以通过信号来通知用户空间进程发生了哪些系统事件
同步和异步
同步
事件、操作或进程是有序的,一个操作必须在另一个操作结束后才能进行的
异步
事件、操作或进程是独立的,可以不等待其他操作便执行
中断机制
概念
是现代计算机系统中的基本机制之一,完成了对计算机各个事件(如时钟、
键盘等)响应工作
硬件中断
是由硬件设备触发的中断,如时钟中断、串口接收中断、外部中断等。当硬
件设备有数据或事件需要处理时,会向
CPU
发送一个中断请求,
CPU
在收到中断请求后,
会立即暂停当前正在执行的任务,进入中断处理程序中处理中断请求。硬件中断具有实
时性强、可靠性高、处理速度快等特点。
软件中断
是由软件程序触发的中断,如系统调用、软中断、异常等。软件中断不是由
硬件设备触发的,而是由软件程序主动发起的,一般用于系统调用、进程切换、异常处
理等任务。软件中断需要在程序中进行调用,其响应速度和实时性相对较差,但是具有
灵活性和可控性高的特点。
完整的信号周期
注册、产生、处理函数(默认/忽略/自定义)、注销
注意:注册、产生、注销不是函数,而是信号的内部机制
信号的编号
每个信号的名字都以字符
SIG
开头。
每个信号和一个数字编码相对应,在头文件
signum.h
中,这些信号都被定义为正整
数。
信号名定义路径:
/usr/include/i386-linux-gnu/bits/signum.h
在
Linux
下,要想查看这些信号和编码的对应关系,可使用命令:
kill -l
不存在编号为
0
的信号。
1-31
号信号称之为常规信号
(
也叫普通信号
,
标准信号
)
34-64
号信号称之为实时信号
(
自定义信号
)
通过
man 7 signal
查看信号帮助文档
注意:9) SIGKILL 和 19) SIGSTOP 信号,不允许忽略和捕捉,只能执行默认动作
SIGQUIT
信号由
:Ctrl + \
触发
信号的产生
当用户按某些终端键时,将产生信号
ctrl+c:SIGINT
ctrl+/:SIGQUIT
ctrl+z:SIGSTOP
kill函数
概念
给指定进程发送指定信号(但不一定杀死该进程)
语法
头文件
#include <signal.h>
#include <sys/types.h>
函数
int kill(pid_t pid,int sig)
参数
pid
pid > 0,将信号传送给进程号为pid的进程
pid = 0,将信号传送给当前进程所在进程组中的所有进程
pid = -1,将信号传送给系统中的所有进程
pid < -1,将信号传送给指定进程组中的所有进程,该进程组号为pid的绝对值
sig
信号的编号(推荐使用宏名而不是数字,因为不同操作系统数字编号所对应的宏名不同)
返回值
成功:0
失败:-1
示例
![](https://img-blog.csdnimg.cn/direct/53bad465af664e80962445c3c20452ed.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/9ca2e84ad6de456d966d80debd82d4e3.png)
raise函数
概念
给当前进程发送指定信号(自己给自己发)
等价于kill(getpid(),sig)
语法
int raise(int sig);
示例
![](https://img-blog.csdnimg.cn/direct/2ae4f368c0ce43e3a1125dacdaaaaa3b.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/934febcfe4ee4ffc848bad6106ada57d.png)
abort函数
概念
给自身进程发送异常终止SIGABRT信号,产生core文件
等价于kill(getpid(),SIGABRT);
core文件
core
就是内核
core
文件会包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息还有各种
函数调用堆栈信息等,我们可以理解为是程序工作当前状态存储生成第一个文件,许多
的程序出错的时候都会产生一个
core
文件,通过工具分析这个文件,我们可以定位到程
序异常退出的时候对应的堆栈调用等信息,找出问题所在并进行及时解决。
语法
#include <stdlib.h>
void abort(void);
注意
1.如果使用abort函数结束进程,进程在结束前会冲刷缓冲区,然后关闭所有文件描述符(0、1、2)
2.即使SIGABRT信号被加入信号阻塞集,一旦进程调用了abort函数,进程也还是会被终止
示例
![](https://img-blog.csdnimg.cn/direct/1923279c1a7b488f947c9225fbddc2d7.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/ad9732d8c3f84e6b84f1f18d1638583d.png)
alarm函数
概念
设置定时器,在指定时间后,内核会给当前进程发送一个SIGALRM信号,进程收到该信号后,默认终止。每个进程有且只有唯一的一个定时器。
语法
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
参数
seconds 秒
返回值
若以前没有设置过定时器,或者设置的定时器已超时,返回0;
若设置过定时器,则返回剩余秒数,并重新设置定时器
注意
定时,与进程状态无关(即自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵
尸……
无论进程处于何种状态,
alarm
都计时 。不阻塞当前进程
示例
![](https://img-blog.csdnimg.cn/direct/945d66cb629b43018efaede661883c7a.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/455463bf1bb549c0ac7093989a9ac5e3.png)
setitimer函数
概念
设置定时器,可以代替alarm,单位为us(微秒),可以实现周期定时
语法
#include <sys/time.h>
int setitimer(int which,const struct itimerval *newvalue,struct itimerval *oldvalue)
参数
which:指定定时器类型,可以是以下三个值之一:
a)
自然定时:
ITIMER_REAL → 14
)
SIGALRM
计算自然时间
b)
虚拟空间计时
(
用户空间
)
:
ITIMER_VIRTUAL → 26
)
SIGVTALRM
只计算
进程占用
cpu
的时间
c)
运行时计时
(
用户
+
内核
)
:
ITIMER_PROF → 27
)
SIGPROF
计算占用
cpu
及执行系统调用的时间
newvalue:一个指向itimerval结构体的指针,间隔时间和启动时间
oldvalue:一个指向itimerval结构体的指针,用于获取旧的定时器的值,如果不需要获取,则传入NULL。
itimerval结构体
struct itimerval
{
struct timerval it_interval; //间隔时间
struct timerval it_value; //延迟时间
timerval结构体
struct timerval
{
long tv_sec; //秒
long tv_usec; //微妙
}
示例
![](https://img-blog.csdnimg.cn/direct/f9ff55389d0846649b42c412a99e6bee.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/89e301e2eab5436ea843da39cabc43bd.png)
先间隔五秒,随后每隔一秒打印一次
pause函数
概念
将正在调用的进程挂起直到捕捉到信号为止
通常用来判断信号是否已到
语法
#include <unistd.h>
int pause(void)
返回值
捕捉到信号才返回-1
示例
![](https://img-blog.csdnimg.cn/direct/a3c8f4952654462fa38c7c718e9977de.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/28d23d508c7e4033a496dd7446aaba58.png)
程序间隔了七秒才打印啦啦啦和-1
信号处理
方式
一个进程收到一个信号的时候,可以用如下方法进行处理:
1
)
SIG_DFL
执行系统默认动作 对大多数信号来说,系统默认动作是用来终止该进程。
2
)
SIG_IGN
忽略此信号
(
丢弃
)
接收到此信号后没有任何动作。
3
)信号处理函数名
:
执行自定义信号处理函数
(
捕获
)
用用户定义的信号处理函数处理
该信号。
【注意】:
SIGKILL
和
SIGSTOP
不能更改信号的处理方式,因为它们向用户提供了一种
使进程终止的可靠方法
捕捉信号并且信号信号的处理方式有两个函数
,signal
和
sigaction
signal函数
概念
注册信号处理函数(不可用于
SIGKILL
、
SIGSTOP
信号),即确定收到信号后处理函
数的入口地址。此函数不会阻塞
语法
#include <signal.h>
//
定义的一个函数指针
typedef void(*sighandler_t)(int);
//signal
函数
sighandler_t signal(int signum, sighandler_t handler);
参数
:
signum
:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过
命令
kill - l("l"
为字母
)
进行相应查看。
handler :
取值有
3
种情况:
SIG_IGN
:忽略该信号
SIG_DFL
:执行系统默认动作
信号处理函数名:自定义信号处理函数
返回值
:
成功:第一次返回
NULL
,下一次返回此信号上一次注册的信号处理函数的地址。
如果需要使用此返回值,必须在前面先声明此函数指针的类型。
失败:返回
SIG_ERR
示例1
![](https://img-blog.csdnimg.cn/direct/35fb333566d442a7b4262f2d67e6689b.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/827a9552bf70484ab0cb803532d02a8f.png)
示例2
![](https://img-blog.csdnimg.cn/direct/11aeb124c6584fe0a3a78bfc8ef940de.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/8e94375d97784bcd99243f623d6018b9.png)
按下ctrl+c时,程序会自动结束
sigacation函数
概念
检查或修改指定信号的设置
语法
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct
sigaction* oldact);
参数
:
signum
:要操作的信号。
act
: 要设置的对信号的新处理方式(传入参数)。
oldact
:原来对信号的处理方式(传出参数)。
注意
:
如果
act
指针非空
,
则要改变指定信号的处理方式
(
设置
),
如果
oldact
指针非空
,
则
系统将此前指定信号的处理方式存入
oldact
。
返回值
:
成功:
0
失败:
-1
struct
sigaction
{
void
(
*
sa_handler
)(
int
);
//
旧的信号处理函数指针
void
(
*
sa_sigaction
)(
int
,
siginfo_t
*
,
void *
);
//
新的信号处理函数指针
sigset_t sa_mask
;
//
信号阻塞集
int
sa_flags
;
//
信号处理的方式
void
(
*
sa_restorer
)(
void
);
//
已弃用
};
/*
1,sa_handler,sa_sigaction:
信号处理函数指针
,
和
signal()
里的函数指针用法
一样,应根据情况给
sa_sigaction
、
sa_handler
两者之一赋值,其取值如下:
a SIGIGN
:忽略该信号
b SIGDFL
:执行系统默认动作
c
处理函数名:自定义信号处理函数
2,sa_mask
:信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号。
3,sa_flags
:用于指定信号处理的行为,通常设置为
0
,表使用默认属性。它可
以是以下值的
“
按位或
”
组合:
SA_RESTART
:使被信号打断的系统调用自动重新发起(已经废弃)
SA_NOCLDSTOP
:使父进程在它的子进程暂停或继续运行时不会收到
SIGCHLD
信号。
SA_NOCLDWAIT
:使父进程在它的子进程退出时不会收到
SIGCHLD
信号,这
时子进程如果退出也不会成为僵尸进程。
SA_NODEFER
:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这
个信号。
SA_RESETHAND
:信号处理之后重新设置为默认的处理方式。
SA_SIGINFO
:使用
sa_sigaction
成员而不是
sa_handler
作为信号处理
函数
void(*sa_sigaction)(int signum, siginfo_t *info, void *context);
参数说明:
signum:
信号的编号。
info:
记录信号发送进程信息的结构体。
context:
可以赋给指向
ucontext_t
类型的一个对象的指针
,
以引用在传递信
号时被中断的接收进程或线程的上下文。
*/
示例
![](https://img-blog.csdnimg.cn/direct/a1ee9a4ae27c4ab49c23c0b526b4617d.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/7fb9094f247c46a1be8f2076df4dcc5b.png)
示例2
![](https://img-blog.csdnimg.cn/direct/b5a652845c1842d6aff73a5f14251580.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/16f6a197c1e742f48556ee0d017593bd.png)
可重入函数
概念
函数可以同时被多个进程调用,并且每个进程都能得到期望的结果
注意
1
、不使用或返回 静态的数据、全局变量(除非用信号量互斥)。
2
、不调用动态内存分配、释放的函数。
3
、不调用任何不可重入的函数(如标准
I/O
函数)
信号集
概念
在
PCB
中有两个非常重要的信号集。一个称之为
“
阻塞信号集
”
,另一个称之为
“
未决信号
集
”
。 这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对其
进行位操作。而需自定义另外一个集合,借助信号集操作函数来对
PCB
中的这两个信
号集进行修改
信号阻塞集:将某些信号加入 集合,对他们设置屏蔽,当屏蔽
x
信号后,再收到该信
号,该信号的处理将暂缓。
未决信号集:未被处理的信号集合。
自定义信号集函数
#include <signal.h>
int sigemptyset(sigset_t *set); //
将
set
集合置空
int sigfillset(sigset_t *set)
;
//
将所有信号加入
set
集合
int sigaddset(sigset_t *set, int signo); //
将
signo
信号加入到
set 集合
int sigdelset(sigset_t *set, int signo); //
从
set
集合中移除
signo 信号
int sigismember(const sigset_t *set, int signo); //
判断信号是否存在于 集合中
信号阻塞集
概念
信号阻塞集也称信号屏蔽集、信号掩码。
每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集。
信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它
,
直到
进程准备好时再将信号通知进程)。 所谓阻塞并不是禁止传送信号
,
而是暂缓信号的
传送。若将被阻塞的信号从信号阻塞集中删除
,
且对应的信号在被阻塞时发生了,进程
将会收到相应的信号。
我们可以通过
sigprocmask()
修改当前的信号阻塞集来改变信号的阻塞情况。
sigprocmask函数
概念
检查或修改信号阻塞集,根据
how
指定的方法对进程的阻塞集合进行修改,新的信号
阻塞集由
set
指定,而原先的信号阻塞集合由
oldset
保存。
语法
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数
how:
信号阻塞集合的修改方法
,
有
3
种情况
:
SIG_BLOCK:
添加
向信号阻塞集合中添加
set
信号集
,
新的信号掩码是
set
和旧信号掩码的并
集
.
相当于
mask=mask|set
。
SIG_UNBLOCK:
删除
从信号阻塞集合中删除
set
信号集
,
从当前信号掩码中去除
set
中的信号
.
相当于
mask=mask&~set
。
SIG_SETMASK:
替换
将信号阻塞集合设为
set
信号集
,
相当于原来信号阻塞集的内容清空
,
然后
按照
set
中的信号重新设置信号阻塞集。相当于
mask=set
。
set:
要操作的信号集地址。若
set
为
NULL,
则不改变信号阻塞集合
,
函数只把当前信
号阻塞集合保存到
oldset
中
oldset:
保存原先信号阻塞集地址
示例
![](https://img-blog.csdnimg.cn/direct/ce358d5a08844a88ab7a63e6985a27cf.png)
运行结果
![](https://img-blog.csdnimg.cn/direct/ef4135ea78464b19966c334b3158e604.png)
![](https://img-blog.csdnimg.cn/direct/ecd88f0a8bf6497b9f3fa84eecabad96.png)