Linux: 守护进程
- (一)前台进程和后台进程
- 前台进程
- 后台进程
- (二)会话、进程组、进程的关系
- (三)守护进程
- 创建守护进程
(一)前台进程和后台进程
前台进程
前台进程是指当前正在终端中运行,并占用标准输入、输出和错误输出的作业。它通常与用户的交互密切相关,例如用户正在使用的应用程序或服务。
后台进程
后台进程则是指不直接与用户交互的应用程序或服务。这类进程可能仍然在执行某些任务,但并不占据用户的注意力。
创建后台进程:
可执行程序 &
jobs
命令:
jobs 是一个用于查看当前 shell 中后台作业状态的内置命令。它主要用于显示由当前 shell 启动并仍在运行或已停止的作业列表。
fg + 后台进程号
命令:
将后台任务提到前台
bg + 后台进程号
:
将已暂停的后台进程重启
(二)会话、进程组、进程的关系
实际上我们使用 XShell 等工具登录 Linux 服务器时,会在服务器中创建一个 会话(bash),可以在该会话内创建 进程,当 进程 间有关系时,构成一个 进程组,组长 进程的 PID 就是该 进程组 的 PGID。
我们通过下面这个例子来验证上面的结论
sleep 1000 | sleep 2000 | sleep 3000 &sleep 100 | sleep 200 | sleep 300
可以发现 每一组的进程组 PGID 都与当前组中第一个被创建的进程 PID 一致,这个进程被称为 组长进程。无论是 后台进程 还是 前台进程,都是从同一个 bash 中启动的,所以它们处于同一个 会话 中,SID 和 终端文件 TTY 都是一样的。
每一个进程组就是执行同一个任务,只不过一个任务被分成了不用进程去执行。
Linux 中一切皆文件,终端文件也是如此,这里的终端其实就是当前 bash 输出结果时使用的文件(也就是屏幕),终端文件位于 dev/pts 目录下,如果向指定终端文件中写入数据,那么对方也可以直接收到
bash 进程本质上就是一个不断运行中的 前台进程,并且自成 进程组。
所以 SID 其实就是 bash 的 PID。在同一个 bash 中启动前台、后台进程,它们的 SID 都是一样的,属于同一个 会话,关联了同一个 终端。
(三)守护进程
守护进程:进程单独成一个会话,并且以后台进程的形式运行。
应用场景:一种长期运行的后台任务,通常用于执行诸如日志记录、定时任务或其他需要持续运行的服务功能。
创建守护进程
方法一:
该函数用于创建守护进程,原型如下:
#include <unistd.h>
int daemon(int nochdir, int noclose);
参数说明:
- nochdir 改变进程的工作路径
通常,daemon 函数会将当前工作目录更改为根目录(“/”),以避免当前目录被卸载。 - noclose 重定向标准输入、标准输出、标准错误
返回值:
- 成功返回 0,失败返回 -1
使用:
程序中调用 daemon(0, 0);
方法二:
通过创造会话手动 创造守护进程,系统提供的接口一键 守护进程化 固然方便,不过大多数程序员都会选择手动 守护进程化(可以根据自己的需求定制操作)
该函数用于创建一个会话。创建会话需要注意不能是一个进程组的组长(即进程组的id不能是该进程的pid)
#include <unistd.h>
pid_t setsid(void);
返回值:
- 成功返回该进程的 pid,失败返回 -1
手动实现守护进程需注意以下几点:
- 忽略异常信号
避免异常信号影响守护进程 - 0、1、2 要做特殊处理(文件描述符)
守护进程一般只要保持运行即可,不需要进行输入输出(比如服务器),若有需要关注的信息可以写入日志中。 - 进程的工作路径可能要改变(从用户目录中脱离至根目录)
改变守护进程的路径到 一个安全的位置, 避免目录的变动影响 守护进程
使用例子:
#pragma once #include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>const std::string nullfile = "/dev/null";void _Daemon(const std::string& cwd = “”)
{// 1.忽略其他信号signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2.将自己设置为独立会话 ---创建会话需要注意不能是一个进程组的组长,通过fork函数来完成。if (fork() > 0) exit(0); // 父进程直接退出setsid();// 3.更改文件的目录if (!cwd.empty()) chdir(cwd.c_str());// 4.将标准输入标准输出标准错误重定向到/dev/null这个垃圾桶文件中int fd = open(nullfile.c_str(), O_RDWR);if (fd > 0){// 重定向dup2(fd, 0); dup2(fd, 1);dup2(fd, 2);close(fd);}
}
/dev/dull 就是一个垃圾桶文件。
总的来说 守护进程用于在后台运行且独立于控制终端的特殊进程。它通常由系统启动并持续运行,提供各种服务而不受用户交互影响。这些进程主要用于执行定期任务、监控系统状态以及响应网络请求等功能。