问题起源
前几日,遇到一个问题,需要收集在容器里一个 daemon 进程的输出日志。一般来说,容器里的进程只需要打日志到标准输出就可以了,但 daemon 进程比较特殊,daemon 进程没有控制终端,也没有继承相应的文件描述符。
上述问题可以转化为一个通用的问题:如何让一个 daemon 进程输出日志到当前的控制台上?
另外,supervisor 及 docker 在启动的时候,是不允许启动后台进程的,这是为什么呢?
在解答之前,先来看一些相关的基础知识。
基础知识
- 控制终端
控制终端的本质是一个设备文件,由会话首进程打开,由终端驱动程序控制。控制终端接收用户从终端的输入,将输入内容传送给与该终端相关联的前台进程;或者发送相应的信号到相应的前台进程。
1.一个控制终端对应着一个标准输入、标准输出及标准错误输出(fd 0, 1, 2)。
2.一个控制终端对应一个会话。
3.一个控制终端对应着一组前台进程组,多组后台进程组,这些进程组属于同一个会话。用户在终端输入 CTRL-C 时,控制终端会发送信号到前台进程组的所有进程。
4.一个控制终端对应着一个会话首进程,该进程是建立会话的第一个进程,通常为 shell 进程,终端断开后收到相应的信号(挂起信号),进行相应的清理工作。 - 会话
一个会话包含多个进程组。
一个会话包含一个会话首进程,只有会话首进程可以打开控制终端。 进程组
多个进程组成一个进程组。有一个进程组长,进程组长的 pid 即为该进程组的 group id。通常通过 fork 调用生成的进程都属于同一个进程组。一个进程只能为它自己或者子进程设置 group id。
会话、进程组、控制终端的关系
查看一个进程的 pid,gpid,spid,command
ps -p 32036 -o pid,ppid,pgid,sid,tpgid,comm
PID PPID PGID SID TPGID COMMAND
32036 1098 32036 32036 -1 sshd
上述命令打印 32036 号进程,父进程为 1098,进程组号为 32036,会话号为 32036。
ps -o pid,ppid,pgid,sid,tpgid,comm
打印前台进程组的进程信息。
ps axjf
查看进程树。
- daemon 进程
daemon 进程:我只想做一个安静的好进程。主要有以下特点:后台运行(无控制终端,且不能打开新的控制终端,不接收控制台信号),无标准输出、标准错误输出(不会打印到控制台,或者重定向到其它文件),无标准输入(不接收控制台输入)。 一些系统调用
umask() 设置文件模式创建屏蔽字。子进程一般会继承父进程的文件模式创建屏蔽字。
#include <sys/types.h> #include <sys/stat.h> mode_t umask(mode_t mask);
setsid() 创建一个新会话。