嵌入式学习笔记--Linux系统编程阶段--DAY06进程间通信-消息队列
1.消息队列
主要应用场景是多对多的进程间通信。
“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。
消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。
消息队列就是一个消息的链表(链表的每一个节点就是一个消息队列)。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。
个人理解:可以将消息队列理解为一个快递柜,送快递的将快递放入快递柜,取快递的去取。
消息队列的特点:
在 ubuntu 某些版本中消息队列限制值如下:
Ø每个消息内容最多为 8K 字节
Ø每个消息队列容量最多为 16K 字节
Ø系统中消息队列个数最多为 1609 个
Ø系统中消息个数最多为 16384 个
2.消息队列的API
要想创建消息队列,先拿到那个唯一的标识符(key值)。
2.1获取唯一的key值
ftok函数:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
key_t key = ftok("/home/qf/桌面/Linux编程",2504);
2.2根据key值创建唯一的消息队列
msgget函数:
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
函数功能:创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的 key 值就能得到同一个消息队列的标识符。
参数:
key:IPC 键值。
msgflg:标识函数的行为及消息队列的权限。
参数:
msgflg 的取值:
IPC_CREAT:创建消息队列。
IPC_EXCL:检测消息队列是否存在。
位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和 open 函数的
mode_t 一样,但可执行权限未使用。
返回值:
成功:消息队列的标识符,失败:返回-1。
代码案例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_CREAT |0666);printf("msgid = %d\n",msgid);return 0;
}
shell 命令 ipcs -q查看消息队列
删除消息队列
ipcrm -q msqid(消息队列的id)
3.消息队列的格式
本质上是一个结构体:
这个消息队列需要自己定义。
typedef struct _msg
{
long mtype; /*消息类型*/ 必须是第一个成员,剩下的随意,顺序,大小都无所谓
char mtext[100]; /*消息正文*/
... /*消息的正文可以有多个成员*/
}MSG;
3.1发送信息
msgsnd函数:
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/
char mtext[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_CREAT |0666);printf("msgid = %d\n",msgid);MSG msg;msg.mtype =10; //消息类型 后续收消息类型为10的进程可以收到此消息strcpy(msg.mtext,"hello world!");msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0);return 0;
}
3.2接收消息
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
注意:若消息队列中有多种类型的消息,msgrcv 获取消息的时候按消息类型获取,不是 先进先出的。 在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消 息,即先进先出原则。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/
char mtext[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);int msgid = msgget(key,IPC_EXCL |0666);MSG msg;msg.mtype =10;msgrcv(msgid,&msg,sizeof(msg)-sizeof(long),msg.mtype,0);printf("%s\n",msg.mtext);return 0;
}
4.消息队列案例
lucy bobo tom互发消息
lucy负责发消息
bobo负责接收消息类型为10的消息
tom负责接收消息类型为20的消息
lucy部分:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/
char name[16];
char data[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);int msgid = msgget(key,IPC_CREAT |0666);MSG msg;msg.mtype =10;strcpy(msg.name,"lucy!");strcpy(msg.data,"i am lucy!");msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0);msg.mtype =20;strcpy(msg.name,"lucy!");strcpy(msg.data,"i am lucy!");msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0);return 0;}
bobo部分:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/
char name[16];
char data[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_EXCL |0666);MSG msg;msg.mtype =10;msgrcv(msgid,&msg,sizeof(msg)-sizeof(long),msg.mtype,0);printf("收了%s的消息\n",msg.name);printf("收到的消息内容是:%s\n",msg.data);return 0;
}
tom部分:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/
char name[16];
char data[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_EXCL |0666);MSG msg;msg.mtype =20;msgrcv(msgid,&msg,sizeof(msg)-sizeof(long),msg.mtype,0);printf("收了%s的消息\n",msg.name);printf("收到的消息内容是:%s\n",msg.data);return 0;
}
5.消息队列的控制
我们不可能每次删除消息队列都用命令删除,也可以在代码中
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
struct msqid_ds {struct ipc_perm msg_perm; /* Ownership and permissions */time_t msg_stime; /* Time of last msgsnd(2) */time_t msg_rtime; /* Time of last msgrcv(2) */time_t msg_ctime; /* Time of last change */unsigned long __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t msg_qnum; /* Current number of messagesin queue */msglen_t msg_qbytes; /* Maximum number of bytesallowed in queue */pid_t msg_lspid; /* PID of last msgsnd(2) */pid_t msg_lrpid; /* PID of last msgrcv(2) */};
struct ipc_perm 结构体类型:
struct ipc_perm {key_t __key; /* Key supplied to msgget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions */unsigned short __seq; /* Sequence number */};
6.消息队列的作业(多人聊天程序)
