[转帖]system()、exec()、fork()三个与进程有关的函数的比较
system()、exec()、fork()三个与进程有关的函数的比较
https://www.cnblogs.com/qingergege/p/6601807.html
启动新进程(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 (1); } if((pid = fork())<0){ status = -1; } else if(pid == 0){ execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); -exit(127); //子进程正常执行则不会执行此语句 } else{ while(waitpid(pid, &status, 0) < 0){ if(errno != EINTER){ status = -1; 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函数) system()函数可以启动一个新的进程. int system (const char *string ) 这个函数的效果就相当于执行sh –c string. 一般 ...
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...
- PHP 执行系统外部命令的函数- system() exec() passthru()
PHP 执行系统外部命令的函数: system() exec() passthru()区别:system() 输出并返回最后一行shell结果.exec() 不输出结果,返回最后一行shell结果,所 ...
- [转帖]Linux下fork函数及pthread函数的总结
Linux下fork函数及pthread函数的总结 https://blog.csdn.net/wangdd_199326/article/details/76180514 fork Linux多进程 ...
- 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函数来创建新的进程.调用进程为父进 ...
随机推荐
- 附2 rabbitmq用户管理、角色管理与权限管理
本文摘自:http://my.oschina.net/hncscwc/blog/262246 1. 用户管理 用户管理包括增加用户,删除用户,查看用户列表,修改用户密码. 相应的命令 (1) 新增一个 ...
- Linux /var/log下的各种日志文件详解
1)/var/log/secure:记录登录系统存取数据的文件;例如:pop3,ssh,telnet,ftp等都会记录在此. 2)/var/log/wtmp:记录登录这的信息记录,被编码过,所以必须以 ...
- ubuntu 环境下编译 hadoop 2.6.0的简单方法
由于服务器一般都64位系统, hadoop网站的release版本32位native库不能运行,所以需要自己在编译一下.以下是我采用的一个编译的过程,比较简单,不用下载各种版本及环境配置,通过命令就能 ...
- 去除列表中的\n 和空字符
s=['\n', 'magnet:?xt=urn:btih:060C0CE5CFAE29A48102280B88943880689859FC\n'] 上面是目标代码,一个列表,中间有\n,我们现在将其 ...
- CCF-201803-3-URL映射(模拟)
Problem CCF-201803-3-URL映射 Time Limit: 1000 mSec Problem Description URL 映射是诸如 Django.Ruby on Rails ...
- 配置DispatcherServlet应该写/还是/*
相亲怎么做 web应用需要放在Tomcat容器中才能启动,Tomcat容器内有一个默认的web.xml文件,在自己项目中配置的XML文件都是继承自Tomcat中的全局XML文件并重写其中相应配置,这种 ...
- Python:Day45 Javascript的String字符串
typeof只能判断普通数据类型, 对于复杂的只是判断出来是一个Object: instanceof 可以判断数据是否是某一类型: alert(s instanceof String); String ...
- bzoj-1787-洛谷-4281(LCA板子题)
传送门(bzoj) 传送门(洛谷) 可以说这道也是一个板子题 由于题中是三个人需经过的路径最短 就会有一点点不太一样 那么 就两两求LCA 这样之后就会出现两种状况 一.所得到的三个LCA是相等的 那 ...
- Winform开发框架之字段权限控制
在我的很多Winform开发项目中(包括混合框架的项目),统一采用了权限管理模块来进行各种权限的控制,包括常规的功能权限(按钮.菜单权限).数据权限(记录的权限),另外还可以进行字段级别的字段权限控制 ...
- IT面试技巧终身受益
面试前的准备 首先我们要穿的得体,因为第一印象对一个面试官来说真的很重要,如果我们面试的时候都不能以一种非常认真的态度去对待,那么可想而知其实我们离面试成功的路渐行渐远,当然这只是说第一印象,并不能代 ...