进程间通信 消息队列
System V IP
- IPC : Inter-Process Communication (进程间通讯)
- System V 是早期的unix 系统,曾经被称为 AT & T System ,是 unix 操作系统中比较重要的一个分支,现在Linux 系统一般都支持 System V IPC
- System V IPC 对象共有三种
- 消息队列
- 共享内存
- 信号量
- System V IPC 是由内核维护的若千个对象,通过 ipcs 命名查询
- 每个 IPC 对象都有一个唯一的 ID,可以通过 ftok() 函数生成,fork 函数具体说明如下:
函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
函数原型
key_t ftok(const char *pathname, int proj_id);
函数参数 - pathname : 文件路径名
- proj_id : 8 it的id 整数
函数返回值
- 成功: 返回合成的 key
- 失败:-1,并设置 errno
注意:
- key 由文件的 inode 节点号 与 proi id 构成
- inode 节点号 : 每个存在的文件操作系统都会有唯一的编号,通过 ls -i命令查看
消息队列简介
- 消息队列就是一个消息的列表,进程可以在消息队列中添加消息和的读取消息
- 消息队列具有一定的FIFO特性,具有无名管道与有名管道的各自的优势,可以支持任意两个进程的进程间通讯
- 消息队列是属于 sytem v ipc 的一种由内核维护与管理 可以通过 ipcs -q 查看
创建消息队列
- 创建消息队列调用 msgget 函数
函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型
int msgget(key_t key, int msgflg);
函数参数- key : 由 ftok 函数合成
- msgflg : 消息队列标志
- IPC CREAT:创建标志
- IPC EXCL: 如果消息队列,则报错,errno 设置为 EEXIST
- 权限控制标志
函数返回值 - 成功 : 返回 消息队列 id
- 失败: 返回-1,并设置 errno
创建一个消息队列,并打印消息队列 ID
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PATHNAME "."
#define PROID 10
int main(){
key_t key;
int msgid;
key = ftok(PATHNAME,PROID);
if(key == -1){
perror("[ERROR] ftok:");
exit(EXIT_FAILURE);
}
msgid = msgget(key,IPC_CREAT | 0666);
if(msgid == -1){
perror("[ERROR] msgget():");
exit(EXIT_FAILURE);
}
printf("msg id: %d\n",msgid);
return 0;
}
通过 ipcs -q 命名查看后:
- 对于已经创建的消息队列,如果 key 一样,则直接获取这个消息队列ID,对于新创建的消息队列,每次消息队列的ID 不一定相同,即使key一致
示例: 创建两个没有血缘关系的进程,使用 消息队列进行通讯
- read.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#define PATHNAME "."
#define PRO_ID 10
#define MSG_TYPE 100
#define MSG_SZ 64
struct msgbuf {
long mtype;
char mtext[MSG_SZ];
};
int main(void){
int ret,msgid;
key_t key;
ssize_t rbytes;
struct msgbuf buf;
key = ftok(PATHNAME,PRO_ID);
if(key == -1){
perror("[ERROR] ftok()");
exit(EXIT_FAILURE);
}
msgid = msgget(key,IPC_CREAT | 0666);
if(msgid == -1){
perror("[ERROR] msgget");
exit(EXIT_FAILURE);
}
fprintf(stdout,"%d\n",msgid);
rbytes = msgrcv(msgid, (void *)&buf,MSG_SZ,MSG_TYPE,0);
if(rbytes == -1){
perror("[ERROR] msgrcv():");
exit(EXIT_FAILURE);
}
fprintf(stdout,"mtype:%ld\n",buf.mtype);
fprintf(stdout,"mtext:%s\n",buf.mtext);
ret = msgctl(msgid,IPC_RMID,NULL);
if(ret == -1){
perror("msgctl():");
exit(EXIT_FAILURE);
}
return 0;
}
- write.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PATHNAME "."
#define PRO_ID 10
#define MSG_TYPE 100
#define MSG_SZ 64
struct msgbuf {
long mtype;
char mtext[MSG_SZ];
};
int main(void){
key_t key;
int msgid,ret;
struct msgbuf buf;
key = ftok(PATHNAME,PRO_ID);
if(key == -1){
perror("ftok()");
exit(EXIT_FAILURE);
}
msgid = msgget(key,IPC_CREAT | 0644);
if(ret == -1){
perror("msgget():");
exit(EXIT_FAILURE);
}
fprintf(stdout,"msgid:%d\n",msgid);
buf.mtype = MSG_TYPE;
strcpy(buf.mtext,"Hello msg");
ret = msgsnd(msgid,(const void *)&buf,strlen(buf.mtext)+1,0);
if(ret == -1){
perror("msgsnd():");
exit(EXIT_FAILURE);
}
return 0;
}
创建两个子进程A与B,父进程分别给两个子进程发型消息,消息类型为 100 与 200%E2%80%8B%0D%0A•%0D%0A父进程从键盘循环接收数据,发送给子进程,输入 quit 则退出
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#define PATHNAME "."
#define PRO_ID 10
#define MSG_TYPE 100
#define MSG_SZ 64
struct msgbuf {
long mtype;
char mtext[MSG_SZ];
};
int main(void){
key_t key;
int msgid,ret;
struct msgbuf buf;
key = ftok(PATHNAME,PRO_ID);
pid_t cpid1,cpid2;
if(key == -1){
perror("ftok()");
exit(EXIT_FAILURE);
}
msgid = msgget(key,IPC_CREAT | 0644);
if(ret == -1){
perror("msgget():");
exit(EXIT_FAILURE);
}
fprintf(stdout,"msgid:%d\n",msgid);
cpid1 = fork();
if(cpid1 > 0){
cpid2 = fork();
}
if(cpid1 == 0){
fprintf(stdout,"Child <%d> start!\n",getpid());
while(1){
ret = msgrcv(msgid,(void *)&buf,MSG_SZ,100,0);
printf("Child1 <%d>, type 100, mtext %s\n",getpid(),buf.mtext);
}
}
else if(cpid2 == 0){
fprintf(stdout,"Child <%d> start!\n",getpid());
while(1){
ret = msgrcv(msgid,(void *)&buf,MSG_SZ,200,0);
printf("Child2 <%d>, type 200, mtext %s\n",getpid(),buf.mtext);
}
}
else if(cpid1 > 0 && cpid2 > 0){
int threadnum;
char msg[MSG_SZ];
scanf("%d %s",&threadnum,msg);
while(strcmp(msg,"quit") != 0){
buf.mtype = MSG_TYPE;
strcpy(buf.mtext,msg);
buf.mtype = threadnum;
//printf("%d %s",threadnum,msg);
ret = msgsnd(msgid,(const void *)&buf,strlen(buf.mtext)+1,0);
if(ret == -1){
perror("msgsnd():");
exit(EXIT_FAILURE);
}
printf("%d\n",ret);
scanf("%d %s",&threadnum,msg);
}
kill(SIGKILL,cpid1);
kill(SIGKILL,cpid2);
}
return 0;
}