1. 创建/获取一个消息队列
- #include <fcntl.h> /* For O_* constants */
- #include <sys/stat.h> /* For mode constants */
- #include <mqueue.h>
- 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);
参数:
name: 消息队列名字;
oflag: 与open函数类型, 可以是O_RDONLY, O_WRONLY, O_RDWR, 还可以按位或上O_CREAT, O_EXCL, O_NONBLOCK.
mode: 如果oflag指定了O_CREAT, 需要指定mode参数;
attr: 指定消息队列的属性;
返回值:
成功: 返回消息队列文件描述符;
失败: 返回-1;
注意-Posix IPC名字限制:
1. 必须以”/”开头, 并且后面不能还有”/”, 形如:/file-name;
2. 名字长度不能超过NAME_MAX
3. 链接时:Link with -lrt.
/** System V 消息队列
通过msgget来创建/打开消息队列
int msgget(key_t key, int msgflg);
**/
2. 关闭一个消息队列
- int mq_close(mqd_t mqdes);
-
3. 删除一个消息队列
- int mq_unlink(const char *name);
-
-
-
-
-
- int main()
- {
- mqd_t mqid = mq_open("/abc", O_CREAT|O_RDONLY, 0666, NULL);
- if (mqid == -1)
- err_exit("mq_open error");
- cout << "mq_open success" << endl;
- mq_close(mqid);
- mq_unlink("/abc");
- cout << "unlink success" << endl;
- }
4. 获取/设置消息队列属性
- int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
- int mq_setattr(mqd_t mqdes, struct mq_attr *newattr,
- struct mq_attr *oldattr);
参数:
newattr: 需要设置的属性
oldattr: 原来的属性
-
- struct mq_attr
- {
- long mq_flags;
- long mq_maxmsg;
- long mq_msgsize;
- long mq_curmsgs;
- };
/** System V 消息队列
通过msgctl函数, 并将cmd指定为IPC_STAT/IPC_SET来实现
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
**/
-
-
- int main(int argc,char **argv)
- {
- mqd_t mqid = mq_open("/test", O_RDONLY|O_CREAT, 0666, NULL);
- if (mqid == -1)
- err_exit("mq_open error");
-
- struct mq_attr attr;
- if (mq_getattr(mqid, &attr) == -1)
- err_exit("mq_getattr error");
- cout << "Max messages on queue: " << attr.mq_maxmsg << endl;
- cout << "Max message size: " << attr.mq_msgsize << endl;
- cout << "current messages: " << attr.mq_curmsgs << endl;
-
- mq_close(mqid);
- return 0;
- }
5. 发送消息
- int mq_send(mqd_t mqdes, const char *msg_ptr,
- size_t msg_len, unsigned msg_prio);
参数:
msg_ptr: 指向需要发送的消息的指针
msg_len: 消息长度
msg_prio: 消息的优先级
/** System V 消息队列
通过msgsnd函数来实现消息发送
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
**/
-
- struct Student
- {
- char name[36];
- int age;
- };
- int main(int argc,char **argv)
- {
- if (argc != 2)
- err_quit("./send <prio>");
-
- mqd_t mqid = mq_open("/test", O_WRONLY|O_CREAT, 0666, NULL);
- if (mqid == -1)
- err_exit("mq_open error");
-
- struct Student stu = {"xiaofang", 23};
- unsigned prio = atoi(argv[1]);
- if (mq_send(mqid, (const char *)&stu, sizeof(stu), prio) == -1)
- err_exit("mq_send error");
-
- mq_close(mqid);
- return 0;
- }
6. 从消息队列中读取消息
- ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
- size_t msg_len, unsigned *msg_prio);
参数:
msg_len: 读取的消息的长度, 注意: 此值一定要等于mq_attr::mq_msgsize的值, 该值可以通过mq_getattr获取, 但一般是8192字节 [this must be greater than the mq_msgsize attribute of the queue (see mq_getattr(3)).]
msg_prio: 保存获取的消息的优先级
返回值:
成功: 返回读取的消息的字节数
失败: 返回-1
注意: 读取的永远是消息队列中优先级最高的最早的消息, 如果消息队列为, 如果不指定为非阻塞模式, 则mq_receive会阻塞;
/** System V 消息队列
通过msgrcv函数来实现消息发送的
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
**/
-
- int main(int argc,char **argv)
- {
- mqd_t mqid = mq_open("/test", O_RDONLY);
- if (mqid == -1)
- err_exit("mq_open error");
-
- struct Student buf;
- int nrcv;
- unsigned prio;
- struct mq_attr attr;
- if (mq_getattr(mqid, &attr) == -1)
- err_exit("mq_getattr error");
-
- if ((nrcv = mq_receive(mqid, (char *)&buf, attr.mq_msgsize, &prio)) == -1)
- err_exit("mq_receive error");
-
- cout << "receive " << nrcv << " bytes, priority: " << prio << ", name: "
- << buf.name << ", age: " << buf.age << endl;
-
- mq_close(mqid);
- return 0;
- }
7. 建立/删除消息到达通知事件
- int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
参数sevp:
NULL: 表示撤销已注册通知;
非空: 表示当消息到达且消息队列当前为空, 那么将得到通知;
通知方式:
1. 产生一个信号, 需要自己绑定
2. 创建一个线程, 执行指定的函数
注意: 这种注册的方式只是在消息队列从空到非空时才产生消息通知事件, 而且这种注册方式是一次性的!
-
- struct sigevent
- {
- int sigev_notify;
- int sigev_signo;
- union sigval sigev_value;
- void (*sigev_notify_function) (union sigval);
- void *sigev_notify_attributes;
- pid_t sigev_notify_thread_id;
- };
- union sigval
- {
- int sival_int;
- void *sival_ptr;
- };
sigev_notify代表通知的方式: 一般常用两种取值:SIGEV_SIGNAL, 以信号方式通知; SIGEV_THREAD, 以线程方式通知
如果以信号方式通知: 则需要设定一下两个参数:
sigev_signo: 信号的代码
sigev_value: 信号的附加数据(实时信号)
如果以线程方式通知: 则需要设定以下两个参数:
sigev_notify_function
sigev_notify_attributes
/** Posix IPC所特有的功能, System V没有 **/
-
-
- mqd_t mqid;
- long size;
- void sigHandlerForUSR1(int signo)
- {
-
- struct Student buf;
- int nrcv;
- unsigned prio;
- if ((nrcv = mq_receive(mqid, (char *)&buf, size, &prio)) == -1)
- err_exit("mq_receive error");
-
- cout << "receive " << nrcv << " bytes, priority: " << prio << ", name: "
- << buf.name << ", age: " << buf.age << endl;
- }
-
- int main(int argc,char **argv)
- {
-
- if (signal(SIGUSR1, sigHandlerForUSR1) == SIG_ERR)
- err_exit("signal error");
-
- mqid = mq_open("/test", O_RDONLY);
- if (mqid == -1)
- err_exit("mq_open error");
-
-
- struct mq_attr attr;
- if (mq_getattr(mqid, &attr) == -1)
- err_exit("mq_getattr error");
- size = attr.mq_msgsize;
-
-
- struct sigevent event;
- event.sigev_notify = SIGEV_SIGNAL;
- event.sigev_signo = SIGUSR1;
- if (mq_notify(mqid, &event) == -1)
- err_exit("mq_notify error");
-
-
- while (true)
- pause();
-
- mq_close(mqid);
- return 0;
- }
-
-
- mqd_t mqid;
- long size;
- struct sigevent event;
- void sigHandlerForUSR1(int signo)
- {
-
-
- if (mq_notify(mqid, &event) == -1)
- err_exit("mq_notify error");
-
-
- struct Student buf;
- int nrcv;
- unsigned prio;
- if ((nrcv = mq_receive(mqid, (char *)&buf, size, &prio)) == -1)
- err_exit("mq_receive error");
-
- cout << "receive " << nrcv << " bytes, priority: " << prio << ", name: "
- << buf.name << ", age: " << buf.age << endl;
- }
-
- int main(int argc,char **argv)
- {
-
- if (signal(SIGUSR1, sigHandlerForUSR1) == SIG_ERR)
- err_exit("signal error");
-
- mqid = mq_open("/test", O_RDONLY);
- if (mqid == -1)
- err_exit("mq_open error");
-
-
- struct mq_attr attr;
- if (mq_getattr(mqid, &attr) == -1)
- err_exit("mq_getattr error");
- size = attr.mq_msgsize;
-
-
- event.sigev_notify = SIGEV_SIGNAL;
- event.sigev_signo = SIGUSR1;
- if (mq_notify(mqid, &event) == -1)
- err_exit("mq_notify error");
-
-
- while (true)
- pause();
-
- mq_close(mqid);
- return 0;
- }
mq_notify 注意点总结:
1. 任何时刻只能有一个进程可以被注册为接收某个给定队列的通知;
2. 当有一个消息到达某个先前为空的队列, 而且已有一个进程被注册为接收该队列的通知时, 只有没有任何线程阻塞在该队列的mq_receive调用的前提下, 通知才会发出;
3. 当通知被发送给它的注册进程时, 该进程的注册被撤销. 进程必须再次调用mq_notify以重新注册(如果需要的话),但是要注意: 重新注册要放在从消息队列读出消息之前而不是之后(如同示例程序);
附-查看已经成功创建的Posix消息队列
#其存在与一个虚拟文件系统中, 需要将其挂载到系统中才能查看
Mounting the message queue filesystem On Linux, message queues are created in a virtual filesystem.
(Other implementations may also provide such a feature, but the details are likely to differ.) This
file system can be mounted (by the superuser, 注意是使用root用户才能成功) using the following commands:
mkdir /dev/mqueue
mount -t mqueue none /dev/mqueue
还可以使用cat查看该消息队列的状态, rm删除:
cat /dev/mqueue/abc
rm abc
还可umount该文件系统
umount /dev/mqueue
附-Makefile
- .PHONY: clean all
- CC = g++
- CPPFLAGS = -Wall -g
- BIN = main
- SOURCES = $(BIN.=.cpp)
- all: $(BIN)
-
- %.o: %.c
- $(CC) $(CPPFLAGS) -c $^ -o $@
- main: main.o
- $(CC) $(CPPFLAGS) $^ -lrt -o $@
-
- clean:
- -rm -rf $(BIN) *.o bin/ obj/ core