【Linux进程】守护进程
目录
作者:爱写代码的刚子
时间:2024.4.27
前言:本篇博客将会介绍守护进程,以及进程组和会话的概念,如何变成守护进程等知识
守护进程
守护进程概念
守护进程也叫做精灵进程,是运行在后台的一种特殊进程他独立于控制终端并且可以周期性的执行某种任务或者等待处理某些发生的事件。守护进程是非常有用的进程,在Linux当中大多数服务器用的就是守护进程比如Web服务器httpd等,同时守护进程完成很多系统的任务。当Linux系统启动的时候,会启动很多系统服务,这些进程服务是没有终端的也就是说你把终端关闭了这些系统服务是不会停止的,他们一直运行着他们有一个名字就叫做守护进程。一般以服务器的方式工作,对外提供服务的服务器,都是以守护进程(精灵进程)的方式在服务器中工作的,一旦启动之后,除非用户主动关闭,否则,一直会在运行。
进程组和会话的概念
-
进程组的相关概念:
-
进程除了有进程的PID之外还有一个进程组,进程组是由一个进程或者多个进程组成。通常他们与同一作业相关联可以收到同一终端的信号
-
每个进程组有唯一的进程组ID,每个进程组有一个进程组组长。如何判断一个进程是不是这个进程组的组长了,通常进程组ID等于进程ID那么这个进程就是对应进程组的组长。
-
-
会话的相关概念:
- 会话是有一个或者多个进程组组成的集合
- 一个会话可以有一个终端,建立与控制终端连接的会话首进程被成为控制进程,一个会话的几个进程组可以分为前台进程和后台进程,而这些进程组的控制终端相同也就是sesion id是一样的当用户使用ctr +c 产生SIGINT信号时内核会发送信号给相应前台进程组的所有进程如果我运行一个程序我们想要把他放到后台运行我们可以在可执行程序的后面加一个& 举个列子:./test & 如果我们想要把他提到前台进程我们可以使用fg
无论是前台进程还是后台进程,都可以向显示器打印,一个session只能有一个前台进程运行,键盘信号只能发给前台进程(谁拥有键盘文件,谁就是前台进程)
-
jobs查看后台进程
-
fg +任务号将后台任务提到前台
-
ctrl + z将前台进程暂停并放到后台
-
bg +任务号将暂停的后台进程重新启动
进程与进程之间不仅有进程独立性的关系,还有进程组的关系
- 上述我创建了三个进程,可以确定的是这三个进程的PPID都是一样的,因为父进程都是Bash。上述三个进程是属于同一个进程组(PGID)的,且会发现启动的第一个进程是进程组的组长
- **ps axj | head -1 && ps axj | grep -E '-bash$'**查看bash进程
bash是独立成进程组独立成会话的一个进程
- while :;do ps ajx | head -1 && ps ajx | grep -E '-bash$';sleep 1;done进行循环打印测试
我们新打开了一个会话,发现bash进程变多了
- 我们创建四个后台进程:
- 查看当前process进程:
- 关闭bash重新登陆bash后查看process进程:
从上面我们可以看到纯后台进程是会收到用户登陆和退出的影响的
不想受到任何用户登陆和注销的影响就要------守护进程化
我们将自成进程组、自成会话的进程叫做守护进程,与bash变成并列关系而不是包含关系
- setsid()函数创建一个session并将调用该函数进程的组ID变成会话的ID
注意,要调用这个函数,调用的进程不能是进程组的组长
【问题】:如何保证自己不是组长?
调用fork()函数,让父进程退出,子进程调用setsid()
if(fork() > 0) exit(0);setsid()
所以守护进程的本质也是孤儿进程
Daemon.hpp
cpp
#pragma once
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
const std::string nullfile = "/dev/null";
//进来的是父进程,出去的是子进程
void Daemon(const std::string &cwd = "")
{
//1. 忽略其他异常信号
signal(SIGCLD,SIG_IGN);//父进程不需要调用wait进行回收子进程
signal(SIGPIPE,SIG_IGN);
signal(SIGSTOP,SIG_IGN);
//2. 将自己变成独立的会话
if(fork() >0) exit(0);
setsid();
//3. 更改调用进程的工作目录
if(!cwd.empty())
{
chdir(cwd.c_str());
}
//Daemon函数可以运用到服务器Start()函数的开头,将该进程变成守护进程,但是我们可能会想到,服务器中会存在大量的打印,往标准输出打
//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);
}
}
- dup2函数
- 将Daemon函数添加到Tcp服务器中,守护进程化
第一行 -D 结尾的就是服务器(守护进程)。它的PPID是1。下面来介绍上述选项的意义:
COMMAND:启动的进程命令名称
TIME:进程启动的时长
UID:是谁启动的
STAT:状态
TPGID:当前进程组和终端的关系(如果是-1,则没有任何关系)
TTY:代表哪一个终端
SID:当前进程的会话ID
PGID:当前进程所属的进程组
PID:当前进程自己的ID
PPID:当前进程的父进程的ID
- 查看重定向信息
- 切换守护进程的目录
- 【问题】:为什么我们能远程登陆服务器?(存在sshd守护进程)
所以守护进程一般以d结尾,一般把日志信息存放到文件中
于是便有了日志信息
- 要特别注意,如果变成了守护进程并改变了路径,log目录的位置也一定要改变!!!
系统的守护进程函数
- 如果参数nochdir为0,则将守护进程的工作目录该为根目录,否则不做处理。
- 如果参数noclose为0,则将守护进程的标准输入、标准输出以及标准错误重定向到
/dev/null
,否则不做处理。