消息队列
消息队列是消息的链表,存放在内存中,由内核维护
IPC对象除了最原始的进程间通信方式,信号,无名管道,有名管道外,还有三种进程间通信方式称之为IPC对象
IPC 对象的分类; 消息队列,共享内存,信号灯集
IPC对象也是在内核空间开辟区域,每一种IPC对象创建好之后,都会将其设置为全局,并且会给其分配一个编号,只要找到唯一的这个编号,就可以进行通信,所以不相关的进程,也可以通过IPC对象进行通信
IPC对象创建好之后,会在当前系统中可见,只要不删除或者不关闭系统,就会一直存在
查看已经创建的IPC对象
ipcs 查看当前系统中所有创建的IPC对象消息队列概述ipcs -q 查看创建的消息队列
ipcs - m 查看创建的共享内存
ipcs -s 查看信号量
ipcrm 删除IPC对象
例如:ipcrm -q msqid 删除标号为msqid的消息队列
消息队列是消息的链表,存放在内存中,由内核维护
消息队列的特点
1、消息队列中的消息是有类型的。
2、消息队列中的消息是有格式的。
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。
System V提供的IPC通信机制需要一个key值,通过key值就可在系统内获得一个唯一的消息队列标识符,key值可以是人为指定的,也可以通过ftok函数获得
如果多个进程想通过IPC对象通信,则必须找到唯一的标识,而唯一的标识是由key值决定的,所以只要key值知道,则就可以实现多个进程通信
ftok函数
#include#include
功能
通过文件名和目标值共同创造一个键值并返回值
参数
pathname:任意一个文件名(文件名或者目录名)
proj_id: 目标值,范围一般0 ~ 127
返回值
成功返回key值,失败返回-1
如果使用frok函数获取键值,得到的键值是由ftok的第一个参数对应文件的信息,和第二个参数一起决定的
#include消息队列的操作 创建消息队列#include #include #include int main() { //使用ftok函数获取key值 //只要保证ftok的第一个参数对应的文件和第二个参数值相同 //则不管程序运行多少遍或者多少个进程获取键值,键值一定都是唯一的 key_t mykey; if((mykey = ftok(".", 100 )) == -1) { perror("fail to ftok"); exit(1); } printf("key = %#xn", mykey); //以16进制方式打印 #是为了得到 0x return 0; }
#includeint msgget(key_t key, int msgflg);
功能
创建一个新的或打开一个已经存在的消息队列,不同的进程调用此函数,只要用相同的key值就能得到同一个消息队列的标识符
参数
key: IPC键值
获取键值,方法1:随便指定一个数,方法2,使用ftok函数获取键值
msgflg:标识函数的行为及消息队列的权限
IPC_CREAT: 创建消息队列
IPC_EXCL:检测消息队列是否存在
位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和open函数的mode_t一样,但可执行权限未使用
msgflg: 消息队列的访问权限,一般设置为IPC_CREAT | IPC_EXCL | 0777 |或者
IPC_CREAT | 0666(这个方法用的多点)
返回值:
成功:消息队列的id,失败:返回-1
#include使用shell命令操作消息队列#include #include #include #include #include int main() { int msqid //通过ftok函数获取ipc的值 key_t mykey; if(( mykey = ftok(".", 100)) == -1) { perror("fail to ftok1"); exit(1); } //通过msgget函数创建一个消息队列 if((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1) { perror("fail to msgget"); exit(1); } printf("msqid = %dn", msqid); system("ipcs -q");//执行系统命令 return 0; }
查看消息队列
ipcs -q
删除消息队列
ipcrm -q msqid
发送信息, 如何将信息写到消息队列当中去
#includeint msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能
将新消息添加到消息队列
参数
msqid: 消息队列的标识符
msgp: 待发送消息结构体的地址
msgsz: 消息正文的字节数
msgflg; 函数的控制属性
0:msgsnd 调用阻塞直到条件满足为止
IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回
返回值
成功:0,失败:-1
案例;
#include#include #include #include #include #include #define N 128 // 代表每一个正文大小是128个字节 typedef struct { long msg_type; //消息类型,必须在结构体的第一个位置并且类型必须是long char msg_text[N];//消息正文,也可以有多个成员并且类型也可以是任意 }MSG;// 名字也可以随便起,不一定就叫MSG #define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long)) //消息正文的大小 int main() { //使用fork函数获取键值 key_t key; if((key = ftok(".", 100)) == -1) { perror("fail to ftok"); exit(1); } //使用msgget创建一个消息队列 int msgid; if((msgid = msgget(key, IPC_CREAT | 0666)) == -1) { perror("fail to ftok"); exit(1); } system("ipcs -q"); //使用msgsnd函数向消息队列中发送数据(写操作) MSG msg1 = {1, "hello world"}; MSG msg2 = {4, "nihao beijing"}; MSG msg3 = {2, "hello kitty"}; MSG msg4 = {3, "welcome to 100 "}; if(msgsnd(msgid, &msg1,MSGTEXT_SIZE ,0) == -1) { perror("fail to msgsnd"); exit(1); } if(msgsnd(msgid, &msg2,MSGTEXT_SIZE ,0) == -1) { perror("fail to msgsnd"); exit(1); } if(msgsnd(msgid, &msg3,MSGTEXT_SIZE ,0) == -1) { perror("fail to msgsnd"); exit(1); } if(msgsnd(msgid, &msg4,MSGTEXT_SIZE ,0) == -1) { perror("fail to msgsnd"); exit(1); } system("ipcs -q"); return 0; }
接收消息,如何从消息队列中将消息读出来 msgrcv()
#include#include #include ssize_t msgrcv (int msqid, void* msgp, size_t msgsz, long msgtyp, int msgflg);
功能
从消息队列中接收数据(读操作),接收的数据会从消息队列中删除
参数
msqid: 消息队列id
msgp:保存接收到的数据的结构体
struct struct_name
{
long mtype; 消息的编号,必须大于0
char mtext[128]; 消息的正文,可以定义多个成员
}
msgsz:消息正文的大小
msgtyp: 设置要接收哪个消息
0 按照写入消息队列的顺序依次读取
>0 只读取消息队列中消息编号为当前参数的第一个消息
<0 只读取消息队列中小于等于当前参数的绝对值中内最小的第一个消息
msgflg: 标志位
0 阻塞
IPC_NOWAIT 非阻塞
返回值
成功: 接收到的消息正文的长度
失败: -1
案例:
#include#include #include #include #include #include #define N 128 typedef struct{ long msg_type; char msg_text[N]; }MSG; #define MSGTEXT_SIZE (sizeof(MSG) ‐ sizeof(long)) int main(int argc, char const *argv[]) { //使用ftok函数获取键值 key_t key; if((key = ftok(".", 100)) == ‐1) { perror("fail to ftok"); exit(1); } //使用msgget函数创建一个消息队列 int msgid; if((msgid = msgget(key, IPC_CREAT | 0777)) == ‐1) { perror("fail to msgget"); exit(1); } system("ipcs ‐q"); //通过msgrcv函数接收消息队列中的信息(读操作) //注意:如果没有第四个参数指定的消息时,msgrcv函数会阻塞等待 MSG msg; //如果第四个参数为0,则按照先进先出的方式读取数据 //if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 0, 0) == ‐1) //如果第四个参数为>0,则获取当前值得消息类型的数据 //if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 2, 0) == ‐1) //如果第四个参数为<0,则获取当前值得绝对值内消息类型最小的数据 if(msgrcv(msgid, &msg, MSGTEXT_SIZE, ‐3, 0) == ‐1) { perror("fail to msgrcv"); exit(1); } printf("recv_msg = %sn", msg.msg_text); system("ipcs ‐q"); return 0; }
消息队列的控制
#includeint msgctl(int msqid, int cmd, struct msqid_ds* buf);
功能:
对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列
参数:
msqid: 消息队列的标识符
cmd: 函数功能的控制
buf: msqid_ds数据类型的地址,用来存放或更改消息队列的属性
cmd: 函数功能的控制
IPC_RMID: 删除由msqid指示的消息队列,将它从系统中删除并破坏数据结构IPC_STAT: 将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中
IPC_SET: 将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值
返回值:
成功:返回0, 失败:返回-1
案例:
#include#include #include #include #include #include #include int main() { //使用ftok函数获取键值 key_t key; if((key = ftok(".", 100)) == -1) { perror("fail to ftok"); exit(1); } //使用msgget函数创建一个消息队列 int msgid; if(( msgid = msgget(key, IPC_CREAT, 0666)) == -1) { perror("fail to msgid"); exit(1); } printf("msgid = %dn", msgid); system("ipcs -q"); //通过msgctl函数删除消息队列 if(msgctl(msgid, IPC_RMID, NULL) == -1) { perror("fail to msgctl"); exit(1); } system("ipcs -q"); return 0; } ~



