进程间通信--pipe/msgqueue/sems/shm

IPC inter-process communication

进程间通信是指在不同进程之间传播或交换信息
进程间通信可分为以下几类

  • 管道(匿名管道和命名管道)
  • system IPC
    • 消息队列(用于数据传输)
    • 共享内存(用于数据共享)
    • 信号量(用于事件通知)
  • POSIX IPC
    • 消息队列
    • 共享内存
    • 互斥量
    • 条件变量
    • 信号量
    • 读写锁

主要介绍常用的如管道、消息队列、信号量、共享内存这几个。

一、管道

管道通常指匿名管道,它也是Unix系统最古老的IPC形式。

1、特点:
  • 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

  • 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

  • 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

2、创建管道
  • 创建匿名管道
    函数原型:
#include<unistd.h>
int pipe(int pipefd[2]);

参数:
pipefd[2]:文件描述符数组,fd[0]表示读端,fd[1]表示写端
返回值:
成功返回0,失败返回错误代码
这里写图片描述
要关闭管道关闭这两个文件描述符即可。

  • 创建命名管道
    函数原型
  int mkfifo( const char* name, mode_t mode)

如mkfifo my.p
(mkfifo创建普通文件)
mode为权限
当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
1.若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
2.若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

等于用一个名字代表内存空间
这里写图片描述

3、打开管道文件
int fd = open( name, O_RDONLY);  //读
int fd = open( name, O_WRONLY); //写
4、匿名管道和命名管道的区别
  1. 匿名管道由pipe函数创建并打开
  2. 命名管道由mkfifo函数创建,用open打开
  3. 创建成功后,FIFO和pipe语义相同。

二、消息队列

消息队列是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。消息队列提供了从一个进程向另一个进程发送一块数据的方法。

1、特点
  • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
  • 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
  • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
2、相关函数原型
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

// 获取消息队列
int msgget(key_t key, int msgflg);
// 往消息队列中发送数据
int msgsnd(int msgid, const void* msgp, size_t msgsz, int msgflg);
// 从消息队列中取数据
size_t msgrcv(int msgid, void* msgp, size_t msgsz, long msgtyp, int msgflg);
3、相关指令

查看IPC对象
ipcs -q
删除IPC对象
ipcrm -Q key
系统中最多能创建多少消息队列
cat /proc/sys/kernel/msgmni
一条消息最多能装多少字节
cat /proc/sys/kernel/msgmax
一个消息队列中所有消息的总字节数
cat /proc/sys/kernel/msgmnb

共享内存

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。

1、特点
  • 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。

  • 因为多个进程可以同时操作,所以需要进行同步。

  • 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

2、函数原型
// 创建或打开共享内存
int shmget(key_t key, size_t size, int flag);
// 让共享内存和本进程建立关系
void* shmat(int id, const char *shmaddr, int flag);
// 卸载掉共享内存(不删除)
int shmdt(void* shmaddr);
// 删除共享内存
int shmctl(int id, int cmd IPC_RMID 删除共享内存, NULL);

当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。

当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。

shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。

shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。

信号量

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

1、特点

信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。

信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。

每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

支持信号量组。

2、原型

最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。

Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

// 创建或打开信号量
int semget(key_t key, int nsems, int flag);
// 设置信号量初值
int semctl(int semid, int semnum, int cmd, su);
// 查看信号量初值
int semctl(int semid, int semnum,GETVAL,0);
// 创建或访问一个信号集
int semop(int semid 信号量的标识码 semget的返回值, struct sembuf sb[], int len 信号量的个数)

猜你喜欢

转载自blog.csdn.net/wandandi/article/details/81081625