




已阅读5页,还剩16页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
进程间通信方式之:消息队列 UNIX早期通信机制之一的信号能够传送的信息量有限,而管道只能传送无格式的字节流,这无疑给应用程序开发带来了不便,消息队列(也称报文队列)则克服了这些缺点. 一、消息队列概述 消息队列从本质上是一链表,它是由一个消息一个消息串联起来的链表.可以将消息看做一个记录,每个具有特定的格式.其中一个进程可以向消息队列内按照一定的规则添加新消息,而另外一些进程则可以从消息队列中读走这些消息. 在Linux系统中,主要有两种消息队列:POSIX消息队列和系统V消息队列.后者目前被大量的使用. 1.POSIX:possible operate system interface可移植操作系统接口. 2.系统V消息队列是随内核持续的,只有在内核重启或人工删除时,该消息队列才会被删除.这里,随内核的持续性是指消息队列并不会随用户程序的结束而消失,除非内核重启或在用户的应用程序中删除了该消息队列,这样消息队列才会被删除.正因为消息队列的内核持续性,系统要求每个消息队列在系统范围内对应唯一的键值.所以,要获取消息队列的描述字,必须提供该消息队列的键值.这里的键值类似于磁盘上的文件,每个文件都需要一个文件名. 二、消息队列与管道的比较 1、消息队列允许一个或多个进程向它写入或读取消息.但消息队列中的消息有这样一个特点,当在一个进程中成功的读取了一条消息后,队列中的这条消息将被删除,其它进程将看不到该消息了.这点类似于管道.比如当读进程从管道内读取到数据后,其它进程将看不到这些数据了. 2、管道和命名管道的通信数据都采用先进先出的原则.而消息队列可以实现消息的随机查询,消息并不一定以先进先出的次序读取,也可以按照消息的类型读取,这比管道或命名管道更有优势. 三、消息队列的操作 常用消息队列的API有如下5种:(1)键值的获取 在Linux中,消息队列的产生是依据消息队列的键值(key)而产生的,且key具有唯一性. #include #include key_t ftok(char *pathname,char proj) 函数功能 :返回文件名对应的键值 参数pathname:路径文件名(通过文件名来获取该文件的键值) 参数proj :项目名(不为0的整数即可)(2)消息的创建创建于打开 #include #include #include int msgget(key_t key,int msgflg) 函数功能 :利用键值(key)来创建或打开消息队列 参数key :键值,由ftok()函数获得. 参数msgflg:标志位 返回值 :与键值key相对应的消息队列描述字,该描述字的功能类 似于文件的文件描述符,或进程的ID.出错返回-1. Attention! msgflg(标志位)常用的几种形式: 1. IPC_CREAT:用于创建新的消息队列 2. IPC_EXCL:建议与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误. 3. IPC_NOWAIT:用户当读写消息队列时,要求无法得到满足,进程不阻塞.比如该消息队列已满,用户无法再向该消息队列内写入数据;或该消息队列已空,用户无法再从该消息队列内读出数据;此时在默认情况下进程会发生阻塞.但如果用户在该标志位处使用了标志位IPC_NOWAIT,则该进程将不会阻塞. 4. 0:这是标志位的默认值0,表示当读写消息队列时,要求无法得到满足,进程阻塞. 创建消息队列的方法: 若整个系统中没有和键值(key)相对应的消息队列,此时调用函数msgget,且标志位msgflg处使用IPC_CREAT,这样就创建了一个消息队列. int open_queue(key_t keyval) int qid; if(qid=msgget(keyval,IPC_CREAT)=-1) return (-1); return qid; /返回该消息队列的ID (3)向消息队列中发送消息 创建好了消息队列后,有些进程就可以向该消息队列内写入消息,有些进程就可以从该消息队列内读取消息. #include #include #include int msgsnd(int msqid,struct msgbuf *msgp,int msgsz,int msgflg) 函数功能:将新消息添加到队列尾端 返回值 :发送失败则返回-1 参数msqid:已打开的消息队列的ID,表示需要向哪一个消息队列内发送消息,由getmsg()函数的返回值决定. 参数*msgp:该结构体内存放了需要发送的消息,这是一个结构体类型的buffer.消息队列与管道的最大区别在于消息队列内的消息具有一定的格式,而管道却没有.而消息队列内消息的格式是由struct msgbuf来决定的. struct msgbuf long mtype; /消息类型,必须为long型的非负数 char mtext1; /消息数据的首地址 成员mtype:表示要发送消息的类型,换句话说,用户可以向消息队列内发送多种类型的消息. 比如当mtype=1时,表示要发送类型为1的消息 当mtype=2时,表示要发送类型为2的消息 而在接收消息队列时,该消息队列内有多种类型的消息,比如有类型1的消息,有类型2的消息,有类型3的消息,用户可以指定只接收哪一种类型的消息. 成员mtext:表示要发送的消息,比如该消息可以从终端键盘获取等,或者是固定的某些消息.当然要发送的消息长度并非是例程中的1,用户可自定义. 参数sgsz :发送消息的长度.注意,长度不能包含消息类型(mtype),而只能包含数据长度(mtext1). 参数msgflg:该标志与前面所讲的标志msgflg具有相同的含义. 标志位为IPC_NOWAIT,表示如果没有满足条件的消息,则调用立即返回,此时errno=ENOMSG.比如消息队列已满,则函数调用立即返回.比如消息队列中没有mtype类型的消息,则函数调用立即返回. 标志位为0是默认值,会阻塞,等待条件满足.即等待消息队列中有了满足条件的消息.(4)从消息队列中读取消息 发送了消息,接下来就需要从另一进程中接收这些消息. #include #include #include int msgrcv(int msqid,struct msgbuf *msgp,int msgsz,long msgtyp,int msgflg) 函数功能:从某一消息队列中读取消息,并将这些信息放入结构体型buffer内.从msqid代表的消息队列中读取一个msgtyp类型的消息,并将消息存放在msgp指向的msgbuf结构体中,当在一个进程中成功的读取了一条消息后,队列中的这条消息将被删除,其它进程将看不到该消息了.这点类似于管道.比如当读进程从管道内读取到数据后,其它进程将看不到这些数据了. 参数msqid :已打开的消息队列的ID,表示从哪一个消息队列中接收消息,由getmsg()函数的返回值决定. 参数*msgp :表示从消息队列中取出消息后,要存放的地方.当然必须存放在内存中了. 参数msgsz :读取消息的长度.注意,长度不能包含消息类型(mtype),而只能包含数据长度(mtext1). 参数msgtyp:由于在发送消息时,指定了该消息的类型.因此,从消息队列中接收消息时,也必须指定只接收什么类型的消息. 参数msgflg:该标志与前面所讲的标志msgflg具有相同的含义.标志位为IPC_NOWAIT,表示如果没有满足条件的消息,则调用立即返回,此时errno=ENOMSG.比如消息队列为空,则函数调用立即返回.比如消息队列中没有mtype类型的消息,则函数调用立即返回. 标志位为0是默认值,会阻塞,等待条件满足.即等待消息队列中有了满足条件的消息. 标志位为IPC_NOERROR,表示如果队列中满足条件的消息内容大于所请求的msgsz字节,则将该消息截断,截断部分将丢失. 读取消息的方法: int read_message(int qid,long type,struct mymsgbuf*qbuf) int result,length; length=sizeof(struct mymsgbuf)-sizeof(long); if(result=msgrcv(qid,qbuf,length,type,0)=-1) /0是默认值,即会阻塞.也可以指定为IPC_NOWAIT,这样就不阻塞了. return -1; return result; 四、基础例程 例程:在一进程中向消息队列内写入消息,在同一进程从该消息队列内读出这些消息. #include #include #include #include #include #include int main(void) key_t key; int msqid; int ret; struct msg_buf long mtype; char data100; ; struct msg_buf msgbuf_snd,msgbuf_rev; key=ftok(/home/lishuai/test.c,a); printf(key = %dn,key); msqid=msgget(key,IPC_CREAT|IPC_EXCL|0666); if(msqid=-1) printf(Creat error!n); return -1; msgbuf_snd.mtype = getpid(); /设置消息队列中消息的格式(只有同一进程才能读取消息) strcpy(msgbuf_snd.data,test hehe); /设置消息队列中消息的内容 ret=msgsnd(msqid,msgbuf_snd,sizeof(msgbuf_snd.data),IPC_NOWAIT); if(ret=-1) printf(send messages error!n); return -1; ret=msgrcv(msgqid,msgbuf_rev,sizeof(msgbuf_rev.data),getpid(),IPC_NOWAIT); if(ret=-1) printf(receive message error!); return -1; printf(the receive message = %sn,msgbuf_rev.data); return 0; 五、消息队列的高级例程/*=程序功能: 实现Linux单机多人QQ聊天(人数2)程序思想: 利用Linux多进程下的消息队列实现特别提示: 在父进程中处理从终端接收的信息,并将其放入消息队列 中;在子进程中只接收应该接收的信息,并将其显示在终 端上.项目文件: ss.c yy.c jj.c项目说明: 该项目中有3个用户,分别是ss、yy、jj,该程序可以实 现三人之间的聊天,类似于腾讯QQ验证方法: 在Linux下打开三个终端,找到相应的ss.c、yy.c、 jj.c,分别进行编译.然后在3个终端下分别执行相应的可 执行程序.编写日期: 2010-10-17 add by 李帅项目提示: 第一次编写,没有很好的整理,其实可以将三个文件合为 一个文件,也没有制作makefile,这是以后的工作了=*/file1: ss.c/*用户ss的信息类型是字符s*/#include #include #include #include #include #include #include #include #include int main(int argc,char *argv) key_t key; int msg_id,pid; char std_receive100; struct msg long type; /消息的类型必须为非负整数,不能是其它类型的数 char name100; char text100; send,recv; if(key=ftok(.,3)0) /为了防止与其它工程的ID重复,这里设置为3 perror(ftok); msg_id=msgget(key,IPC_CREAT|0666); /该消息队列对于所有用户都是可读、可写 /建议不要将子进程的创建放在死循环中. /可以单独将进程的创建放在死循环外,并将父、子进程的执行分别用两个死循环. /若父进程或子进程中没有死循环,语句执行完毕后,进程会消失.如果加入死循环,这样进程永不消失. if(pid=fork()0) perror(fork); else if(0=pid) while(1) /子进程循环接收其它用户的消息 if(msg_id=msgget(key,IPC_CREAT|0666)0) /重读该消息队列,判断它是否被打开 exit(0); /正常退出该进程 msgrcv(msg_id,&recv,sizeof(recv),s,0); /仅接收消息类型为s(对应一ASCII码)的消息 printf(r%s:%sss:,,recv.text); fflush(stdout); else while(1) /父进程循环从(终端)键盘的数据,并将这些数据放入消息队列中 printf(ss:); fflush(stdout); fgets(std_receive,100,stdin); /从(终端)键盘获取字符串 send.type=std_receive0; /将字符串的第一个字符作为消息标志,比如给用户ss发生消息,则s是消息类型 strcpy(,ss); /将用户ss的提示符ss:也发送到消息队列中,这样,其它用户可以知道是ss用户的信息 strcpy(send.text,&std_receive2); /跳过空格,从字符串的第二个字符开始起,作为发送的内容 if(msg_id=msgget(key,IPC_CREAT|0666)0) /重读该消息队列,判断它是否被打开 exit(0); /正常退出该进程 msgsnd(msg_id,&send,sizeof(send),0); return 0;打开第一个终端:rootlocalhost lishuai# gcc ss.c -o ss -Wall -O2 -grootlocalhost lishuai# ./ss file2: yy.c/*用户yy的信息类型是字符y*/#include #include #include #include #include #include #include #include #include int main(int argc,char *argv) key_t key; int msg_id,pid; char std_receive100; struct msg long type; char name100; char text100; send,recv; if(key=ftok(.,3)0) perror(ftok); msg_id=msgget(key,IPC_CREAT|0666); if(pid=fork()0) perror(fork); else if(0=pid) while(1) if(msg_id=msgget(key,IPC_CREAT|0666)0) exit(0); msgrcv(msg_id,&recv,sizeof(recv),y,0); printf(r%s:%syy:,,recv.text); fflush(stdout); else while(1) printf(yy:); fflush(stdout); fgets(std_receive,100,stdin); send.type=std_receive0; strcpy(,yy); strcpy(send.text,&std_receive2); if(msg_id=msgget(key,IPC_CREAT|0666)0) exit(0); msgsnd(msg_id,&send,sizeof(send),0); return 0;打开第二个终端:rootlocalhost lishuai# gcc yy.c -o yy -Wall -O2 -grootlocalhost lishuai# ./yy file3: jj.c/*用户jj的信息类型是字符j*/#include #include #include #include #include #include #include #include #include int main(int argc,char *argv) key_t key; int msg_id,pid; char std_recei
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年仓储安全专员招聘面试题及解析大全
- 2025年现代物业管理发展趋势分析模拟题集及答案
- 2025年室内设计师中级实战模拟题集及答案解析
- 广西桂林市部分学校2024-2025学年高一下学期5月月考生物试卷(解析版)
- 广东省部分学校20225届高三5月月考生物试题(解析版)
- 2025年护士招聘面试技巧与常见问题解答指南
- 2025年质量管理工程师中级资格面试模拟题解析
- 2025年特岗教师招聘笔试考试指南高中地理实战版
- 北京中学2024-2025学年高一下学期期中测试(英才班)生物试题(解析版)
- 2025年特岗教师招聘面试高中地理模拟题与备考策略
- 医院护理管理课件
- 软件咨询面试题目及答案
- 2025年艾梅乙知识竞赛试题及答案
- 云南航空产业投资集团招聘笔试真题2024
- 2025年高考语文全国一卷试题真题及答案详解(精校打印)
- 附录E-IATF16949条款过程对照表
- 电网调度自动化维护员岗位培训题库简答题
- 中国古代文学史《第二章:诗经》PPT课件(完整版)
- 云南省地质灾害群测群防手册
- 高级催乳师培训课程讲义
- 第三届韬奋杯全国出版社青编校大赛校对试题(已编辑)
评论
0/150
提交评论