Linux的IPC消息实现机制分析及Word格式.docx
- 文档编号:18998679
- 上传时间:2023-01-02
- 格式:DOCX
- 页数:34
- 大小:426.53KB
Linux的IPC消息实现机制分析及Word格式.docx
《Linux的IPC消息实现机制分析及Word格式.docx》由会员分享,可在线阅读,更多相关《Linux的IPC消息实现机制分析及Word格式.docx(34页珍藏版)》请在冰豆网上搜索。
Msqid_ds队列也包括两个等待队列:
一个用于向消息队列写,另一个用于读。
每一次一个进程试图向写队列写消息,它的有效用户和组的标识符就要和队列的ipc_perm数据结构的模式比较。
如果进程可以向这个队列写,则消息会从进程的地址空间写到msg数据结构,放到消息队列的最后。
每一个消息都带有进程间约定的,应用程序指定类型的标记。
但是,因为Linux限制了可以写的消息的数量和长度,可能会没有空间容纳消息。
这时,进程会被放到消息队列的写等待队列,然后调用调度程序选择一个新的进程运行。
当一个或多个消息从这个消息队列中读出去的时候会被唤醒。
从队列中读是一个相似的过程。
进程的访问权限一样被检查。
一个读进程可以选择是不管消息的类型从队列中读取第一条消息还是选择特殊类型的消息。
如果没有符合条件的消息,读进程会被加到消息队列的读等待进程,然后运行调度程序。
当一个新的消息写到队列的时候,这个进程会被唤醒,继续运行。
1.3设计任务书:
小组成员任务分配情况及每人所占工作比例
小组成员:
负责:
分析消息队列的控制函数(sys_msgctl)以及与它相关的函数freeque,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。
所占工作比例25%。
分析消息的发送函数(real_msgsnd)以及与它相关的函数sys_msgsnd,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。
分析消息的接收函数(real_msgrcv)以及与它相关的函数sys_msgrcv,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。
分析消息队列的创建函数(sys_msgget)以及与它相关的函数newque、findkey、msg_init,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。
2.前言(绪论)(设计的目的、意义等)
在操作系统中,有些进程存在着相互制约的关系,这些制约关系来源于并行进程的相互合作和资源共享。
为了使合作进程和资源共享进程能协调一致的向前推进,必须使他们保持联系,一边相互了解。
进程相互间需要交换一定数量的信息,以便协调一致共同完成指定的任务.这种机制就叫做进程间通信,或IPC.在linux中支持UNIXSYSTEMV的三种通信机制:
消息队列,信号量和共享内存.现就消息队列这种机制进行分析,以达到充分理解Linux内核代码及消息队列机制的具体实现过程中的数据结构及方法。
3.LINUX的消息函数各个子模块分析
3.1Msgrcv:
(负责)
功能:
用msgrcv函数系统调用从msqid消息队列中读取一条信息并将其放入消息段指针msgp指向的结构。
msgsz给出mtext的字节数,如果所接收的消息比msgsz大且msgflg&
MSG_NOERROR为真,则按msgsz的大小截断而不通知调用进程。
从消息队列中取得指定类型的消息.。
说明:
系统调用从由msqid指定的消息队列中读取一个由msgtyp指定类型的消息到由msgp指向的缓冲区中,同样的,该缓冲区的结构如前所述,包括消息类型和消息正文.msgsz为可接收的消息正文的字节数.若接收到的消息正文的长度大于msgsz,则会被截短到msgsz字节为止(当消息标志msgflg&
MSG_NOERROR为真时),截掉的部份将被丢失,而且不通知消息发送进程.
msgtyp指定消息类型:
.为0则接收消息队列中第一个消息.
.大于0则接收消息队列中第一个类型为msgtyp的消息.
.小于0则接收消息队列中第一个类型值不小于msgtyp绝对值且类型值又最小的消息.
msgflg指定操作行为:
.若(msgflg&
IPC_NOWAIT)是真的,调用进程会立即返回,若没有接收到消息则返回值为-1,error设置为ENOMSG.
IPC_NOWAIT)不是真的,则调用进程会被挂起直到下面情况之一发生:
.队列中的消息的类型是有效的.
.消息队列标志被系统删除.系统调用返回-1.
.调用进程接收到未被忽略的中断信号,调用进程继续执行或被终止.
调用成功后,对应指定的消息队列的相关结构做如下动作:
.消息数(msg_qnum)减1.
.消息队列最近接收进程号(msg_lrpid)改为调用进程号.
.消息队列接收时间(msg_rtime)改为当前系统时间.
以上信息可用命令ipcs-a看到.
返回值:
调用成功则返回值等于接收到实际消息正文的字节数.不成功则返回-1.
数据结构分析:
图1数据结构总体结构
Structmsqid_ds:
代表一个消息队列。
它的成员如下:
msg_perm:
表明那个进程可以读写这个消息队列;
msg_first:
指向队列中第一个消息的指针;
msg_last:
指向队列中最后一个消息的指针;
msg_stime:
纪录消息被送入队列的最后时间;
msg_rtime:
纪录从队列中读出消息的最后时间;
msg_ctime:
上一次改变队列的时间;
(可以是队列创立的时间或者是上一次用msgctl系统调用来设置参数的时间。
)
wwait:
等待进入消息队列的消息队列;
rwait:
如果在接收消息的时候没有可以接收的消息,那么根据这个设置来看是返回一个错误代码表示读消息失败还是阻塞等待消息到来;
msg_cbytes:
当前队列中消息的总字节数;
msg_qnum:
队列中消息的总数;
msg_qbytes:
队列中允许存储的消息的最大字节数;
msg_lspid:
最后消息发送方的PID;
msg_lrpid:
最后消息接收方的PID;
有关常量及相关错误信息的含义:
常量含义:
staticstructmsqid_ds*msgque[MSGMNI];
//消息队列
staticintmsgbytes=0;
//消息队列中所有消息的总字节数
staticintmsghdrs=0;
//消息队列的队头
staticunsignedshortmsg_seq=0;
staticintused_queues=0;
//已用的消息队列数
staticintmax_msqid=0;
//消息队列最大的ID值
staticstructwait_queue*msg_lock=NULL;
//消息队列锁定,不让等待进程进入
错误信息含义:
EINVAL22/*Invalidargument*/
EFAULT14/*Badaddress*/
EIDRM43/*Identifierremoved*/
EACCES13/*Permissiondenied*/
EAGAIN11/*Tryagain*/
EINTR4/*Interruptedsystemcall*/
ENOMEM12/*Outofmemory*/
E2BIG7/*Arglisttoolong*/
ENOMSG42/*Nomessageofdesiredtype*/
ENOSPC28/*Nospaceleftondevice*/
EPERM1/*Operationnotpermitted*/
ENOENT2/*Nosuchfileordirectory*/
EEXIST17/*Fileexists*/
接收消息函数real_msgrcv的分析:
代码及注释
staticintreal_msgrcv(intmsqid,structmsgbuf*msgp,size_tmsgsz,longmsgtyp,intmsgflg)
{//取消息函数,该函数为实际操作函数
structmsqid_ds*msq;
//每个消息队列占一个msqid_ds结构,include/linux/msg.h/
structipc_perm*ipcp;
//访问权限控制结构
structmsg*tmsg,*leastp=NULL;
//存放消息内容的结构体
structmsg*nmsg=NULL;
intid;
if(msqid<
0||(long)msgsz<
0)
return-EINVAL;
//若消息长度值越界则返回错误信息
id=(unsignedint)msqid%MSGMNI;
//MSGMNI为消息队列的最大长度,
//取模的作用是保证消息队列的个数不越界
msq=msgque[id];
//msgque[]是为全局变量,为系统所能维持的消息队列的
//个数消息队列标识号对两段信息进行编码
if(msq==IPC_NOID||msq==IPC_UNUSED)//指针无效或者所分配的地址被破坏
//则返回错误信息
ipcp=&
msq->
msg_perm;
//将消息的控制信息赋值给ipcp
/*findmessageofcorrecttype.//寻找正确类型的消息
*msgtyp=0=>
getfirst.//取队列中第一个消息给nmsg.
*msgtyp>
0=>
getfirstmessageofmatchingtype.//取队列中第一个消息给nmsg.
*msgtyp<
getmessagewithleasttypemustbe<
abs(msgtype).//取队列中msgtyp最小的消息给nmsg.
*/
while(!
nmsg){
if(msq->
msg_perm.seq!
=(unsignedint)msqid/MSGMNI){
return-EIDRM;
}
if(ipcperms(ipcp,S_IRUGO)){//判断该进程是否有权读取该消息队列
return-EACCES;
if(msgtyp==0)//msgtyp为0时取队列中第一条消息
nmsg=msq->
msg_first;
elseif(msgtyp>
0){//当msgtyp大于0时
if(msgflg&
MSG_EXCEPT){
//若标志字msgflg中设置了MSG_EXCEPT则进行如下操作
for(tmsg=msq->
tmsg;
tmsg=tmsg->
msg_next)//初值为tmsg=msq->
msg_first,
//当tmsg不为NULL时循环,步进条件为tmsg=tmsg->
msg_next
if(tmsg->
msg_type!
=msgtyp)
//当遇到消息类型不同于所要
break;
//求类型时跳出循环
nmsg=tmsg;
//上一段代码取出队
//列中第一条消息类型与所给类型不符合的消息。
}else{
msg_next)
if(tmsg->
msg_type==msgty
//取出该消息这一段代码的作用
//是取出队列中第一条消息类型与所给类型相符的消息。
break;
nmsg=tmsg;
}
}
else{//当msgtyp小于0时
for(leastp=tmsg=msq->
//初始设置
tmsg=tmsg->
msg_next)//leastp为消息队列头
msg_type<
leastp->
msg_type)
//若当前消息的类型码小
//于leastp的类型码
leastp=tmsg;
//将临时当前消息指针tmsg赋值给leastp
if(leastp&
&
=-msgtyp)
//若leastp存在且其指向
//消息的类型码小于给定类型码的绝对值
nmsg=leastp;
//将leastp赋值给当前指针nmsg
if(nmsg){/*donefindingamessage*/
//如果其中存在符合类型的消息
if((msgsz<
nmsg->
msg_ts)&
!
(msgflg&
MSG_NOERROR))
{
return-E2BIG;
//若消息允许大小(msgsz)小于消息队列中特定消
//并且没有设置MSG_NOERROR则取消息失败
msgsz=(msgsz>
msg_ts)?
msg_ts:
msgsz;
//在消息允许大小msgsz和消息队列中特定消息大小nmsg->
msg_ts
//取较大的值赋值给msgsz
if(nmsg==msq->
msg_first)//若所取消息为消息队列头
msq->
msg_first=nmsg->
msg_next;
//则将nmsg->
msg_next重新置为消息队列头
else{//若所取消息不是消息队列头
tmsg=tmsg->
if(tmsg->
msg_next==nmsg)
//循环搜索到要取消息的前一个指针tmsg
break;
tmsg->
msg_next=nmsg->
//重新设置msg_next指针域
if(nmsg==msq->
msg_last)//若所取消息是消息队列尾
msq->
msg_last=tmsg;
//重新设置msg_last指针域
if(!
(--msq->
msg_qnum))//若取出消息后队列中已无消息
msg_last=msq->
msg_first=NULL;
//设队列首与队列尾为NULL
msq->
msg_rtime=CURRENT_TIME;
//取消息时间更新为当前时间
msg_lrpid=current->
pid;
//取消息进程号更新为当前进程号
msgbytes-=nmsg->
msg_ts;
//全局变量msgbytes用来统计使用的消息字节的大小
//减少相应的字节
msghdrs--;
//全局变量msghdrs用来统计小心队列的个数,加1
msg_cbytes-=nmsg->
//队列中消息字节数减少
wake_up(&
wwait);
//唤醒所有在该消息队列上等待的写消息进程
if(put_user(nmsg->
msg_type,&
msgp->
mtype)||
copy_to_user(msgp->
mtext,nmsg->
msg_spot,msgsz))
//将消息赋值到用户空间
msgsz=-EFAULT;
kfree(nmsg);
//释放空间
returnmsgsz;
//返回取得消息的大小
}else{//若没有找到符合消息类型的消息
IPC_NOWAIT){//若设置IPC_NOWAIT
return-ENOMSG;
//则返回错误代码
if(signal_pending(current)){//有信号要调用当前进程
return-EINTR;
interruptible_sleep_on(&
rwait);
//让该进程在读进程上阻塞
}
return-1;
//返回-1表示接收失败
}
图2接收消息的函数real_msgrcv流程图
利用消息队列进行通信的实例:
<
1>
发送端代码:
#include<
stdio.h>
#include<
stdlib.h>
sys/ipc.h>
sys/msg.h>
string.h>
#include<
sys/types.h>
time.h>
structmsgbuf{
longtype;
charptr[0];
};
intmain(intargc,char*argv[]){
key_tkey;
key=ftok(argv[1],100);
intmsgid;
msgid=msgget(key,IPC_CREAT|0600);
pid_tpid;
pid=fork();
if(pid==0){
while
(1){
printf("
pleaseinputmsgtosend:
"
);
charbuf[128];
fgets(buf,128,stdin);
structmsgbuf*ptr=malloc(sizeof(structmsgbuf)+strlen(buf)+1);
ptr->
type=1;
memcpy(ptr->
ptr,buf,strlen(buf)+1);
msgsnd(msgid,ptr,strlen(buf)+1,0);
free(ptr);
}
else{
structmsgbuf{
longtype;
charptr[1024];
};
structmsgbufmybuf;
memset(&
mybuf,'
\0'
sizeof(mybuf));
msgrcv(msgid,&
mybuf,1024,2,0);
printf("
recvmsg:
%s\n"
mybuf.ptr);
2>
接收端代码:
charptr[0];
intmain(intargc,char*argv[])
key=ftok(argv[1],100);
msgid=msgget(key,IPC_CREAT|0600);
pid=fork();
if(pid==0)//send
{
while
(1)
{
charbuf[128];
fgets(buf,128,stdin);
type=2;
//sendmsgtype=2
memcpy(ptr->
free(ptr);
else
charptr[1024];
memset(&
mybuf,1024,1,0);
//recvmsgtype=2
3>
运行截图
3.2Msgget:
取得一个消息队列。
调用者提供消息队列
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux IPC 消息 实现 机制 分析