Linux 会话与守护进程

参考:Linux session(会话)

会话(session)

Linux session 一般是指 shell session。Shell session 是终端中当前的状态,在终端中只能有一个 session。当我们打开一个新的终端时,总会创建一个新的 shell session。

就进程间的关系来说,session 由一个或多个进程组组成。一般情况下,来自单个登录的所有进程都属于同一个 session。

会话是由会话中的第一个进程创建的,一般情况下是打开终端时创建的 shell 进程。该进程也叫 session 的领头进程。Session 中领头进程的 PID 也就是 session 的 SID。

Session 中的每个进程组被称为一个 job,有一个 job 会成为 session 的前台 job(foreground),其它的 job 则是后台 job(background)。每个 session 连接一个控制终端(control terminal),控制终端中的输入被发送给前台 job,从前台 job 产生的输出也被发送到控制终端上。

如果 session 关联的是伪终端,这个伪终端本身就是随着 session 的建立而创建的,session 结束,那么这个伪终端也会被销毁。

nohup命令

一般情况下 session 的领头进程是 shell 进程,如果它处于前台,我们可以使用 exit 命令或者是 ctrl + d 让它退出。或者我们可以直接通过 kill 命令杀死 session 的领头进程。这里面的原理是:当系统检测到挂断(hangup)条件时,内核中的驱动会将 SIGHUP 信号发送到整个 session。通常情况下,这会杀死 session 中的所有进程。

如果我们在 session 中执行了 nohup 等类似的命令,当 session 消亡时,相关的进程并不会随着 session 结束,原因是这些进程不再受 SIGHUP 信号的影响。

例如:执行 $ nohup sleep1000 >/dev/null2>&1 &

Linux 会话与守护进程_第1张图片

此时 sleep 进程的 sid 和其它进程是相同的,还可以通过 pstree 命令看到进程间的父子关系。

Linux 会话与守护进程_第2张图片

如果我们退出当前 session 的领头进程(bash),sleep 进程并不会退出,这样我们就可以放心的等待该进程运行结果了。nohup 并不改变进程的 sid,同时也说明在这种情况中,虽然 session 的领头进程退出了,但是 session 依然没有被销毁(至少 sid 还在被引用)。重新建立连接,通过下面的命令查看 sleep 进程的信息,发现进程的 sid 依然是 7837,但是此时的 sleep 已经被系统的 1 号进程 systemd 收养了。

setsid函数

pid_t setsid(void); 创建一个会话,并以自己的 ID 设置进程组 ID,同时也是新会话的 ID

成功返回调用进程的会话 ID,失败返回-1,设置 error。

它的目的是让进程在后台执行命令,实现方式就是让命令进程运行在一个新的与终端脱离的 session 中。当一个进程通过调用 setsid 成为一个新的 session 领头进程时,它会与控制终端断开连接。该进程直接被系统的 1 号进程 systemd 收养。

守护进程(daemon)

daemon 进程。通常运行于操作系统后台,脱离控制终端,是被init进程收养的孤儿进程。一般不与用户直接交互。周期性的等待。

某个事件发生或周期性执行某一动作。

不受用户登录注销影响,守护进程在关闭终端时依然坚挺;而后台进程会随用户退出而停止,除非加上nohup。通常采用以 d 结尾的命名方式。

创建守护进程,最关键的一步是调用 setsid 函数创建一个新的 Session,并成为 Session Leader。

步骤:

1. fork 子进程,让父进程终止。

2. 子进程调用 setsid() 创建新会话

3. 通常根据需要,改变工作目录位置 chdir(), 防止目录被卸载。

4. 通常根据需要,重设 umask 文件权限掩码,影响新文件的创建权限。

5. 通常根据需要,关闭/重定向 文件描述符

6. 守护进程 业务逻辑。while()

int main(int argc, char *argv[])
{
    pid_t pid;
    int ret, fd;
    
    pid = fork();
    // 父进程终止,而fork出来的子进程,在父进程自杀后成为孤儿进程,进而被操作系统的init进程接管,因此脱离终端控制。
    if (pid > 0)
        exit(0);
    
    pid = setsid(); //创建新会话
    
    ret = chdir("/home/......"); // 改变工作目录位置
    
    umask(0022); // 改变文件访问权限掩码
    
    close(STDIN_FILENO); // 关闭文件描述符 0
    
    fd = open("/dev/null", O_RDWR); // 把命令的输出重定向到/dev/null来丢弃脚本的全部输出。  
    
    dup2(fd, STDOUT_FILENO); // 重定向 stdout 和 stderr
    dup2(fd, STDERR_FILENO);
    
    while (1); // 模拟 守护进程业务.
    
    return 0;
}

你可能感兴趣的:(Linux,linux)