守护进程
守护进程是在后台运行不受终端控制的进程。
- 脱离终端
- 避免执行过程中的信息在终端上显示
- 不会被任何终端所产生的终端信息打断
进程组
- 每个进程除了有一个进程ID(PID)之外,还属于一个进程组
- 进程组是一个或多个进程的集合,同一进程组中的各进程接收来自同一终端的各种信号
- 每个进程组有一个组长进程。组长进程的进程组ID等于其进程ID
会话
会话(session)是一个或多个进程组的集合,非进程组长的进程调用 setsid 函数(原型:pid_t setsid(void) )建立一个会话:
- 该进程变成新会话的会话首进程(session leader,会话首进程是创建该会话的进程)。此时,该进程是新会话的唯一进程。
- 该进程成为一个新进程组的组长进程。新进程组ID是该调用进程的进程ID
- 该进程没有控制终端。如果调用setsid之前该进程有一个控制终端,那么这种联系也被切断
控制终端
控制终端就是用户登录linux时的窗口(TTY),控制终端必定有1个会话,1个会话最多有1个控制终端(会话组长未打开控制终端时无控制终端)。关闭控制终端,该控制终端中的进程退出。
# 查看进程组所属控制终端,TTY列为控制终端
ps -fu [UID]
构建一个守护进程
- 调用
fork
创建子进程。父进程终止,让子进程(孤儿进程一般由init收养)在后台继续执行。 - 子进程调用
setsid
产生新会话期并失去控制终端,调用setsid()使子进程进程成为新会话组长和新的进程组长,同时失去控制终端。 - 忽略SIGHUP信号。会话组长进程终止会向其他进程发该信号,造成其他进程终止。
- 调用fork再创建子进程。子进程终止,子子进程继续执行,由于子子进程不再是会话组长,从而禁止进程重新打开控制终端。
- 改变当前工作目录为根目录。一般将工作目录改变到根目录,这样进程的启动目录也可以被卸掉。
- 关闭打开的文件描述符,打开一个空设备,并复制到标准输出和标准错误上。 避免调用的一些库函数依然向屏幕输出信息。
关闭文件描述符后,通过
open("/dev/null", O_RDWR)
将文件描述符0,1,2分配出去。当一个用户进程被创建的时候,系统会自动为该进程创建三个数据流stdin、stdout、stderr(默认为终端,可重定向到其他)。在默认情况下,stdout是行缓冲的,他的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕。而stderr是无缓冲的,会直接输出。
umask(0)
,重设文件创建掩码清除从父进程那里继承来的文件创建掩码,设为0。
umask(x):设置允许当前进程创建文件或目录的最大权限为 ~x,umask(0)即最大允许777权限,实现机制:创建文件时的权限与x取反后相与 = (~x) & mode
- 用openlog函数建立与syslogd的连接
// 来自《APUE》一书
void daemonize(const char *cmd