进程组,也叫作业,代表一个或者多个进程的集合。每个进程都属于一个进程组,父进程创建子进程的时候,默认是子进程和父进程属于同一进程组。进程组ID为第一个进程的ID,进程组的第一个进程为组长进程,也就是组长进程的ID和进程组的ID一样。进程组的标识符为PGID。
进程组拥有生命周期,开始时间为首进程创建组的时刻,结束时间是最后一个成员进程离开组的时刻。一个进程可能会因为终止而退出进程组,也可能是加入了另外一个进程组。进程组首进程不用是最后一个离开进程组,只要进程组有一个进程存在,进程组就存在,与组长进程是否终止无关。
Linux是用户控制的多作业操作系统,系统允许多个系统用户同时提交作业,而一个系统用户可以用多个shell提交作业。作业有两种运行方式:前台运行和后台运行
前台运行可以控制当前终端或窗口,并且接收用户输入
后台运行是不激活当前终端或者窗口中运行,而是在用户看不到情况下运行。
键盘输入只有前台进程可以读取,后台进程无法得到键盘输入值
会话是一个或者多个进程组的集合。会话首进程是创建该新会话的进程,进程ID会成为会话ID。
一般是用管道将几个进程编程一个进程组。
Shell[node@localhost code]$ proc2 | proc3 &[node@localhost code]$ proc4 | proc5 | proc6 &# &表示将进程组放在后台执行
Shell# 用管道和 sleep 组成一个进程组放在后台运行[node@localhost code]$ sleep 100 | sleep 200 | sleep 300 &# 查看 ps 命令打出来的列描述信息[node@localhost code]$ ps axj | head -n1
#include
pid_t setsid(void);
setsid
函数不需要任何参数。它的作用是创建一个新的会话(Session),并使调用它的进程成为该会话的会话首进程(Session Leader)。
注意:如果调用的进程原来进程组组长,就会报错 ,为了避免这种情况发生,可以使用fork创建子进程,父进程终止,子进程继续执行,因为子进程会继承父进程的进程组ID,而进程ID则是新分配的,就不会出现错误情况。
假设父进程的进程 ID 是 1234,它所在的进程组 ID 也是 1234(假设父进程是进程组组长)。当父进程调用
fork
创建子进程时:
子进程的进程 ID:假设子进程的进程 ID 是 1235。
子进程的进程组 ID:子进程会继承父进程的进程组 ID,即 1234。
子进程不是进程组组长:因为子进程的进程 ID 是 1235,而进程组 ID 是 1234,所以子进程不是进程组组长。
会话首进程具有唯一进程ID的单个进程,可以将会话首进程的进程ID当作会话ID。注意:会话ID也叫会话首进程的进程组ID,因为会话首进程总是一个进程组的组长进程,两者是等价的。
会话首进程的特性:
会话首进程是会话中的第一个进程。
会话首进程的进程组 ID 等于它的进程 ID。
会话首进程的进程组 ID 也作为会话 ID。
会话 ID 的定义:
会话 ID 是会话首进程的进程组 ID。
因为会话首进程总是它所在进程组的组长,所以会话 ID 和会话首进程的进程组 ID 是相同的。
用于将进程从用户会话中分离出来,在后台独立运行,即使用户注销或者关闭终端,进程也能运行。
好的!让我用一个生活中的例子来类比守护进程的概念,帮助你更好地理解为什么需要守护化。
### 生活中的类比:自动售货机
假设你正在经营一家咖啡店,店门口有一个自动售货机,专门提供咖啡、饮料等商品。这个自动售货机就像一个“守护进程”,它需要满足以下要求:
#### 1. **持续运行**
- **守护进程**:Web服务器需要在后台持续运行,即使用户注销或关闭终端,服务器仍然可以继续运行。
- **生活例子**:自动售货机需要24小时不间断运行,即使店员下班或顾客离开,售货机仍然可以继续提供服务。#### 2. **独立运行**
- **守护进程**:Web服务器在后台独立运行,不依赖于用户会话。
- **生活例子**:自动售货机独立运行,不需要店员在场。顾客可以随时使用售货机购买商品,而不需要店员的协助。#### 3. **自动启动和关闭**
- **守护进程**:Web服务器在系统启动时自动启动,并在系统关闭时优雅地关闭。
- **生活例子**:自动售货机在每天早上自动启动,晚上自动关闭。即使店员忘记开启或关闭售货机,它也能自动运行。#### 4. **资源管理**
- **守护进程**:Web服务器释放不必要的资源,避免占用过多系统资源。
- **生活例子**:自动售货机在运行过程中会自动管理资源,例如:
- 定期检查库存,自动补充商品。
- 自动清理垃圾,保持机器内部清洁。
- 自动记录销售数据,方便店主查看。#### 5. **稳定性**
- **守护进程**:Web服务器需要在后台稳定运行,即使遇到错误也能自动恢复。
- **生活例子**:自动售货机需要稳定运行,即使遇到故障(如卡币、缺货等),也能自动报警并尝试恢复。例如:
- 如果机器卡币,售货机会自动退币并提示顾客。
- 如果商品缺货,售货机会自动显示“缺货”信息,并在补货后恢复销售。### 总结
守护进程化的主要目的是让一个服务在后台独立、持续、稳定地运行,就像自动售货机一样:
- **持续运行**:无论用户是否在场,服务都能继续运行。
- **独立运行**:不依赖于用户会话,即使用户注销或关闭终端,服务仍然可以继续运行。
- **自动启动和关闭**:在系统启动时自动启动,在系统关闭时优雅地关闭。
- **资源管理**:自动管理资源,减少对系统的干扰。
- **稳定性**:即使遇到错误,也能自动恢复,确保服务的稳定性。通过守护化,服务可以像自动售货机一样,为用户提供可靠的、不间断的服务。
第一步是忽略SIGPIPE和SIGCHLD信号。SIGPIPE信号在写入一个已关闭的管道时触发,忽略它可以防止进程因管道断开而终止。SIGCHLD的忽略是为了防止子进程结束时父进程收到信号,导致产生僵尸进程。第二步是fork一个子进程,然后父进程退出。这样做是为了让子进程脱离父进程的控制,成为孤儿进程,由init进程接管。这一步之后,子进程继续执行,而父进程结束,这样进程就不再与终端关联。第三步是调用setsid()创建新的会话组,使子进程成为会话的领头进程,脱离原来的终端。这一步很重要,因为这样进程就不再有任何控制终端了。第四步,如果nochdir参数为0(即默认情况),则将工作目录改为根目录。这样做是为了防止进程的当前目录位于某个挂载的文件系统中,导致该文件系统无法卸载。第五步处理标准输入、输出和错误的重定向。如果noclose参数为0,即默认情况下,打开/dev/null并复制到标准输入、输出和错误。这样守护进程的所有输出都会被丢弃,不会显示在终端上。这里使用了open函数以读写模式打开/dev/null,然后通过dup2将文件描述符0、1、2重定向到该文件。之后关闭原始的fd。
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include "Log.hpp"
#include "Common.hpp"
using namespace LogModule;
const std::string dev="/dev.null";
void Daemon(int nochdir,int noclose)
{
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
if(fork()>0)
exit(0);
setsid();
if(nochdir==0)
chdir("/");
if(noclose==0)
{
int fd=::open(dev.c_str(),O_RDWR);
if(fd<0)
{
LOG(LogLevel::FATAL)<<"open"<
安装与卸载操作
前面两条cp命令把编译生成的二进制文件强制复制到系统全局路径/usr/bin,后面两条rm命令就会删除/usr/bin中的两个文件
cp -f ./bin/ServerNetCald /usr/bin
cp -f ./bin/client_netcal /usr/bin
rm -rf /usr/bin/ServerNetCald
rm -f /usr/bin/client_netcal
输出目录与打包
创建号output目录和子目录bin,conf,log,将创建好的二进制文件,配置文件和脚本复制到output,打包为output.tgz
.PHONY:output
output:
mkdir output
mkdir -p output/bin
mkdir -p output/conf
mkdir -p output/log
cp ServerNetCald output/bin
cp client_netcal output/bin
cp test.conf output/conf
cp install.sh output/
cp uninstall.sh output/
tar czf output.tgz output
解释下面代码:
cp -f ./bin/ServerNetCald /usr/bin
cp -f ./bin/client_netcal /usr/bin
rm -rf /usr/bin/ServerNetCald
rm -f /usr/bin/client_netcal
.PHONY:all
all:ServerNetCald client_netcal
ServerNetCald:main.cc
g++ -o $@ $^ -std=c++17 -ljsoncpp -static -lpthread
client_netcal:TcpClient.cc
g++ -o $@ $^ -std=c++17 -ljsoncpp -static -lpthread
.PHONY:output
output:
@mkdir output
@mkdir -p output/bin
@mkdir -p output/conf
@mkdir -p output/log
@cp ServerNetCald output/bin
@cp client_netcal output/bin
@cp test.conf output/conf
@cp install.sh output/
@cp uninstall.sh output/
@tar czf output.tgz output
.PHONY:clean
clean:
rm -rf ServerNetCald client_netcal output output.tgz