<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="http://gentoo-zh.org/extern.php?action=feed&amp;tid=421&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo中文社区 / IPC]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=421</link>
		<description><![CDATA[IPC 最近发表的帖子。]]></description>
		<lastBuildDate>Thu, 01 Sep 2022 06:09:20 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[IPC]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=427#p427</link>
			<description><![CDATA[<p>IPC是Inter-Process Communication的缩写，直译为进程间通信，说白了就是进程间发消息。我们在上一节中把这种消息传递比作邮政系统，但实际上这种比喻并不全对。有的消息机制是很像收发邮件的，这种叫做异步IPC，意思是说，发信者发完就去干别的了，收信者也一样，看看信箱里没信，也不坐在旁边傻等。而有另一种消息机制正好相反，被称为同步IPC，它不像邮寄，倒像接力赛，发送者一直等到接收者收到消息才肯放手，接收者也一样，接不到就一直等着，不干别的。<br />当然你可以把同步IPC也比作邮寄，只不过寄信的人从把信投到信箱里的那一刻开始，就住在邮局不走了，其他什么也不干了，就等着邮局说：“哥们儿，你的信对方已经收到了，放心回家吧！”这才恋恋不舍地离开。收信的也一样，一旦决定收信，就守在自家信箱前面不走了，一直等，连觉也不睡，望穿秋水，等信拿在手里了，这才回屋，每收一次信，就得瘦个十几斤。<br />我们都是性情中人，我们选择傻等，或曰同步IPC。<br />同步IPC有若干的好处，比如：<br />• 操作系统不需要另外维护缓冲区来存放正在传递的消息；<br />• 操作系统不需要保留一份消息副本；<br />• 操作系统不需要维护接收队列（发送队列还是需要的）；<br />• 发送者和接收者都可在任何时刻清晰且容易地知道消息是否送达；<br />• 从实现系统调用的角度来看，同步IPC更加合理──当使用系统调用时，我们的确需要等待内核返回结果之后再继续。<br />这些特性读者可能无法一下子全部明白，不要紧，我们接下来写完代码，你就全都明白了。<br />实现IPC<br />Minix的IPC机制我们已经明白了，它的核心乃在于“int SYSVEC”这个软中断以及与之对应的sys_call()这个函数。增加一个系统调用对我们来讲已是信手拈来的事，按照表7.6一步一步来就好了。我们把这个新的系统调用起名为sendrec。sendrec和sys_sendrec的函数体分别见下面两段代码：<br />25 sendrec:<br />26&#160; &#160; &#160; &#160; &#160;mov eax, _NR_sendrec<br />27&#160; &#160; &#160; &#160; &#160;mov ebx, [esp + 4] ; function<br />28&#160; &#160; &#160; &#160; &#160;mov ecx, [esp + 8] ; src_dest<br />29&#160; &#160; &#160; &#160; &#160;mov edx, [esp + 12] ; p_msg<br />30&#160; &#160; &#160; &#160; &#160;int INT_VECTOR_SYS_CALL<br />31&#160; &#160; &#160; &#160; &#160;ret<br />53 /*****************************************************************************<br />54 * sys_sendrec<br />55 *****************************************************************************/<br />56 /**<br />57 * &lt;Ring 0&gt; The core routine of system call ‘sendrec()’.<br />58 *<br />59 * @param function SEND or RECEIVE<br />60 * @param src_dest To/From whom the message is transferred.<br />61 * @param m Ptr to the MESSAGE body.<br />62 * @param p The caller proc.<br />63 *<br />64 * @return Zero if success.<br />65 *****************************************************************************/<br />66 PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p)<br />67 {<br />68&#160; &#160; &#160; &#160; &#160;assert(k_reenter == 0); /* make sure we are not in ring0 */<br />69&#160; &#160; &#160; &#160; &#160;assert((src_dest &gt;= 0 &amp;&amp; src_dest &lt; NR_TASKS + NR_PROCS) ||<br />70&#160; &#160; &#160; &#160; &#160;src_dest == ANY ||<br />71&#160; &#160; &#160; &#160; &#160;src_dest == INTERRUPT);<br />72<br />73&#160; &#160; &#160; &#160; &#160;int ret = 0;<br />74&#160; &#160; &#160; &#160; &#160;int caller = proc2pid(p);<br />75&#160; &#160; &#160; &#160; &#160;MESSAGE* mla = (MESSAGE*)va2la(caller, m);<br />76&#160; &#160; &#160; &#160; &#160;mla-&gt;source = caller;<br />77<br />78&#160; &#160; &#160; &#160; &#160;assert(mla-&gt;source != src_dest);<br />79<br />80&#160; &#160; &#160; &#160; &#160;/**<br />81&#160; &#160; &#160; &#160; &#160;* Actually we have the third message type: BOTH. However, it is not<br />82&#160; &#160; &#160; &#160; &#160;* allowed to be passed to the kernel directly. Kernel doesn’t know<br />83&#160; &#160; &#160; &#160; &#160;* it at all. It is transformed into a SEND followed by a RECEIVE<br />84&#160; &#160; &#160; &#160; &#160;* by ‘send_recv()’.<br />85&#160; &#160; &#160; &#160; &#160;*/<br />86&#160; &#160; &#160; &#160; &#160;if (function == SEND) {<br />87&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;ret = msg_send(p, src_dest, m);<br />88&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;if (ret != 0)<br />89&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;return ret;<br />90&#160; &#160; &#160; &#160; &#160;}<br />91&#160; &#160; &#160; &#160; &#160;else if (function == RECEIVE) {<br />92&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;ret = msg_receive(p, src_dest, m);<br />93&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;if (ret != 0)<br />94&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;return ret;<br />95&#160; &#160; &#160; &#160; &#160;}<br />96&#160; &#160; &#160; &#160; &#160;else {<br />97&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;panic(”{sys_sendrec}␣invalid␣function:␣”<br />98&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;”%d␣(SEND:%d,␣RECEIVE:%d).”, function, SEND, RECEIVE);<br />99&#160; &#160; &#160; &#160; &#160;}<br />100&#160; &#160; &#160; &#160;<br />101&#160; &#160; &#160; &#160; &#160;return 0;<br />102 }<br />表7.6的最后一步中提到，如果参数个数与以前的系统调用比有所增加，则需要修改kernel.asm中的sys_call。额外要注意，我们新加的参数是通过edx这个参数传递的，而save这个函数中也用到了寄存器dx，所以我们同时需要修改save，见如下代码：<br />&#160; &#160;311 ; =============================================================================<br />&#160; &#160;312 ; save<br />&#160; &#160;313 ; =============================================================================<br />&#160; &#160;314 save:<br />&#160; &#160;315&#160; &#160; &#160; &#160; &#160;pushad ; ‘.<br />&#160; &#160;316&#160; &#160; &#160; &#160; &#160;push ds ; |<br />&#160; &#160;317&#160; &#160; &#160; &#160; &#160;push es ; | 保存原寄存器值<br />&#160; &#160;318&#160; &#160; &#160; &#160; &#160;push fs ; |<br />&#160; &#160;319&#160; &#160; &#160; &#160; &#160;push gs ; /<br />&#160; &#160;320<br />&#160; &#160;321&#160; &#160; &#160; &#160; &#160;;; 注意，从这里开始，一直到‘mov esp, StackTop’，中间坚决不能用push/pop 指令，<br />&#160; &#160;322&#160; &#160; &#160; &#160; &#160;;; 因为当前esp 指向proc_table 里的某个位置，push 会破坏掉进程表，导致灾难性后果！<br />&#160; &#160;323<br />=&gt; 324&#160; &#160; &#160; &#160; &#160;mov esi, edx ; 保存edx，因为edx 里保存了系统调用的参数<br />&#160; &#160;325&#160; &#160; &#160; &#160; &#160;;（没用栈，而是用了另一个寄存器esi）<br />&#160; &#160;326&#160; &#160; &#160; &#160; &#160;mov dx, ss<br />&#160; &#160;327&#160; &#160; &#160; &#160; &#160;mov ds, dx<br />&#160; &#160;328&#160; &#160; &#160; &#160; &#160;mov es, dx<br />&#160; &#160;329&#160; &#160; &#160; &#160; &#160;mov fs, dx<br />&#160; &#160;330<br />=&gt; 331&#160; &#160; &#160; &#160; &#160;mov edx, esi ; 恢复edx<br />&#160; &#160;332<br />&#160; &#160;333&#160; &#160; &#160; &#160; &#160;mov esi, esp&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;;esi = 进程表起始地址<br />&#160; &#160;334<br />&#160; &#160;335&#160; &#160; &#160; &#160; &#160;inc dword [k_reenter]&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; ;k_reenter++;<br />&#160; &#160;336&#160; &#160; &#160; &#160; &#160;cmp dword [k_reenter], 0&#160; &#160; &#160; &#160; &#160; &#160; &#160;;if(k_reenter ==0)<br />&#160; &#160;337&#160; &#160; &#160; &#160; &#160;jne .1&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;;{<br />&#160; &#160;338&#160; &#160; &#160; &#160; &#160;mov esp, StackTop&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; ; mov esp, StackTop &lt;--切换到内核栈<br />&#160; &#160;339&#160; &#160; &#160; &#160; &#160;push restart&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;; push restart<br />&#160; &#160;340&#160; &#160; &#160; &#160; &#160;jmp [esi + RETADR - P_STACKBASE]&#160; &#160; &#160;; return;<br />&#160; &#160;341 .1:&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; ;} else { 已经在内核栈，不需要再切换<br />&#160; &#160;342&#160; &#160; &#160; &#160; &#160;push restart_reenter&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;; push restart_reenter<br />&#160; &#160;343&#160; &#160; &#160; &#160; &#160;jmp [esi + RETADR - P_STACKBASE]&#160; &#160; &#160;; return;<br />&#160; &#160;344&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; ;}<br />&#160; &#160;345<br />&#160; &#160;346<br />&#160; &#160;347 ; =============================================================================<br />&#160; &#160;348 ; sys_call<br />&#160; &#160;349 ; =============================================================================<br />&#160; &#160;350 sys_call:<br />&#160; &#160;351&#160; &#160; &#160; &#160; &#160;call save<br />&#160; &#160;352<br />&#160; &#160;353&#160; &#160; &#160; &#160; &#160;sti<br />&#160; &#160;354&#160; &#160; &#160; &#160; &#160;push esi<br />&#160; &#160;355<br />&#160; &#160;356&#160; &#160; &#160; &#160; &#160;push dword [p_proc_ready]<br />=&gt; 357&#160; &#160; &#160; &#160; &#160;push edx<br />&#160; &#160;358&#160; &#160; &#160; &#160; &#160;push ecx<br />&#160; &#160;359&#160; &#160; &#160; &#160; &#160;push ebx<br />&#160; &#160;360&#160; &#160; &#160; &#160; &#160;call [sys_call_table + eax * 4]<br />=&gt; 361&#160; &#160; &#160; &#160; &#160;add esp, 4 * 4<br />&#160; &#160;362<br />&#160; &#160;363&#160; &#160; &#160; &#160; &#160;pop esi<br />&#160; &#160;364&#160; &#160; &#160; &#160; &#160;mov [esi + EAXREG - P_STACKBASE], eax<br />&#160; &#160;365&#160; &#160; &#160; &#160; &#160;cli<br />&#160; &#160;366<br />&#160; &#160;367&#160; &#160; &#160; &#160; &#160;ret<br />sys_sendrec()这个函数被设计得相当简单，它可以描述为：把SEND消息交给msg_send()处理，把RECEIVE消息交给msg_receive()处理。<br />msg_send()和msg_receive()这两个函数我们过一会儿细细分解，先来看看之前没出现过的assert()和panic()。这两个函数虽然起的是辅助作用，但绝对不是可有可无，因为在我们接下来要处理的消息收发中，有一些编程细节还真容易让人迷糊，这时候assert()就大显神威了，它会在错误被放大之前通知你。panic()的作用也类似，用于通知你发生了严重的错误。</p>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Thu, 01 Sep 2022 06:09:20 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=427#p427</guid>
		</item>
	</channel>
</rss>
