相关概念介绍:
1.内部命令和外部命令
bash shell的命令分为两类:外部命令和内部命令。外部命令是通过系统调用或独立的程序实现的,如sed、awk等等。内部命令是由特殊的文件格式(.def)所实现,如cd、history、exec等等。
exec和source都属于bash内部命令(builtins commands),在bash下输入man exec或man source可以查看所有的内部命令信息。
2.fork的概念
fork是linux的系统调用,用来创建子进程(child process)。子进程是父进程(parent process)的一个副本,从父进程那里获得一定的资源分配以及继承父进程的环境。子进程与父进程唯一不同的地方在于pid(process id)。
3.环境变量
环境变量是传给子进程的变量,遗传性是本地变量和环境变量的根本区别。
只能单向从父进程传给子进程。不管子进程的环境变量如何变化,都不会影响父进程的环境变量。
4.运行shell script的方法
① scripts文件开头加入 #!/bin/sh
新产生一个子进程,然后执行相应的scripts,scripts结束后回到原父进程继续执行后续命令
② source命令
在当前进程下执行,不再启用其他进程。
source命令即点(.)命令。在bash下输入man source,找到source命令解释处,可以看到解释”Read and execute commands from filename in the current shell environment and …”。从中可以知道,source命令是在当前进程中执行参数文件中的各个命令,而不是另起子进程(或sub-shell)。
③ exec命令
新产生一个子进程,然后执行相应的scripts,scripts结束后退出,不再执行原父进程后续命令
在bash下输入man exec,找到exec命令解释处,可以看到有”No new process is created.”这样的解释,这就是说exec命令不产生新的子进程。
5.exec与source的区别
(1) 系统调用exec是以新的进程去代替原来的进程,但进程的PID保持不变。因此,可以这样认为,exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。原进程的代码段,数据段,堆栈段被新的进程所代替。
一个进程主要包括以下几个方面的内容:
① 一个可以执行的程序
② 与进程相关联的全部数据(包括变量,内存,缓冲区)
③ 程序上下文(程序计数器PC,保存程序执行的位置)
(2) exec是一个函数簇,由6个函数组成,分别是以excl和execv打头的。
执行exec系统调用,一般都是这样,用fork()函数新建立一个进程,然后让进程去执行exec调用。我们知道,在fork()建立新进程之后,父进各与子进程共享代码段,但数据空间是分开的,但父进程会把自己数据空间的内容copy到子进程中去,还有上下文也会copy到子进程中去。而为了提高效率,采用一种写时copy的策略,即创建子进程的时候,并不copy父进程的地址空间,父子进程拥有共同的地址空间,只有当子进程需要写入数据时(如向缓冲区写入数据),这时候会复制地址空间,复制缓冲区到子进程中去。从而父子进程拥有独立的地址空间。而对于fork()之后执行exec后,这种策略能够很好的提高效率,如果一开始就copy,那么exec之后,子进程的数据会被放弃,被新的进程所代替。
6.exec与system的区别
(1) exec是直接用新的进程去代替原来的程序运行,运行完毕之后不回到原先的程序中去。
(2) system是调用shell执行你的命令,system=fork+exec+waitpid,执行完毕之后,回到原先的程序中去。继续执行下面的部分。
总之,如果你用exec调用,首先应该fork一个新的进程,然后exec. 而system不需要你fork新进程,已经封装好了。
7.验证
脚本parent.sh内容
#!/bin/bash
A=B
echo "PID FOR parent.sh before exec/source/fork:$$"
export A
echo "parent.sh:\$A is $A"
case $1 in
exec)
echo "using exec,,,,,"
exec ./fork.sh ;; #执行完成后结束
source)
echo "using source...."
. ./fork.sh ;; #执行完成后继续执行
*)
echo "using fork by default...."
./fork.sh ;; #另起一个进程来执行 fork.sh 执行后继续执行parent.sh
esac
echo "PID FOR parent.sh after exec/source/fork :$$"
echo "parent.sh:\$A is $A"
脚本fork.sh内容
#!/bin/bash
echo "PID for fork.sh:$$"
echo "fork.sh get \$A=$A from parent.sh"
A=C
export A
echo "fork.sh: \$A is $A"
为脚本添加可执行权限。
执行结果
# ./parent.sh exec
PID FOR parent.sh before exec/source/fork:24394
parent.sh:$A is B
using exec,,,,,
PID for fork.sh:24394
fork.sh get $A=B from parent.sh
fork.sh: $A is C ##执行fork.sh后parent.sh后续内容未执行,PID不变
# ./parent.sh source
PID FOR parent.sh before exec/source/fork:24441
parent.sh:$A is B
using source....
PID for fork.sh:24441
fork.sh get $A=B from parent.sh
fork.sh: $A is C
PID FOR parent.sh after exec/source/fork :24441
parent.sh:$A is C ##执行fork.sh后parent.sh后续内容继续执行,PID不变
# ./parent.sh fork
PID FOR parent.sh before exec/source/fork:24472
parent.sh:$A is B
using fork by default....
PID for fork.sh:24473
fork.sh get $A=B from parent.sh
fork.sh: $A is C
PID FOR parent.sh after exec/source/fork :24472
parent.sh:$A is B ##执行fork.sh后parent.sh后续内容继续执行,PID改变,$A被fork.sh(子进程)改变不能传递给parent.sh(父进程)
参考:https://www.cnblogs.com/zhaoyl/archive/2012/07/07/2580749.html
参考:https://www.cnblogs.com/NullPoint/p/3561028.html