linux进程间通信:POSIX 消息队列

文章目录

        • 基本介绍
        • 相关编程接口
        • 编程实例
          • 消息队列通信实例
          • 消息队列属性设置实例

基本介绍

关于消息队列的基本介绍,前面在学习system V的消息队列时已经有过了解,linux进程间通信:system V消息队列

  1. 支持不同进程之间以消息(messages)的形式进行数据交换,消息能够拥有自己的标识,且内核使用链表方式进行消息管理。
  2. 进程之间的通信角色为:发送者和接受者
    发送者:
    a. 获取消息队列的ID(key或者msgid)
    b. 将数据放入一个带有标识的消息结构体,发送到消息队列
    接受者:
    a. 获取消息队列的ID
    b. 指定标识的消息从消息队列中读出,然后进一步后续处理
  3. 支持不同的进程标记不同的消息优先级(0,1,2…),并由内核态维护对应消息类型的链表。
  4. 内核态的0号消息类型维护了一个链表,用来保存按照时间顺序加入的消息

相关编程接口

  • mq_open :创建或打开一个消息队列
  • mq_send :向消息队列写入一条消息
  • mq_receive :从消息队列中读取一条消息
  • mq_close :关闭进程打开的消息队列
  • mq_unlink :删除一个消息队列
  • mq_setattr :设置消息队列的一些额外属性
  • mq_getattr :获取消息队列的一些额外属性
  • mq_notify :异步通知
  1. 创建或打开一个消息队列
    a. 头文件
    b. 函数使用:
    mqd_t mq_open(const char *name, int oflag);
    mqd_t mq_open(const char *name, int oflag, mode_t mode,struct mq_attr *attr);
    c. 函数功能:创建一个新的POSIX消息队列或者打开一个已存在的消息队列。且消息队列是由name标识
    d. 函数参数:

    • name 用来标识需要创建或者打开的ipc对象
    • oflag O_CREAT/O_RDONLY/O_WRONLY/O_RDWR/O_EXCL/O_NOBLOCK
      标记使用什么样的方式打开ipc对象
    • mode 位掩码,用来设置权限 8进制
    • attr 设置消息队列的属性,若为NULL,使用默认属性,linux 3.5以后版本也可以通过/proc查看设置
      主要数据结构如下:
       struct mq_attr {
         long mq_flags;       /* Flags (ignored for mq_open()) */
         long mq_maxmsg;      /* Max. # of messages on queue */
         long mq_msgsize;     /* Max. message size (bytes) */
         long mq_curmsgs;     /* # of messages currently in queue
                                 (ignored for mq_open()) */
      };
      

    e. 函数返回值
    成功:返回消息队列对象的描述符
    失败:返回-1,并设置errno

  2. 关闭一个消息队列
    a. 头文件 :#include
    b. 函数使用:int mq_close(mqd_t mqdes);
    c. 函数功能:关闭一个描述符为mqdes的消息队列
    d. 返回值:
    成功:返回0
    失败:返回-1
    e. 注意:POSIX消息队列在进程终止或者执行execve()时会自动关闭

  3. 删除消息队列
    a. 头文件: #include
    b. 函数使用: int mq_unlink(const char *name);
    c. 函数功能:删除通过name标识的消息队列;在所有进程使用完该队列之后销毁该队列;若打开该队列的所有进程都已关闭,则立即删除
    d. 返回值:
    成功:0
    失败:-1

  4. 向消息队列中写入消息
    a. 头文件: #include
    b. 函数使用: int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
    c. 函数功能:将msg_ptr所指向的缓冲区中的消息内容添加到描述符mqdes所引用的消息队列中
    d. 函数参数:

    • mqdes :消息队列描述符
    • msg_ptr: 指向存放消息的缓冲区指针
    • msg_len: 消息的长度[10,8192]
    • msg_prio: 消息队列中按优先级排序,设置为0标识无需优先级;该参数为POSIX消息队列和system V消息队列的差异,即POSIX是通过优先级来区分消息,system V是通过消息类型来区分消息

    e. 返回值
    成功:0
    失败:-1

  5. 从消息队列中接收数据
    a. 头文件: #include
    b. 函数使用: ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
    c. 函数功能:默认从mqdes引用的消息队列中删除一条优先级最高、存放时间最长的消息;并将删除的消息保存在msg_ptr;
    这里如果接收时指定优先级,可以读取指定优先级的消息,并存放在msg_ptr
    d. 函数参数:

    • mqdes :消息队列描述符
    • msg_ptr: 指向存放消息的缓冲区指针
    • msg_len: 消息的长度[10,8192]
    • msg_prio: 消息队列中按优先级排序,设置为0标识无需优先级;

    e. 返回值
    成功:消息接收的字节数
    失败:-1

  6. 获取/设置消息队列的属性信息
    a. 头文件 #include
    b. 函数使用:
    int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
    int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
    c. 函数功能:获取或者修改一个由mqdes标识的消息队列的属性信息
    d. 函数参数:

    • mqdes 消息队列的唯一标识
    • newattr 设置修改后的mq_attr结构体类型的属性信息
    • oldattr 修改前的属性信息,如果当前参数为NULL,则默认设置从mq_getattr中获取到的属性信息

    关于消息队列的属性设置如下两种方式:

    • 通过proc文件系统
      echo num > /proc/sys/fs/mqueue/queues_max
      echo num > /proc/sys/fs/mqueue/msg_max
      echo num > /proc/sys/fs/mqueue/msgsize_max
    • 通过POSIX系统调用接口设置
      mq_setattr 仅能设置mq_flags
      mq_open 仅设置 mq_maxmsgmq_msgsize

编程实例

最终创建的POSIX消息队列的实例存放在/dev/mqueue路径下

消息队列通信实例

mq_demo.c

#include 
#include 
#include 
#include 
#include 

#include 

int main()
{
   mqd_t mq_id;
   //创建一个消息队列
   if ((mq_id = mq_open("/posix_msgqueue",O_RDWR | O_CREAT, 0666, NULL)) == -1) {
   	printf("mq_open failed \n");
   	_exit(-1);
   }

   struct mq_attr mq;
   //获取消息队列的各个属性
   if (mq_getattr(mq_id ,&mq)  == -1 ){
   	_exit(-1);
   }
   printf("mq_flags %ld\n",mq.mq_flags); //mq_open默认将当前属性忽略,则一般为0
   printf("mq_maxmsg %ld\n",mq.mq_maxmsg); //消息队列可以接收的最大消息容量为10个,当达到10个,则当前进程阻塞
   printf("mq_msgsize %ld\n",mq.mq_msgsize); //每个消最大容量
   printf("mq_curmsgs %ld\n",mq.mq_curmsgs);

   int ret;
   if ((ret = fork()) == -1) {
   	printf("fork failed \n");
   	_exit(-1);
   }
   //创建子进程用来接收消息队列的信息,且接收的优先级为NULL,即默认接收优先级最高且存放时间最长的消息
   else if (ret == 0 ) {
   	char msg_buf[mq.mq_msgsize];
   	memset (msg_buf , 0 , mq.mq_msgsize);
   	while(1) {
   		if (mq_receive(mq_id,msg_buf,mq.mq_msgsize,NULL) == -1) {
   			printf("mq_receive failed \n");
   			_exit(-1);
   		}
   		printf("child process receive msg :%s \n",msg_buf);
   		sleep(1);
   	}
   }
   else {
   	while(1) {
   		//父进程用来发送消息队列的信息,并设置发送的消息优先级为1
   		//如果父进程发送多个优先级消息,子进程指定具体的一个优先级消息进行接收即可
   		if (mq_send(mq_id,"hello world",sizeof("hello world"),1) == -1) {
   			printf("mq_send failed\n");
   			_exit(-1);
   		}
   		printf("parent process :send msg to mqueue success\n");
   		sleep(1);
   	}
   }
   //关闭当前进程对消息队列 mq_id的引用
   mq_close(mq_id);
   sleep(5);
   
   //引用计数为0时,删除当前进程创建的消息队列
   if(mq_unlink("/posix_msgqueue") == -1) {
   		printf("mq_unlink failed\n");
   		_exit(-1);
   }
   
   return 0;
}

输出如下:
linux进程间通信:POSIX 消息队列_第1张图片

消息队列属性设置实例

我们知道如下三个参数,POSIX消息队列默认的属性值为

mq_flags = 0
mq_maxmsg = 10
mq_msgsize = 8192

通过如下代码可以分别看到mq_openmq_setattr对属性值的设置

#include 
#include 
#include 
#include 
#include 

int main () {
    mqd_t mq_id;
    /*使用mq_open同时对三个参数更改*/
	struct mq_attr addr;
	addr.mq_flags = O_NONBLOCK;
	addr.mq_maxmsg = 5;
	addr.mq_msgsize = 4096;
	//将需要设置对属性参数设置为addr
	mq_id = mq_open("/notify",O_WRONLY | O_CREAT, 0666, &addr);
	if (mq_id == -1) {
		printf("mq_open failed\n");	
		_exit(-1);
	}

	if (mq_getattr(mq_id,&addr) == -1) {
		printf("mq_getattr failed\n");
		_exit(-1);
	}
	//打印最终对设置结果
	printf("mq_open set mq_flags = %ld\n",addr.mq_flags);
	printf("mq_open set mq_maxmsg = %ld\n", addr.mq_maxmsg);
	printf("mq_open set mq_msgsize = %ld\n", addr.mq_msgsize);
	
	/*同样对所有的属性参数,使用mq_setattr进行设置*/
    struct mq_attr attr;
	attr.mq_flags = O_NONBLOCK;
	attr.mq_maxmsg = 8;
	attr.mq_msgsize = 2048;
	if(mq_setattr(mq_id,&attr,NULL) == -1) {
		printf("mq_set attr failed \n");
		_exit(-1);
	}
	if (mq_getattr(mq_id,&attr) == -1) {
		printf("mq_getattr failed\n");
		_exit(-1);
	}

	printf("mq_setattr set mq_flags = %ld\n",attr.mq_flags);
	printf("mq_setattr set mq_maxmsg = %ld\n", attr.mq_maxmsg);
	printf("mq_setattr set mq_msgsize = %ld\n", attr.mq_msgsize);
	return 0;
}

输出结果如下:

mq_open set mq_flags = 0
mq_open set mq_maxmsg = 5
mq_open set mq_msgsize = 4096
mq_setattr set mq_flags = 2048
mq_setattr set mq_maxmsg = 5
mq_setattr set mq_msgsize = 4096

可见,mq_flags参数只有mq_setattr系统调用能够成功设置
mq_maxmsg和mq_msgsize对属性仅能由mq_opn系统调用设置成功

你可能感兴趣的:(#,编程语言C,#,linux操作系统:进程管理,编程语言)