守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程最重要的特性是后台运行

比如周期进行系统校验的进程

1、进程组

进程组就是一个或多个进程的集合。这些进程并不是孤立的,他们彼此之间或者存在父子、兄弟关系,或者在功能上有相近的联系。每个进程都有父进程,而所有的进程以init进程为根,形成一个树状结构,进程组的出现是为了统一的管理进程,每个进程能且仅能属于一个进程组。进程组有组号和组长,但是直到组内的所有进程都结束了进程组才会结束

获取当前进程所在的进程组的组ID
pid_t getpgrp(void);
获取指定进程所在的进程组的组ID
pid_t getpgid(pid_t pid);
- pid: 指定的进程的PID
将一个进程送到另外的一个进程组,创建一个新的进程组
int setpgid(pid_t pid, pid_t pgid);
- pid: 要操作的进程的PID
- pgid: 进程组ID

2、会话

一个用户登录一次系统就形成一次会话 。一个会话可包含多个进程组,但只能有一个前台进程组。每个会话都有一个会话首领(leader),即创建会话的进程。一个会话可以有一个控制终端。这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。建立与控制终端连接的会话首进程被称为控制进程

一次登录形成一个会话。一个会话可包含多个进程组,但只能有一个前台进程组。

// 获取进程所属的会话ID
  #include <unistd.h>
  pid_t getsid(pid_t pid);
  	返回值:
  		成功: 会话ID, 失败: -1 #include <unistd.h> 
  // 创建一个会话
  // 在哪个进程中调用该函数, 这个进程就会被提升为会话
  //  - 没有任何职务的进程才满足条件, 普通的进程
  //  - 这个普通的进程会脱离原来的操作终端
  pid_t setsid(void);

进程属于一个进程组,进程组属于一个会话,会话可能有也可能没有控制终端。

一般而言,当用户在某个终端上登录时,一个新的会话就开始了。进程组由组中的领头进程标识,领头进程的进程标识符就是进程组的组标识符。类似地,每个会话也对应有一个领头进程。

同一会话中的进程通过该会话的领头进程和一个终端相连,该终端作为这个会话的控制终端。一个会话只能有一个控制终端,而一个控制终端只能控制一个会话。用户通过控制终端,可以向该控制终端所控制的会话中的进程发送键盘信号。

同一会话中只能有一个前台进程组,属于前台进程组的进程可从控制终端获得输入,而其他进程均是后台进程,可能分属于不同的后台进程组。

当我们打开多个终端窗口时,实际上就创建了多个终端会话。每个会话都会有自己的前台工作和后台工作。

 

创建守护进程的步骤

- 1. 父进程创建子进程, 杀死父进程 -> 必须
- 2. 将子进程提升为会话 -> 必须
setsid();
- 3. 修改进程的工作目录, 工作目录切换到不能被卸载的目录中: / /home -> 不是必须
目的: 防止有些不安全目录被卸载:
在U盘总启动一个进程, 把U盘拔了, 进程无法正常运行
chdir();
- 4. 修改umask掩码 -> 不是必须
umask();
- 5. 关闭/重定向文件描述符 -> 不是必须
- 标准输入 -> close(0)
- 标准输出 -> close(1)
- 标准错误 -> close(2)
- 重定向: 设备文件: /dev/null ->int fd = open("/dev/null", O_RDWR);
dup2(0, fd)