Minix进程间通信分析

进程间通信(IPC: Inter-Process Communication), 可以直接理解为进程间互相发送消息,进程间互相发送消息的方式有很多,比如消息传递,管道,共享内存等都可以,本文主要分析消息传递方式在Minix内核中是如何工作的。

为什么需要进行进程间通信?宏、微内核系统调用区别?

进程A要给B发送消息,说明进程A有求于进程B,A和B共同协作才能完成一项任务,如何只有A是完成不了的。
举个实际点的例子,在Linux中用户进程是不能直接操作内存的,申请内存要经过系统调用进程内核态来申请,然后把申请的结果返回给用户进程,从刚才描述可以看出来,用户进程执行到申请内存这个操作时,把控制权交给内核,由内核分配内存,返回结果后,控制权返回给用户进程,用户进程继续执行.
比如 void *addr = malloc(10);, 执行到这一句时,就要陷入内核态了,由内核完成工作。

也就是说,只有用户进程是完成不了工作的,它要申请内存,但它没有这个权限,必需要要由内核来完成,上面就相当于用户进程与内核协作完成了工作。

在微内核OS中,上面所说的申请内存的操作只有用户进程同样不能完成,也需要其他模块的协作(比如叫内存管理模块),当用户进程执行到申请内存的操作时,会给内存管理模块发送一个消息,告诉它谁要申请内存,申请多大的内存,然后内存管理程序收到消息,分配内存后,把结果放到消息中,发送回用户进程,用户进程继续执行。

在这里,是由用户进程和内存管理模块协作完成工作的。

从上面宏内核与微内核进行内存申请过程的描述,可以看出它们的区别:
宏内核在上面过程中是没有进行进程间通信的(只有一个用户进程)

在这里插入图片描述

微内核在用户进程与内存管理模块(也是一个进程)间进行了通信才完成的工作。

在这里插入图片描述

本图只是表达,内存管理模块已经不在是在内核中的,而是单独的一个进程,详细的消息传递过程下面会分析。

消息传递过程分析:

在用户进程中调用getpid(), 是如何得到pid的。
注:本文只关注执行过程,细节读者可自行研究(比如传递的参数,设置的某些变量等)

在这里插入图片描述
lib/getpid.c

其中callm1定义如下:


lib/call.c

可以看到最终调用了callx

在这里插入图片描述
lib/call.c

定义如下, 好了现在来分析一下目前完成的工作
用户进程调用getpid()最终是调用了callx(MM, GETPID); 第一个参数是目的进程,它是一个常量,GETPID也是一个常量,你得告诉目标进程执行哪一个功能吧, GETPID表示执行GETPID这一项功能。

先不着急往下分析,先补充一些Minix的知识。
在Minix启动时,会创建一些重要的进程,比如,内存管理,系统任务等进程,(详细创建过程见kernel/main.c)这些进程创建完成之后就可以开始执行了,而且是一直存在的并且独立于内核的, 这些进程同普通进程一样参与调度,Minix的调度方式(注意版本)是数组,在创建进程时,会在数组相应位置存放进程pcb的指针,所以用下标就可以表示某个进程了(比如上面内存管理模块放在第1项上,MM的值也被定义为1,会从相应位置取出pcb,再进行下一步处理)

在这里插入图片描述
tools/build.c

Minix系统调用对应的函数入口是s_call,


kernel/main.c

好了,我们继续,刚才执行到callx函数了,注意我们现在还是在用户态中,为什么要强调这个呢,因为我们马上就要进入内核态了,在callx函数中,调用了一个非常重要的函数
sendrev(), 这是一个汇编函数如下:

在这里插入图片描述

可以看到send(), receive(), sendrev()的定义,看名字也能看出来,发送,接收,发送,并接收,(发送并接收的意思就是发出了一个请求,并且希望得到响应,比如getpid(), 请求发出去了,你总得告诉我结果吧)

设置一些中断需要的参数(本文不详细解释),然后int SYSVEC,上面说了这是Minix的系统调用入口,也就是说执行完int SYSVEC后就进入了内核态了,SYSVEC号中断对应的入口是s_call(上面说过),就去执行s_call了,这也是一个汇编函数:


kernel/mpx88.s

设置参数,调用了sys_call()函数,从注释,结合上面传递过程,调用效果大体如下:
sys_call(发送并接收, 当前用户进程, MM, 消息体)
当前用户进程:执行getpid()的用户进程
MM:目标进程,内存管理程序
消息体:里面包含了让MM干活所需要的信息(干什么活:GETPID)

在这里插入图片描述

kernel/proc.c [代码已删减]

这是Minix中唯一的一个系统调用,不像Linux(read, write, open …)

可以看到Minix的系统调用就干了一件事,消息处理(发送,接收)

那我们我们的getpid()执行到这一步会干什么呢?前面说了,我们是要发送并接收(也就是function参数),也就是说,我们会先send, 把消息发出去,再receive接收消息。

下面我们再来分析一个mini_send, mini_receive函数

mini_send()

在这里插入图片描述
kernel/proc.c [代码已删减]

上面有几句英文注释其实已经解释的很明白了,再详细解释一个,
三个参数:谁发的消息(这里是调用getpid()的用户进程),发给谁(MM), 发什么消息(MM应该执行什么操作等信息)

发送消息时,目标进程有两种情况:1、正在等着你发消息 2、对方没有等待,正在运行(比如你这个进程运行的快,对方还没执行到recive等待接收,你先执行到发送消息了)

如果对方正在等待,把消息复制到目标进程的消息区中(cp_mess), 然后解锁目标进程(消息已经来了,不用等了)
如果对方没有等待,那么就阻塞自己(用户进程)(unready()),把自己加入到对方的等待队列中。(由下面的receive解除阻塞)

mini_recv()

在这里插入图片描述
kernel/proc.c [代码已删减]

参数:caller: 本进程 src: 想要谁的消息(比如我只想要A进程发的消息,B发的消息我不理) m_ptr: 本进程

一个进程想接收消息时,会从本进程的消息发送等待队列中查找,可能有好几个进程给本进程发消息,查找出匹配的后解除发送进程的阻塞(上面解释send函数时,如果对方没有等待,发送方会把自己阻塞),现在消息取出来了,发送方没必要阻塞了。
如果在消息队列中没有找到,就阻塞自己等待消息。(由上面的send解除阻塞)

现在回到我们的getpid(), 进入系统调用sys_call后先执行mini_send(), 注意此时MM也是正在调度队列中的一个进程(早已启动,阻塞等待消息),执行mini_send后,对方在等待,消息复制给MM,解除MM阻塞(上面send分析过程),然后执行mini_recv(), 假定MM刚被解除阻塞,还没执行GETPID任务,那么本用户进程消息等待队列为空(MM还没发消息,还没处理),然后用户进程就阻塞了。

阻塞了???好像我们的getpid()执行到这就结束了,那怎么能行,pid还没呢到呢。
下面就轮到MM出场了。刚刚用户进程解除了MM的阻塞

在Minix启动时,MM就被创建,并开始运行了, 在没有授受到消息时会处于阻塞状态。

如下:

在这里插入图片描述
mm/main.c [代码已删减整理]

在MM进程被创建时(系统启动时就已经创建),就进入这个死循环,在没有消息到来时,会阻塞于receive函数(调用上面的mini_rec函数),等待消息的到来。

刚才的getpid()已经把自己的消息复制给了MM,解除了MM的阻塞,然后等待MM的回应,那么现在MM收到消息(GETPID)后被解除阻塞就开始继续向下执行, *call_vec[mm_call], 就是在执行相应的功能,然后把结果通过消息机制回应给刚才调用getpid的用户进程(reply()函数同send/recv).

刚才用户进程阻塞在mini_recv()了,(执行系统调用时mini_send, 然后mini_recv), 现在收到MM的回应,那就解除了阻塞,继续执行,层层返回,用户进程就得到了返回值。

call_vec是一个存放系统所支持的调用, 存放的是各调用的指针,见下图, 最开始的GETPID那个常量就是这个数组的下标,定位到相应的系统函数:

在这里插入图片描述
mm/table.c

现在用户进程可以继续向下执行了,MM这个系统进程在向用户进程回应了消息之后,继续死循环,然后阻塞在receive()处,准备为下一个请求服务。


Minix源码版本:minix1-master

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值