system,popen函数与fork的区别

1.system函数

这里写图片描述
说明:
①system函数在执行的过程中经过fork—>exec—>wait,但system在执行的过程中会一直等待,直到shell运行完才退出,所以system为串行执行

②system在执行的过程中对SIGCHLD、SIGINT、SIGQUIT都做了处理

③SIGCHLD是子进程在退出的时候给父进程发的一个信号,system中屏蔽了SIGCHLD信号,原因是为了system调用能够及时的退出并且能够正确获取子进程的退出状态,成功回收子进程

2.popen函数

这里写图片描述
说明:
①popen函数在执行时不需要等待shell执行完才退出,所以popen是并行执行

②popen在执行时对SIGCHLD、SIGINT、SIGQUIT不做处理

③popen创建的子进程如果不执行pclose,popen创建的子进程就会变成僵尸进程

④popen用创建管道的方式启动一个进程,并调用shell。因为管道是单向的,所以type参数只能定义成只读或只写,结果流也相应的是只读或只写;command参数是一个字符串指针,指向的是一个以NULL结束符结尾的字符串,这个字符串包含了一个shell命令,这个命令被送到/bin/sh以-c参数执行,即由shell执行;type参数也是一个指向以NULL结束符结尾的字符串指针,这个字符串以’r’或’w’指明。

⑤popen没有屏蔽SIGCHLD,原因是popen是并行的,如果调用进程在pclose之前执行一个wait操作的话就会获取到popen创建的子进程状态,这样在调用pclose的时候就会回收子进程失败,返回-1,同时设置error为ECHLD,标志pclose无法获取子进程状态。

⑥ popen函数的返回值是一个普通的标准I/O流, 它只能用 pclose函数来关闭, 而不是 fclose 函数. 向这个流的写入被转化为对 command 命令的标准输入; 而 command 命令的标准输出则是和调用 popen, 函数的进程相同,除非这个被command命令自己改变. 相反的, 读取一个 “被popen了的” 流, 就相当于读取 command 命令的标准输出, 而 command 的标准输入则是和调用 popen, 函数的进程相同.

3.fork函数

fork用来创建一个子进程,父子进程共享代码,对于数据段和堆栈段,系统将父进程复制一份给子进程。子进程一旦开始运行,虽然继承了父进程的所有数据,但实际上数据已经分开了,互不影响,即不再共享数据。如果要让两个进程共享一些数据,就要使用一些函数(shmget,shmat,shmdt等)实现。

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

vfork和fork一样,也是创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,不会复制页表。因为子进程会立即调用exec,于是也就不会存放该地址空间。不过在子进程中调用exec或exit之前,他在父进程的空间中运行。

为什么会有vfork,因为以前的fork当它创建一个子进程时,将会创建一个新的地址空间,并且拷贝父进程的资源,而往往在子进程中会执行exec调用,这样,前面的拷贝工作就是白费力气了,这种情况下,聪明的人就想出了vfork,它产生的子进程刚开始暂时与父进程共享地址空间(其实就是线程的概念了),因为这时候子进程在父进程的地址空间中运行,所以子进程不能进行写操作,并且在儿子“霸占”着老子的房子时候,要委屈老子一下了,让他在外面歇着(阻塞),一旦儿子执行了exec或者exit后,相当于儿子买了自己的房子了,这时候就相当于分家了。

vfork和fork之间的另一个区别是:
vfork保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

由此可见,这个系统调用是用来启动一个新的应用程序。其次,子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。

为了避免这些问题,需要确保一旦调用vfork(),子进程就不从当前的栈框架中返回,并且如果子进程改变了父进程的数据结构就不能调用exit函数。子进程还必须避免改变全局数据结构或全局变量中的任何信息,因为这些改变都有可能使父进程不能继续。

通常,如果应用程序不是在fork()之后立即调用exec(),就有必要在fork()被替换成vfork()之前做仔细的检查。

用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序,当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行,因为调用exec并不创建新进程,所以前后的进程id 并未改变,exec只是用另一个新程序替换了当前进程的正文,数据,堆和栈段。

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值