🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12891150.html
目录
前言
💬 hello! 各位铁子们大家好哇。
今日更新了Linux守护进程的内容
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
进程组
什么是进程组
每一个进程除了有一个进程ID(PID)之外还属于一个进程组。进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程。每一个进程组也有一个唯一的进程组 ID(PGID), 并且这个 PGID 类似于进程ID,同样是一个正整数, 可以存放在 pid_t 数据类型中。
如上图,三个进程的PGID是一样的,也就是说这三个进程属于同一个组,并且组长是这三个进程中,第一个被创建的进程。
介绍ps的两个选项:
- -e 选项表示 every 的意思, 表示输出每一个进程信息
- -o 选项以逗号操作符(,)作为定界符, 可以指定要输出的列
进程组长
每一个进程组都有一个进程组长。 进程组长的 ID 等于其进程 ID。
- 进程组组长的作用: 进程组组长可以创建一个进程组或者创建该组中的进程
- 进程组的生命周期: 从进程组创建开始到其中最后一个进程离开为止。注意:只要某个进程组中有一个进程存在, 则该进程组就存在, 这与其组长进程是否已经终止无关。
补充
在命令行中,./直接启动的任务,一般就是前台进程。如果想放在后台运行,就在后面加一个&。
同一个会话中,允许同时存在多个进程组。但是,任何时刻,只允许有一个前台进程(组),可以允许多个后台进程组。
如上图,当bash是前台进程时,pwd指令能生效。当别的进程成为前台进程,bash进程就自动成为后台进程了,此时ls,pwd等指令就无效了。
会话
什么是会话
会话可以看成是一个或多个进程组的集合, 一个会话可以包含多个进程组。每一个会话也有一个会话 ID(SID)
会话id,一般是会话中第一个进程,一般是bash。
如何创建会话
可以调用 setseid 函数来创建一个会话, 前提是调用进程不能是一个进程组的组长。
pid_t setsid(void);
功能:创建会话
返回值:创建成功返回 SID, 失败返回-1
该接口调用之后会发生:
- 调用进程会变成新会话的会话首进程。 此时, 新会话中只有唯一的一个进程
- 调用进程会变成进程组组长。 新进程组 ID 就是当前调用进程ID
- 该进程没有控制终端。 如果在调用 setsid 之前该进程存在控制终端,则调用之后会切断联系
需要注意的是: 这个接口如果调用进程原来是进程组组长, 则会报错,为了避免这种情况, 我们通常的使用方法是先调用 fork 创建子进程, 父进程终止,子进程继续执行, 因为子进程会继承父进程的进程组 ID, 而进程 ID 则是新分配的,就不会出现错误的情况。
会话id(SID)
会话首进程是具有唯一进程 ID 的单个进程, 那么我们可以将会话首进程的进程ID当做是会话 ID。注意:会话 ID 在有些地方也被称为 会话首进程的进程组ID,因为会话首进程总是一个进程组的组长进程, 所以两者是等价的。
控制终端
在 UNIX 系统中,用户通过终端登录系统后得到一个 Shell 进程,这个终端成为Shell 进程的控制终端。控制终端是保存在 PCB 中的信息,我们知道fork 进程会复制PCB中的信息,因此由 Shell 进程启动的其它进程的控制终端也是这个终端。默认情况下没有重定向,每个进程的标准输入、标准输出和标准错误都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。另外会话、进程组以及控制终端还有一些其他的关系,我们在下边详细介绍一下:
- 一个会话可以有一个控制终端,通常会话首进程打开一个终端(终端设备或伪终端设备)后,该终端就成为该会话的控制终端。
- 建立与控制终端连接的会话首进程被称为控制进程。
- 一个会话中的几个进程组可被分成一个前台进程组以及一个或者多个后台进程组。
- 如果一个会话有一个控制终端,则它有一个前台进程组,会话中的其他进程组则为后台进程组。
- 无论何时进入终端的中断键(ctrl+c)或退出键(ctrl+\),就会将中断信号发送给前台进程组的所有进程。
- 如果终端接口检测到调制解调器(或网络)已经断开,则将挂断信号发送给控制进程(会话首进程)。
这些特性的关系如下图所示:
作业控制
什么是作业(job)和作业控制(Job Control)
作业是针对用户来讲,用户完成某项任务而启动的进程,一个作业既可以只包含一个进程,也可以包含多个进程,进程之间互相协作完成任务,通常是一个进程管道。
Shell 分前后台来控制的不是进程而是作业 或者进程组。一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,Shell 可以同时运⾏一个前台作业和任意多个后台作业,这称为作业控制。
作业号
放在后台执⾏的程序或命令称为后台命令,可以在命令的后面加上&符号从而让Shell 识别这是一个后台命令,后台命令不用等待该命令执⾏完成,就可立即接收新的命令,另外后台进程执行完后会返回一个作业号以及一个进程号(PID)。
如上图,[1]是作业号,作业号后面的数字是进程组的最后一个进程id。
再启动一个后台进程,我们通过ls是看不到的。可以通过 jobs 查看。
通过 fg 作业号,我们可以把后台进程变成前台进程,然后就可以通过ctrl+c终止了。
当我们把2号后台进程变成了前台进程,我们又想把它变成后台进程该怎么办?可以先ctrl+z暂停该进程,然后bash就会变成前台进程,再通过 bg 作业号 来把2号进程变成后台。
上面的例子中,可以看到作业号后面会带有+ - 号:
关于默认作业:对于一个用户来说,只能有一个默认作业(+),同时也只能有一个即将成为默认作业的作业(-),当默认作业退出后,该作业会成为默认作业。
- + : 表示该作业号是默认作业,即当前正在运行的。
- -:表示该作业即将成为默认作业
- 无符号: 表示其他作业
注意: 当通过 fg 命令切回作业时,若没有指定作业参数,此时会将默认作业切到前台执行,即带有“+”的作业号的作业
作业状态
常见的作业状态如下表所示:
作业控制相关的信号
Ctrl + Z 可以将前台作业挂起,实际上是将STGTSTP 信号发送至前台进程组作业中的所有进程, 后台进程组中的作业不受影响。在unix系统中, 存在 3 个特殊字符可以使得终端驱动程序产生信号,并将信号发送至前台进程组作业, 它们分别是 :
- Ctrl + C: 中断字符, 会产生 SIGINT 信号
- Ctrl + \: 退出字符, 会产生 SIGQUIT 信号
- Ctrl + Z:挂起字符, 会产生 STGTSTP 信号
通过下图可以看到作业控制的功能:
守护进程
进程组,无论是前台还是后台,都属于同一个会话。
如果想让一个进程与会话的创建和注销毫无关系,我们可以把该进程组从会话中拿出来,让他变成一个独立的会话,这样曾经的会话即使删掉了,也不会影响进程组1。这种在一个会话中,只有一个进程或进程组的,就叫做守护进程。守护进程是孤儿进程的一种特殊情况。
那如何将进程变成守护进程呢?下面是将任务变成守护进程的函数
#pragma once
#include<iostream>
#include<string>
#include<unistd.h>
#include<cstdlib>
#include<signal.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
const std::string defaultpath="/";
const std::string defaultdev="/dev/null";
void Daemon(bool ischdir,bool isclose)
{
//1.忽略不要的信号
signal(SIGCHLD,SIG_IGN);
signal(SIGPIPE,SIG_IGN);
//2.fork
if(fork()>0)
exit(0);
//3.setsid
setsid();
//4.确认是否要更改工作目录
if(ischdir)
chdir(defaultpath.c_str());
//5.对012进行重定向
if(isclose)
{
::close(0);
::close(1);
::close(2);
}
else
{
int fd=open(defaultdev.c_str(),O_RDWR);
if(fd>0)
{
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
::close(fd);
}
}
}