2.4 守护进程
2.4.1 进程、进程组、会话
关联与权限
- 进程间管理模式不仅包含父子关系,还包含如进程、
进程组
、会话
如此的组关系,会话
包含进程组
、进程组
包含进程
。 - 进程的组关系与父子关系间不存在依赖关系,即任何亲缘关系的进程可以存在任意的组关系。但进程的组关系在逻辑上与亲缘关系有一定的默认联系:
- 使用
bash
启动进程pgid
(进程组ID
):设置为pid
sid
(会话ID
):设置为bash
进程的sid
- 使用
fork
创建进程:pgid
:设置为父进程
的pid
sid
:设置为父进程
的sid
- 也就是说,
bash
会为启动进程创建新的进程组
,fork
在原进程组
下创建进程。
- 使用
- 权限:一个
会话
中至多有一个进程组
占用控制终端
(具有控制权的终端
),这也是会话
名称的由来;进程通常只有操作自己与子进程
的pgid
与sid
的权限,否则抛出Operation not permitted
错误。
组关系管理函数
getpgid
、setpgid
#include <sys/types.h>
#include <unistd.h>
// get process group identifier
// pid:
// process identifier, or 0 standing for calling process
// return value:
// process group identifier, or -1 for error
pid_t getpgid(pid_t pid);
// set process group identifier
// pid:
// process identifier, or 0 standing for calling process
// pgid:
// process group identifier set to,
// or 0 standing for creating a group which ID is equal to the calling process ID
// return value:
// return 0 for success, -1 for error
int setpgid(pid_t pid, pid_t pgid);
// About more
// $ man 2 getpgid
getsid
、setsid
setsid
时,当前进程不能是进程组
组长。
// get session identifier
// pid:
// process identifier, or 0 standing for calling process
// return value:
// session identifier, or -1 for error
pid_t getsid(pid_t pid);
// $ man 2 getsid
// set session identifier
// return value:
// new session identifier, or -1 for error
// NOTE:
// If the calling process isn't the group leader, a new session and group which ID is equal to pid will be created. That means the new session will contain the only process, and it is both the leader of session and group. Otherwise, an error will be raised.
pid_t setsid(void);
// $ man 2 setsid
2.4.2 守护进程
-
守护进程
(demon process
、精灵进程
)是一个没有控制终端
的进程,在启动时即脱离控制终端
而使用户无法直接观测到它的存在。守护进程
进程名通常以d
结尾,用于服务进程
,可以长时间运行。 -
守护进程
需要脱离控制终端
,但依然在终端
中启动,因此创建守护进程
需要一些步骤:- 创建
进程分支
并退出父进程
- 创建
会话
- 清除
UMASK
以确保进程拥有所需的权限(可选) - 切换
工作目录
到根目录
(可选) - 清空
文件描述符
列表(可选) - 将
标准输入输出
重定向到/dev/null
(建议) - 业务逻辑
- 创建
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
// 创建进程分支, 并退出父进程
int pid = fork();
if (pid) exit(0);
// 创建新会话
setsid();
// 切换到根目录
chdir("/");
// 关闭文件描述符列表 (此处文件描述符列表已空)
// 重定向标准输入输出
int fd_null = open("/dev/null", O_RDWR);
for (int i = 0; i < 3; i++) {
dup2(i, fd_null);
}
// 核心业务
// ...
return 0;
}