作业控制 (Job Control)
作业控制是大多数 shell 提供的一个功能,允许用户在单个终端上同时运行和管理多个命令(作业)。
-
前台作业 (Foreground Job): 在终端中启动,它会独占终端的输入和输出。在它运行期间,shell 会被挂起,等待该作业完成。
-
后台作业 (Background Job): 通过在命令后添加
&符号启动。它不会占用终端,你可以在它运行时继续在 shell 中输入其他命令。 -
Ctrl+Z(SIGTSTP)暂停前台进程Ctrl+C(SIGINT)终止当前前台进程Ctrl+D(EOF) -
常用命令:
jobs,fg,bg,kill %<job_id>。
进程组 (Process Group)
进程组是一个或多个进程的集合。系统中的每个进程都属于一个进程组。
-
目的: 主要用于作业控制,方便将信号(如
SIGINT,SIGTSTP)发送到一组相关的进程。例如,当你在终端按下Ctrl+C时,SIGINT信号会被发送到当前前台作业的整个进程组。 -
进程组ID (PGID): 每个进程组都有一个唯一的 ID。
-
进程组领导 (Process Group Leader): 进程组中第一个创建的进程,其 PID 通常就是该进程组的 PGID。
-
一个管道(pipeline)中的所有进程(例如
cat file | grep "text" | wc -l)通常属于同一个进程组。
会话 (Session)
会话是一个或多个进程组的集合。它提供了一个更高层次的进程组织方式。
-
目的: 将一个用户登录到退出期间创建的所有进程组织在一起。
-
会话ID (SID): 每个会话都有一个唯一的 ID。
-
会话领导 (Session Leader): 创建该会话的进程。通常,这是用户登录时启动的 shell 进程。
-
控制终端 (Controlling Terminal): 一个会话通常与一个控制终端相关联。当控制终端断开连接时(例如关闭终端窗口或网络断开),内核会向会话领导发送
SIGHUP信号,后者通常会将其传播给会话中的所有进程,导致它们终止。
setsid() 系统调用
setsid() 是一个关键的系统调用,用于创建一个新的会话。
- 功能:
1. 调用 setsid() 的进程会成为一个新会话的会话领导。
2. 该进程会成为一个新进程组的进程组领导。
3. 该进程会脱离它之前的控制终端。
-
前提: 调用
setsid()的进程不能是某个现有进程组的领导者。为了确保这一点,通常的做法是fork()一个子进程,然后让父进程退出,子进程再调用setsid()。 -
用途: 这是创建守护进程(Daemon)的标准方法。通过创建一个没有控制终端的新会hs话,守护进程可以确保自己不会因为终端的关闭而意外终止。
lxc-attach 后台进程导致退出卡住的原因分析
问题场景
-
用户在终端中执行
lxc-attach <container_name>。 -
在
lxc-attach创建的 shell 中,启动一个后台作业。 -
输入
exit尝试退出lxc-attach的 shell。 -
此时,终端卡住,用telnet 连接然后kill 掉才可以回到host 主机的shell
原因剖析
这个问题的核心在于进程关系和信号处理。
- 进程结构:
- 当你运行 lxc-attach 时,它会在容器内启动一个新的 shell 进程(如 bash)。
- 这个新的 shell 是 lxc-attach 进程的子进程。
- 重要的是,这个新 shell 没有成为新的会话领导。它与 lxc-attach 进程、以及你最初的登录 shell 位于同一个会话中,并共享同一个控制终端。
- 启动后台进程:
- 当你在 lxc-attach 的 shell 中运行一个后台进程。
- 这个 后台进程与 lxc-attach 的 shell 属于同一个进程组。
- 执行
exit:
- 你输入 exit,lxc-attach 的 shell 进程开始退出流程。
- shell 进程本身会终止。
- lxc-attach 进程在等待其子进程(即那个 shell)完全终止。
- 卡住的根源:
- shell 进程虽然终止了,但它启动的后台进程仍然在运行。
- 这个后台进程仍然是前台进程组的一部分(相对于控制终端而言),或者说它仍然与控制终端关联。
- 控制终端的驱动程序会发现,虽然前台的 shell 退出了,但该进程组里还有其他进程 在运行。此时,终端会处于一种“挂起”或“等待”状态,因为它需要处理这个仍在运行的后台进程的标准输入/输出(即使它不读写)。
- lxc-attach 进程本身也在等待,因为它可能需要清理与子进程相关的所有资源。只要 这个后台进程还在运行,整个进程链就无法干净地结束。
- 只有当 这个后台进程结束后,整个进程组才算完全终结,控制终端的锁定状态被解除,lxc-attach 进程也随之退出,你才能回到原来的 shell 提示符。
-
解决办法:
nohupsetsiddisown