Linux的IPC消息实现机制分析及.docx

上传人:b****6 文档编号:6004478 上传时间:2023-01-02 格式:DOCX 页数:34 大小:426.53KB
下载 相关 举报
Linux的IPC消息实现机制分析及.docx_第1页
第1页 / 共34页
Linux的IPC消息实现机制分析及.docx_第2页
第2页 / 共34页
Linux的IPC消息实现机制分析及.docx_第3页
第3页 / 共34页
Linux的IPC消息实现机制分析及.docx_第4页
第4页 / 共34页
Linux的IPC消息实现机制分析及.docx_第5页
第5页 / 共34页
点击查看更多>>
下载资源
资源描述

Linux的IPC消息实现机制分析及.docx

《Linux的IPC消息实现机制分析及.docx》由会员分享,可在线阅读,更多相关《Linux的IPC消息实现机制分析及.docx(34页珍藏版)》请在冰豆网上搜索。

Linux的IPC消息实现机制分析及.docx

Linux的IPC消息实现机制分析及

东北大学秦皇岛分校

计算机与通信工程学院

操作系统课程设计

 

设计题目Linux的IPC消息实现机制分析及

进程管理器

 

专业名称

计算机科学与技术

班级学号

学生姓名

指导教师

王翠荣

设计时间

2015.01.05—2015.01.15

 

课程设计任务书

专业:

计算机科学与技术学号:

学生姓名(签名):

设计题目:

Linux的IPC消息实现机制的分析

一、设计实验条件

综合楼808实验室

二、设计任务及要求

仔细阅读设计题目要求,对设计内容进行详细分析、设计。

每组同学需有明确分工,并在提交的课程设计报告中能够充分体现具体分工情况,设计报告中体现个人分工部分的内容不应低于正文60%。

报告内容应包括题目分析、总体设计、代码实现(如果是内核分析题目此部分为内核主要代码和算法等的分析)、结果展示和课程设计总结几个组成部分。

报告格式按照“计算机与通信工程学院课程设计模板”中的要求编写。

三、设计报告的内容

1.设计题目分析与设计任务(设计任务书)

1.1设计题目:

进程间通信(IPC)的一个主要通信方式是通过消息,通过对Linux内核进行分析,说明Linux用于消息实现的主要数据结构,以及实现的方法。

1.2设计任务分析:

Linux采用消息队列的方式来实现消息传递。

SystemV的消息队列(messagequeues)是进程之间互相发送消息的一种异步(asynchronously)方式,在这种情形之下,发送方不必等待接收方检查它的消息——即在发送完消息后,发送方就可以从事其它工作了——而接收方也不必一直等待消息。

新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。

消息队列允许一个或多个进程写消息,一个或多个进程读取消息。

Linux维护了一系列消息队列的msgque向量表。

其中的每一个单元都指向一个msqid_ds的数据结构,完整描述这个消息队列。

当创建消息队列的时候,从系统内存中分配一个新的msqid_ds的数据结构并插入到向量表中。

每一个msqid_ds数据结构都包括一个ipc_perm的数据结构和进入这个队列的消息的指针。

另外,Linux保留队列的改动时间,例如上次队列写的时间等。

Msqid_ds队列也包括两个等待队列:

一个用于向消息队列写,另一个用于读。

每一次一个进程试图向写队列写消息,它的有效用户和组的标识符就要和队列的ipc_perm数据结构的模式比较。

如果进程可以向这个队列写,则消息会从进程的地址空间写到msg数据结构,放到消息队列的最后。

每一个消息都带有进程间约定的,应用程序指定类型的标记。

但是,因为Linux限制了可以写的消息的数量和长度,可能会没有空间容纳消息。

这时,进程会被放到消息队列的写等待队列,然后调用调度程序选择一个新的进程运行。

当一个或多个消息从这个消息队列中读出去的时候会被唤醒。

从队列中读是一个相似的过程。

进程的访问权限一样被检查。

一个读进程可以选择是不管消息的类型从队列中读取第一条消息还是选择特殊类型的消息。

如果没有符合条件的消息,读进程会被加到消息队列的读等待进程,然后运行调度程序。

当一个新的消息写到队列的时候,这个进程会被唤醒,继续运行。

1.3设计任务书:

小组成员任务分配情况及每人所占工作比例

小组成员:

负责:

分析消息队列的控制函数(sys_msgctl)以及与它相关的函数freeque,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。

所占工作比例25%。

负责:

分析消息的发送函数(real_msgsnd)以及与它相关的函数sys_msgsnd,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。

所占工作比例25%。

负责:

分析消息的接收函数(real_msgrcv)以及与它相关的函数sys_msgrcv,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。

所占工作比例25%。

负责:

分析消息队列的创建函数(sys_msgget)以及与它相关的函数newque、findkey、msg_init,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。

所占工作比例25%。

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.

.若(msgflg&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*/

ENOMEM12/*Outofmemory*/

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)//指针无效或者所分配的地址被破坏

return-EINVAL;//则返回错误信息

ipcp=&msq->msg_perm;//将消息的控制信息赋值给ipcp

/*findmessageofcorrecttype.//寻找正确类型的消息

*msgtyp=0=>getfirst.//取队列中第一个消息给nmsg.

*msgtyp>0=>getfirstmessageofmatchingtype.//取队列中第一个消息给nmsg.

*msgtyp<0=>getmessagewithleasttypemustbe

*/

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->msg_first;tmsg;tmsg=tmsg->msg_next)//初值为tmsg=msq->msg_first,

//当tmsg不为NULL时循环,步进条件为tmsg=tmsg->msg_next

if(tmsg->msg_type!

=msgtyp)

//当遇到消息类型不同于所要

break;//求类型时跳出循环

nmsg=tmsg;//上一段代码取出队

//列中第一条消息类型与所给类型不符合的消息。

}else{

for(tmsg=msq->msg_first;tmsg;tmsg=tmsg->msg_next)

if(tmsg->msg_type==msgty

//取出该消息这一段代码的作用

//是取出队列中第一条消息类型与所给类型相符的消息。

break;

nmsg=tmsg;

}

}

else{//当msgtyp小于0时

for(leastp=tmsg=msq->msg_first;tmsg;//初始设置

tmsg=tmsg->msg_next)//leastp为消息队列头

if(tmsg->msg_typemsg_type)

//若当前消息的类型码小

//于leastp的类型码

leastp=tmsg;//将临时当前消息指针tmsg赋值给leastp

if(leastp&&leastp->msg_type<=-msgtyp)

//若leastp存在且其指向

//消息的类型码小于给定类型码的绝对值

nmsg=leastp;//将leastp赋值给当前指针nmsg

}

if(nmsg){/*donefindingamessage*/

//如果其中存在符合类型的消息

if((msgszmsg_ts)&&!

(msgflg&MSG_NOERROR))

{

return-E2BIG;

//若消息允许大小(msgsz)小于消息队列中特定消

//并且没有设置MSG_NOERROR则取消息失败

}

msgsz=(msgsz>nmsg->msg_ts)?

nmsg->msg_ts:

msgsz;

//在消息允许大小msgsz和消息队列中特定消息大小nmsg->msg_ts

//取较大的值赋值给msgsz

if(nmsg==msq->msg_first)//若所取消息为消息队列头

msq->msg_first=nmsg->msg_next;

//则将nmsg->msg_next重新置为消息队列头

else{//若所取消息不是消息队列头

for(tmsg=msq->msg_first;tmsg;

tmsg=tmsg->msg_next)

if(tmsg->msg_next==nmsg)

//循环搜索到要取消息的前一个指针tmsg

break;

tmsg->msg_next=nmsg->msg_next;

//重新设置msg_next指针域

if(nmsg==msq->msg_last)//若所取消息是消息队列尾

msq->msg_last=tmsg;

//重新设置msg_last指针域

}

if(!

(--msq->msg_qnum))//若取出消息后队列中已无消息

msq->msg_last=msq->msg_first=NULL;

//设队列首与队列尾为NULL

msq->msg_rtime=CURRENT_TIME;//取消息时间更新为当前时间

msq->msg_lrpid=current->pid;//取消息进程号更新为当前进程号

msgbytes-=nmsg->msg_ts;

//全局变量msgbytes用来统计使用的消息字节的大小

//减少相应的字节

msghdrs--;//全局变量msghdrs用来统计小心队列的个数,加1

msq->msg_cbytes-=nmsg->msg_ts;//队列中消息字节数减少

wake_up(&msq->wwait);

//唤醒所有在该消息队列上等待的写消息进程

if(put_user(nmsg->msg_type,&msgp->mtype)||

copy_to_user(msgp->mtext,nmsg->msg_spot,msgsz))

//将消息赋值到用户空间

msgsz=-EFAULT;

kfree(nmsg);//释放空间

returnmsgsz;//返回取得消息的大小

}else{//若没有找到符合消息类型的消息

if(msgflg&IPC_NOWAIT){//若设置IPC_NOWAIT

return-ENOMSG;//则返回错误代码

}

if(signal_pending(current)){//有信号要调用当前进程

return-EINTR;

}

interruptible_sleep_on(&msq->rwait);//让该进程在读进程上阻塞

}

}

return-1;//返回-1表示接收失败

}

图2接收消息的函数real_msgrcv流程图

利用消息队列进行通信的实例:

<1>发送端代码:

#include

#include

#include

#include

#include

#include

#include

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];

};

while

(1){

structmsgbufmybuf;memset(&mybuf,'\0',sizeof(mybuf));

msgrcv(msgid,&mybuf,1024,2,0);printf("recvmsg:

%s\n",mybuf.ptr);

}

}

}

<2>接收端代码:

#include

#include

#include

#include

#include

#include

#include

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)//send

{

while

(1)

{

printf("pleaseinputmsgtosend:

");

charbuf[128];

fgets(buf,128,stdin);

structmsgbuf*ptr=malloc(sizeof(structmsgbuf)+strlen(buf)+1);

ptr->type=2;//sendmsgtype=2

memcpy(ptr->ptr,buf,strlen(buf)+1);

msgsnd(msgid,ptr,strlen(buf)+1,0);

free(ptr);

}

}

else

{

structmsgbuf{

longtype;

charptr[1024];

};

while

(1)

{

structmsgbufmybuf;

memset(&mybuf,'\0',sizeof(mybuf));

msgrcv(msgid,&mybuf,1024,1,0);//recvmsgtype=2

printf("recvmsg:

%s\n",mybuf.ptr);

}

}

}

<3>运行截图

3.2Msgget:

(负责)

功能:

取得一个消息队列。

调用者提供消息队列

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1