Shell 中有很多方法产生子进程,比如以新进程的方式运行 Shell 脚本,使用组命令、管道、命令替换等,但是这些子进程是有区别的。

子进程的概念是由父进程的概念引申而来的。在 Linux 系统中,系统运行的应用程序几乎都是从 init(pid为 1 的进程)进程派生而来的,所有这些应用程序都可以视为 init 进程的子进程,而 init 则为它们的父进程。

使用pstree -p命令就可以看到 init 及系统中其他进程的进程树信息(包括 pid):

systemd(1)─┬─ModemManager(796)─┬─{ModemManager}(821)

│ └─{ModemManager}(882)

├─NetworkManager(975)─┬─{NetworkManager}(1061)

│ └─{NetworkManager}(1077)

├─abrt-watch-log(774)

├─abrt-watch-log(776)

├─abrtd(773)

├─accounts-daemon(806)─┬─{accounts-daemon}(839)

│ └─{accounts-daemon}(883)

├─alsactl(768)

├─at-spi-bus-laun(1954)─┬─dbus-daemon(1958)───{dbus-daemon}(1960)

│ ├─{at-spi-bus-laun}(1955)

│ ├─{at-spi-bus-laun}(1957)

│ └─{at-spi-bus-laun}(1959)

├─at-spi2-registr(1962)───{at-spi2-registr}(1965)

├─atd(842)

├─auditd(739)─┬─audispd(753)─┬─sedispatch(757)

│ │ └─{audispd}(759)

│ └─{auditd}(752)

本教程基于 CentOS 7 编写,CentOS 7 为了提高启动速度使用 systemd 替代了 init。CentOS 7 之前的版本依然使用 init。

Shell 脚本是从上至下、从左至右依次执行的,即执行完一个命令之后再执行下一个。如果在 Shell 脚本中遇到子脚本(即脚本嵌套,但是必须以新进程的方式运行)或者外部命令,就会向系统内核申请创建一个新的进程,以便在该进程中执行子脚本或者外部命令,这个新的进程就是子进程。子进程执行完毕后才能回到父进程,才能继续执行父脚本中后续的命令及语句。

子进程的创建

了解 Linux 编程的读者应该知道,使用 fork() 函数可以创建一个子进程;除了 PID(进程ID)等极少的参数不同外,子进程的一切都来自父进程,包括代码、数据、堆栈、打开的文件等,就连代码的执行位置(状态)都是一样的。

也就是说,fork() 克隆了一个一模一样的自己,身高、体重、颜值、嗓音、年龄等各种属性都相同。当然,后期随着各自的发展轨迹不同,两者会变得不一样,比如 A 好吃懒做越来越肥,B 经常健身成了一个肌肉男;但是在 fork() 出来的那一刻,两者都是一样的。

Linux 还有一种创建子进程的方式,就是子进程被 fork() 出来以后立即调用 exec() 函数加载新的可执行文件,而不使用从父进程继承来的一切。什么意思呢?

比如在 ~/bin 目录下有两个可执行文件分别叫 a.out 和 b.out。现在我运行 a.out,就会产生一个进程,比如叫做 A。在进程 A 中我又调用 fork() 函数创建了一个进程 B,那么 B 就是 A 的子进程,此时它们是一模一样的。但是,我调用 fork() 后立即又调用 exec() 去加载 b.out,这可就坏事了,B 进程中的一切(包括代码、数据、堆栈等)都会被销毁,然后再根据 b.out 重建建立一切。这样一折腾,B 进程除了 ID 没有变,其它的都变了,再也没有属于 A 的东西了。

你看,同样是创建子进程,但是结果却大相径庭:

第一种只使用 fork() 函数,子进程和父进程几乎是一模一样的,父进程中的函数、变量、别名等在子进程中仍然有效。

第二种使用 fork() 和 exec() 函数,子进程和父进程之间除了硬生生地维持一种“父子关系”外,再也没有任何联系了,它们就是两个完全不同的程序。

对于 Shell 来说,以新进程的方式运行脚本文件,比如bash ./test.sh、chmod +x ./test.sh; ./test.sh,或者在当前 Shell 中使用 bash 命令启动新的 Shell,它们都属于第二种创建子进程的方式,所以子进程除了能继承父进程的环境变量外,基本上也不能使用父进程的什么东西了,比如,父进程的全局变量、局部变量、文件描述符、别名等在子进程中都无效。

但是,组命令、命令替换、管道这几种语法都使用第一种方式创建进程,所以子进程可以使用父进程的一切,包括全局变量、局部变量、别名等。我们将这种子进程称为子 Shell(sub shell)。

子 Shell 虽然能使用父 Shell 的的一切,但是如果子 Shell 对数据做了修改,比如修改了全局变量,那么这种修改只能停留在子 Shell,无法传递给父 Shell。不管是子进程还是子 Shell,都是“传子不传父”。

总结

子 Shell 才是真正继承了父进程的一切,这才像“一个模子刻出来的”;普通子进程和父进程是完全不同的两个程序,只是维持着父子关系而已。

c语言 子进程,子Shell和子进程相关推荐

  1. Shell 脚本 — 多行注释、开启子/不开启子进程执行、转义带颜色输出、读取键盘输入、输入输出重定向、单双引号、命令替换、读取变量、系统变量、正则过滤、算术运算、一行多条命令、字符串比较

    1. 多行注释 #!/bin/bash<< COMMENT This is multi line comment COMMENTecho "This is demo script ...

  2. [shell问答录]:命令、进程、子shell...

    2019独角兽企业重金招聘Python工程师标准>>> 前些天在CU上讨论一个统计正在执行的脚本数量的问题过程中,发现自己对于shell如何执行命令方面了解还是甚少,惭愧惭愧...期 ...

  3. powershell执行c语言文件,c语言中调用shell脚本

    [转]c语言中调用shell脚本 关于在c语言中调用shell脚本,先是在百度上百度了一下 下面的这个应该是说的比较详细的 dreamerkxz.blog.163.com/blog/static/82 ...

  4. Linux中父shell 与 子shell :subshell 和 child shell

    子shell的概念 中文翻译过来的"子shell"叫法,实际上包括了"subshell"和"child shell". subshell是指 ...

  5. linux更改语言脚本,Linux shell脚本入门——shell语言脚本【CentOS】

    认识脚本 是使用一种特定的描述性语言,依据一定的格式编写的可执行文件.脚本语言又被称为扩建的语言, 或者动态语言, 是一种编程语言, 用来控制软件应用程序, 脚本通常是以文本 (ASCⅡ) 保存, 只 ...

  6. 统计文件种类数+获取子shell返回值的其它方法

    前言 只是作为一个shell的小小练习和日常统计用,瞎折腾的过程中也是摸到了获取子shell返回值的几种方法: 肯定还有别的方法,跟进程间的通信相关,希望你能提出建议和补充,谢谢~ 完整程序: #! ...

  7. Linux编程 9 (shell类型,shell父子关系,子shell用法)

    一. shell类型 1.1  交互式 bin/ shell程序 当用户登录到某个虚拟控制台终端或是在GUI中启动终端仿真器时,默认的shell程序就会开始运行.系统启动什么样的shell程序取决于你 ...

  8. linux source 子shell,Linux------source

    |--source命令: source命令也称为"点命令",也就是一个点符号(.),是bash的内部命令. 功能:使Shell读入指定的Shell程序文件并依次执行文件中的所有语句 ...

  9. Linux中shell运行方式,linux脚本中父shell与子shell 执行的几种方式

    本文主要介绍以下几个命令的区别: shell subshell source $ (commond) `commond` Linux执行Scripts有两种方式,主要区别在于是否建立subshell ...

最新文章

  1. 宏基因组实战6. 不比对快速估计基因丰度Salmon
  2. hbase集群间数据迁移
  3. linux编译树莓派内核,编译树莓派 4B Linux 5.9 内核
  4. x86汇编从实模式到保护模式-程序加载器
  5. RestTemplate技术预研-认识RestTemplate
  6. 别想赖账,今天微信又可以发520红包了
  7. ubuntu下安装python的gevent模块遇到的一个问题
  8. JVM第二节:JVM 中的对象
  9. SIM800L透传模式配置
  10. pythonic希腊典故_希腊神话习语(整理)
  11. 头条 上传图片大小_无锡抖音巨量运营培训南天值得选择——鹰手营子矿头条...
  12. linux下好用的chm阅读器
  13. mysql错误1820 you must reset your password using...
  14. 排兵布阵问题java语言_hdu 4539 郑厂长系列故事——排兵布阵
  15. 谷歌浏览器windows以及mac系统下设置跨域
  16. 考研面试常问问题汇总(包含答案)
  17. JavaWeb基础5——HTTP,TomcatServlet
  18. hexo+github创建个人博客--深入篇
  19. CSS中#号、.号、*号详解
  20. BT5下metasploit升级报错解决方法

热门文章

  1. mysql 字典索引_【大白话mysql】你真的了解 mysql 索引吗?
  2. 保存到数据库乱码mysql_Linux下MySQL保存进去数据为乱码的解决办法
  3. c语言课程设计修改订单流程图,C语言课程设计————写下流程图! 谢谢
  4. 八皇后问题python_python八皇后问题的解决方法
  5. pandas划分数据
  6. 【转】DIB位图(Bitmap)的读取和保存
  7. 【转】人工智能-1.2.2 神经网络是如何进行预测的
  8. 【转】什么是SIEM?
  9. 修改oracle默认日期,Oracle 修改默认日期格式
  10. thinkphp index.php隐藏,thinkphp5怎么隐藏index.php入口文件?