Linux Shell 笔记

By | 2021年12月31日

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 '主进程退出'

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注