1 wait
wait是用来阻塞当前进程的执行,直至指定的子进程执行结束后,才继续执行。请看下面的演示:
#!/bin/bash set -e # wait命令可以使当前shell进程挂起,等待所指定的由当前shell产生的子进程退出后,wait命令才返回。wait命令的参数可以是进程ID或是job specification。 sleep 5 & sleep 10 & echo '等待第二个子进程结束' wait %2 echo 'Finished' sleep 5 & sleep 10 & echo '等待所有子进程结束' # Bash shell还有一个内置变量:$!,用来记录最后一个被创建的后台进程 echo $! wait echo '主进程退出'
sleep 时无法接收到 SIGTERM,但可以通过 wait 方式实现。下面是 mongodb 容器的 entrypoint.sh,mongoDB启动需要一定时间,启动完成后我们才能设置副本集,等待启动的过程就用了 sleep:
#!/bin/bash _term() { # 测试请执行 docker stop <containerId>,会发送 SIGTERM 信号 echo "> mongod --shutdown --dbpath /data/db" mongod --shutdown --dbpath /data/db } trap _term HUP INT QUIT TERM set -e set -- $@ --dbpath /data/db --bind_ip 0.0.0.0 --port 27017 --replSet rs --oplogSize 128 echo ">>> starting mongo..." exec "$@" & child1=$! # 这里使用 wait 让 sleep 后台运行,以便在 sleep 时可以接收到 SIGTERM 信号。sleep 前台运行,调用 docker stop 是接收不到的。 # 运行速度慢的机器需要更久的 sleep sleep 30s & child2=$! wait $child2 echo ">>> set docker ip to replica set ..." #ipAddr=$(cat /etc/hosts | grep `hostname` | awk '{print $1}') #echo '>>> hostname:' $ipAddr echo ">>> initiate replica set..." mongo ${USR_LOCAL_BIN}/script.js echo ">>> initialization is finished." wait $child1
2 设陷阱捕捉 SIGTERM 信号
通常trap都在脚本中使用,主要有2种功能:
(1)忽略信号。当运行中的脚本进程接收到某信号时(例如误按了CTRL+C),可以将其忽略,免得脚本执行到一半就被终止。
(2)捕捉到信号后做相应处理。主要是清理一些脚本创建的临时文件,然后退出。
Signal Value Comment ─────────────────────────────────────────────────────────────────────────────────────── SIGHUP 1 终止进程,特别是终端退出时,此终端内的进程都将被终止 SIGINT 2 中断进程,几乎等同于sigterm,会尽可能的释放执行clean-up,释放资源,保存状态等(CTRL+C) SIGQUIT 3 从键盘发出杀死(终止)进程的信号 SIGKILL 9 强制杀死进程,该信号不可被捕捉和忽略,进程收到该信号后不会执行任何clean-up行为,所以资源不会释放,状态不会保存 SIGTERM 15 杀死(终止)进程,几乎等同于sigint信号,会尽可能的释放执行clean-up,释放资源,保存状态等 SIGSTOP 19 该信号是不可被捕捉和忽略的进程停止信息,收到信号后会进入stopped状态 SIGTSTP 20 该信号是可被忽略的进程停止信号(CTRL+Z) ───────────────────────────────────────────────────────────────────────────────────────
每个信号其真实名称并非是SIGXXX,而是去除SIG后的单词,每个信号还有其对应的数值代号,在使用信号时,可以使用这3种方式中的任一一种。例如SIGHUP,它的信号名称为HUP,数值代号为1。 在上面所列的信号列表中,KILL和STOP这两个信号无法被捕捉。一般来说,在设置信号陷阱时,只会考虑HUP、INT、QUIT、TERM这4个会终止、中断进程的信号。
- SIGTERM
“kill pid” 会发送SIGTERM到进程pid.
This is the termination signal sent by killcommand by default.
此进程可被捕获。 - SIGINT
在终端中敲入interrupt key(DELETE或ctrl+c)会产生SIGINT信号。这个信号会被发送到进程(inforeground process group)。当我们想终止一个失控程序(runaway program),可以发送这个信号。用于结束前台进程。 - SIGKILL
“kill -9 pid” 会发送SIGKILL到进程pid.
Linux下有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2)
*init进程完成系统的初始化. 是系统中所有其它用户进程的祖先进程。主要作用是处理僵尸进程。当某个父进程比子进程提前消亡时,父进程会给子进程重新寻找“养父进程”,一般就是进程1,由进程1负责处理该子进程的消亡。
下面看个Demo:
#!/bin/bash _term() { echo "主进程捕捉到了终止信号,准备强制kill子进程:" $child echo ">>> kill -9" $child kill -9 $child } trap _term HUP INT QUIT TERM echo "Doing some initial work..."; sleep 300 & child=$! echo '子进程ID:' $child # 如果我们这里不调用 wait,主进程会退出,此时子进程会分配给 PID=1的进程,PID为1是个特殊进程,sleep指定时间后,这个子进程也将销毁 wait "$child" echo '主进程退出'