目录
一、进程组
二、会话
(1)什么是会话
(2)如何创建一个会话
三、守护进程
一、进程组
之前我们学习过进程,其实每一个进程除了有一个进程 ID(PID)之外 还属于一个进程组。进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程。 每一个进程组也有一个唯一的进程组 ID(PGID), 并且这个 PGID 类似于进程 ID, 同样是一个正整数, 可以存放在 pid_t 数据类型中。
而每一个进程组都有一个组长进程。进程组的ID就等于组长进程的ID。
但是要注意:组长进程是不能创建新的会话的。要想创建一个新的会话,可以让该组长进程fork创建子进程,由子进程去创建新会话,然后组长进程直接退出即可。
二、会话
(1)什么是会话
刚刚我们谈到了进程组的概念, 那么会话又是什么呢? 会话其实和进程组息息相关,
会话可以看成是一个或多个进程组的集合, 一个会话可以包含多个进程组。每一个会
话也有一个会话 ID(SID)
在我们用Xshell打开一个中断窗口的时候,可以看到他显示的是新建会话(0)。
一个会话中只能有一个前台程序(前台程序是和显示器、键盘关联的程序,由他来响应用户的输入输出)默认情况下,创建一个新的会话的前台程序就是命令行解释器程序bash,这也是为什么我们能在终端中输入命令的原因。
后台程序和前台程序相对应,他没有直接关联显示器和键盘。如果想让一个程序是以后台程序的方式运行,只需要在以前./myprocess这种方式后面加上&取地址符号即可./myprecess &
为什么要谈及会话呢?我们之前写的无论是tcp服务器还是udp服务器,都是直接在命令行中运行的,但是这样非常的不安全,万一有人不小心Ctrl+C了,或者是将Xshell关闭了(会话关闭的时候,其中运行的进程都会终止),那么该服务器就结束了。为了防止这种情况,我们采取将服务器新建一个会话,而非打开Xshell创建的会话,这样即使我们Xshell关闭了,该服务器进程仍然一直在机器中运行。
(2)如何创建一个会话
可以调用 setseid 函数来创建一个会话, 前提是调用进程不能是一个进程组的组长。
该接口调用之后会发生:
需要注意的是: 这个接口如果调用进程原来是进程组组长, 则会报错, 为了避免这种情况, 我们通常的使用方法是先调用 fork 创建子进程, 父进程终止, 子进程继续执行, 因为子进程会继承父进程的进程组 ID, 而进程 ID 则是新分配的, 就不会出现错误的情况。
三、守护进程
守护进程就是我们上面说到的会话。在Xshell关闭后,用setsid系统调用创建的会话不会被关闭,想要关闭必须要使用kill -9命令。以后一个服务器想要不被退出,就直接调用下面的Daemon函数即可。
#pragma once
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
const char *root = "/";
const char *dev_null = "/dev/null";
void Daemon(bool ischdir, bool isclose)
{
// 1. 忽略可能引起程序异常退出的信号
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
// 2. 让自己不要成为组长
if (fork() > 0)
exit(0);
// 3. 设置让自己成为一个新的会话, 后面的代码其实是子进程在走
setsid();
// 4. 每一个进程都有自己的 CWD,是否将当前进程的 CWD 更改成为 /根目录
if (ischdir)
chdir(root);
// 5. 已经变成守护进程啦,不需要和用户的输入输出,错误进行关联了
if (isclose)
{
close(0);
close(1);
close(2);
}
else
{
// 这里一般建议就用这种
int fd = open(dev_null, O_RDWR);
if (fd > 0)
{
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
}
}
在unistd.h中也有写好的函数,可以直接将一个进程变成守护进程。但是由于兼容性,细节操作等问题,我们常常需要自己手动编写daemon函数,而不是直接调用库里面的函数。