嵌入式Linux并发程序设计,进程间通信方式,System V IPC,消息队列,打开/创建msgget(), 发送消息msgsnd(),格式,接收消息msgrcv(),控制消息队列 msgctl()

16 篇文章 0 订阅

1,消息队列

  1. 消息队列是System V IPC对象的一种
  2. 消息队列由消息队列ID来唯一标识
  3. 消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等
  4. 消息队列可以按照类型来发送/接收消息(不同的进程可以通过一个消息队列,实现向某一个进程发送消息;可以在消息队列中为每个进程定义不同的消息类型,当前进程只需接受自己的类型的消息,发送对方的类型的消息)

2,消息队列结构

在这里插入图片描述

3,消息队列使用步骤

  1. 打开/创建消息队列 msgget
  2. 向消息队列发送消息 msgsnd
  3. 从消息队列接收消息 msgrcv
  4. 控制消息队列 msgctl

3.1,打开/创建消息队列 msgget()

#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

  1. 成功时返回消息队列的id,失败时返回EOF
  2. key 和消息队列关联的key IPC_PRIVATE 或 ftok
  3. msgflg 标志位 IPC_CREAT|0666

3.1.1,打开/创建消息队列—示例msgget()

……
int main() 
{
   int msgid;
   key_t key;

   if ((key = ftok(., ‘q’)) == -1) {
      perror(“ftok”);  exit(-1);
   }
   if ((msgid = msgget(key, IPC_CREAT|0666)) < 0) {
      perror(“msgget”); exit(-1);
   }
   …… 
   return 0;
}

3.2,向消息队列发送消息 msgsnd()

#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t size,int msgflg);

  1. 成功时返回0,失败时返回-1
  2. msgid 消息队列id
  3. msgp 消息缓冲区地址(存放的是要发送的消息)
  4. size 消息正文长度
  5. msgflg 标志位 0(阻塞方式,表示消息发送成功了才返回) 或 IPC_NOWAIT(不需要等待消息完全发送成功就可以返回,如:消息队列满了)

3.2.1,消息格式

  1. 通信双方首先定义好统一的消息格式
  2. 用户根据应用需求定义结构体类型
  3. 首成员类型为long,代表消息类型(正整数)
  4. 其他成员都属于消息正文

3.2.2,消息发送—示例

typedef  struct 
{
   long mtype;
   char mtext[64];
} MSG;
#define  LEN  (sizeof(MSG) – sizeof(long))
int main() 
{
   MSG buf;
   ……
   buf.mtype = 100; 
   fgets(buf.mtext, 64, stdin);
   msgsnd(msgid, &buf,LEN, 0);
   …… 
   return 0;
}

3.3,从消息队列接收消息 msgrcv()

#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, void *msgp, size_t size, long msgtype,int msgflg);

  1. 成功时返回收到的消息长度,失败时返回-1
  2. msgid 消息队列id
  3. msgp 消息缓冲区地址
  4. size 指定接收的消息长度 (超过指定长度的部分会丢失)
  5. msgtype 指定接收的消息类型,指定成0表示接收消息队列中最早的一个消息 ,指定成负数表示按优先级接受
  6. msgflg 标志位 0(0表示接收不成功则阻塞,直到有消息了,或消息队列被删除了出错返回,或被信号打断了) 或 IPC_NOWAIT

3.3.1,消息接收—示例

typedef  struct {
   long mtype;
   char mtext[64];
} MSG;
#define  LEN  (sizeof(MSG) – sizeof(long))
int main() {
   MSG buf;
   ……
   if (msgrcv(msgid, &buf,LEN, 200, 0) < 0) {
      perror(“msgrcv”);
      exit(-1);
   }
   ……
}

3.4,控制消息队列 msgctl()

#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int cmd, struct msqid_ds *buf);

  1. 成功时返回0,失败时返回-1
  2. msgid 消息队列id
  3. cmd 要执行的操作 IPC_STAT(获取属性) / IPC_SET (设置属性)/ IPC_RMID(删除消息队列,第三个参数传NULL)
  4. buf 存放消息队列属性的地址

消息队列的删除和共享内存不一样。共享内存系统会检查,所有进程都取消映射了才会真的删除,而消息只要进程一执行IPC_RMID,消息队列会立刻被删除

4,消息队列—示例

要求:两个进程通过消息队列轮流将键盘输入的字符串发送给对方,接收并打印对方发送的消息

/******clientA.c******/
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct{
	long mtype;
	char mtext[64];
}MSG; 

#define LEN (sizeof(MSG) - sizeof(long))
#define TypeA 100
#define TypeB 200

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;
	MSG buf;

	if((key = ftok(".",'q')) < 0)
	{
		perror("ftok");
		exit(-1);
	}

	if((msgid = msgget(key,IPC_CREAT|0666)) < 0)
	{
		perror("msgget");
		exit(-1);
	}

	while(1)
	{
		buf.mtype = TypeB;
		printf("input >");
		fgets(buf.mtext,64,stdin);
		msgsnd(msgid,&buf,LEN,0);
		if(msgrcv(msgid,&buf,LEN,TypeA,0) < 0)
		{
			perror("msgrcv");
			exit(-1);
		}
		printf("recv from clientB: %s",buf.mtext);
	}

	return 0;
}
/******clientB.c******/
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct{
	long mtype;
	char mtext[64];
}MSG; 

#define LEN (sizeof(MSG) - sizeof(long))
#define TypeA 100
#define TypeB 200

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;
	MSG buf;

	if((key = ftok(".",'q')) < 0)
	{
		perror("ftok");
		exit(-1);
	}

	if((msgid = msgget(key,IPC_CREAT|0666)) < 0)
	{
		perror("msgget");
		exit(-1);
	}
	while(1)
	{
		if(msgrcv(msgid,&buf,LEN,TypeB,0) < 0)
		{
			perror("msgrcv");
			exit(-1);
		}
		printf("recv from clientA: %s",buf.mtext);
	
		buf.mtype = TypeA;
		printf("input >");
		fgets(buf.mtext,64,stdin);
		msgsnd(msgid,&buf,LEN,0);
	}

	return 0;
}
步骤终端一终端二
·在终端一运行clinntA
·在终端二中可用命令ipcs -q看到系统消息队列中多了一消息队列
linux@linux:~/test/interprocess/message_queue$ ./clientA.out
input >
linux@linux:~/test/interprocess/message_queue$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x7107f38d 32768 linux 666 0 0
·在终端二运行clinntB
·在两个中断中可以交替输入,打印信息
linux@linux:~/test/interprocess/message_queue$ ./clientA.out
input >ABC
input >recv from clientB: ZXCV
linux@linux:~/test/interprocess/message_queue$ ./clientB.out
recv from clientA: ABC
input >ZXCV
·在终端二关闭clinntB(Ctrl-C)
·在终端一中继续输入
·在终端二中可用命令ipcs -q看到系统消息队列仍然存在
input >qwe
^C
linux@linux:~/test/interprocess/message_queue$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x7107f38d 32768 linux 666 64 1
·在终端二输入ipcrm -q 32768手动删除消息队列
·可以在终端一中看到clinntA自动结束运行
linux@linux:~/test/interprocess/message_queue$ ipcrm -q 32768

改进,增添退出处理,程序结束,自动删除消息队列

/******clientA.c******/ 
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct{
	long mtype;
	char mtext[64];
}MSG; 

#define LEN (sizeof(MSG) - sizeof(long))
#define TypeA 100
#define TypeB 200

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;
	MSG buf;

	if((key = ftok(".",'q')) < 0)
	{
		perror("ftok");
		exit(-1);
	}

	if((msgid = msgget(key,IPC_CREAT|0666)) < 0)
	{
		perror("msgget");
		exit(-1);
	}

	while(1)
	{
		buf.mtype = TypeB;
		printf("input >");
		fgets(buf.mtext,64,stdin);
		msgsnd(msgid,&buf,LEN,0);
		if(strcmp(buf.mtext,"quit\n") == 0)break;
		if(msgrcv(msgid,&buf,LEN,TypeA,0) < 0)
		{
			perror("msgrcv");
			exit(-1);
		}
		if(strcmp(buf.mtext,"quit\n") == 0)
		{
			msgctl(msgid,IPC_RMID,NULL);
			exit(0);
		}
		printf("recv from clientB: %s",buf.mtext);
	}

	return 0;
}
/******clientB.c******/ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct{
	long mtype;
	char mtext[64];
}MSG; 

#define LEN (sizeof(MSG) - sizeof(long))
#define TypeA 100
#define TypeB 200

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;
	MSG buf;

	if((key = ftok(".",'q')) < 0)
	{
		perror("ftok");
		exit(-1);
	}

	if((msgid = msgget(key,IPC_CREAT|0666)) < 0)
	{
		perror("msgget");
		exit(-1);
	}
	while(1)
	{
		if(msgrcv(msgid,&buf,LEN,TypeB,0) < 0)
		{
			perror("msgrcv");
			exit(-1);
		}
		if(strcmp(buf.mtext,"quit\n") == 0)
		{
			msgctl(msgid,IPC_RMID,NULL);
			exit(0);
		}
		printf("recv from clientA: %s",buf.mtext);
	
		buf.mtype = TypeA;
		printf("input >");
		fgets(buf.mtext,64,stdin);
		msgsnd(msgid,&buf,LEN,0);
		if(strcmp(buf.mtext,"quit\n") == 0)break;
	}

	return 0;
}

改进思路:
·通过多进程/线程实现同时收发消息
·引入服务器端,实现不同通信方式

/******clientA.c******/ 
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct{
	long mtype;
	char mtext[64];
}MSG; 

#define LEN (sizeof(MSG) - sizeof(long))
#define TypeA 100
#define TypeB 200

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;
	MSG buf;
	pid_t pid;

	if((key = ftok(".",'q')) < 0)
	{
		perror("ftok");
		exit(-1);
	}

	if((msgid = msgget(key,IPC_CREAT|0666)) < 0)
	{
		perror("msgget");
		exit(-1);
	}
	if((pid = fork()) < 0)
	{
		perror("fork");
		exit(-1);
	}
	else if (pid == 0)
	{
		while(1)
		{
			buf.mtype = TypeB;
			printf("input >");
			fgets(buf.mtext,64,stdin);
			msgsnd(msgid,&buf,LEN,0);
			if(strcmp(buf.mtext,"quit\n") == 0)break;
		}
	}
	else
	{
		while(1)
		{
			if(msgrcv(msgid,&buf,LEN,TypeA,0) < 0)
			{
				perror("msgrcv");
				exit(-1);
			}
			if(strcmp(buf.mtext,"quit\n") == 0)
			{
				msgctl(msgid,IPC_RMID,NULL);
				exit(0);
			}
			printf("recv from clientB: %s",buf.mtext);
		}
		kill(pid,SIGUSR1);
	}
	return 0;
}
/******clientB.c******/ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct{
	long mtype;
	char mtext[64];
}MSG; 

#define LEN (sizeof(MSG) - sizeof(long))
#define TypeA 100
#define TypeB 200

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;
	MSG buf;
	pid_t pid;

	if((key = ftok(".",'q')) < 0)
	{
		perror("ftok");
		exit(-1);
	}

	if((msgid = msgget(key,IPC_CREAT|0666)) < 0)
	{
		perror("msgget");
		exit(-1);
	}
	if((pid = fork()) < 0)
	{
		perror("fork");
		exit(-1);
	}
	else if(pid == 0)
	{
		while(1)
		{
			if(msgrcv(msgid,&buf,LEN,TypeB,0) < 0)
			{
				perror("msgrcv");
				exit(-1);
			}
			if(strcmp(buf.mtext,"quit\n") == 0)
			{
				msgctl(msgid,IPC_RMID,NULL);
				exit(0);
			}
			printf("recv from clientA: %s",buf.mtext);
		}
	}
	else
	{
		while(1)
		{
			buf.mtype = TypeA;
			printf("input >");
			fgets(buf.mtext,64,stdin);
			msgsnd(msgid,&buf,LEN,0);
			if(strcmp(buf.mtext,"quit\n") == 0)break;
		}
		kill(pid,SIGUSR1);
	}

	return 0;
}
  • 2
    点赞
  • 3
    评论
  • 7
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 3 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

nice梦醉天宇

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值