建站提交历史文章,原文写作时间 2023 年 2 月前后。

守护进程

进程、进程组、会话

关联与权限

  • 进程间管理模式不仅包含父子关系,还包含如进程、进程组会话如此的组关系,会话包含进程组进程组包含进程
  • 进程的组关系与父子关系间不存在依赖关系,即任何亲缘关系的进程可以存在任意的组关系。但进程的组关系在逻辑上与亲缘关系有一定的默认联系:
    • 使用bash启动进程
      • pgid进程组ID):设置为pid
      • sid会话ID):设置为bash进程的sid
    • 使用fork创建进程:
      • pgid:设置为父进程pid
      • sid:设置为父进程sid
    • 也就是说,bash会为启动进程创建新的进程组fork在原进程组下创建进程。
  • 权限:一个会话中至多有一个进程组占用控制终端(具有控制权的终端),这也是会话名称的由来;进程通常只有操作自己与子进程pgidsid的权限,否则抛出Operation not permitted错误。

组关系管理函数

  • getpgidsetpgid
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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
  • getsidsetsid
  • setsid时,当前进程不能是进程组组长。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//     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

守护进程

  • 守护进程daemon process精灵进程)是一个没有控制终端的进程,在启动时即脱离控制终端而使用户无法直接观测到它的存在。守护进程进程名通常以d结尾,用于服务进程,可以长时间运行。

  • 守护进程需要脱离控制终端,但依然在终端中启动,因此创建守护进程需要一些步骤:

    • 创建进程分支并退出父进程
    • 创建会话
    • 清除UMASK以确保进程拥有所需的权限(可选)
    • 切换工作目录根目录(可选)
    • 清空文件描述符列表(可选)
    • 标准输入输出重定向到/dev/null(建议)
    • 业务逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#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;
}