fork&exec

来源:百度文库 编辑:神马文学网 时间:2024/04/30 13:38:02
中国IT动力,最新最全的IT技术教程最新100篇 |推荐100篇 |专题100篇 |排行榜 |搜索 |在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 未整理篇 | 技术讨论
ASPJSPHP工程ASP.NET网站建设UMLJ2EESUN.NETVCVBVFP网络维护数据库DB2SQL2000OracleMysql服务器Win2000OfficeCDreamWeaverFireWorksFlashPhotoShop上网宝典CorelDraw协议大全网络安全微软认证
当前位置: >程序开发 >编程语言 >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        
#include        
#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创建出来的子进程消亡以后就不会滞留在进程表里面.
[ 收藏此页到VIVI |Younote |365Key |Blogchina |POCO |人人 |亿友 |我摘 |和讯 |拇指 |天极 ]
首页 |投资与合作 |服务条款 |隐私政策 |收藏本站 |设为首页 |新用户注册 |免责声明 |使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有
_xyz