【Linux】进程间关系与守护进程

目录

进程组

什么是进程组

组长进程

会话

什么是会话

作业控制

守护进程


进程组

什么是进程组

我们在命令行中输入sleep 1 | sleep 2 | sleep 3,然后查看进程,

我们看到它们有不同的pid,表明它们是不同的进程,它们的ppid是一样的,那么它们是兄弟进程。除了pid和ppid外,我们看到第三列还有pgid(process group id,进程组id),它们对应的pgid是一样的,表明它们属于同一个组,这三个进程的组长是第一个被创建的进程,即sleep 1000。我们再来写这样一段简单代码:

我们发现这一个进程也有自己的pgid,其pgid就是它自己的pid本身。也就是说,哪怕只有一个进程,也能自成进程组。

我们再来写这样一段代码:

我们发现,它们的pid不一样说明这是两个进程。但是我们发现它们的pgid是一样的,就是父进程的pid。如果我们将来在写一个多进程程序的时候,多个进程其实是以进程组的方式来共同完成一件任务的。进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程

组长进程

每一个进程组都有一个组长进程,组长id就是其进程id。

  • 进程组长的作用:可以创建一个进程组或者创建该进程组中的进程。
  • 进程组的生命周期:从进程组创建开始到其中最后一个进程离开为止。注意:只要某个进程组中有一个进程存在,则该进程就存在,与其组长进程是否已经终止无关

-e 选项表示every的意思,表示输出每一个进程的信息。

-o 选项以逗号操作符(,)作为分界符,可以指定要输出的列

会话

什么是会话

在使用xshell登录linux服务器时,linux会为我们创建一个终端文件,还创建一个bash进程,以进程组的方式来给我们提供命令行解释的服务。bash进程通过终端文件进行读写数据,

一个会话内部可以有多个进程组。

我们再看这样的命令:

加上&表示让进程在后端运行,同时继续运行自己写的process_task进程:

我们发现,运行的sleep和process_task,它们属于不同的进程组。更重要的是,后面有一列叫SID,即会话ID,它们的会话ID是一样的,会话ID是一样的。会话ID一般是会话中的第一个进程,一般是bash。会话可以看成是一个或多个进程组的集合, 一个会话可以包含多个进程组。 每一个会话也有一个会话 ID(SID)

如果想把这个进程放在后台,就在后面加&。

这种前台进程运行时,我们输入一些命令pwd、ls,发现没有反应。

实际上,同一个会话中,可以运行同时存在多个进程组,但是任何时刻只允许一个前台进程(组),可以允许多个后台进程(组)。当刚开始时,前台进程是bash,输入指令,bash就会响应,而当执行我们自己的任务进程时,我们的任务就变成了前台进程了,这是bash自动把自己切换到后台。当输入指令时,都给了前台进程,bash就收不到了,输入的命令当然就无法执行了。当把我们自己的进程终止了,bash又把自己重新切换到前台,等待用户输入。所以,我们现在发现,所谓前后台,就是谁应该从标准输入中获取数据!这就是后台进程使用ctrl+c也杀不掉的原因。这样正是我们自己运行的程序可以通过ctrl+c终止。

作业控制

作业是针对用户来讲, 用户完成某项任务而启动的进程, 一个作业既可以只包含一个进程, 也可以包含多个进程, 进程之间互相协作完成任务, 通常是一个进程管道。

当我们把完成某个作业的进程放在后台运行时,如下

1\]表示作业号, 我们再创建一个作业, ![](https://i-blog.csdnimg.cn/direct/389b8820ed8847719860347f24bda122.png) 作业号就是2了,如果我们想查询当前的作业,可以使用jobs (-l), ![](https://i-blog.csdnimg.cn/direct/79a75537f5d04cd2bfb20d2f2903d6bf.png) 这两个作业都在后台,如果想把某个作业放在前台,比如想把1号作业放在前台运行,就输入fg 1,此时这个作业就能接收键盘输入的命令, ![](https://i-blog.csdnimg.cn/direct/198e7ba1cfb340e58c04406b573ab1c5.png) 如果想把刚才移到前台的作业放到后台,就需要先暂停这个前台作业,使用ctrl+z暂停, ![](https://i-blog.csdnimg.cn/direct/7f0f230599ff45faaa759de91e2d54f9.png) 然后再输入bg 2,将2号作业放到后台, ![](https://i-blog.csdnimg.cn/direct/e12f4a7bed3b407b8f7f60fbba88722f.png) 关于默认作业:对于一个用户来说,只能有一个默认作业(+) ,同时也只能有一个即将成为默认作业的作业(-),当默认作业退出后,该作业会成为默认作业。 **常见作业状态** |-------------------|-------------------| | 作业状态 | 含义 | | 正在运行【Running】 | 后台作业(\&),表示正在执行 | | 完成【Done】 | 作业已完成,返回的状态码为0 | | 完成并退出【Done(code)】 | 作业已完成并退出,返回的状态码非0 | | 已停止【Stopped】 | 前台作业,当前被Ctrl+Z终止 | | 已终止【Terminated】 | 作业被终止 | ## 守护进程 进程组,无论是前台还是后台,都属于同一个会话。如果我们把某一个进程组从当前会话a中拿出来,形成一个单独的会话b,这样和刚才的会话a由包含关系变成并列关系。即使把a会话删除,也并不影响b会话,我们把这个进程称为**守护进程**。这个守护进程不会因用户登录或退出受影响,那一个进程如何将自己变成独立的会话从而变成守护进程呢?就需要使用setsid函数: #include pid_t setsid(void); # 功能:创建会话 # 返回值:创建成功返回SID,失败返回-1 当某一个进程调用setsid函数来创建一个会话,前提是调用进程不能是一个进程组的组长。**需要注意的是,这个接口如果调用进程原来是进程组组长,则会报错。为了避免这种情况,可以使用fork创建子进程,然后让父进程退出,子进程调用setsid**。此时这个子进程(守护进程)就变成了孤儿进程,守护进程是孤儿进程的一种特殊情况。 该调用接口后会发生: 1. 调用进程会变成新会话的会话首进程。此时,新会话中只有一个唯一一个进程。 2. 调用进程会变成进程组的组长,新进程组ID就是当前调用进程ID。 3. 该进程没有控制终端,如果在调用setsid之前该进程存在控制终端,则调用之后会切断联系。 在变成守护进程后,不需要和用户进行输入输出了,我们可以将012重定向到/dev/null,这样就从/dev/null里读不到内容,写入/dev/null的内容也会被自动丢弃(/dev/null是一个字符文件)。也就是说,我们可以以读写方式打开/dev/null,把012全部重定向到/dev/null。并且可以选择是否更改工作目录。我们把守护进程写成一个函数Daemon: ```cpp #include #include #include #include #include #include #include #include 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); } } } ``` ![](https://i-blog.csdnimg.cn/direct/a68051401dc445b6bedbcc656c57666f.png) 然后再main函数中调用Daemon,让当前进程成为守护进程, ![](https://i-blog.csdnimg.cn/direct/40f3a08012914024a0faa9261771f4d0.png) 此时这个process_task进程并没有结束,而是在后端运行。由于process_task不属于当前会话,因此不会阻塞我们输入的命令。并且我们可以查到这个进程: ![](https://i-blog.csdnimg.cn/direct/d009b5539b874139a6ac5fadd0c77e65.png) 我们发现其ppid是1,也就是变成了孤儿进程,被系统领养。其pid、pgid、sid都是1086564,自成进程组,自成会话。其所对应的默认工作路径: ![](https://i-blog.csdnimg.cn/direct/4840194b056a4a7c98a6fba7b324191e.png) 其所对应的012都指向了/dev/null: ![](https://i-blog.csdnimg.cn/direct/6a178111bcf44fb9b45a579a25d5be09.png) 未来如果后面有打印输入的程序,就什么都不做。 如果想让这种进程结束,只能使用kill命令。如果我们把Deamon里面的参数设为true,那么 ![](https://i-blog.csdnimg.cn/direct/9c561c79a65a4df48ec04df87038e7c0.png) 发现其工作目录变成了根目录, ![](https://i-blog.csdnimg.cn/direct/1e80d6f4dafa403facf537a888355a1b.png) 由于所有的012都被关掉了,就看不到了。 至此,我们将一个进程守护进程化了。现在假如写了一个网络服务器,在启动这个服务器之前就可以守护它了!

相关推荐
Dklau-c9 分钟前
『Linux_网络』 第一章 网络基础概念
linux·服务器·网络
Pseudo…9 分钟前
linux shell编程规范与变量(一)
linux·运维·服务器
Gold Steps.11 分钟前
真实企业级K8S故障案例:ETCD集群断电恢复与数据保障实践
服务器·k8s·高可用·故障恢复
Gold Steps.12 分钟前
基于Kubeadm实现K8S集群扩缩容指南
运维·容器·k8s
xhzth7091115 分钟前
kali安装 老版本的frida
linux
Z字小熊饼干爱吃保安23 分钟前
nginx介绍和几种安装方法
linux·运维·nginx·云计算
s_little_monster42 分钟前
【Linux】线程控制函数
linux·运维·服务器·经验分享·笔记·学习·学习方法
kfepiza1 小时前
硬盘分区格式之GPT(GUID Partition Table)笔记250406
linux·windows·笔记·gpt
姝孟2 小时前
Linux学习笔记 1
linux·笔记·学习