【进程间通信】常用方式汇总
随着我们的进程越来越多,难免不同进程之间要互相传输一些数据,那么这个时候该怎么办呢?
下面主要简单了解一下,进程间通信(InterProcess Communication,IPC)的几种实现方式!
1、管道模型
管道模型与软件生命周期模型——瀑布模型(Waterfall Model)很相似。
所谓的瀑布模型,其实就是将整个软件开发过程分成多个阶段,往往是上一个阶段完全做完,才将输出结果交给下一个阶段。
还记得咱们最初学 Linux 命令的时候,有下面这样一行命令:
ps -ef | grep 关键字 | awk '{print $2}' | xargs kill -9
这里面的竖线“|”就是一个管道。它会将前一个命令的输出,作为后一个命令的输入。
从管道的这个名称可以看出来,管道是一种单向传输数据的机制,它其实是一段缓存,里面的数据只能从一端写入,从另一端读出。如果想互相通信,我们需要创建两个管道才行。
管道又可以分为匿名管道和命名管道!
1.1 匿名管道
如上命令:
ps -ef | grep 关键字 | awk '{print $2}' | xargs kill -9
匿名管道:用"|” 表示的管道,意思就是这个类型的管道没有名字,用完了就销毁了。竖线代表的管道随着命令的执行自动创建、自动销毁。用户甚至都不知道自己在用管道这种技术,就已经解决了问题。
1.2 命名管道
命名管道,这个类型的管道需要通过 mkfifo
命令显式地创建。
mkfifo donge #建立一个管道
donge
就是这个管道的名称。管道以文件的形式存在,这也符合 Linux 里面一切皆文件的原则。
下面我们看一下文件类型
ls -l
prw-rw-r-- 1 dong dong 0 Sep 28 17:09 donge
可以看到,这个文件的类型是 p
,就是 pipe
的意思。
往管道中写入数据
echo "hello world" > donge
这个时候,管道里面的内容没有被读出,这个命令就是停在这里的,即进程被堵塞。
这说明当一个项目组要把它的输出交接给另一个项目组做输入,当没有交接完毕的时候,前一个项目组是不能撒手不管的。
重新打开一个终端,读出管道数据
cat < hello
hello world
一方面,我们能够看到,管道里面的内容被读取出来,打印到了终端上;
另一方面,echo 那个命令正常退出了,也即交接完毕,前一个项目组就完成了使命,可以解散了。
管道通信,我们可以看出,瀑布模型的开发流程效率比较低下,因为团队之间无法频繁地沟通。而且,管道的使用模式,也不适合进程间频繁的交换数据。
2、消息队列
消息队列可以理解为发邮件,每一封邮件都视为一个独立的数据单元,也就是消息体,每个消息体都是固定大小的存储块,在字节流上不连续。
这个消息结构的定义我写在下面了。这里面的类型 type 和正文 text 没有强制规定,只要消息的发送方和接收方约定好即可。
struct msg_buffer {
long mtype;
char mtext[1024];
};
2.1 创建消息队列
消息队列的创建,需要用到msgget
函数
int msgget(key_t key, int msgflg);
-
key
:该参数是消息队列的唯一标识,由ftok
生成。 -
msgflg
:取值有以下几个选择:IPC_CREAT
、IPC_EXCL
,这两个参数详细的作用可以man msgflg
看详细介绍。 -
返回值:返回一个近乎唯一的
Message queue id
那么,
key
是如何由ftok
生成的呢?
我们可以指定一个文件,调用ftok
,它会根据这个文件的 inode
,生成一个近乎唯一的 key
。
key_t ftok(const char *pathname, int proj_id);
pathname
:文件信息,必须指定在一个存在的,可访问的文件proj_id
:8bit
的数据,0-255
随意设定
这样就可以获得一个近乎唯一的key
了!
只要在这个消息队列的生命周期内,这个文件不要被删除就可以了。只要不删除,无论什么时刻,再调用 ftok,也会得到同样的 key。
综上,创建一个消息队列只需两步:
①:ftok
生成一个key
②:msgget
生成一个消息队列的ID
如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
int main() {
int messagequeueid;
key_t key;
if((key = ftok("/root/messagequeue/messagequeuekey", 1)) < 0)
{
perror("ftok error");
exit(1);
}
printf("Message Queue key: %d.\n", key);
if ((messagequeueid = msgget(key, IPC_CREAT|0777)) == -1)
{
perror("msgget error");
exit(1);
}
printf("Message queue id: %d.\n", messagequeueid);
}
ftok
要指定一个存在的文件,所以我们在执行之前,需要创建该文件。
查看消息队列
System V IPC 体系有一个统一的命令行工具:
ipcmk
,ipcs
和ipcrm
用于创建、查看和删除IPC
对象。
查看创建的IPC
对象:ipcs -q
dong@ubuntu:~//Interprocess_Communication$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
0x01110005 0 dong 777 0 0
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
2.2 发送消息
消息队列发送消息,主要调用msgsnd
函数
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid
:该参数是msgget
所得到的message queue
的id
msgp
:消息结构体
struct msg_buffer {
long mtype;
char mtext[1024];
};
msgsz
:表示消息结构体中,mtext
最大长度msgflg
:一位掩码,可取值有:IPC_NOWAIT
、MSG_COPY
、MSG_EXCEPT
、MSG_NOERROR
,取值说明可见man msgsnd