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函数

头文件

  1. #include<unistd.h>
  2. #include<sys/types.h>

函数原型

  1. 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()三个与进程有关的函数的比较的更多相关文章

  1. system()、exec()、fork()三个与进程有关的函数的比较

    启动新进程(system函数) system()函数可以启动一个新的进程. int system (const char *string ) 这个函数的效果就相当于执行sh –c string. 一般 ...

  2. linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程

    本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生)      使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...

  3. PHP 执行系统外部命令的函数- system() exec() passthru()

    PHP 执行系统外部命令的函数: system() exec() passthru()区别:system() 输出并返回最后一行shell结果.exec() 不输出结果,返回最后一行shell结果,所 ...

  4. [转帖]Linux下fork函数及pthread函数的总结

    Linux下fork函数及pthread函数的总结 https://blog.csdn.net/wangdd_199326/article/details/76180514 fork Linux多进程 ...

  5. Linux 进程创建一(system和fork)

    一:system系统调用 #include <stdlib.h> int system(const char *string); system函数传递给/bin/sh -c 来执行stri ...

  6. 读书笔记-APUE第三版-(8)进程控制

    进程ID 每一个进程都有一个唯一的进程ID.几个特殊进程: 0号进程是内核进程,一般是调度进程swapper. 1号进程init,是用户进程(以root权限执行/sbin/init),负责初始化. 几 ...

  7. Linux内核设计与实现 总结笔记(第三章)进程

    进程管理 进程:处于执行期的程序. 线程:在进程中活动的对象 虚拟机制 虚拟处理器:多个进程分享一个处理器 虚拟内存:多个线程共享虚拟内存 一.进程描述符和任务结构 进程存放在双向循环链表中(队列), ...

  8. nodejs(三) --- nodejs进程与子进程

    嗯,对于node的学习还远远不够,这里先做一个简单的api的记录,后续深入学习. 第一部分:nodejs中的全局对象之process进程对象 在node中的全局对象是global,相当于浏览器中的wi ...

  9. Linux进程的创建函数fork()及其fork内核实现解析【转】

    转自:http://www.cnblogs.com/zengyiwen/p/5755193.html 进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进 ...

随机推荐

  1. 《生命》第四集:Fish (鱼类)

    旗鱼,是游动最快的鱼,他们不仅速度快,背上的鱼鳍还能吓唬成群的沙丁鱼,他们依靠速度与技巧结队捕食. 飞鱼,继续讲述了一下,飞鱼可以飞起来,把捕食者远远甩掉:飞鱼保护后代的方式是把卵产在水中的树叶上,很 ...

  2. ES5-ES6-ES7_解构赋值

    解构赋值的概念 ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring) 传统对变量赋值的方式,以前,为变量赋值,只能直接指定值. var a = 1; ...

  3. SQLite这么娇小可爱,不多了解点都不行啊

    简介 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统.它的设计目标是嵌入式的,目前Android和iOS的设备内置的都是SQLite数据库.SQLite虽然娇小,但也支持事务和 ...

  4. http和https工具类 (要注意httpclient版本号和log4j的版本号)

    1 工具类 package dd.com; import java.io.IOException; import java.security.cert.CertificateException; im ...

  5. java实现随机四则运算

    使用JAVA编程语言,独立完成一个包含3到5个数字的四则运算练习,软件基本功能要求如下: 程序可接收一个输入参数n,然后随机产生n道加减乘除练习题,每个数字在 0 和 100 之间,运算符在3个到5个 ...

  6. P2440 木材加工(二分+贪心)

    思路:这里就要看往那边贪心了,因为解决的是最大值最小化,最小值最大化.也就是说当满足大于等于c时,l=mid+1这样的二分得到的就是在所有满足条件函数下的最右端. #include<iostre ...

  7. Spring Security(十):3. What’s New in Spring Security 4.2 (新功能)

    Among other things, Spring Security 4.2 brings early support for Spring Framework 5. You can find th ...

  8. python 的__init__ 和__new__ 区别

    在此介绍一下  __init__ 和  __new__ 先后调用的区别 代码如下: # __init__ 和 __new__的区别 # 通常在编代码时,__init__ 较为常见,但是__new__却 ...

  9. 深入理解Mybatis技术与原理

    目录 第1章 Mybatis简介 1.1 传统的JDBC编程 1.2 ORM模型 1.4 MyBatis 1.5 什么时候用MyBatis 第2章 MyBatis入门 2.2 MyBatis构成 2. ...

  10. 热泪盈眶的五十岁 | James Altucher

    我是一名程序员,但我不爱看技术博客,因为要吸取知识点,看源代码.官方文档和书永远比看技术博客要好.对于博客这种偏碎片的媒介,我倾向于看一些短小精炼.有一点深度的叙述,Altucher刚好符我目前的品味 ...