﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-wuzimian-随笔分类-操作系统</title><link>http://www.cppblog.com/wuzimian/category/19455.html</link><description>努力努力再努力，坚持坚持再坚持</description><language>zh-cn</language><lastBuildDate>Tue, 05 Jun 2012 11:07:31 GMT</lastBuildDate><pubDate>Tue, 05 Jun 2012 11:07:31 GMT</pubDate><ttl>60</ttl><item><title>Linux 创建子进程 fork() exec()</title><link>http://www.cppblog.com/wuzimian/archive/2012/06/05/177655.html</link><dc:creator>吴自勉</dc:creator><author>吴自勉</author><pubDate>Tue, 05 Jun 2012 07:03:00 GMT</pubDate><guid>http://www.cppblog.com/wuzimian/archive/2012/06/05/177655.html</guid><wfw:comment>http://www.cppblog.com/wuzimian/comments/177655.html</wfw:comment><comments>http://www.cppblog.com/wuzimian/archive/2012/06/05/177655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wuzimian/comments/commentRss/177655.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuzimian/services/trackbacks/177655.html</trackback:ping><description><![CDATA[今天复习操作系统，创建子进程这地方还是比较好玩的。<br />创建子进程：  fork（），这个函数的作用是，创建一个子进程，且这个“子进程”完全是“父进程”的一个“拷贝”，只有进程号不一样。<br />相当于把父进程对应的内存拷贝一份到子进程对应的内存区域。<br />exec()这个系统调用，是把一个新的程序转载到进程的内存空间里面去。<br /><br />Linux下编译运行：<br /><div style="background-color:#eeeeee;font-size:13px;BORDER:1px solid #CCCCCC;PADDING-RIGHT: 5px;PADDING-BOTTOM: 4px;PADDING-left: 4px;PADDING-TOP: 4px;WIDTH: 98%;word-break:break-all"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000; ">#include</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">sys</span><span style="color: #000000; ">/</span><span style="color: #000000; ">types.h</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />#include</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">stdio.h</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />#include</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">unistd.h</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /><br /> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> main()<br />{<br />  pid_t pid;<br />  </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> num</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />  pid</span><span style="color: #000000; ">=</span><span style="color: #000000; ">fork();              //   父进程返回的pid是大于零的，而创建的子进程返回的pid是 等于0的，这个机制正好用来区分 父进程和子进程<br />  </span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(pid</span><span style="color: #000000; ">==</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)//子进程<br />  {  <br />     num</span><span style="color: #000000; ">++</span><span style="color: #000000; ">;  <br />     printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">  %d  \n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,num);                 //如果没有 \n ， num是输不出来的，为什么？因为printf()会把num放在缓冲区，暂时不输出，<br />                                                        //而下一行execlp()系统调用会把子进程的内存清空<br />                                                        //装进‘ls’程序，自然的，缓冲区中的num也就没有了。<br />     execlp(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/bin/ls</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,</span><span style="color: #000000; ">"</span><span style="color: #000000; ">ls</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,NULL);          //  将ls程序装进子进程<br />     printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">after execlp:  %d  \n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,num)；//这一行程序始终得不到执行，为什么？因为上一行的execlp()已经将内存清空，<br />                                                          //所以内存中压根就没有这一行代码。<br />  }<br />  </span><span style="color: #0000FF; ">else      //父进程</span><span style="color: #000000; "><br />  {      <br />         wait(NULL);   //等待子进程结束<br />         printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">  %d  </span><span style="color: #000000; ">"</span><span style="color: #000000; ">,num);<br />         printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">\nchild eomplete</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />  }<br />  <br />  <br />}</span></div>其实很简单啦。<br /><br /><br /><br /><br /><br /><img src ="http://www.cppblog.com/wuzimian/aggbug/177655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuzimian/" target="_blank">吴自勉</a> 2012-06-05 15:03 <a href="http://www.cppblog.com/wuzimian/archive/2012/06/05/177655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux系统调用接口、 系统调用例程 和 内核服务例程之间的关系</title><link>http://www.cppblog.com/wuzimian/archive/2012/06/05/177626.html</link><dc:creator>吴自勉</dc:creator><author>吴自勉</author><pubDate>Tue, 05 Jun 2012 02:48:00 GMT</pubDate><guid>http://www.cppblog.com/wuzimian/archive/2012/06/05/177626.html</guid><wfw:comment>http://www.cppblog.com/wuzimian/comments/177626.html</wfw:comment><comments>http://www.cppblog.com/wuzimian/archive/2012/06/05/177626.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wuzimian/comments/commentRss/177626.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wuzimian/services/trackbacks/177626.html</trackback:ping><description><![CDATA[转自：维库电子开发网  http://data.weeqoo.com/2009/2/2009217113941152763.html<br /><br /><font face="Verdana">系统调</font>用接口<font face="Verdana">的主要任务是把进程从用户态切换到内核态。在具有保护机制的计算机系统中，用户必须通过软件中断或陷阱，才能使进程从用户态切换为内核态。</font><font face="Verdana"><br />在i386体系中，Linux的系统调用接口是通过调用软中断指令“int ＄ 
Ox80”使进程从用户态进入内核态的，这个过程也叫做“陷入”。当系统调用接口调用软中断指令“int ＄ 
Ox80”时，这个指令会发生一个中断向量码为128的中断请求，并在中断响应过程中将进程由用户态切</font><font face="Verdana">换为内核态。</font><font face="Verdana"><br /><br />因为Linux只允许系统调用接口使用128这一个软中断向量，这也就意味着<font color="#FF0000">所有的</font>系统调用接口必须<font color="#FF0000">共享</font>这一个中断通道，并在<font color="#FF0000">同一个</font>中断服务例程中（这里的中断服务例程就是对应于中断号为128的中断服务例程，通过查中断向量表得到）调用<font color="#FF0000">不同的</font>内核服务例程，所以，系统调用接口除了要引发“int ＄ 
Ox80”软中断之外，为了进人内核后能调用不同的内核服务例程，还要提供识别内核服务例程的参数，这个参数叫做“系统调用号”。也就是说，所有可为进程提供服务的内核服务例程都应具有一个唯一的系统调用号。当然，系统调用接口还应为内核服务例程准各必要的参数。</font><p><font face="Verdana">　　综上所述，系统调用接口需要完成以下几个任务：</font></p><p><font face="Verdana">　　●</font><font face="Verdana">要保护用户态的现场，即把处理器的用户态运行环境保护到进程的内核堆栈。</font></p><p><font face="Verdana">　　●</font><font face="Verdana">为内核服务例程准备参数（包括“系统调用号”--linux中规定用寄存器EAX传递），并定义返回值的存储位置。</font></p><p><font face="Verdana">　　●</font><font face="Verdana">用软中断指令“int ＄ Ox80”发生一个中断向量码为128的中断请求，以使进程进入内核态。</font></p><p><font face="Verdana">　　●跳转到系统调用例程。</font></p><p><font face="Verdana">　　●系统调用例程结束后返回。</font></p><p><font face="Verdana">　　系统调用例程是系统提供的一个通用的汇编语言程序．其实它是一个中断向量为128的中断服务程序，其入口为system_call。它应完成的任务有：</font></p><p><font face="Verdana">　　●接受系统调用接口的参数。</font></p><p><font face="Verdana">　　●根据系统调用号，转向对应的内核服务例程，并将相关参数传递给内核服务例程。</font></p><p><font face="Verdana">　　●在内核服务例程结束后，自中断返田到系统凋甩接口.</font></p><p><font face="Verdana">　　系统调用的过程如图所示。</font></p><p><font face="Verdana">　　从图中可以看到，系统调用接口是用高级语言来编写的，而通过调用中断指令陷入内核后的系统调用例程（即</font><font face="Verdana">中断向量为128的中断服务程序</font><font face="Verdana">）则是用汇编语言编写的。</font></p><p><font face="Verdana">　　</font>为了通过系统调用号来调用不同的内核服务例程，系统必须维护一个系统调用表，这个表实质上就是系统调用号与内核服务函数的对照表。Linux是用数组sys_call_tabl来作为这个表的，在这个表的每个表项中存放着对应内核服务例程的指针，而该表项的下标就是该内核服务例程的系统调用号。Linux规定，在i386体系中，系统调用号由处理器的寄存器eax来传递。下图中的系统调用例程，通过查EAX中的系统调用号与sys_call_tabl来确定相应的内核服务函数。<br /></p><p><font face="Verdana">                                                                                       <font color="#FF0000">图1</font><br /></font></p><font face="Verdana"><p align="center"><img alt="系统调用的处理过程" src="http://www.weeqoo.com/UploadFile/2009/2/17/200902171139417901.gif" border="0" /></p></font><p><font face="Verdana"><br /><br />　　                                                     <br /></font></p><p><font face="Verdana">　　系统调用表Sys_call_table的部分内容列举如下：</font></p><p><font face="Verdana">                                                          <font color="#FF0000">                                 图二</font><br /></font></p><font face="Verdana"><p align="center"><img src="http://www.weeqoo.com/UploadFile/2009/2/17/200902171139413887.gif" border="0" /></p></font><br />老吴曰：  以上所说的是我们在用户程序中直接调用系统调用的执行步骤，比如说，我们在程序中调用 fork()这个系统调用来产生一个子进程，那么执行过程就是上面图一的几个步骤，而如果我们在程序中调用的是 比如C语言的 库函数printf()，那么执行过程又是怎样的呢？ 这个问题要牵扯到编译器的事。比如说，在linux上，我们用gcc命令编译一个程序，printf()这个库函数最终会被编译成调用write()这个系统调用，我们也可以用strace命令跟踪程序，也会发现printf()函数最终是通过write()系统调用来实现它的功能的。         而如果我们在windows上编译printf(),那么printf()就会被编译成windows上相应的系统<br />调用。<br /><br /><br /><br /><img src ="http://www.cppblog.com/wuzimian/aggbug/177626.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wuzimian/" target="_blank">吴自勉</a> 2012-06-05 10:48 <a href="http://www.cppblog.com/wuzimian/archive/2012/06/05/177626.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>