Linux进程间通信_图文

Linux进程间通信
Version 1.0

需要掌握的要点
? IPC的概念和分类 ? 匿名管道 ? 命名管道(FIFO) ? 信号 ? 信号量 ? 共享内存 ? 消息队列
2

Linux下进程间通信概述

? Linux下的进程通信手段基本上是从Unix平台上的进程通信 手段继承而来的
? Linux集合了System V IPC(贝尔实验室)和socket(BSD)的进程 间通信机制(BSD)的优势

? Unix进程间通信(IPC)方式包括 管道、FIFO以及信号。
? System V进程间通信(IPC)包括 System V消息队列、System V信号量以 及System V共享内存区。
? Posix 进程间通信(IPC)包括 Posix消息队列、Posix信号量以及Posix 共享内存区。

最初UNIX的进程间通信 System V 进程间通信 Socket进程间通信

Linux 进程间
通信

POSIX进程间通信

3

Linux下进程间通信概述
? 常用的进程间通信机制
– (1)管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系 进程间的通信,有名管道,除具有管道所具有的功能外,它还允许无亲缘 关系进程间的通信。
– (2)信号(Signal):信号是在软件层次上对中断机制的一种模拟,它是 比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信 号与处理器收到一个中断请求效果上可以说是一样的。
– (3)消息队列(Messge Queue):消息队列是消息的链接表,包括Posix消 息队列SystemV消息队列。具有写权限的进程可以按照一定的规则向消息 队列中添加新消息;对消息队列有读权限的进程则可以从消息队列中读取 消息。
– (4)共享内存(Shared memory):可以说这是最有效的进程间通信方式 。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方 进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制, 如互斥锁和信号量等。
– (5)信号量(Semaphore):主要作为进程之间以及同一进程的不同线程 之间的同步和互斥手段。
– (6)套接字(Socket):这是一种更为一般的进程间通信机制,它可用于 网络中不同机器之间的进程间通信,应用非常广泛。
4

无名管道 和有名管道
? 管道简介
– 管道主要包括两种:无名管道和有名管道。
– 无名管道特点
? 它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄 弟进程之间)。
? 半双工的通信模式,具有固定的读端和写端。 ? 管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的
read()、write()等函数。但是它不是普通的文件,并不属于其他任何 文件系统,并且只存在于内存中
– 有名管道特点
? 它可以使互不相关的两个进程实现彼此通信。 ? 该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立
了管道之后,两个进程就可以把它当作普通文件一样进行读写操作, 使用非常方便。 ? FIFO严格地遵循先进先出规则,对管道及FIFO的读总是从开始处返 回数据,对它们的写则把数据添加到末尾,它们不支持如lseek()等文 件定位操作。
5

无名管道
? 无名管道系统调用
– 管道创建和关闭
? 管道是基于文件描述符的通信方式,当一个管道建立时,它会创建 两个文件描述符fds[0]和fds[1],其中fds[0]固定用于读管道,而fd[1] 固定用于写管道,这样就构成了一个半双工的通道。
? 管道关闭时只需将这两个文件描述符关闭即可,可使用普通的 close()函数逐个关闭各个文件描述符。

用户进程

fd[0]

fd[1]



内核



无名管道

6

无名管道
? 无名管道系统调用
– pipe()
? 创建管道可以通过调用pipe()来实现.
– pipe()语法:
7

无名管道

? 无名管道系统调用
– 父子进程管道的文件描述符对应关系

父进程

fd[0]

fd[1]

内核 管道

子进程

fd[0]

fd[1]

8

无名管道
? 无名管道系统调用
– 管道读写注意点: ? 只有在管道的读端存在时,向管道写入数据才有意义。否则 ,向管道写入数据的进程将收到内核传来的SIGPIPE信号( 通常为Broken pipe错误)。 ? 向管道写入数据时,Linux将不保证写入的原子性,管道缓冲 区一有空闲区域,写进程就会试图向管道写入数据。如果读 进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞 。 ? 父子进程在运行时,它们的先后次序并不能保证,因此,在 为了保证父子进程已经关闭了相应的文件描述符,可在两个 进程中调用sleep()函数,当然这种调用不是很好的解决方法 ,在后面学到进程之间的同步与互斥机制之后,请读者自行 修改本小节的实例程序。
9

有名管道
? 有名管道(FIFO)
– 有名管道可以实现任意连个进程之间的通信,并不限 制两个进程间有亲缘关系;
– 有名管道作为一种特殊的文件存放在文件系统中,使 用完毕仍然存在,除非对其进行删除。而无名管道只 存在于内存当中。
– 有名管道也只能用于单向传输。
10

有名管道
? 有名管道(FIFO)
– 通过mkfifo()创建有名管道,可以指定管道的路径和打开的模式。 – mkfifo()函数语法:
11

管道通信
? 有名管道(FIFO)
– FIFO相关出错信息
12

管道通信
? 有名管道(FIFO)
– FIFO读写
? 对于读进程 – 若该管道是阻塞打开,且当前FIFO内没有数据,则对读进程而 言将一直阻塞到有数据写入。 – 若该管道是非阻塞打开,则不论FIFO内是否有数据,读进程都 会立即执行读操作。即如果FIFO内没有数据,则读函数将立刻 返回0。
? 对于写进程 – 若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入 。 – 若该管道是非阻塞打开而不能写入全部数据,则读操作进行部 分写入或者调用失败。
13

信号通信
? 信号概述
– 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方 式
– 信号可以直接进行用户空间进程和内核之间的交互,内核也可以 利用它来通知用户空间进程发生了哪些系统事件。它可以在任何 时候发给某一进程,而无需知道该进程的状态。
– 如果该进程当前并未处于执行态,则该信号就由内核保存起来, 直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻 塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进 程。
14

信号通信
? 信号概述
– 信号是进程间通信机制中惟一的异步通信机制,可以看作是异步 通知,通知接收信号的进程有哪些事情发生了。信号机制经过 POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可 以传递附加信息(Payload)。
– 信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或 者其他硬件故障);软件来源,最常用发送信号的系统函数有 kill()、raise()、alarm()、setitimer()和sigqueue()等,软件来源还包括一 些非法运算等操作。
– 进程可以通过3种方式来响应一个信号。
? 忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略: SIGKILL及SIGSTOP。
? 捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函 数。
? 执行缺省操作,Linux对每种信号都规定了默认操作 。
15

Linux中的信号机制
? Linux信号机制是在应用软件层次上对中断机制的一种模拟, 是一种异步通信方式
? 每个进程都有一个自己私有的信号处理函数映射表,当该进程 成收到一个信号时,对应的信号处理函数被触发执行。
? 一个进程可以向另外一个进程发送信号,也可以向自己发送信 号;操作系统内核也可以向一个进程发送信号,以通知某些硬 件事件。
? 信号处理函数映射表中共有64个表项。前32个信号,编号为1 ~ 31,有预定义的含义和处理函数;后32个作为扩充。
? 前32个是不可靠信号(非实时的),后32个为可靠信号(实时信 号)。不可靠信号和可靠信号的区别在于前者不支持排队,可 能会造成信号丢失,而后者不会。

回调函数例一:Linux中的信号机制

信号通信
? 信号概述
– 信号缺省操作
18

回调函数例一:Linux中的信号机制
? 如何向一个进程发送信号?

回调函数例一:Linux中的信号机制
? 如何设置信号关联动作?

信号通信
? 信号发送和捕捉
– 信号发送:kill()和raise() kill()函数同读者熟知的kill系统命令一样,可以发送信号给进程或 进程组,它不仅可以中止进程(实际上发出SIGKILL信号),也可以 向进程发送其他信号。 kill()函数语法:
21

信号通信
? 信号发送和捕捉
– 信号捕捉:alarm()、pause()
? alarm()也称为闹钟函数,它可以在进程中设置一个定时器,当定时器 指定的时间到时,它就向进程发送SIGALARM信号 。
? pause()函数是用于将调用进程挂起直至捕捉到信号为止(该信号没有 被阻塞)。这个函数很常用,通常可以用于判断信号是否已到。
22

信号通信
? 信号发送和捕捉
– 信号的处理:signal、sigaction
? 使用signal()处理信号时,只需要指出要处理的信号和处理函数即可。 它主要是用于前32种非实时信号的处理,不支持信号传递信息,但是 由于使用简单、易于理解,因此也受到很多程序员的欢迎 。
? signal()函数的语法:
23

信号通信
? 信号发送和捕捉
– 信号的处理:signal、sigaction
? sigaction() 函数相对于signal()更加健壮,推荐使用这个
24

信号通信
? 信号发送和捕捉
– 信号的处理:signal、sigaction
? sigaction()函数中第2个和第3个参数用到的sigaction结构 struct sigaction { void (*sa_handler)(int signo); sigset_t sa_mask; int sa_flags; void (*sa_restore)(void); } sa_handler是一个函数指针,指定信号处理函数,这里除可以是用户自定义的处
理函数外,还可以为SIG_DFL(采用缺省的处理方式)或SIG_IGN(忽略信号)。它的 处理函数只有一个参数,即信号值。
sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被 屏蔽,在调用信号捕获函数之前,该信号集要加入到信号的信号屏蔽字中。
sa_flags中包含了许多标志位,是对信号进行处理的各个选择项
25

信号集函数
? 使用信号集函数组处理信号时涉及一系列的函数,这些函 数按照调用的先后次序可分为以下几大功能模块:创建信 号集合、注册信号处理函数以及检测信号。
? 其中,创建信号集合主要用于处理用户感兴趣的一些信号 ,其函数包括以下几个。
? ? sigemptyset():将信号集合初始化为空。 ? ? sigfillset():将信号集合初始化为包含所有已定义的信号
的集合。 ? ? sigaddset():将指定信号加入到信号集合中去。 ? ? sigdelset():将指定信号从信号集合中删去。 ? ? sigismember():查询指定信号是否在信号集合之中。

信号通信
? 信号发送和捕捉
– 总之,在处理信号时,一般遵循如图6.13所示的 操作流程。

信号量
? 信号量概述
– 信号量是用来解决进程之间的同步与互斥问题的一种进程之间通 信机制,包括一个称为信号量的变量和在该信号量下等待资源的 进程等待队列,以及对信号量进行的两个原子操作(PV操作)。
– 信号量对应于某一种资源,取一个非负的整型值。信号量值指的 是当前可用的该资源的数量,若它等于0则意味着目前没有可用的 资源。
– PV原子操作的具体定义为:
? P操作:如果有可用的资源(信号量值>0),则占用一个资源(给信 号量值减去一,进入临界区代码);如果没有可用的资源(信号量值等 于0),则被阻塞到,直到系统将资源分配给该进程(进入等待队列, 一直等到资源轮到该进程)。
? V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个 阻塞进程。如果没有进程等待它,则释放一个资源(给信号量值加一 )。
28

信号量

? 信号量概述

– 常见的使用信号量访问临界区的伪代码所下所示:

–{

– /* 设R为某种资源,S为资源R的信号量*/

– INIT_VAL(S); */

/* 对信号量S进行初始化

– 非临界区;

– P(S);

/* 进行P操作 */

– 临界区(使用资源R); 进入该区*/

/* 只有有限个(通常只有一个)进程被允许

– V(S);

/* 进行V操作 */

– 非临界区;

–}

? 最简单的信号量是只能取0和1两种值,这种信号量被叫做二维信号 量。在本节中,主要讨论二维信号量。二维信号量的应用比较容易
地扩展到使用多维信号量的情况。

29

信号量
? 信号量编程
– 使用信号量的步骤:
? 第一步:创建信号量或获得在系统已存在的信号量,此时需要调用 semget()函数。不同进程通过使用同一个信号量键值来获得同一个信 号量。
? 第二步:初始化信号量,此时使用semctl()函数的SETVAL操作。当使 用二维信号量时,通常将信号量初始化为1。
? 第三步:进行信号量的PV操作,此时调用semop()函数。这一步是实 现进程之间的同步和互斥的核心工作部分。
? 第四步:如果不需要信号量,则从系统中删除它,此时使用semclt()函 数的IPC_RMID操作。此时需要注意,在程序中不应该出现对已经被 删除的信号量的操作。
30

信号量
? 信号量编程
– semget()函数语法:
31

信号量
? 信号量编程
– semctl()函数语法:
32

信号量
? 信号量编程
– semop()函数语法:
33

? 例子
– Init_sem() – Del_sem() – Sem_p() – Sem_v()

共享内存
? 共享内存是一种最为高效的进程间通信方式,进程可以 直接读写内存,而不需要任何数据的拷贝
? 为了在多个进程间交换信息,内核专门留出了一块内存 区,可以由需要访问的进程将其映射到自己的私有地址 空间
? 进程就可以直接读写这一内存区而不需要进行数据的拷 贝,从而大大提高的效率。
? 由于多个进程共享一段内存,因此也需要依靠某种同步 机制,如互斥锁和信号量等
35

共享内存
? 共享内存原理示意图

进程1 地址空间

内核 共享内存区

进程2 地址空间

进程1的共享 地址映射 内存的映射区

分配的共享内存

地址映射 进程2的共享内 存的映射区

36

共享内存
? 共享内存实现的步骤:
– 创建共享内存,这里用到的函数是shmget,也就是从内存中获得一 段共享内存区域
– 映射共享内存,也就是把这段创建的共享内存映射到具体的进程 空间中去,这里使用的函数是shmat
– 到这里,就可以使用这段共享内存了,也就是可以使用不带缓冲 的I/O读写命令对其进行操作
– 撤销映射的操作,其函数为shmdt
37

共享内存
? shmget函数语法:
38

共享内存
? shmat函数语法:
39

共享内存
? shmdt函数语法:
40

消息队列
? 消息队列就是一些消息的列表。用户可以在消息队列中 添加消息和读取消息等。从这点上看,消息队列具有一 定的FIFO特性,但是它可以实现消息的随机查询,比 FIFO具有更大的优势。同时,这些消息又是存在于内核 中的,由“队列ID”来标识。
41

消息队列
? 消息队列的实现包括创建或打开消息队列、添加消息、 读取消息和控制消息队列这四种操作
? 创建或打开消息队列使用的函数是msgget,这里创建的 消息队列的数量会受到系统消息队列数量的限制
? 添加消息使用的函数是msgsnd函数,它把消息添加到已 打开的消息队列末尾
? 读取消息使用的函数是msgrcv,它把消息从消息队列中 取走,与FIFO不同的是,这里可以指定取走某一条消息
? 控制消息队列使用的函数是msgctl,它可以完成多项功能 。
42

消息队列
? msgget函数语法:
43

消息队列
? msgsnd函数语法:
44

消息队列
? msgrcv函数语法:
45

消息队列
? msgctl函数语法:
46


相关文档

Linux进程间通信
LINUX进程间通信2.
Linux进程间通信管道
Linux环境进程间通信
Linux环境进程间通信(三)
Linux环境进程间通信(一)
linux课件 linux进程间通信
[整理]linux环境进程间通信全.
(整理)linux环境进程间通信全.
电脑版