Linux会话与守护进程
会话(Session)
会话:多个进程组的集合。
概念:
一个session对应着一个控制终端, 我们常见的 Linux session 一般是指 shell session。Shell session 是终端中当前的状态,在终端中只能有一个 session。当我们打开一个新的终端时,总会创建一个新的 shell session。
就进程间的关系来说,session 由一个或多个进程组组成。一般情况下,来自单个登录的所有进程都属于同一个 session。
Session 中的每个进程组被称为一个 job,有一个 job 会成为 session 的前台 job(foreground),其它的 job 则是后台 job(background)。
前台job主要负责通过控制终端与用户进行交互,而后台job主要负责周期性的执行某项任务,例如输出内容,监听某个端口。
每个 session 连接一个控制终端(control terminal),控制终端中的输入被发送给前台 job,从前台 job 产生的输出也被发送到控制终端上。同时由控制终端产生的信号,比如 ctrl + z 等都会传递给前台 job。
一般情况下 session 和终端是一对一的关系,当我们打开多个终端窗口时,实际上就创建了多个 session。
意义:
Session 的意义在于:多个工作(job)在一个终端中运行,其中的一个为前台 job,它直接接收该终端的输入并把结果输出到该终端。其它的 job 则在后台运行。有效的实现了对进程组的分组管理。就如同进程组的存在可以方便向一组特定的进程发送信号,会话也可以很好的实现这种分组的功能。进程组和会话的发明是为了更好的实现工作控制。
操作:
getsid 函数:
获取当前进程的会话 id
1 | pid_t getsid(pid_t pid) |
成功返回调用进程会话 ID,失败返回-1。
setsid 函数:
创建一个会话,并以自己的 ID 设置进程组 ID,同时也是新会话的 ID
1 | pid_t setsid(void) |
成功返回调用进程的会话 ID,失败返回-1。
创建注意事项:
- 调用进程不能是进程组组长,该进程变成新会话首进程
- 该进程成为一个新进程组的组长进程
- 需要 root 权限(ubuntu 不需要)
- 新会话丢弃原有的控制终端,该会话没有控制终端
- 该调用进程是组长进程,则出错返回
- 建立新会话时,先调用 fork,父进程终止,子进程调用 setsid
补充:一个进程组的id为进程组长的id,如果是父进程fork出了子进程。那么父进程即为这一进程组的组长进程。因为创建会话的调用进程不能为组长进程。因此需要用子进程创建新的会话。
为什么进程组组长不能创建会话:
进程组ID是进程组组长的PID.会话ID是会话首进程的PID.成功调用setsid()之后,进程组ID,会话ID和PID应该相同.
但是,对于进程组负责人,进程组ID已等于PID.如果能够调用setsid(),则它的进程组ID保持不变,因此:
>进程组组长属于新会话;
>其他进程组成员属于旧会话.
因此,在这种情况下,我们有一个进程组,其成员属于不同的会话. POSIX希望禁止这种情况。
为什么禁止:
控制终端会将信号发送给前台进程组。
只有当进程组里面的所有进程共享同一控制终端的时候,发送的信号才对其有意义.
所以规定,来自同一会话的所有进程共享同一控制终端,来自不同会话的进程不能共享同一控制终端
因此,如果我们要求进程组的所有成员共享同一控制终端,则我们还应要求它们成为同一会话的成员.
守护进程
概念
守护进程(daemon 进程)通常运行于操作系统后台,脱离控制终端。一般不与用户直接交互。周期性的等待某个事件发生或周期性执行某一动作。 不受用户登录注销影响。通常采用以 d 结尾的命名方式,例如httpd等。
创建:
守护进程创建步骤:
- fork 子进程,让父进程终止。
- 子进程调用 setsid() 创建新会话
- 通常根据需要,改变工作目录位置 chdir(), 防止目录被卸载。
- 通常根据需要,重设 umask 文件权限掩码,影响新文件的创建权限。
- 通常根据需要,关闭/重定向 文件描述符
- 执行守护进程的业务逻辑
实例:
1 |
|
运行效果:
用ps ajx查看,结果如下: