system()、exec()、fork()三个与进程有关的函数的比较
启动新进程(system函数)
system()函数可以启动一个新的进程。
int system (const char *string )
这个函数的效果就相当于执行sh –c string。
一般来说,使用system函数远非启动其他进程的理想手段,因为它必须用一个shell来启动需要的程序。这样对shell的安装情况,以及shell的版本依赖性很大。
system函数的特点:
建立独立进程,拥有独立的代码空间,内存空间
等待新的进程执行完毕,system才返回。(阻塞)
替换进程映像(exec函数)
exec函数可以用来替换进程映像。执行exec系列函数后,原来的进程将不再执行,新的进程的PID、PPID和nice值与原先的完全一样。其实执行exec系列函数所发生的一切就是,运行中的程序开始执行exec调用中指定的新的可执行文件中的代码。
exec函数的特点:
当进程调用一种exec函数时,源进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。特别地,在原进程中已经打开的文件描述符,在新进程中仍将保持打开,除非它们的“执行时关闭标志”(close on exec flag)被置位。任何在原进程中已打开的目录流都将在新进程中被关闭。
复制进程映像(fork函数)
fork函数
头文件
- #include<unistd.h>
- #include<sys/types.h>
函数原型
- pid_t fork( void);
返回值:
若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
关于fork函数的作用,《Linux程序设计》中是这样解释的:
我们可以通过调用fork创建一个新进程。这个系统调用复制当前进程,在进程表中新建一个新的表项,新表项中的许多属性与当前进程是相同的。新进程几乎与元进程一模一样,执行的代码也完全相同,但是新进程有自己的数据空间、环境和文件描述符。
这个解释其实过于笼统,很多细节问题都没有说。下面就简单说一下调用fork时发生的一些细节问题。或者叫fork函数的特点:
首先,现在的UNIX系统和Linux系统都采用写时复制技术(COW:Copy On Write)。使用这种技术,当调用fork函数时,新的进程只是拥有自己的虚拟内存空间,而没有自己的物理内存空间。新进程共享源进程的物理内存空间。而且新内存的虚拟内存空间几乎就是源进程虚拟内存空间的一个复制。
我们知道,进程空间可以简单地分为程序段(正文段)、数据段、堆和栈四部分(简单这样理解)。采用写时复制的fork函数,当执行完fork后的一定时间内,新的进程(子进程)和源进程的进程空间关系如下图:
如上图,fork执行时,Linux内核会为新的进程P2创建一个虚拟内存空间,而新的虚拟空间中的内容是对P1虚拟内存空间中的内容的一个拷贝。而P2和P1共享原来P1的物理内存空间。
当然要理解“写时复制”中,上图中所展示的状态是会发生变化的。什么时候回发生变化呢?就是,父子两个进程中任意一个进程对数据段、栈区、堆区进行写操作时,上图中的状态就会被打破,这个时候就会发生物理内存的复制,这也就是叫“写时复制”的原因。发生的状态转变如下:
我们发现,P2有了属于自己的物理内存空间。值得注意的是,各个段之间发生的变化应当是独立的,也就是说,如果只有数据段发生了写操作那么就只有数据段进行写时复制。而堆、栈区域依然是父子进程共享。还有一个需要注意的是,正文段(程序段)不会发生写时复制,这是因为通常情况下程序段是只读的。子进程和父进程从fork之后,基本上就是独立运行,互不影响了。
此外需要特别注意的是,父子进程的文件描述符表也会发生写时复制。
还有一个叫vfork的函数,这个做法更加火爆,内核连子进程的虚拟地址空间结构也不创建了,直接共享了父进程的虚拟空间,当然了,这种做法就顺水推舟的共享了父进程的物理空间
system()、exec()、fork()函数比较
首先比较一下exec()函数和fork()。这两个函数一个是换药不换汤(execl函数),另一个是换汤不换药(fork函数)。那么什么是汤、什么又是药呢?我们知道进程是个很复杂的东西。从task_struct 结构体的代码量上就可以看出来(task_struct是Linux内核中用来描述进程的一个结构体,这个结构体光代码貌似就有好几屏)。我们可以把进程的PID、PPID和nice值等看作是汤,而把进程空间(简单理解就是正文段、数据段、堆、栈等)看作是药。
exec()函数是换药不换汤,就是说执行exec函数后,并没有产生新的进程,也就是汤还是那些汤,进程的PID、PPID和nice值等没有发生变化。但是exec()函数却将药换了,也就是将进程空间换掉了,新的进程空间是为了执行新的程序所准备的,所以新的进程空间与原进程空间并没有什么关系。
fork()函数是换汤不换药,意思是执行fork()函数后,产生了新的进程,新的进程的PID、PPID与原来原来的进程不同,说明父子进程是两个不同的进程,但是fork并没有把药换掉,而是将药复制了一份给子进程。fork刚执行后的一段时间内,父子进程有着相同的状态(进程空间中的东西都一样,因为fork采用“写时复制”,一开始父子进程共享物理内存空间)。但是一旦父子进程中有一个进程试图修改进程空间,这时父子进程就各自拥有了各自的进程空间,简单地理解,从这一时刻器,父子进程就是两个独立的进程,谁都不会影响谁(实际上还是有一定影响的,在这里可以忽略),父子进程之间的关联仅剩下它们共享的代码段了。
对于system函数,我们可以先看一下它的源代码:
int system(const char * cmdstring)
{
pid_t pid;
int status; if(cmdstring == NULL){ return ();
} if((pid = fork())<){ status = -;
}
else if(pid == ){
execl("/bin/sh", "sh", "-c", cmdstring, (char *));
-exit(); //子进程正常执行则不会执行此语句
}
else{
while(waitpid(pid, &status, ) < ){
if(errno != EINTER){
status = -;
break;
}
}
}
return status;
}
我们看到system()函数实际上就是先执行了fork函数,然后新产生的子进程立刻执行了exec函数,我们前面说个fork函数换汤不换药,exec函数换药不换汤,那么system函数就是既换汤也换了药,也就是system函数会产生新进程,这就意味着新进程的PID、PPID等与原进程不同。system也会产生新的进程空间,而且新的进程空间是为新的程序准备的,所以和原进程的进程空间没有任何关系(不像fork新进程空间是对原进程空间的一个复制)。还要注意的是,system函数代码中else部分执行了wait函数,这就意味着,原进程会等待子进程执行完毕(阻塞)
最后还要注意的一个问题是关于文件描述符的。
exec函数执行后,原来打开的文件描述符依然存在。
fork函数执行后,原来打开的文件描述符会复制一份到新的进程中,之后两个进程之间的文件描述符就相对独立了。
system函数先执行fork函数,这之后两个进程的文件描述符就相对独立了。之后exec函数并不影响文件描述符。
system()、exec()、fork()三个与进程有关的函数的比较的更多相关文章
- [转帖]system()、exec()、fork()三个与进程有关的函数的比较
system().exec().fork()三个与进程有关的函数的比较 https://www.cnblogs.com/qingergege/p/6601807.html 启动新进程(system函数 ...
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...
- Linux 进程创建一(system和fork)
一:system系统调用 #include <stdlib.h> int system(const char *string); system函数传递给/bin/sh -c 来执行stri ...
- 读书笔记-APUE第三版-(8)进程控制
进程ID 每一个进程都有一个唯一的进程ID.几个特殊进程: 0号进程是内核进程,一般是调度进程swapper. 1号进程init,是用户进程(以root权限执行/sbin/init),负责初始化. 几 ...
- Linux内核设计与实现 总结笔记(第三章)进程
进程管理 进程:处于执行期的程序. 线程:在进程中活动的对象 虚拟机制 虚拟处理器:多个进程分享一个处理器 虚拟内存:多个线程共享虚拟内存 一.进程描述符和任务结构 进程存放在双向循环链表中(队列), ...
- nodejs(三) --- nodejs进程与子进程
嗯,对于node的学习还远远不够,这里先做一个简单的api的记录,后续深入学习. 第一部分:nodejs中的全局对象之process进程对象 在node中的全局对象是global,相当于浏览器中的wi ...
- Linux进程的创建函数fork()及其fork内核实现解析【转】
转自:http://www.cnblogs.com/zengyiwen/p/5755193.html 进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进 ...
- PHP 执行系统外部命令的函数- system() exec() passthru()
PHP 执行系统外部命令的函数: system() exec() passthru()区别:system() 输出并返回最后一行shell结果.exec() 不输出结果,返回最后一行shell结果,所 ...
- Linux进程的创建函数fork()及其fork内核实现解析
进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进程,被创建的进程为子进程. fork函数的接口定义如下: #include <unistd.h& ...
随机推荐
- WinForm 控件(下)
10.PictureBox 外观,Image可以选择图片路径行为,SizeMode可以设置图片大小布局方式 11.Imagelist--图片集 imageList1.Images[n]; 12.not ...
- sql增删查改
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...
- Android jni 编程3(对基本类型一维整型数组的操作)总结版
主要学习资料:黑马程序员的NDK方法使用(生产类库so) jni编程指南中文版(已上传至博客园) 博主文章(它使用的是VS和eclipse联合开发):http://www.c ...
- Win8下,以管理员身份启动VS项目
之前一直是先以管理员身份启动VS,然后再打开项目的,比较麻烦,找了好久,总算有一个处理方案了 在Windows7下 通常使用修改属性的方式:在任意快捷方式上右击,选择属性,选择高级,选择以管理员身份启 ...
- 网站SEO,HTTP请求的关键数字----6
客户端浏览器向服务器请求一个网页素材. 那么网页素材是通过什么方式,什么顺序被下载下来的呢. 我今天做了个简单的测试. 首先,准备测试文件 写一个网页,网页中引用若干的资源文件. 同一文件的不同的参数 ...
- linux php安装memcached扩展
memcached的安装包括:1.服务端的安装:2.客户端的安装 一.服务器端安装 方法一: memcached 依赖于libevent 库,因此我们需要先安装libevent. 假设将源码放在/us ...
- Htmlunit使用
//模拟一个浏览器 WebClient webClient = new WebClient(); //设置webClient的相关参数 webClient.setJavaScriptEnabled(t ...
- 总结ASP.NET MVC Web Application中将数据显示到View中的几种方式
当我们用ASP.NET MVC开发Web应用程序的时候,我们都是将需要呈现的数据通过"Controllers"传输到"View"当中,怎么去实现,下面我介绍一下 ...
- Android 调用jepg库进行图片压缩,保持图片不失真
1. 浅谈为什么Android和iOS图片质量差距那么大? 首先来说,作为一个安卓狗,机器当然用的是安卓的手机.现在的安卓手机大多数都会以高清拍照,动不动就几千万柔光相机来吸引各种买家.买来后,拍照发 ...
- C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...