概述
学习目标:
- 理解利用PIPE和FIFO实现进程间数据通信的原理
- 掌握相应编程方法
- 理解Linux管道命令实现原理
- 理解消息队列结构和通信原理
- 掌握利用消息队列进行进程间数据通信的编程方法
- 理解共享内存原理
- 掌握利用共享内存实现进程间数据共享的编程方法
- 理解IPC信号量API函数使用方法
- 掌握利用IPC信号量实现进程同步互斥编程方法
主要内容:
- 管道通信:无名管道、命名管道
- 消息队列
- 共享内存
- IPC信号量
7.1 管道通信
7.1.1 什么是管道
- 定义:是一种具有生活中“输油管”、“输水管”特性的一种进程间通信机制,管道是操作系统内核实现的。一个进程从管道一端写入数据,另一个进程从管道另一端读出,从而实现了数据传输。
- 实质:操作系统内核的一个内存缓冲区。
- 管道典型应用
多个命令的输入输出首尾相接cmd1 | cmd2 | …
7.1.2 命名管道FIFO及应用编程
什么是命名管道:
- 一种特殊的文件类型,称为FIFO,类型为p,具有文件名、创建时间、访问权限等几乎所有的文件属性。
- 数据只存在于操作系统内核的缓冲区内存中,不写入磁盘块,因此管道文件在磁盘上是没有数据块的。
- 操作方法:文件操作命令,如read,echo
- 编程读写:UNIX I/O函数:……
- 与文件相比:数据不经过外存,读写速度快
创建FIFO文件
- 用命令创建
$ mkfifo /tmp/myfifo1
$ ls -lF /tmp/myfifo1
- 编程创建:
#include<sys/types>
#include<sys/stat.h>
int mkfifo(const char *filename , mode_t mode);
//若成功返回0,失败返回-1,错误原因存在ermo中
使用命令访问FIFO
不同程序使用FIFO通信
7.1.3 利用FIFO传输任意类型数据
- 任何数据结构都存在于某个内存块中,将该内存块看成write、read函数调用的缓冲区,就可通过FIFO将任何数据结构传递给其它进程。
- 假设我们要传递一个类型为T变量var,定义为T var。现要将其通过命名管道”myfifo”从进程A发送给进程B处理。
- 方法是把变量var所在单元看成一个缓冲区,其地址为&var,长度为sizeof(T)
- 发送方将缓冲区内容写入管道,接收方将收到信息存入缓冲区,并将缓冲区看成一个T类型指针
- 如果要编程发送一个数组arr(定义为T arr[N]),可以每次读写一个数组元素,或将整个数组作为一个整体进行发送。
7.1.4 无名管道PIPE及应用
虽然命名管道已经是一种比较方便易用的管道了,但对父子进程来说,由于子进程可以直接继承父进程拥有的打开文件描述符资源,在父子进程间采用管道机制通信,甚至连文件名都可以省略,使进程间通信程序得到进一步简化,甚至还可实现更强的应用功能。这种管道称为无名管道。
创建无名管道
#include <unistd.h>
int pipe(int fds[2]);
- 返回文件描述符fds[1]是管道的写端,fds[0]是管道读端
父子进程间利用无名管道进行通信
7.1.5 使用PIPE及应用
示例pipe3.c
- 父子进程通过管道连接,父进程将字符串”1234567890”传递给子进程处理,子进程执行od程序对收到的文本进行计数处理。
- 思路:先创建管道,创建子进程,通过dup调用将父进程输出导入到子进程输入
- 调用Pipe后情况
7.1.6 使用FIFO的客户端/服务端应用程序
要求:
通过命名管道来编写一个简单的客户/服务器应用程序:客户端发送请求,服务器进程接受请求,对它们进行处理,将字母转换为大写,并把结果数据返回给发送请求的客户端。
设计思路:
1. 管道设置 ClinetServer:共用1个,serv_fifo,用PID标识来自哪个Client ServerClient:每个Client一个,名称:cli_<PID>_fifo
2. 信息发送和接收缓冲区结构
#define BUFFER_SIZE 20 struct data_to_pass_st { //客户端发来数据的结构
pid_t client_pid; //客户进程的PID
char some_data[BUFFER_SIZE - 1]; //存放消息的用户缓冲区
};
7.2 消息队列
- 基本思想:进程间以数据块(消息)为单位来传递数据,是进程之间传递数据的简单有效方法
- 它独立于发送和接收进程而存在,消除了在同步命名管道的打开和关闭时遇到的麻烦
- 每个数据块可被看成某种类型,可发送任何类型数据
- MSGMAX:消息的最大长度。