中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档 | 网通镜像
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > C/C++
fork、exec系列与system、popen区别
作者:未知 时间:2005-09-13 19:20 出处:ChinaUnix.net 责编:chinaitpower
              摘要:fork、exec系列与system、popen区别

两个问题,请大家一起讨论。


1、fork和exec系列调用前后,进程在内存的“数据段”,“堆栈段”和“代码段”有什么不同?除此以外,fork和exec系列调用还有什么区别?



2、system和popen有什么区别?都常用在什么场合?

 无双 回复于:2003-05-06 20:00:26
文件描述字在fork和exec中都是保持打开的
我学理这个比较重要

 无双 回复于:2003-05-06 20:01:01
有点跑题了
但是还是请大家思考一下其它比较重要的地方

 uiibono 回复于:2003-05-06 22:42:38
对于fork():
1、子进程复制父进程的所有进程内存到其内存地址空间中。父、子进程的
 “数据段”,“堆栈段”和“代码段”完全相同,即子进程中的每一个字节都 
  和父进程一样。
2、子进程的当前工作目录、umask掩码值和父进程相同,fork()之前父进程
  打开的文件描述符,在子进程中同样打开,并且都指向相同的文件表项。
3、子进程拥有自己的进程ID。

对于exec():
1、进程调用exec()后,将在同一块进程内存里用一个新程序来代替调用
  exec()的那个进程,新程序代替当前进程映像,当前进程的“数据段”,
 “堆栈段”和“代码段”背新程序改写。
2、新程序会保持调用exec()进程的ID不变。
3、调用exec()之前打开打开的描述字继续打开(好像有什么参数可以令打开
  的描述字在新程序中关闭)

 蓝色键盘 回复于:2003-05-07 09:28:42
1、fork()
    一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。

   事实上,目前大多数的unix系统在实现上并没有作真正的copy。一般的,CPU都是以“页”为单位分配空间的,象INTEL的CPU,其一页在通常情况下是4K字节大小,而无论是数据段还是堆栈段都是由许多“页”构成的,fork函数复制这两个段,只是“逻辑”上的,并非“物理”上的,也就是说,实际执行fork时,物理空间上两个进程的数据段和堆栈段都还是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区 
别,系统就将有区别的“页”从物理上也分开。系统在空间上的开销就可以达到最小。 

2、对于exec系列函数
    一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。不过exec类函数中有的还允许继承环境变量之类的信息,这个通过exec系列函数中的一部分函数的参数可以得到。

 蓝色键盘 回复于:2003-05-07 12:41:54
子进程继承了父进程的几乎所有的属性,包括如下:  
. 实际UID,GID和有效UID,GID.  
. 环境变量.  
. 附加GID.  
. 调用exec()时的关闭标志.  
. UID设置模式比特位.  
. GID设置模式比特位.  
. 进程组号.  
. 会话ID.  
. 控制终端.  
. 当前工作目录.  
. 根目录.  
. 文件创建掩码UMASK.  
. 文件长度限制ULIMIT.  
. 预定值, 如优先级和任何其他的进程预定参数, 根据种类不同决定是否可以继承.  
. 还有一些其它属性.  


但子进程也有与父进程不同的属性,这点很重要: 
. 进程号, 子进程号不同与任何一个活动的进程组号.  
. 父进程号.  
. 子进程继承父进程的文件描述符或流时,具有自己的一个拷贝并且与父进程和其它子进程共享该资源.  
. 子进程的用户时间和系统时间被初始化为0.  
. 子进程的超时时钟设置为0.  
. 子进程的信号处理函数指针组置为空.  
. 子进程不继承父进程的记录锁. 

 无双 回复于:2003-05-07 12:56:34
上面这些比较重要

同时编程中也应该注意是继承了什么内容
如不用的文件描述符在fork后应该关闭

 nile 回复于:2003-05-07 17:06:19
基本上 fork + exec = system()

比如你的一个进程想运行几个外部程序, 比如sqlplus什么的。 可以fork子进程, 然后在子进程中exec(sqlplus)

我觉得基本和在你的进程中调用system()差不多, 更灵活? 谁给说说看。

 无双 回复于:2003-05-07 17:51:39
使用system不是很安全,并且需要起一个shell
增加系统开锁

 wangrujun 回复于:2003-06-26 11:18:20
[quote:9f644af7d4="蓝色键盘"]
1、fork()
    一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。不过exec类函数中有的还允许继承环境变量之类的信息,这个通过exec系列函数中的一部分函数的参数可以得到。 
[/quote:9f644af7d4]     

如键盘兄所述,exec 会代替原先的进程,但是为什么exec之后,仍然可以返回呢?

比如下面的程序,在第一个exec后,程序仍然可以继续运行。无论是出错处理或者是再执行下一个exec,表现都非常棒。

请教一下,是不是在exec时,系统仍然为进程设置了堆栈以保存原来进程的所有信息呢?是由内核实现的吗?与平常的function有什么不同呢?

如果我理解正确的话,我想这个也能算是exec与fork的区别之一:exec时,内核需要保存堆栈.fork时,内核不需要保存堆栈。

如果上面的说法正确的话,那么exec一定比fork要慢。
因为fork时,系统只要拷贝子进程的一次。而exec时,首先要把原来进程入栈,另外还需要把新进程拷贝到原来的进程之中。
当exec返回时,还有一个原来进程出栈的操作,也需要切换到内核态执行。

其三,如果exec的进程比原进程要大的话,还需要重新申请内存。如果比原来进程要小得多的话,是不是也浪费了相当多的内存?

请键盘和无双两位老大多多指教呀。

还有个建议,这个帖子应该加精的。大家好查看的。
[code:1:9f644af7d4]
#include        <sys/types.h>
#include        <sys/wait.h>
#include        "ourhdr.h"

char    *env_init[] = { "USER=unknown", "PATH=/tmp", NULL };

int
main(void)
{
        pid_t   pid;

        if ( (pid = fork()) < 0)
                err_sys("fork error");
        else if (pid == 0) {    /* specify pathname, specify environment */
                if (execle("/usr/home/mike/study/C/APUE/echoarg",
                                   "echoarg", "myarg1", "MY ARG2", (char *) 0,
                                   env_init) < 0)
                        err_sys("execle error");
        }
        if (waitpid(pid, NULL, 0) < 0)
                err_sys("wait error");

        if ( (pid = fork()) < 0)
                err_sys("fork error");
        else if (pid == 0) {    /* specify filename, inherit environment */
                if (execlp("echoarg",
                                   "echoarg", "only 1 arg", (char *) 0) < 0)
                        err_sys("execlp error");
        }
        exit(0);
}[/code:1:9f644af7d4]

 syshunter 回复于:2003-06-26 14:03:15
补充下popen,很长时间没接触这东西,我只能凭着印象说:
popen(char *command,char *type);它创建一个管道,创建一个子进程用SHELL来执行command,按照type指定管道流的方向,比如读或写,这其实等同于用pipe创建管道实现父进程与子进程之间通信。

 tinywind 回复于:2003-06-26 14:13:02
[quote:6dd8b97d83="无双"]使用system不是很安全,并且需要起一个shell
增加系统开锁[/quote:6dd8b97d83] 
system()需要调shell吗?我认为是fork后直接exec。

 wangrujun 回复于:2003-06-26 16:09:02
system是由shell实现的。并不是ansi标准函数。所以需要调shell来实现。应该是这样吧
所以不同系统应该不一样呀,如果提供了专门的C函数接口,应该是fork 后exec。

不知道这样说对不对呀

 无双 回复于:2003-06-26 18:46:53
看看它的man 
是启动一个shell

 肉球 回复于:2003-06-27 09:03:40
[quote:5930f82591="无双"]看看它的man 
是启动一个shell[/quote:5930f82591]     

是呀,man里面说的很清楚。

 wangrujun 回复于:2003-06-27 09:12:01
请问无双版主,我在上面所说的关于fork和exec的区别对吗?主要是入栈出栈那块。

 无双 回复于:2003-06-27 11:14:39
exec的话并不会复制旧进程信息
而是用新进程映象直接替换掉旧进程的地址空间(包括数据与代码段)

但是进程描述表中的文件描述符没有关闭
还有其它旧进程内信息没有关闭(如信号量等 ,这可以看它们的man 里面有说明)

 wangrujun 回复于:2003-06-27 12:57:37
请教无双:

    既然数据段和代码段都被新进程映象直接替换,而且内核或exec都没有复制旧进程信息,那么 exec执行完后,为什么原来的进程还存在,并且可以继续执行代码呢?
    进程描述表中的文件描述符和旧进程内其它信息没有关闭,这句话的意思是不是说,当exec执行完成,内核再从磁盘上载入原来旧进程的信息(数据段和代码段),然后根据原先的信息继续执行代码,是这样吗?

 无双 回复于:2003-06-27 14:21:49
我试试看先

 无双 回复于:2003-06-27 14:34:52
pid=0是子进程
子进程执行完后会退出(因为有exec ,所以只会执行exec部分 不会执行后面的 后面的只是父进程在执行)

 无双 回复于:2003-06-27 14:35:29
我没有试

现在还没有时间

如果你的不是这样那么请跟贴

 wangrujun 回复于:2003-06-27 15:11:50
啊,我现在明白了。
其实我前面的说法都错了。系统没有保持任何原进程的信息。

还是因为粗心没注意到fork后面实际上子进程就退出了。真不好意思。

 qjlemon 回复于:2003-06-27 15:28:36
在OCI程序里打开一个数据库连接以后再fork,子进程再关闭这个连接,这时候父进程也会断开。郁闷。 。。
可见fork以后父子进程的这种很隐蔽的关联很值得注意

 wangrujun 回复于:2003-06-27 16:15:51
是不是父进程非法操作啦

 qjlemon 回复于:2003-06-27 16:34:48
倒也没有非法操作,只是报了个操作失败。

 无双 回复于:2003-06-27 16:52:31
可能是ORACLE对方的连接根据IP和端口来判断

FORK后两个进程使用的还是同一个连接
但是一个CLOSE时对端也会对应CLOSE 连接

 无双 回复于:2003-06-27 16:53:03
不过这个问题确实应该注意

 qjlemon 回复于:2003-06-27 16:55:53
这个是否是因为socket在fork之后其中一个进程close而影响到另一个进程?
但我认为还有一种可能是OCI的实现方式是用一个类似于session id之类的东西,不管你是多少个进程,反正你用这个session id关闭了连接,那这个id就再不能用了

 无双 回复于:2003-06-27 17:03:11
也有可能

 direstrait 回复于:2003-07-06 12:54:27
unix高级环境编程里面的一个不带信号处理的system版本
[code:1:c12fa5e6d4]
int system(const char *cmdline)
{
        pid_t pid;
        int status;
        if(cmdline=NULL)
                return (1);
        if((pid=fork())<0)
        {
                status=-1;
        }else if(pid==0)
        {
                execl("/bin/sh","sh","-c",cmdline,(char *)0);
                _exit(127);
        }else
        {
                while(waitpid(pid,&status,0)<0)
                if(errno!=EINTR)
                        {status=-1;
                         break;
                        }
        }
        return(status);
}
[/code:1:c12fa5e6d4]
可见在unix环境下,是需要shell的.

 bird_ken 回复于:2004-03-03 14:35:41
还有一个区别就是当用fork创建出来的子进程, 当子进程运行完以后(即自动消亡), 在进程表里面还是有这些消亡子进程的项, 而用system创建出来的子进程消亡以后就不会滞留在进程表里面.

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有