Linux系统编程64 进程,线程间通信2 - 消息队列

XSI -> SysV

IPC -> Inter process communication 进程间通信的三种机制
主动端:先发包的一方
被动端:先收包的一方 ,一定是被动端先运行 等着收包

mhr@ubuntu:~/Desktop/xitongbiancheng/communication$ ipcs
Message Queues 消息队列 ,双工
key        msqid      owner      perms      used-bytes   messages 
msgget();
msgop();
msgctl();

Shared Memory Segments 信号量数组
key        shmid      owner      perms      bytes      nattch     status 
 
Semaphore Arrays  共享内存
key        semid      owner      perms      nsems  

关于 key值:

在这里插入图片描述

key:ftok(),产生IPC Key值,将路径名和项目标识符转换为System V IPC密钥

NAME
       ftok - convert a pathname and a project identifier to a System V IPC key

SYNOPSIS
       #include <sys/types.h>
       #include <sys/ipc.h>

       key_t ftok(const char *pathname, int proj_id);

其中参数fname是指定的文件名,这个文件必须是存在的而且可以访问的。id是子序号,它是一个8bit的整数。即范围是0~255。当函数执行成功,则会返回key_t键值,否则返回-1。在一般的UNIX中,通常是将文件的索引节点取出,然后在前面加上子序号就得到key_t的值。

msgget(): 用来创建新的消息队列或获取已有的消息队列

NAME
       msgget - get a System V message queue identifier 获取一个消息队列的ID

SYNOPSIS
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

/*
key: 消息队列key值,函数将它与已有的消息队列对象的关键字进行比较来判断消息队列对象是否已经创建
msgflg : 创建当前消息队列的特殊指定

IPC_CREAT:如果消息队列对象不存在,则创建之,否则则进行打开操作;
IPC_EXCL:和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建,否则产生一个错误并返回。

*/

 int msgget(key_t key, int msgflg);

RETURN VALUE
       If successful, the return value will be the message queue identifier (a nonnegative integer), otherwise -1 with errno indicating the error.

如果单独使用IPC_CREAT 标志,msgget()函数要么返回一个已经存在的消息队列对象的标识符,要么返回一个新建立的消息队列对象的标识符。如果将IPC_CREAT 和IPC_EXCL标志一起使用,msgget()将返回一个新建的消息对象的标识符,或者返回-1 如果消息队列对象已存在。IPC_EXCL 标志本身并没有太大的意义,但和IPC_CREAT 标志一起使用可以用来保证所得的消息队列对象是新创建的而不是打开的已有的对象

msgsnd(),msgrcv() : 发送 接受 消息队列

NAME
       msgrcv, msgsnd - System V message queue operations

SYNOPSIS
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

//发送
/*
msqid:消息队列ID
msgp :待发送数据位置
msgsz :待发送数据大小
msgflg :特殊要求
*/
       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);


//接收
/*
msqid:消息队列ID
msgp :收取消息存放位置,注意空间格式
msgsz :真正收取有效数据信息的大小,而不是全部的数据信息大小:sizeof(rbuf)-sizeof(long)
msgtyp:是否挑选消息来接收,比如接收第几个包,所以msg消息队列本质上已经不算是队列了,因为队列一定是先进先出。
msgflg:特殊指定操作
*/
       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);

注意:
       The msgp argument is a pointer to a caller-defined structure of the following general form:

           struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };

RETURN VALUE
       On failure both functions return -1 with errno indicating the error, otherwise msgsnd() returns 0 and msgrcv() returns the number of bytes actually copied into the mtext array.

msgctl():控制一个消息队列,如销毁,设置等等

NAME
       msgctl - System V message control operations

SYNOPSIS
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

/* 对消息队列msqid 执行 cmd命令,buf表示是否需要传参
cmd:
IPC_STAT
IPC_SET
IPC_RMID  删除当前消息队列
IPC_INFO (Linux-specific)
MSG_INFO (Linux-specific)
MSG_STAT (Linux-specific)
*/
       int msgctl(int msqid, int cmd, struct msqid_ds *buf);

实验:进程间通信 – 消息队列, 这里是无亲缘关系的进程间通信。当然了,有亲缘关系的进程也可以使用消息队列通信。

proto.h

#ifndef PROTP_H_
#define PROTO_H_


#define KEYPATH "/etc/services"
#define KEYPROJ 'g'

#define NAMESIZE 32

struct msg_st
{
 long mtype;// 仿照   struct msgbuf 
	char name[NAMESIZE];
	int math;
	int chinese;
};

#endif

rcver.c 接收端进程

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>

#include "proto.h"

int main(void)
{
	key_t key;
	int msgid;
	struct msg_st rbuf;

	key = ftok(KEYPATH,KEYPROJ);//获取IPC 键值
	if(key < 0)
	{
		perror("ftok");
		exit(1);
	}

	msgid = msgget(key,IPC_CREAT|0600); //创建 消息队列 返回IP
	if(msgid < 0)
	{
		perror("msgget");
		exit(1);
	}

	while(1)
	{
 //从目标消息队列 接受数据,注意接收数据的大小是有效数据的大小
		if(msgrcv(msgid,&rbuf,sizeof(rbuf)-sizeof(long),0,0) < 0)
		{
			perror("msgrcv");
			exit(1);
		}
		printf("NAME = %s\n",rbuf.name);
		printf("MATH = %d\n",rbuf.math);
		printf("CHINESE = %s\n",rbuf.chinese);

	}

	msgctl(msgid,IPC_RMID,NULL);//销毁消息队列

	
	exit(0);
}

snder.c 发送端进程

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <string.h>
#include "proto.h"

int main()
{
	key_t key;
	int msgid;
	struct msg_st sbuf;

	key = ftok(KEYPATH,KEYPROJ);//获取消息队列 IPC 键值
	if(key < 0)
	{
		perror("ftok");
		exit(1);
	}


	msgid = msgget(key,0);//获得已创建好的目标消息队列 的 ID
	if(msgid < 0)
	{
		perror("msgget");
		exit(1);
	}

//初始化数据
	sbuf.mtype = 1;
	strcpy(sbuf.name,"MHR");
	sbuf.math = rand()%100;
	sbuf.chinese = rand()%100;

// 发送数据
	if(msgsnd(msgid,&sbuf,sizeof(sbuf)-sizeof(sbuf),0) < 0)
	{
		perror("msgsnd");
		exit(1);
	}

	puts("OK");
	
	exit(0);
	

}

需要注意的是,如果先执行 10次 发送端进程,再执行一次接收端进程,那么接收端会一次性接受 之前10次的 发送端发送的数据。这是因为
消息队列有一个缓存消息的能力,具体能缓存多大的数据,可以 ulimit -a 查看

mhr@ubuntu:~/Desktop/xitongbiancheng/communication/msg/basic$ ulimit -a
...
POSIX message queues     (bytes, -q) 819200
...
mhr@ubuntu:~/Desktop/xitongbiancheng/communication/msg/basic$ 

ipcs 查看当前IPC

ipcrm : ipcrm - remove certain IPC resources 删除指定IPC 参数入下

如 删除指定 消息队列:

先查看 目标消息队列 id
mhr@ubuntu:~/Desktop/xitongbiancheng/communication/msg/basic$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
0x670100d2 0 mhr 600 0 0

删除:
mhr@ubuntu:~/Desktop/xitongbiancheng/communication/msg/basic$ ipcrm -q 0

OPTIONS

   -M, --shmem-key shmkey
          Remove the shared memory segment created with shmkey after the last detach is performed.

   -m, --shmem-id shmid
          Remove the shared memory segment identified by shmid after the last detach is performed.

   -Q, --queue-key msgkey
          Remove the message queue created with msgkey.

   -q, --queue-id msgid
          Remove the message queue identified by msgid.

   -S, --semaphore-key semkey
          Remove the semaphore created with semkey.

   -s, --semaphore-id semid
          Remove the semaphore identified by semid.

   -V, --version
          Display version information and exit.

   -h, --help
          Display help text and exit.

猜你喜欢

转载自blog.csdn.net/LinuxArmbiggod/article/details/114849370
今日推荐