首页 > 代码库 > 进程间通信---IPC对象 之 消息队列

进程间通信---IPC对象 之 消息队列

IPC对象,既我们所说的进程间通信,下面就来总结一下都有哪些方式以及怎么使用。

一 消息队列

1 消息队列的创建:

int msgget(key_t key, int msgflg);
功能:获取指定的key值的消息队列ID
参数:
@key  

<1>IPC_PRIVATE:每次都会创建一个新的消息队列 [用在亲缘关系的进程间痛惜]
<2>ftok函数获的key [用在非亲缘关系进程间通信]
key_t ftok(const char *pathname, int proj_id);
功能:根据文件的ID 和 proj_id的低8bit产生一个key值 
参数:
@pathname  一个文件名 
@proj_id   一个字符
返回值:
成功返回key,失败返回-1 
@msgflg :IPC_CREAT , IPC_EXCL

IPC_CREAT | 0666  -> 如果执行的key的消息队列不存在,则创建新的消息队列 
IPC_CREAT | IPC_EXCL -> 判别指定key的消息队列是否存在,如果存在则报错 

返回值:
成功返回消息队列ID,失败返回-1

2 添加消息 
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
@msqid  消息队列的ID 
@msgp   消息存放的地址 
@msgsz  消息正文的大小 
@msgflg 0:阻塞的方式调用   IPC_NOWAIT:非阻塞的方式调用 

返回值:成功返回0,失败返回-1 

消息结构体:
typedef struct 
{
//第一个字段必须是消息类型
long type;

//消息正文部分 
char buf[1024];
int pid;
..
}MSG;

//计算消息正文的大小 
#define MTXT_SIZE  (sizeof(MSG) - sizeof(long))

3 接受消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数:
@msqid  消息队列的ID 
@msgp   消息存放的地址 
@msgsz  消息正文的大小 
@msgtyp 消息的类型
@msgflg 0:阻塞的方式调用   IPC_NOWAIT:非阻塞的方式调用 

返回值:成功返回接受的消息正文大小,失败返回-1

我们可以通过下面的一个例子来了解消息队列的具体通信方式:

就是对于不是亲缘关系的两个进程A B进行通信:

都可以实现双方的收发:

#include <head.h>

//the struct of mesage
typedef struct
{
	long type;

	char buf[1024];
	int pid;
}MSG;
#define MTXT_SIZE (sizeof(MSG) - sizeof(long))
#define TYPEA 100
#define TYPEB 200

// ./a.out filename
int main(int argc, const char *argv[])
{
	if(argc < 2)
	{
		fprintf(stderr,"Usage : %s argv[1]\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	int key;
	key = ftok(argv[1],'A'); //get key 
	if(key < 0)
	{
		fprintf(stderr,"Fail to ftok \n",strerror(errno));
		exit(EXIT_FAILURE);
	}
	printf("key : %#x\n",key);
	int msg_id;
	msg_id = msgget(key,IPC_CREAT | 0666); //create mesage queue
	if(msg_id < 0)
	{
		perror("Fail to msgget");
		exit(EXIT_FAILURE);
	}
	printf("msg_id :%d\n",msg_id);

	MSG msg;

	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
		perror("Fail to fork");
		exit(EXIT_FAILURE);
	}
	if(pid == 0)
	{
		while(1)
		{
			ssize_t ret;
			ret = msgrcv(msg_id,&msg,MTXT_SIZE,TYPEA,0);
			if(ret < 0)
			{
				perror("Fail to msgrcv");
				exit(EXIT_FAILURE);
			}

			printf("TYPE :%ld\n",msg.type);
			printf("PID  :%d\n",msg.pid);
			printf("MTXT :%s\n",msg.buf);

			if(strncmp(msg.buf,"quit",4) == 0)
			{
				kill(getppid(),SIGUSR1);
				break;
			}
		}
	}
	if(pid > 0)
	{
		msg.pid = getpid();
		msg.type = TYPEB;

		while(1)
		{
			printf(">");
			
			fgets(msg.buf,sizeof(msg.buf),stdin);
			msg.buf[strlen(msg.buf)-1] = '\0';

			if(msgsnd(msg_id,&msg,MTXT_SIZE,0)< 0)
			{
				perror("Fail to msgsnd");
				exit(EXIT_FAILURE);
			}
			if(strncmp(msg.buf,"quit",4) == 0)
			{
				kill(getpid(),SIGUSR1);//使退出一个时全都退出进程
				break;
			}
		}
	}
	return 0;
}

另一个:

#include <head.h>

//the struct of mesage
typedef struct
{
	long type;

	char buf[1024];
	int pid;
}MSG;
#define MTXT_SIZE (sizeof(MSG) - sizeof(long))
#define TYPEA 200
#define TYPEB 100

// ./a.out filename
int main(int argc, const char *argv[])
{
	MSG msg;
	int key;
	int msg_id;
	pid_t pid;

	if(argc < 2)
	{
		fprintf(stderr,"Usage : %s argv[1]\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	key = ftok(argv[1],'A'); //get key 
	if(key < 0)
	{
		fprintf(stderr,"Fail to ftok \n",strerror(errno));
		exit(EXIT_FAILURE);
	}
	printf("key : %#x\n",key);

	msg_id = msgget(key,IPC_CREAT | 0666); //create mesage queue
	if(msg_id < 0)
	{
		perror("Fail to msgget");
		exit(EXIT_FAILURE);
	}
	printf("msg_id :%d\n",msg_id);

	pid = fork();
	if(pid < 0)
	{
		perror("Fail to fork");
		exit(EXIT_FAILURE);
	}
	if(pid == 0)
	{
		while(1)
		{
			ssize_t ret;
			ret = msgrcv(msg_id,&msg,MTXT_SIZE,TYPEA,0);
			if(ret < 0)
			{
				perror("Fail to msgrcv");
				exit(EXIT_FAILURE);
			}

			printf("TYPE :%ld\n",msg.type);
			printf("PID  :%d\n",msg.pid);
			printf("MTXT :%s\n",msg.buf);

			if(strncmp(msg.buf,"quit",4) == 0)
			{
				kill(getppid(),SIGUSR1);
				break;
			}
		}
	}
	if(pid > 0)
	{
		msg.pid = getpid();
		msg.type = TYPEB;

		while(1)
		{
			printf(">");
			fgets(msg.buf,sizeof(msg.buf),stdin);
			msg.buf[strlen(msg.buf)-1] = '\0';

			if(msgsnd(msg_id,&msg,MTXT_SIZE,0)< 0)
			{
				perror("Fail to msgsnd");
				exit(EXIT_FAILURE);
			}
			if(strncmp(msg.buf,"quit",4) == 0)
			{
			
				kill(getpid(),SIGUSR1);

				if(msgctl(msg_id,IPC_RMID,NULL) < 0)
				{
					perror("Fail to msgctl");
					exit(EXIT_FAILURE);
				}

				break;
			}
		}
	}
	return 0;
}

好了,上面就是有关消息队列的相关了解。