Download presentation
Presentation is loading. Please wait.
1
第4章 进程间通信 3
2
4.4 Linux消息缓冲通信(消息队列) 4.4.1 消息缓冲通信概述
消息队列满足IPC通信机制的通用规则:使用一个消息队列标识符(键值key,参考上节中的ipc_perm结构)来唯一标识一个消息队列,并依此检查访问权限。 通过多进程共享同一个消息队列实现。 消息队列本身是一个临界资源,需实现互斥操作。 2018/12/6 操作系统与实验 第4章2
3
2. Linux消息缓冲通信的数据结构 ⑴.消息缓冲区msgbuf 该数据结构用来存放需要发送或接收的消息类型和消息正文。
/usr/src/linux-2.4/include/linux/msg.h文件中描述如下: /* message buffer for msgsnd and msgrcv calls */ struct msgbuf { long mtype; //消息类型,由用户约定 char mtext[1]; //消息正文 }; mtext[1]是消息正文,该域可由程序员定义为任意数据结构,但消息及消息队列的长度有限,其设置在系统配置时可以改变,其缺省值定义在 /usr/src/linux-2.4/include/linux/msg.h中: #define MSGMNI /* <= IPCMNI */ /* max # of msg queue identifiers */ #define MSGMAX /* <= INT_MAX */ /* max size of message (bytes) */ #define MSGMNB /* <= INT_MAX */ /* default max size of a message queue */ 2018/12/6 操作系统与实验 第4章2
4
2. Linux消息缓冲通信的数据结构 ⑵.每条消息使用一个数据结构msg_msg进行描述,并通过指针*next与msgbuf链接成一个完整的消息实体。同一消息队列中的消息通过m_list表组织起来,形成一个满足同一key值的消息队列。 msg_msg数据结构在文件/usr/src/linux-2.4/ipc/msg.c中定义为: /* one msg_msg structure for each message */ struct msg_msg { struct list_head m_list; long m_type; int m_ts; /* message text size */ struct msg_msgseg* next; /* the actual message follows immediately */ }; 2018/12/6 操作系统与实验 第4章2
5
⑶.消息队列链表msgque Linux内核维护一个消息队列链表msgque,该表描述了当前系统中的消息队列。链表中每个元素指向一个描述消息队列的kern_ipc_perm结构,以及与该消息队列有关的3个队列结构的头元素:消息缓冲队列、接收进程等待队列、发送进程等待队列。同时,还保留有关队列变动的时间以及进程标识等信息。其结构定义在/usr/src/linux-2.4/ipc/msg.c文件中: /* one msq_queue structure for each present queue on the system */ struct msg_queue { struct kern_ipc_perm q_perm; time_t q_stime; /* last msgsnd time */ time_t q_rtime; /* last msgrcv time */ time_t q_ctime; /* last change time */ unsigned long q_cbytes; /* current number of bytes on queue */ unsigned long q_qnum; /* number of messages in queue */ unsigned long q_qbytes; /* max number of bytes on queue */ pid_t q_lspid; /* pid of last msgsnd */ pid_t q_lrpid; /* last receive pid */ struct list_head q_messages; struct list_head q_receivers; struct list_head q_senders; }; 2018/12/6 操作系统与实验 第4章2
6
3. Linux消息缓冲通信数据结构之间的关系
msg_queue 发送进程pid 接收进程pid …… msg_msg *next msgbuf i ker_ipc_perm key q_messages q_receivers q_sanders 图‑给定key值的消息队列 2018/12/6 操作系统与实验 第4章2
7
发送消息原语与接收消息原语的同步互斥算法描述如下:
/*mutex 为互斥信号量,s1表示消息队列上是否有消息,s2表示消息队列是否有空(允许长度)*/ int mutex= 1,s1=0,s2=n; /*发送消息原语*/ send() { P(s2); P(mutex); 将消息缓冲区msgbuf挂到消息队列; V(mutex); V(s1); } /*接收消息原语*/ receive() { P(s1); P(mutex); 从消息队列取消息缓冲区msgbuf; V(mutex); V(s2) } 2018/12/6 操作系统与实验 第4章2
8
4. 发送和接收消息执行过程 2018/12/6 操作系统与实验 第4章2
9
发送消息进程与接收消息进程: 发送进程在调用send()原语之前,先要作好以下准备工作: ⑴.申请一个消息缓冲区msgbuf;
接收进程在接收到消息后要做以下善后处理: ⑴.将消息缓冲区msgbuf中的消息复制到进程存储空间; ⑵.释放消息缓冲区msgbuf; 2018/12/6 操作系统与实验 第4章2
10
共提供了4个系统调用 4.4.2 Linux消息缓冲通信的系统调用 msgget() Msgsnd() msgrcv() Msgctl()
所用头函数: # include <sys/types.h > # include <linux/msg.h > 2018/12/6 操作系统与实验 第4章2
11
4.4.2 Linux消息缓冲通信的系统调用 1. 创建一个消息队列或获取已存在消息队列的标识 格式: 功能: 参数说明: 返回:
1. 创建一个消息队列或获取已存在消息队列的标识 格式: int msgget ( key_t key, int msgflag ); 功能: 创建标识为key值的消息队列或者获取已存在的消息队列的描述符msgid。 参数说明: msgqid——该系统调用返回的消息队列描述符,-1表示失败; key——用户指定的消息队列标识符,为一正整数,其值可以由用户指定,如果使用IPC_PRIVATE则由系统产生key值; flag——用户设置的标志或访问方式,其值由操作权限和控制命令进行或运算得到。 返回: 正确返回该消息队列的描述符msgid; 错误返回-1。 通常使用语句:msgid = msgget(Key, 0666|IPC_CREAT); 2018/12/6 操作系统与实验 第4章2
12
2. 发送一条消息到指定的消息队列 格式: 功能: 参数说明: 返回:
2. 发送一条消息到指定的消息队列 格式: int msgsnd( int msgid, struct msgbuf*msgp, int size, int flag ); 功能: 发送一个消息给指定描述符的消息队列。将msgp所指向的msgbuf中的消息复制到消息数据结构并挂到指定消息队列尾,唤醒等待消息的进程。 参数说明: msgid——执行msgget()返回的消息队列的描述符; msgp——指向用户存储区的一个消息缓冲msgbuf的指针,在msgbuf中包含消息类型和消息正文,其结构参见4.4.1 节 size——由msgp指向的数据结构中字符数组的长度(消息长度); flag ——规定当核心用尽内部空间时应执行的动作,例如:若在flag中设置了IPC_NOWAIT,则当消息队列中的字节数超过最大值msgsnd立即返回,否则msgsnd睡眠。flag可置0。 返回: 成功返回0; 错误返回-1。 2018/12/6 操作系统与实验 第4章2
13
3. 从消息队列接收消息 格式: 功能: 参数说明: 返回:
3. 从消息队列接收消息 格式: int msgrcv( int msgid, struct msgbuf*msgp, int size, int type, int flag); 功能: 从指定的消息队列接收一个消息。将消息复制到msgp所指的msgbuf中,从消息队列中删除此消息,若消息未到则调用进程阻塞插入等待消息队列尾。 参数说明: msgid——消息队列描述符; msgp—— 用来存放要接收消息的用户消息缓冲msgbuf的地址; size ——msgp中数据数组的大小; type——用户要读的消息类型: =0--接收队列的第一个消息; >0--接收类型type的第一个消息; <0—接收小于或等于|type|的最低类型的第一个消息。 flag——规定若该队列无消息,操作系统核心应当做什么,可置0。 返回: 成功返回接收到的数据长度; 错误返回-1。 2018/12/6 操作系统与实验 第4章2
14
4. 对消息队列的操作 格式: 功能: 参数说明: 返回:
4. 对消息队列的操作 格式: int msgctl( int msgid, int cmd, struct msgid_ds *buf ); 功能: 查询一个消息队列的状态;设置或修改它的状态;撤消一个消息队列。 参数说明: msgid——该消息队列的描述符; cmd——规定命令的类型: IPC_STAT查询消息队列状态,将与msgid相关联的消息队列首标读入buf; IPC_SET设置或修改消息队列状态,设置有效用户、组标识、操作允许权,及字节数; IPC_RMID撤消描述符为msgid的消息队列; Buf——含有控制参数或查询结果的用户缓冲区的地址,可为0。 返回: 调用成功返回0, 不成功返回-1。 注意: 设置和撤消消息队列的进程需要有一定的权限,如超级用户、具有有效用户ID、符合msg_perm权限设置。 2018/12/6 操作系统与实验 第4章2
15
4.4.3 消息缓冲通信应用举例 1. 使用方法 ⑴.使用系统调用函数msgget()创建或者获取指定key值的消息队列:
消息缓冲通信应用举例 1. 使用方法 ⑴.使用系统调用函数msgget()创建或者获取指定key值的消息队列: 给定key值的消息队列不存在时为创建,已创建后再执行为获取。 消息缓冲区定义: struct my_msg { long int my_msg_type; char some_text[MAXMSG]; }msg; 用户缓冲区定义: char buffer[BUFSIZ]; 2018/12/6 操作系统与实验 第4章2
16
消息缓冲通信应用举例 ⑵.发送消息进程使用发送消息系统调用函数msgsnd()发送消息;接收进程使用系统调用函数msgrcv()接收消息。 将buffer中的消息复制到消息缓冲区,然后发送消息缓冲区到消息队列: msg.my_msg_type=1; //设置消息类型为1 strcpy(msg.some_text,buffer); //将buffer的消息复制到消息缓冲区 msgsnd(msgid,&msg,512,0); //发消息到消息队列 从消息msgid队列上接收一个消息并显示消息内容: msgrcv(msgid,&msg, BUFSIZ, 0, 0); printf(“%s”,msg.some_text); ⑶.消息队列不再使用时,使用系统调用函数msgctl()删除消息队列。 msgctl(msgid,IPC_RMID,0); //撤消指定的消息队列 2018/12/6 操作系统与实验 第4章2
17
4.4.3 消息缓冲通信应用举例 [例4-5] 编程实现消息的发送与接收:
消息缓冲通信应用举例 [例4-5] 编程实现消息的发送与接收: 发送进程sndfile.c,将要发送的消息从键盘输入,每输入一行就作为一条消息发送,用“end”作为结束消息。 接收进程rcvfile.c从消息队列上逐个取出消息并显示输出,也用“end” 作为结束消息。设消息队列的key为1234。 结束信息“end”的判断可以使用函数:strncmp(str1,str2,n); 其中n表示需要比较的字符个数; 其功能是比较str1和str2的前n个字符。 2018/12/6 操作系统与实验 第4章2
18
2018/12/6 操作系统与实验 第4章2 发送进程sndfile.c #include<stdio.h>
#include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<linux/msg.h> #define MAXMSG //定义消息长度 struct my_msg //定义消息缓冲区数据结构 { long int my_msg_type; char some_text[MAXMSG]; }msg; main( ) int msgid; //定义消息缓冲区内部标识 char buffer[BUFSIZ]; //定义用户缓冲区 msgid=msgget(1234,0666|IPC_CREAT); //创建消息队列,key为1234 while(1) puts("Enter some text:"); //提示键入消息内容 fgets(buffer,BUFSIZ,stdin); //标准输入送buffer msg.my_msg_type=1; //设置消息类型为1 strcpy(msg.some_text,buffer); //buffer送消息缓冲 msgsnd (msgid,&msg,MAXMSG,0); //发送消息到消息队列 if(strncmp(msg.some_text,"end",3)==0) /消息为“end”则结束 break; } exit(0); } 2018/12/6 操作系统与实验 第4章2
19
2018/12/6 操作系统与实验 第4章2 接收进程rcvfile.c #include<stdio.h>
#include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<linux/msg.h> #define MAXMSG //定义消息长度 struct my_msg //定义消息f缓冲区数据结构 { long int my_msg_type; char some_text[MAXMSG]; }msg; main() int msgid; //定义消息缓冲区内部标识 long int msg_to_receive=0; msgid=msgget(1234, 0666|IPC_CREAT); //获取消息队列,key为1234 while (1) msgrcv (msgid, &msg, BUFSIZ, msg_to_receive, 0); //接收消息 printf ("You wrote:%s",msg.some_text); //显示消息 if (strncmp (msg.some_text,"end",3)==0) //消息为“end”则结束 break; } msgctl (msgid, IPC_RMID,0); //撤消消息队列 exit (0); 2018/12/6 操作系统与实验 第4章2
20
消息缓冲通信应用举例 在创建或获取一个消息缓冲队列时,其key值可以由用户给出也可以由系统生成。本例中,由于发送与接收是两个完全独立的进程,因此双方只能使用事先约定好的key值,以用户指定的方式给出。如果发送和接收进程都是同一个家族的子进程,则可以由其祖先创建后,子孙使用,因而可以采用由系统指定key值的方式,这种情况可以使用参数IPC_PRIVATE取代我们设定的key值1234。 2018/12/6 操作系统与实验 第4章2
21
小结 消息缓冲优缺点、适用场合及其应用 自带同步工具,使用起来较方便。(优点) 该通信方式不适合信息量大或信息交换频繁的场合。 (缺点)
存在用户普通缓冲区与消息缓冲区之间的信息复制过程,时间开销较大。 消息缓冲区自数据结构msg_msg存在空间的开销。 消息队列机制是客户/服务器模型中常用的进程通信方式。 2018/12/6 操作系统与实验 第4章2
22
作业 第4章 4、5、6 2018/12/6 操作系统与实验 第4章2
Similar presentations