文章目录

  • ​​1.system v IPC 和POSIX IPC​​
  • ​​2.消息队列函数​​

1.system v IPC 和POSIX IPC

  • system v IPC主要是:
    (1)消息队列
    (2)共享内存;
    (3)信号量;
  • POSIX IPC主要是:
    (1)消息队列;
    (2)共享内存;
    (3)信号量;
    (4)互斥锁;
    (5)条件变量
    (6)读写锁
    (7)自旋锁
    (8)文件锁

2.消息队列函数

  • mq_open函数
    只有两个参数的(1)只能打开消息队列,不能创建消息队列
功能:用来创建和访问一个消息队列
原型:
(1)mqd_t mq_open(const char *name, int oflag);
(2)mqd_t mq_open(const char *name, int oflag, mode_t mode,
struct mq_attr *attr);

参数:
name:某个消息队列的名字
oflag:与open函数类似,可以是O_RDONLY,O_WRONLY,O_RDWR,还可以按位或上O_CREAT,O_EXCL,O_NONBLOCK等
mode:如果oflag指定了O_CREAT,需要设置mode

返回值:
成功返回消息队列文件描述符;
失败返回-1
  • eg:NetworkProgramming-master (1)\NetworkProgramming-master\P34mqopen.c
//
// Created by wangji on 19-8-14.
//

// p34 poxis消息队列

#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <string.h>

#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>

using namespace std;

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);

int main(int argc, char** argv)
{
// mq_overview
mqd_t mqid = mq_open("/abc", O_CREAT | O_RDWR, 0666, NULL);//O_RDWR读写方式,NULL默认属性
if (mqid == ((mqd_t) -1))
{
ERR_EXIT("mq_open");
}

printf("mqopen success\n");
mq_close(mqid);//没有关闭的话,在程序结束时也会自动关闭
printf("mqclose success\n");
return 0;
}

=======================================================Makefile文件新======================================
## 注意BIN=的可执行文件,必须是XXXX.c 的XXXX名字!!!
###$(CC)和rm前面是tab,其余用空格隔开
.PHONY:clean all
CC=cc
CFLAGS=-Wall -g
BIN=01mq_open
01mq_open:01mq_open.o
$(CC) $(CFLAGS) $^ -o $@ -lrt
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)
  • 在OS的哪里可以查看POSIX消息队列?具体位置如下面的截图
man 7 mq_overview 或者man mq_overview
消息队列存在于系统的虚拟文件系统中,但是该文件系统得挂载到某个目录底下才能用

卸载消息队列:
umount /dev/mqueue

(P34)posix消息队列_#include

  • 查看消息队列状态:
cat /dev/mqueue/abc
QSIZE:消息队列当前的字节数

(P34)posix消息队列_消息队列_02

  • POSIX IPC名字限定
    (1)必须以/打头,并且后续不能有其它/,eg:/somename
    (2)长度不能超过NAME_MAX
  • mq_close函数
    关闭不代表删除,仅仅代表不能通过该消息队列描述符来访问队列了
功能:关闭消息队列

原型:
mqd_t mq_close(mqd_t mqdes);

参数:
mqdes:消息队列描述符

返回值:
成功返回0;
失败返回-1
  • 查看文件连接数 :实际上是删除连接数,当连接数为0时,才将文件删除,下面的1代表的是连接数

    补充文件引用计数的查看:
june_hou@ubuntu:~$ lsof |grep test.log 
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
a.out 3586 june_hou 3u REG 252,7 0 357 test.log
a.out 3586 june_hou 4u REG 252,7 0 357 test.log
  • mq_unlink函数
功能:删除消息队列

原型:
mqd_t mq_unlink(const char *name);


参数:
name:消息队列的名字

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

-eg:NetworkProgramming-master (1)\NetworkProgramming-master\P34myunlink.c

//
// Created by wangji on 19-8-14.
//

// p34 poxis消息队列

#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <string.h>

#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>

using namespace std;

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);

int main(int argc, char** argv)
{
// mq_overview
mqd_t mqid = mq_open("/abc", O_RDWR);
if (mqid == ((mqd_t) -1))
{
ERR_EXIT("mq_open");
}

printf("mqopen success\n");

if (mq_unlink("/abc") == -1)
{
ERR_EXIT("mq_unlink");
}

printf("mqunlink success\n");

mq_close(mqid);

return 0;
}
  • mq_getattr函数和mq_setattr函数
    posix 消息队列既可以随内核持续,也可以随文件系统持续,但是至少是随内核持续(当访问该消息队列最后一个进程结束的时候,该消息队列不会被删除)
功能:获取/设置消息队列属性

原型:
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);//指定mqid,将属性获取至attr

int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr,
struct mq_attr *oldattr);//指定mqid,设置新的属性,返回原来的属性

返回值:
成功返回0;
失败返回-1;
  • eg:NetworkProgramming-master (1)\NetworkProgramming-master\P34mqgetattr.c
//
// Created by jxq on 19-8-14.
//

// p34 poxis消息队列

#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <string.h>

#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>

using namespace std;

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);

int main(int argc, char** argv)
{
// mq_overview
mqd_t mqid = mq_open("/abc", O_RDONLY);
if (mqid == ((mqd_t) -1))
{
ERR_EXIT("mq_open");
}

//printf("mqopen success\n");

struct mq_attr attr;//man mq_getattr得到

if (mq_getattr(mqid, &attr) == -1)
{
ERR_EXIT("mq_getattr");
}
//当前消息队列最多容纳的消息的个数
//消息队列中每条消息的最大字节数
//当前消息的个数
printf("max #msg = %ld max #bytes/msg = %ld #currently on queue = %ld\n", attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);

mq_close(mqid);

return 0;
}
  • mq_send函数
功能:发送消息

原型:
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned int msg_prio);
参数:
mqdes:消息队列描述符
msg_ptr:指向消息的指针;
msg_len:消息长度;
msg_prio:消息优先级


返回值:
成功返回0;
失败返回-1;
  • eg:NetworkProgramming-master (1)\NetworkProgramming-master\P34mqsend.c
//
// Created by wangji on 19-8-14.
//

// p34 poxis消息队列

#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <string.h>

#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>

using namespace std;

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);

struct student
{
char name[32];
int age;
}STU;

int main(int argc, char** argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s <msg_prio>\n", argv[0]);
}

// mq_overview
mqd_t mqid = mq_open("/abc", O_WRONLY);
if (mqid == ((mqd_t) -1))
{
ERR_EXIT("mq_open");
}

//printf("mqopen success\n");
STU stu;
strcpy(stu.name, "hello");
stu.age = 20;

if (mq_send(mqid, (const char*)&stu, sizeof(stu), atoi(argv[1])) == -1)
{
ERR_EXIT("mq_send");
}

printf("send success\n");
mq_close(mqid);

return 0;
}
  • 测试结果如下:

    消息的大小36字节刚好是student结构体的大小
  • mq_receive函数
功能:接收消息

原型:
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);

参数:
mqdes:消息队列描述符
msg_ptr:返回可接收到的消息
msg_len:消息长度
msg_prio:返回接收到的消息优先级

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

注意:
(1)len应该是消息队列中每条消息长度的最大值
(2)返回指定消息队列总最高优先级的最早消息
  • eg:NetworkProgramming-master (1)\NetworkProgramming-master\P34mqreceive.c
//
// Created by wangji on 19-8-14.
//

// p34 poxis消息队列

#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <string.h>

#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>

using namespace std;

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);

struct student
{
char name[32];
int age;
}STU;

int main(int argc, char** argv)
{
// mq_overview
mqd_t mqid = mq_open("/abc", O_RDONLY);
if (mqid == ((mqd_t) -1))
{
ERR_EXIT("mq_open");
}

//printf("mqopen success\n");

struct mq_attr attr;

if (mq_getattr(mqid, &attr) == -1)
{
ERR_EXIT("mq_getattr");
}

size_t size = attr.mq_msgsize;
STU stu;
unsigned prio;

//len应该是消息队列中每条消息长度的最大值
if (mq_receive(mqid, (char*)&stu, size, prio) == (mqid)-1)//mq_receive(mqid, (char*)&stu, size, NULL)
{
ERR_EXIT("mq_receive");
}

printf("student name = %s, age = %d\n, prio=%u", stu.name, stu.age,prio);
// printf("student name = %s, age = %d\n", stu.name, stu.age);
mq_close(mqid);

return 0;
}
  • 测试:
    连续往消息队列中塞入4条消息
    接收到的消息的第一条消息优先级是最高的
  • (P34)posix消息队列_#include_03

  • mq_notify函数
功能:建立或者删除消息到达通知事件

原型:
mqd_t mq_notify(mqd_t mqdes, const struct sigevent *sevp);//建立注册,注册的话需要指定第2个参数

参数:
mqdes:消息队列描述符
notification:
(1)非空表示当消息到达且消息队列先前为空,那么将得到通知
(2)NULL表示撤销已注册的通知

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

通知方式:
(1)创建一个信号
(2)创建一个线程执行一个指定的函数
  • struct sigevent
#include <signal.h>

union sigval { /* Data passed with notification */
int sival_int; /* Integer value */
void *sival_ptr; /* Pointer value */
};

struct sigevent {

//如果以信号的方式来通知的话,需要指定sigev_signo和sigev_value:如果以线程的方式来通知的话,需要指定后两个带void的参数
int sigev_notify; /* Notification method *///通知方式:信号SIGEV_SIGNAL或者线程SIGEV_THREAD
int sigev_signo; /* Notification signal *///哪一个信号,如果传递的是实时信号,则可以传递数据,则需要关注sigev_value
union sigval sigev_value; /* Data passed with
notification */
void (*sigev_notify_function) (union sigval);
/* Function used for thread
notification (SIGEV_THREAD) */
void *sigev_notify_attributes;
/* Attributes for notification thread
(SIGEV_THREAD) */
pid_t sigev_notify_thread_id;
/* ID of thread to signal (SIGEV_THREAD_ID) */
};
  • mq_notify注意
    (1)必须是消息队列从空到有,才能够得到通知(若消息队列先前是有消息的,则得不到通知。主要是某个进程注册了,就能够获取到这种通知)
    (2)当消息队列有消息,就会得到一个通知,通知是一次性的,要多次得到通知,就要多次注册

    (3)任何时刻只能一个进程可以被注册为接收某个给定队列的通知
    (4)当有一个消息到达某个先前为空的队列,而且已有一个进程被注册为接收该队列的通知时,只有没有任何线程阻塞在该队列的mq_receive调用的前提下,通知才会发出
    (5)当通知被发送给它的注册进程时,其注册被撤销掉。进程必须再次调用mq_notify以重新注册(如果需要的话),重新注册要放在从消息队列读出消息之前而不是之后。(若放在后面,当消息接受完,消息队列为空,当新的消息过来了,就没有办法得到通知,所以mq_notify一般放在mq_receive之前)
  • eg:NetworkProgramming-master (1)\NetworkProgramming-master\P34mqnotify.c
//
// Created by wangji on 19-8-14.
//

// p34 poxis消息队列

#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <string.h>

#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
#include<signal.h>

using namespace std;
//man mq_notify就有下行这块代码
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);

struct student
{
char name[32];
int age;
};

size_t size;
mqd_t mqid;
struct sigevent sigv;


//提供函数的实现
void handler(int s)
{
mq_notify(mqid, &sigv);//多次注册
student stu;

unsigned prino;
if (mq_receive(mqid, (char*)&stu, size, &prino) == -1)
{
ERR_EXIT("mq_receive");
}

printf("student name = %s, age = %d prino = %u\n", stu.name, stu.age, prino);
}

int main(int argc, char** argv)
{
// mq_overview
mqid = mq_open("/abc", O_RDONLY);
if (mqid == ((mqd_t) -1))
{
ERR_EXIT("mq_open");
}

//printf("mqopen success\n");
struct mq_attr attr;
if (mq_getattr(mqid, &attr) == -1)
{
ERR_EXIT("mq_getattr");
}
size = attr.mq_msgsize;//获取最大消息的大小保存在size

//注册一个信号,安装一个信号
signal(SIGUSR1, handler); //定义一个信号函数,当SIGALRM信号发过来时,执行handler函数

sigv.sigev_notify = SIGEV_SIGNAL;
sigv.sigev_signo = SIGUSR1;//SIGUSR1是十号信号

//注册一个消息到达的通知
mq_notify(mqid, &sigv);

while (true)//要有死循环,否则通知到,就没收时间去接收他
{
pause();
}

mq_close(mqid);

return 0;
}
  • 测试结果如下:
  • 最后,总结一下cat /dev/mqueue/abc消息队列的所有状态
    (1)QSIZE:表示消息队列容量
    (2)NOTIFY:表示通知方式,eg:这里是SIGEV_SIGNAL=0
    (3)SIGNO:表示以哪个信号来进行通知,当前是10号信号
    (4)NOTIFY_PID:表示这个通知会发送给哪个进程,18979就是./06mq_notify这个进程