Chinaunix

标题: fork、exec系列与system、popen区别 [打印本页]

作者: 蓝色键盘    时间: 2003-05-06 19:52
标题: fork、exec系列与system、popen区别
两个问题,请大家一起讨论。


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



2、system和popen有什么区别?都常用在什么场合?
作者: 无双    时间: 2003-05-06 20:00
标题: fork、exec系列与system、popen区别
文件描述字在fork和exec中都是保持打开的
我学理这个比较重要
作者: 无双    时间: 2003-05-06 20:01
标题: fork、exec系列与system、popen区别
有点跑题了
但是还是请大家思考一下其它比较重要的地方
作者: uiibono    时间: 2003-05-06 22:42
标题: fork、exec系列与system、popen区别
对于fork():
1、子进程复制父进程的所有进程内存到其内存地址空间中。父、子进程的
“数据段”,“堆栈段”和“代码段”完全相同,即子进程中的每一个字节都
  和父进程一样。
2、子进程的当前工作目录、umask掩码值和父进程相同,fork()之前父进程
  打开的文件描述符,在子进程中同样打开,并且都指向相同的文件表项。
3、子进程拥有自己的进程ID。

对于exec():
1、进程调用exec()后,将在同一块进程内存里用一个新程序来代替调用
  exec()的那个进程,新程序代替当前进程映像,当前进程的“数据段”,
“堆栈段”和“代码段”背新程序改写。
2、新程序会保持调用exec()进程的ID不变。
3、调用exec()之前打开打开的描述字继续打开(好像有什么参数可以令打开
  的描述字在新程序中关闭)
作者: 蓝色键盘    时间: 2003-05-07 09:28
标题: fork、exec系列与system、popen区别
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
标题: fork、exec系列与system、popen区别
子进程继承了父进程的几乎所有的属性,包括如下: 
. 实际UID,GID和有效UID,GID. 
. 环境变量. 
. 附加GID. 
. 调用exec()时的关闭标志. 
. UID设置模式比特位. 
. GID设置模式比特位. 
. 进程组号. 
. 会话ID. 
. 控制终端. 
. 当前工作目录. 
. 根目录. 
. 文件创建掩码UMASK. 
. 文件长度限制ULIMIT. 
. 预定值, 如优先级和任何其他的进程预定参数, 根据种类不同决定是否可以继承. 
. 还有一些其它属性. 


但子进程也有与父进程不同的属性,这点很重要:
. 进程号, 子进程号不同与任何一个活动的进程组号. 
. 父进程号. 
. 子进程继承父进程的文件描述符或流时,具有自己的一个拷贝并且与父进程和其它子进程共享该资源. 
. 子进程的用户时间和系统时间被初始化为0. 
. 子进程的超时时钟设置为0. 
. 子进程的信号处理函数指针组置为空. 
. 子进程不继承父进程的记录锁. 
作者: 无双    时间: 2003-05-07 12:56
标题: fork、exec系列与system、popen区别
上面这些比较重要

同时编程中也应该注意是继承了什么内容
如不用的文件描述符在fork后应该关闭
作者: nile    时间: 2003-05-07 17:06
标题: fork、exec系列与system、popen区别
基本上 fork + exec = system()

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

我觉得基本和在你的进程中调用system()差不多, 更灵活? 谁给说说看。
作者: 无双    时间: 2003-05-07 17:51
标题: fork、exec系列与system、popen区别
使用system不是很安全,并且需要起一个shell
增加系统开锁
作者: wangrujun    时间: 2003-06-26 11:18
标题: fork、exec系列与system、popen区别
原帖由 "蓝色键盘" 发表:

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

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

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

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

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

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

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

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

还有个建议,这个帖子应该加精的。大家好查看的。

  1. #include        <sys/types.h>;
  2. #include        <sys/wait.h>;
  3. #include        "ourhdr.h"

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

  5. int
  6. main(void)
  7. {
  8.         pid_t   pid;

  9.         if ( (pid = fork()) < 0)
  10.                 err_sys("fork error");
  11.         else if (pid == 0) {    /* specify pathname, specify environment */
  12.                 if (execle("/usr/home/mike/study/C/APUE/echoarg",
  13.                                    "echoarg", "myarg1", "MY ARG2", (char *) 0,
  14.                                    env_init) < 0)
  15.                         err_sys("execle error");
  16.         }
  17.         if (waitpid(pid, NULL, 0) < 0)
  18.                 err_sys("wait error");

  19.         if ( (pid = fork()) < 0)
  20.                 err_sys("fork error");
  21.         else if (pid == 0) {    /* specify filename, inherit environment */
  22.                 if (execlp("echoarg",
  23.                                    "echoarg", "only 1 arg", (char *) 0) < 0)
  24.                         err_sys("execlp error");
  25.         }
  26.         exit(0);
  27. }
复制代码

作者: syshunter    时间: 2003-06-26 14:03
标题: fork、exec系列与system、popen区别
补充下popen,很长时间没接触这东西,我只能凭着印象说:
popen(char *command,char *type);它创建一个管道,创建一个子进程用SHELL来执行command,按照type指定管道流的方向,比如读或写,这其实等同于用pipe创建管道实现父进程与子进程之间通信。
作者: tinywind    时间: 2003-06-26 14:13
标题: fork、exec系列与system、popen区别
原帖由 "无双" 发表:
使用system不是很安全,并且需要起一个shell
增加系统开锁

system()需要调shell吗?我认为是fork后直接exec。
作者: wangrujun    时间: 2003-06-26 16:09
标题: fork、exec系列与system、popen区别
system是由shell实现的。并不是ansi标准函数。所以需要调shell来实现。应该是这样吧
所以不同系统应该不一样呀,如果提供了专门的C函数接口,应该是fork 后exec。

不知道这样说对不对呀
作者: 无双    时间: 2003-06-26 18:46
标题: fork、exec系列与system、popen区别
看看它的man
是启动一个shell
作者: 肉球    时间: 2003-06-27 09:03
标题: fork、exec系列与system、popen区别
原帖由 "无双" 发表:
看看它的man
是启动一个shell
   

是呀,man里面说的很清楚。
作者: wangrujun    时间: 2003-06-27 09:12
标题: fork、exec系列与system、popen区别
请问无双版主,我在上面所说的关于fork和exec的区别对吗?主要是入栈出栈那块。
作者: 无双    时间: 2003-06-27 11:14
标题: fork、exec系列与system、popen区别
exec的话并不会复制旧进程信息
而是用新进程映象直接替换掉旧进程的地址空间(包括数据与代码段)

但是进程描述表中的文件描述符没有关闭
还有其它旧进程内信息没有关闭(如信号量等 ,这可以看它们的man 里面有说明)
作者: wangrujun    时间: 2003-06-27 12:57
标题: fork、exec系列与system、popen区别
请教无双:

    既然数据段和代码段都被新进程映象直接替换,而且内核或exec都没有复制旧进程信息,那么 exec执行完后,为什么原来的进程还存在,并且可以继续执行代码呢?
    进程描述表中的文件描述符和旧进程内其它信息没有关闭,这句话的意思是不是说,当exec执行完成,内核再从磁盘上载入原来旧进程的信息(数据段和代码段),然后根据原先的信息继续执行代码,是这样吗?
作者: 无双    时间: 2003-06-27 14:21
标题: fork、exec系列与system、popen区别
我试试看先
作者: 无双    时间: 2003-06-27 14:34
标题: fork、exec系列与system、popen区别
pid=0是子进程
子进程执行完后会退出(因为有exec ,所以只会执行exec部分 不会执行后面的 后面的只是父进程在执行)
作者: 无双    时间: 2003-06-27 14:35
标题: fork、exec系列与system、popen区别
我没有试

现在还没有时间

如果你的不是这样那么请跟贴
作者: wangrujun    时间: 2003-06-27 15:11
标题: fork、exec系列与system、popen区别
啊,我现在明白了。
其实我前面的说法都错了。系统没有保持任何原进程的信息。

还是因为粗心没注意到fork后面实际上子进程就退出了。真不好意思。
作者: qjlemon    时间: 2003-06-27 15:28
标题: fork、exec系列与system、popen区别
在OCI程序里打开一个数据库连接以后再fork,子进程再关闭这个连接,这时候父进程也会断开。郁闷。 。。
可见fork以后父子进程的这种很隐蔽的关联很值得注意
作者: wangrujun    时间: 2003-06-27 16:15
标题: fork、exec系列与system、popen区别
是不是父进程非法操作啦
作者: qjlemon    时间: 2003-06-27 16:34
标题: fork、exec系列与system、popen区别
倒也没有非法操作,只是报了个操作失败。
作者: 无双    时间: 2003-06-27 16:52
标题: fork、exec系列与system、popen区别
可能是ORACLE对方的连接根据IP和端口来判断

FORK后两个进程使用的还是同一个连接
但是一个CLOSE时对端也会对应CLOSE 连接
作者: 无双    时间: 2003-06-27 16:53
标题: fork、exec系列与system、popen区别
不过这个问题确实应该注意
作者: qjlemon    时间: 2003-06-27 16:55
标题: fork、exec系列与system、popen区别
这个是否是因为socket在fork之后其中一个进程close而影响到另一个进程?
但我认为还有一种可能是OCI的实现方式是用一个类似于session id之类的东西,不管你是多少个进程,反正你用这个session id关闭了连接,那这个id就再不能用了
作者: 无双    时间: 2003-06-27 17:03
标题: fork、exec系列与system、popen区别
也有可能
作者: direstrait    时间: 2003-07-06 12:54
标题: fork、exec系列与system、popen区别
unix高级环境编程里面的一个不带信号处理的system版本

  1. int system(const char *cmdline)
  2. {
  3.         pid_t pid;
  4.         int status;
  5.         if(cmdline=NULL)
  6.                 return (1);
  7.         if((pid=fork())<0)
  8.         {
  9.                 status=-1;
  10.         }else if(pid==0)
  11.         {
  12.                 execl("/bin/sh","sh","-c",cmdline,(char *)0);
  13.                 _exit(127);
  14.         }else
  15.         {
  16.                 while(waitpid(pid,&status,0)<0)
  17.                 if(errno!=EINTR)
  18.                         {status=-1;
  19.                          break;
  20.                         }
  21.         }
  22.         return(status);
  23. }
复制代码

可见在unix环境下,是需要shell的.
作者: bird_ken    时间: 2004-03-03 14:35
标题: fork、exec系列与system、popen区别
还有一个区别就是当用fork创建出来的子进程, 当子进程运行完以后(即自动消亡), 在进程表里面还是有这些消亡子进程的项, 而用system创建出来的子进程消亡以后就不会滞留在进程表里面.
作者: yagamiiori    时间: 2006-10-09 23:01
提示: 作者被禁止或删除 内容自动屏蔽
作者: sunyang_5516    时间: 2006-10-12 13:48
看完这个再回头看APUE又有新的收获~!
作者: cuicp    时间: 2006-10-12 16:07
看了一下,大有收获!
有几个问题:
代码1:
int main()
{
        pid_t pid;
        int i = 0;
        printf("asfsda\n");

                if( (pid=fork()) < 0)
                        fprintf(stdout, "fork error on %d\n",i);
                else if( pid > 0)
                        printf("parent fork  %d\n",i);
                else   
                {      
                    printf("child fork %d\n", i);
                    //exit(0);       //这里不明白
                }      
      
        
        return(0);
}

1.这个程序为什么子进程不执行  printf("asfsda\n");呢?子进程不是复制了父进程的所有代码吗?
2.子进程中加不加exit(0); 有什么对执行代码有什么区别?
代码2:
int main()
{
        pid_t pid;
        int i = 0;
        printf("asfsda\n");
     for(i=0;i<3;i++)
        while(1)        
        {      
                if( (pid=fork()) < 0)
                        fprintf(stdout, "fork error on %d\n",i);
                else if( pid > 0)
                        printf("parent fork  %d\n",i);
                else   
                {      
                    printf("child fork %d\n", i);
                  //exit(0);   //不明白的地方
                }      
        }      
        
        return(0);
}
1.这个代码多了个循环,在子进程中加不加exit(0);我都试了一下,结果是:
(1)加exit(0);结果:
asfsda
parent fork  0
parent fork  1
child fork 0
child fork 1
child fork 2
parent fork  2
不加exit(0);结果:
asfsda
parent fork  0
parent fork  1
parent fork  2
child fork 0
parent fork  1
child fork 1
parent fork  2
child fork 1
parent fork  2
parent fork  2
child fork 2
child fork 2
child fork 2
child fork 2
据我分析是子进程又fork了子进程的原因.具体可参考http://bbs.chinaunix.net/viewthr ... &highlight=fork .也就是说子进程从for()开始循环.
这里我就不明白了,为什么子进程没有执行printf("asfsda\n");而是从for开始执行呢?是fork函数对循环作特殊处理了吗?


那位高人给解答一下,谢谢了!!
作者: cuicp    时间: 2006-10-12 16:19
原帖由 yagamiiori 于 2006-10-9 23:01 发表
原因很简单,当父进程创建了一个socket的时候,fork一个子进程,子进程继承了个这个文件描述符,当子进程关闭这个socket时,oracle端如果是使用select、poll、epoll这种技术会捕捉的到,然后关闭对端。这时候父进 ...



说一下我的看法:
我看过一个socket程序的服务端,他的功能就是接收链接,然后创建子进程执行一些功能,

signal( SIGCHLD, ctld_deal_child );//信号SIGCHLD处理
signal( SIGPIPE, SIG_IGN );//忽略SIGPIPE
while(1){
clisock = ctld_accept( svrsock, &sockin, &addrlen );
if (0 == (ret = fork())){
  g_session.sock = clisock;
  //子进程功能实现!
  exit(0);

}}else if( ret > 0 ){/* Parent */
        ctld_close( clisock );
        }else{
              CTLD_DEBUG( MAIN_TAG, "Fork error, possible?\n" );//日志
        }
}
程序在创建子进程后在父进程中关闭了clisock ,程序执行不会有问题.
yagamiiori所说的情况是不是父进程没有处理信号SIGPIPE呀?

高手解释一下,谢谢!!




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2