System V IPC包含三种进程间通信机制,有消息队列,信号灯(也叫信号量),共享内存。此外还有System V IPC的补充版本POSIX IPC,这两组IPC的通信方法基本一致,本章以System V IPC为例介绍Linux进程通信机制。
可以用命令“ipcs”查看三种IPC,“ipcrm”删除IPC对象。在i.MX6ULL终结者开发板终端输入“ipcs”查看系统中存在的IPC信息:
这些 IPC对象存在于内核空间,应用层使用IPC通信的步骤为:
1.获取 key值,内核会将 key值映射成IPC标识符,获取key值常用方法:
(1)在get调用中将IPC_PRIVATE常量作为key值。
(2)使用ftok()生成key。
2.执行IPC get调用,通过key获取整数IPC标识符id,每个id表示一个IPC对象。
3.通过id访问IPC对象。
4.通过id控制IPC对象
创建这三种IPC对象都要先获取key值,然后根据key获取id,用到的函数如下:
ftok()
:获取key,定义如下:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
参数含义:
pathname:路径名或文件名。
proj_id:同一个文件根据此值生成多个key值,int型或字符型,多个若想访问同一IPC对象,此值必须相同。
返回值:成功返回key值,失败返回-1。
下面介绍消息队列:
消息队列是类 unix系统中一种数据传输的机制,其他操作系统中也实现了这种机制,可见这种通信机制在操作系统中有重要地位。
Linux内核为每个消息队列对象维护一个msqid_ds,每个msqid_ds对应一个id,消息以链表形式存储,并且msqid_ds存放着这个链表的信息。
消息队列的特点:
1.发出的消息以链表形式存储,相当于一个列表,进程可以根据id向对应的“列表”增加和获取消息。
2.进程接收数据时可以按照类型从队列中获取数据。
消息队列的使用步骤:
1.创建key;
2.msgget()通过key创建(或打开)消息队列对象id;
3.使用msgsnd()/msgrcv()进行收发;
4.通过msgctl()删除ipc对象
通过msgget()调用获取到id后即可使用消息队列访问IPC对象,消息队列常用API如下:
msgget()
:获取IPC对象唯一标识id,定义如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数含义:
key:通过f tok或IPC_PRIVATE获取的key值;
msgflg:指定匹配的结构,通常使用 IPC_CREAT。
msgsnd()
:发送数据
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void msgp, size_t msgsz, int msgflg);
参数含义:
msqid:IPC对象对应的 id;
msgp:消息指针,消息包含类型和字段;
msgsz:传输字段的大小,不算消息中的类型type占的内容;
在终端输入“man msgsnd
”查看定义。
用于传输的消息格式通常为一个结构体,名字可以自定义,例如:
struct msgbuf {
long mtype; / message type, must be > 0 /
char mtext[1]; / message data */
};
其中“mtype”为消息类型,mtext为字段。
msgflg:用于控制msgsnd的操作,填写非阻塞IPC_NOWAIT;
返回值:成功返回0,失败返回-1.
msgrcv()
:接收消息,定义如下:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数含义:
msqid:IPC对象对应的 id;
msgp:消息指针,消息包含类型和字段;
msgsz: 消息里的字段大小;
msgtyp:消息里的类型;
msgflg:位掩码,不止一个。
返回值:成功返回接收到的字段大小,错误返回-1。
msgctl()
:控制操作,删除消息队列对象等,定义如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数含义:
msqid:IPC对象对应的id;
cmd:对消息队列对象进行的操作,
IPC_STAT:将与消息队列关联的msqid_ds复制到buf;
IPC_RMID:删除消息队列对象及msqid_ds。
IPC_SET:把 buf更新到msqid_ds。
buf:描述消息队列的结构体msqid_ds。
实验代码在msg_que/目录下:路径为:11_Linux系统开发进阶\Linux系统编程_章节使用资料。
send进程(代码见send.c)将数据发送到消息队列,并指定消息类型type,QT进程接收指定type为1的消息。
send.c:
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
struct msg_buf{
long mtype;
char mtext[32];
};
#define MSG_LEN sizeof(struct msg_buf)-sizeof(long)
int main(int argc, const char *argv[])
{
key_t key;
int msgid;
int ret;
key = ftok("/", 133);
if(key==-1){
perror("ftok");
return -1;
}
msgid = msgget(key,IPC_CREAT|0666);
if(msgid==-1){
perror("msgget");
return -1;
}
struct msg_buf m_buf;
m_buf.mtype=1;
strcpy(m_buf.mtext,"IPC message Queues 1");
int re;
re = msgsnd(msgid,&m_buf,MSG_LEN,0);
if(re==-1){
perror("msgsnd");
return -1;
}
return 0;
}
交叉编译到开发板:arm-linux-gnueabihf-gcc -o send send.c,
运行./send,查看系统中的IPC对象:
Qt进程代码在Qrecv/文件夹下,
执行四次./send向消息队列发送4条消息,将Qt程序交叉编译后在yocto文件系统运行,连续点击“get”按钮接收,后台打印如下:
GUI界面显示如下:
可以发现Qt进程能够接收到数据,这就是消息队列的基本用法。