进程间通讯(待补充)

IPC 概念(后续补充)

IPC 持续性

管道

pipe管道

需要一个父进程先使用 pipe 函数获得两个文件描述符 fd[0] 和 fd[1],fd[0] 用于读入,fd[1] 用于写入,然后 fork 一个子进程,然后通过fd 数组进行数据传输;

我们平时写的 shell 命令是典型的管道,如 who | sort | lp

在这里插入图片描述

半双工管道和全双工管道

pipe 是半双工的,即数据传输是单向的,只能使用 fd[0] 进行读入,使用 fd[1] 写入

理想上 使用单管道的全双工实现,如图
在这里插入图片描述

管道就是一个缓冲区,使用一个管道时,相当于使用了一个缓冲区实现,所有数据都写入到缓冲区的末尾,这可能导致一方在读取数据时,读取到自己之前写入的数据

下图为全双工管道的正确实现

在这里插入图片描述

使用一个管道只能实现半双工,实现全双工需要使用到两个半双工管道

缺点

使用 pipe 只能使用在有亲缘关系的进程中,使用也叫做匿名管道

FIFO

FIFO 和 pipe 管道类似,但是不同的是 FIFO 是有名字的,这意味着进程间不需要有亲缘关系,所以也叫有名管道

使用 mkfifo 函数来创建的

//mkfifo的函数声明
int mkfifo(const char *pathname, mode_t mode);//成功返回0,出错返回-1
1.pathname
	一个 Unix 路径名,也是创建的 FIFO 的名字
2.mode
	为权限位(读写权限,创建者和组用户等,总共 6 个常量)

使用完 mkfifo 必须使用 open 函数才能获取到文件描述符,代码如下

//open的函数声明
int open(const char*pathname, int flags, mode_t mode);
1.pathname
	要打开或创建的目标文件
2.flags
	可以传入多个参数选项,设置多个参数时进行"位或"运算
	参数:O_RDONLY:   只读打开
  		 O_WRONLY:   只写打开
  		 O_RDWR:     读,写打开
3.mode
	(该参数设置值待考证,但以下说明应该可以确定)
     O_CREAT:		若文件不存在,则创建它

创建一个 FIFO ,获取他的读写文件描述符,再关闭 FIFO 的代码

#define FIFO1 "/tmp/fifo.1"
if(mkfifo(FIFO1, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
    printf("can't create %s", FIFO1);
}
int readfd = open(FIFO1, O_RDONLY, 0);
int writefd = open(FIFO1, O_WRONLY, 0);
unlink(FIFO1)

pipe 在关闭所有进程后管道就会自动消失,而 FIFO 需要使用 unlink 函数才能在文件系统中删除

IPC 持续性

pipe 和 FIFO 的 IPC 持续性都是随进程

缺点与解决办法

pipe 和 FIFO 使用的是字节流 I/O 模型,是不记录边界的,我们不会对数据进行检查;

举个例子:当我们读取到100个字节时,我们不知道是一个进程一次写了100字节 还是 多个进程分多次写入总数是100字节

当我们希望数据可以具有某种数据结构 或者 在写入多个信息时可以正确的读出数据时,不记录边界的方式会出现错误

大致解决方法有三种

  1. 使用特殊的分隔符来分隔消息,需要数据中使用到该分隔符的地方进行转义
  2. 使用显式长度,在每一个消息前记录消息的长度
  3. 每次连接一个记录,(在网络应用中使用 TCP 连接,在 IPC 应用中使用 IPC 连接)

管道要求写入者进程在写入消息之前,是需要有读出者进程在等待读取消息;(此话有错误

如果写入者在读入者等待之前写入数据是没意义的

消息队列

消息队列可以认为是一个消息链表,有写权限的线程可以向队列中写消息,有读权限的的线程可以向队列中取走消息(Unix网络编程卷2 中原话是有足够权限的线程,目前本人还不太理解足够的意思,我们姑且认为只有读写权限)

消息队列不要求读出者早于写入者存在

消息队列主要有 Posix 消息队列和 System V 队列,接下来内容我们只讨论 Posix 的消息队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ovnh9GUW-1619186943077)(D:\Desktop\note\linux与操作系统\消息队列示意图.png)]

队列的头节点放置两个队列的属性

  1. mq_maxmsg:队列所允许的最大队列数
  2. mq_msgsize:每个消息的最大大小

创建、关闭、删除队列

使用 mq_open 函数创建队列

#include <mqueue.h>
mqd_t mqd_open(const char *name, int oflag, ···);

oflag:和 open 函数的 flag 参数一致,可能按位或上O_CREAT、O_EXCL、O_NONBLOCK

返回值为消息队列描述符,作为其他7个消息队列的函数的第一个参数

使用 mq_close 函数关闭队列

#include <mqueue.h>
int mq_close(mqd_t mqdes);

和文件的 close 函数类似,调用后进程不再使用该描述符,但是消息队列不会删除,一个进程终止时是就相当于调用该函数,关闭所有打开的消息队列

使用 mq_unlink 函数输出

#include <mqueue.h>
int mq_unlink(const char *name);
每个消息队列会保存一个引用计数器,在计数值 >0 时就可以调用 mq_unlink 会删除该名字,但已经打开的进程还可以使用(待考证),等到所有进程都关闭该队列,即计数器为0,才会彻底删除
mq_close 也可以使计数器减一

获取消息队列的属性

mq_attr 结构:

struct mq_attr{
	long mq_flags;
	long mq_maxmsg;
	long mq_msgsize;
	long mq_curmsgs;
};

mq_getattr 函数 获取队列属性

mq_setattr 函数 设置队列属性

#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);
均成功返回0,出错则为-1

发送和接收消息

mq_send 函数发送消息

mq_receive 函数接收消息

#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio);
ptr 为消息
prio 为消息的优先级
返回:成功返回0,出错返回-1
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *priop);
priop 如果是非空指针,就用于存放返回的优先级
len 注意不可以小于 mq_attr 结构中的 mq_msgsize 的大小,否则会失败,可以先用 mq_getattr 获取一下(血的教训);
返回:ssize_t 在32位计算机中为 int 型,在64位计算机为 long 型,成功返回的消息中的字节数,失败返回-1

mq_notify函数(待补充)


信号量(待补充)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值