管道
管道其实质是由内核管理的一个缓冲区
形象地认为管道的两端连接着两个进程:一个进程进行信息输出,将数据写入管道;
另一个进程进行信息输入,从管道中读取信息。
管道分为:
匿名管道:只能用于有亲缘关系的进程间通信,进程退出后管道会被销毁。
命名管道:命名管道与进程的联系较弱,相当于一个读写内存的接口,进程退出后,命名管道依然存在。
pipe函数
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建匿名管道
dup2函数
#include <unistd.h>
int dup2(int oldfd, int newfd);
- 其功能是将参数oldfd的文件描述符复制给newfd
- 若函数调用成功则返回newfd
- 否则返回-1,并设置errno。
popen/pclose函数
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen函数的功能:
调用pipe()函数创建管道;
调用fork()函数创建子进程;
之后在子进程中通过execve()函数调用shell命令执行相应功能。
pclose函数的功能:
关闭popen()打开的I/O流;
通过调用wait()函数等待子进程命令执行结束;
返回shell的终止状态,防止产生僵尸进程。
命名管道
#include <sys/type.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:创建命名管道(FIFO文件),命名管道与系统中的一个路径名关联,以文件的形式存在于文件系统中,通过FIFO的路径名访问FIFO文件,实现进程间通信。
FIFO文件和普通文件的区别:
- FIFO文件是对内存进行操作;
- 普通文件是存储在硬盘;
- 对内存的的读取会比硬盘的读写要快很多;
- 两个进程通过普通文件通信当然也是可以的。
消息队列
消息队列的本质是一个存放消息的链表,该链表由内核来维护。一个消息队列由一个标识符(即队列key)来标识。
消息队列的通信机制传递的数据具有某种结构,而不是简单的字节流;
向消息队列中写数据,实际上是向这个数据结构中插入一个新结点;
从消息队列中读数据,实际上是从这个数据结构中删除一个结点;
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法;
消息队列具有和管道一样的不足,每个数据块的最大长度是有上限的,系统上全体队列的最大总长度也是有上限的。
使用消息队列实现进程间通信的步骤如下:
- (1)创建消息队列;
- (2)发送消息到消息队列;
- (3)从消息队列中读取数据;
- (4)删除消息队列。
用户消息缓冲区
struct msgbuf{
long int msgtype; //消息类型
anytype data; //要发送的数据,可以为任意类型
};
msgget函数
#include <sys/msg.h>
int msgget(key_t key, int msgflg); //创建消息队列,返回值为该队列号
功能:创建一个消息队列或获取一个已经存在的消息队列。
msgsnd函数
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//发送消息
功能:向指定消息队列发送一个消息;如果msgflg = 0,调用函数的进程会被挂起,直到消息写入消息队列为止
msgrcv函数
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);//接受信息,msgtyp需要指明接受的数据type
功能:从消息队列中读取消息,被读取的消息会从消息列表中移除。
msgctl函数
#include <sys/msg.h>
#include <sys/ipc.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);//对指定消息队列进行控制
//内核为每个消息队列维护一个msqid_ds结构,用于消息队列的管理
struct msqid_ds {
struct ipc_perm msg_perm; //所有者和权限标识
time_t msg_stime; //最后一次发送消息的时间
time_t msg_rtime; //最后一次接收消息的时间
time_t msg_ctime; //最后改变的时间
unsigned long __msg_cbytes; //队列中当前数据字节数
msgqnum_t msg_qnum; //队列中当前消息数
msglen_t msg_qbytes; //队列中允许的最大字节数
pid_t msg_lspid; //最后发送消息的进程pid
pid_t msg_lrpid; //最后接收消息的进程pid
};
功能:对指定消息队列进行控制。
键值和标识符
键值(ID):ID是msgget函数的返回值,一个非负整数,属于进程间通信的内部名,用来确保使用同一个消息队列。内部名即在进程内部使用,是消息队列在进程级别的唯一标识,这样的标识方法是不能支持进程间通信的。
标识符(key): key是实现进程与消息队列关联的关键,属于进程间通信的外部名,是消息队列在内存级别的唯一标识。当多个进程,针对同一个key调用msgget函数,这些进程得到的ID其实是标识了同一个进程间通信的结构。多个进程间就可以通过这个进程间通信的结构进行通信。