- 4
- 0
- 约1.95千字
- 约 11页
- 2016-11-27 发布于河南
- 举报
进程消息系统中消息发送的全过程
进程消息系统中消息发送的全过程 MINIX3的C库中(src/lib/i386/rts/_ipc.s文件中)提供了函数:Send(dest,message) 用来对消息进行发送,这些库函数仅供驱动程序、服务器进程及用户系统调用库函数使用,而不供用户随意使用,用户进程只能在系统调用库函数中向PM、FS、RS服务器发送消息并等待应答,相应消息的类型也已经由PM、FS、RS预先定义好。 第一步:send(dst,message)将函数调用的参数dst, message 以及消息通信的功能号SEND=1保存在寄存器eax、ebx、ecx中,并通过int SYSVEC(int 33)进入核心态。 __send: push ebp mov ebp, esp push ebx mov eax, SRC_DST(ebp) ! eax 目标地址 mov ebx, MESSAGE(ebp) ! ebx 放消息指针 mov ecx, SEND ! SEND标识 int SYSVEC ! trap调度mpx386.s中的_s_call pop ebx pop ebp ret 第二步:执行33号中断处理程序s_call(06649行)。在转到06649行执行前,先将栈从用户态切换到核心态下,现行栈位于用户进程的proc的现场保存区中,栈中已压入原来用户栈的栈顶指针ss,esp,还有断点的eflags和cs,eip,此时ss选择的是核心数据段,但ds选择的还是用户数据段。下面的程序主要是保存断点其余的现场信息到proc中,包括esi,edi,ds,es,fs,gs;但eax,ecx,edx,ebx等信息不用保存;然后将ds也指到核心数据段;再启用真正的核心栈;并先将函数参数压栈,再去调用sys_call(function, src_dest, m_ptr)。 第三步:执行07480行sys_call(function, src_dest, m_ptr)。可以看出,对发送来讲,在发送前要做一系列的检查: 首先要检查当前进程是否有向其他进程发送消息的特权(如用户进程就没该特权,用户进程只有发送并等应答,或者是ECHO功能),另外还规定不能单向地发消息给任务。 看对方p_nr是否合法; 看消息地址正确与否(是否完全在自己的数据空间中) 看当前进程是否能向目的进程发送消息(如用户进程,只能想服务器进程发送消息并等应答) 最后调用mini_send()真正完成发送过程。 第四步:执行07591行mini_send(caller_ptr, dst, m_ptr, flags)。它首先检查此次发送是否会引起死锁,然后看接收者是否正在等待发送进程的消息,如果是则直接将消息从发送进程的数据空间复制到接收进程的数据空间(采用特权级别为1的类似于核心ES的段描述符进行数据的复制),否则将发送进程加到接收进程的p_caller_q队列中,并将发送进程置成阻塞状态(但目前仍然是发送进程正在执行)。mini_send()过程中没出错则返回OK(即使阻塞了,发送还是成功的),否则则返回错误码(如检测到可能发生死锁时返回ELOCKED。 最后返回到sys_call()中的07565行。 第五步:从mini_send(caller_ptr, dst, m_ptr, flags)返回到07565行继续执行sys_call(),并将mini_send()的返回值作为sys_call()的返回值(发送过程没出错时返回OK,否则返回错误码)返回到06674行33号中断处理程序内。C函数的返回值是被保存在eax寄存器中的。 第六步:继续从 06674行起执行33号中断处理程序内的指令,它首先将sys_call()的返回值保存到进程现场保存区保存eax值的地方(06674行),然后执行restart进行中断返回。 第七步:执行restart进行CPU的分派,CPU可能分派给原来正在执行的进程,也可能分派给新的进程(比如:发送进程因为发送而阻塞,则在“第四步”执行mini_send()时会将它从就绪队列中移除,此时dequeue()会选择新进程,next_ptr将指向选中的新进程)。 第八步:发送进程得到CPU时,它将从send()中INT SYSVEC指令后面的那条指令继续执行,它从栈中恢复ebx,ebp,然后执行函数返回指令。函数的返回值就是保存eax中有mini_send()返回的返回值。 * *
原创力文档

文档评论(0)