从一道面试题来学习前台进程和后台进程、孤儿进程和僵尸进程

news2024/11/28 9:48:50

1、面试题介绍

以前面试,面试官问了一个问题,大意是:

我们在终端中,通过执行 python main.py 命令,会启动一台前台进程直到程序结束。现在我还是想通过执行 python main.py ,启动一个后台进程,让后台进程运行我们的业务逻辑。这个时候应该怎么做呢?

回答上面这道题,需要先了解什么是前台进程和后台进程,什么是孤儿进程和僵尸进程?接下来,我们先一起看看前台进程和后台进程,以及孤儿进程和僵尸进程。最后再通过编写代码来完成面试题的需求。

2、前台进程和后台进程

2.1 什么是前台进程

在 Linux 中,前台进程是指当前正在运行的进程,它与用户交互并占用终端。当用户在终端中输入命令时,该命令所启动的进程就是前台进程。

前台进程会占用终端,直到它执行完毕或者被中断(例如按下 Ctrl+C)。在前台进程运行期间,用户可以通过键盘输入命令或者发送信号来与进程交互。

2.2 什么是后台进程

Linux 后台进程是指在终端中运行的进程,但是不会占用终端的输入输出,而是在后台运行

在 Linux 中,可以通过在命令后面加上 & 符号来将进程放到后台运行。例如,运行命令 command & 就可以将 command 进程放到后台运行。

后台进程可以在终端关闭后继续运行,也可以同时运行多个后台进程。可以使用 jobs 命令查看当前运行的后台进程,使用 fg 命令将后台进程切换到前台运行,使用 bg 命令将前台进程切换到后台运行。

需要注意的是,后台进程在另外的终端是查看不到任何信息的,如果需要查看进程的输出信息,一般是将输出重定向到文件中,然后使用 tail 命令实时查看输出。

2.3 前台进程、后台进程如何切换

在 Linux 中,可以使用以下命令将前台进程切换到后台进程:

  1. 使用 Ctrl + Z 将当前正在运行的前台进程挂起。
  2. 使用 bg 命令将挂起的进程切换到后台运行。

使用 command & ,这样的方式是一开始就将 command 进程放到后台运行。

我们通过下面这个例子,来感受下前台进程和后台进程是如何切换的。

1、编写 test.py文件。

import os
import time


def main():
    a = 1
    while True:
        time.sleep(1)
        a += 1
        print("a---->", a)
        if a > 30:
            break


if __name__ == '__main__':
    main()

2、通过 python test.py执行程序。然后使用ctrl + Z 是程序暂停到后台。注意,这个时候程序不会再往下执行了。

  • 如果想程序继续往下执行,使用 bg 命令将其切换到后台运行。

  • 还有一种方式是:在最开始执行命令的时候,加上 &。即python main.py &

python test.py
a----> 2
a----> 3
^Z
[1]  + 1761 suspended  python test.py
sample_test [master●] %

3、通过jobs命令查询后台进程。

sample_test [master●] % jobs          
[1]  + suspended  python test.py
sample_test [master●] %

4、使用fg命令将指定的后台进程切换到前台运行。(fg %N,这里的N是 jobs返回的序号,并不是进程的PID)

    
sample_test [master●] % fg %1     
[1]  + 1761 continued  python test.py
a----> 4
a----> 5
a----> 6
a----> 7

3、孤儿进程和僵尸进程

通过上面的讲解,我们知道了什么是后台进程和前台进程。接下来,我们再讲讲什么是孤儿进程和僵尸进程。

3.1 什么是孤儿进程

孤儿进程是指父进程已经结束或者异常退出,而子进程还在运行的情况下,子进程就会变成孤儿进程。孤儿进程会被操作系统的init进程接管init进程会成为孤儿进程的新的父进程,并负责回收孤儿进程的资源,避免资源泄露和浪费。

因此一般来说,孤儿进程并不会有什么危害。

我们来看一个关于孤儿进程的例子:

在main函数中,创建子进程,然后让父进程睡眠1s,让子进程先运行打印出其进程id(pid)以及父进程id(ppid);随后子进程睡眠3s(此时会调度到父进程运行直至结束),目的是让父进程先于子进程结束,让子进程有个孤儿的状态;最后子进程再打印出其进程id(pid)以及父进程id(ppid);观察两次打印 其父进程id(ppid)的区别。

import os
import time


def main():
    pid = os.fork()  # 创建子进程
    if pid < 0:
        # 创建失败
        print("fork failed!")
        exit(1)
    elif pid == 0:  # 子进程
        print("I am the child process.")
        print("pid:%d, ppid:%d " % (os.getpid(), os.getppid()))
        # 子进程睡眠3s,保证父进程先退出,此后子进程成为孤儿进程
        time.sleep(3)
        # 注意查看孤儿进程的父进程变化
        print("after sleep, pid:%d, ppid:%d" % (os.getpid(), os.getppid()))
        assert os.getppid() == 1
        print("child process is exited.")
    else:
        print("I am father process.")
        # 为保证子进程先运行,让父进程睡眠1s
        time.sleep(1)
        print("father process is exited.")




if __name__ == '__main__':
    main()

执行结果:

运行结果表明:当父进程结束后,子进程成为了孤儿进程。因为它的父进程id(ppid)变成了1,即init进程成为该子进程的父进程了。

3.2 什么是僵尸进程

Linux 僵尸进程是指已经结束执行的进程,但是其父进程还没有对其进行处理,导致该进程的进程描述符仍然存在于系统中,这种进程被称为僵尸进程

僵尸进程不会占用系统资源,但是如果大量的僵尸进程存在,会占用系统的进程描述符资源,导致系统进程描述符资源耗尽,从而导致系统崩溃。

为了避免僵尸进程的出现,父进程需要及时对其进行处理,可以使用 wait() 或 waitpid() 等系统调用来等待子进程结束并回收其资源。另外,也可以使用信号处理机制,在父进程中注册 SIGCHLD 信号处理函数来处理子进程结束的信号。

关于僵尸进程的例子

在main函数中,创建子进程,然后让父进程睡眠30s,让子进程先终止(注意和孤儿进程例子的区别);这里子进程结束后父进程没有调用wait/waitpid函数获取其状态,用ps查看进程状态可以看出子进程为僵尸状态。

import os
import time


def main():
    pid = os.fork()  # 创建子进程
    if pid < 0:
        # 创建失败
        print("fork failed!")
        exit(1)
    elif pid == 0:  # 子进程
        print("I am the child process.I am exited.")
        exit(0)
    else:
        print("I am father process.")
        #  父进程睡眠30s等待子进程退出,且没有调用wait/waitpid获取其状态
        #  子进程会成为僵尸进程
        time.sleep(30)
        print("father process is exited.")




if __name__ == '__main__':
    main()

开一个终端,在终端是运行test.py

在子进程结束,父进程睡眠(还没退出)的时候,再开一个终端用PS查看进程状态。

注意:

  1. 任何一个子进程(init除外)在exit()之后,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。
  2. 如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。
  3. 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
  4. 如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是系统中有时候会有很多僵尸进程的原因。

3.3 总结

  • 孤儿进程:父进程已亡,子进程成为孤儿,被init进程收养,由init进程对它们完成状态收集工作,因此一般没有坏的影响。
  • 僵尸进程:子进程已亡,父进程没给子进程收尸,子进程成为僵尸进程,占用系统资源。

4、面试题解决方式

现在再回过头来看 面试题的要求:

在终端中执行 python main.py命令,启动后台进程来进行业务处理。

那么我们可以利用孤儿进程的特性,完成上面的需求。

1、通过 os.fork()创建子进程。

2、创建完成后,让父进程退出,子进程继续运行。

简单案例:

import os
import time


def main():
    pid = os.fork()  # 创建子进程
    a = 0
    if pid < 0:
        # 创建失败
        print("fork failed!")
        exit(1)
    elif pid == 0:  # 子进程
        for i in range(10):
            time.sleep(1)
            a += i
        print("child process calculate result: %d" % a)
    else:
        print("I am father process.")
        exit(0)


if __name__ == '__main__':
    main()
sample_test [master●] % python test.py
I am father process.


10秒钟过后
sample_test [master●] % child process calculate result: 45

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/866706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s的yaml文件管理

声明式管理方法&#xff1a; 1.适合于对资源的修改操作2.声明式资源管理方法依赖于资源配置清单文件对资源进行管理资源配置清单文件有两种格式&#xff1a;yaml&#xff08;人性化&#xff0c;易读&#xff09;&#xff0c;json&#xff08;易于api接口解析&#xff09;3.对资…

【实操】2023年npm组件库的创建发布流程

2022年的实践为基础&#xff0c;2023年我再建一个组件库【ZUI】。步骤回顾&#xff1a; 2022年的npm组件包的发布删除教程_npm i ant-design/pro-components 怎么删除_啥咕啦呛的博客-CSDN博客 1.在gitee上创建一个项目,相信你是会的 2.创建初始化项目&#xff0c;看吧&#…

SpringBoot使用自定义事件监听器的demo

记录一下SpringBoot自定义事件监听器的使用方法 案例源码:SpringBoot使用自定义事件监听器的demo 使用的SpringBoot2.0.x版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><…

【Opencv入门到项目实战】(十):项目实战|文档扫描|OCR识别

所有订阅专栏的同学可以私信博主获取源码文件 文章目录 1.引言1.1 什么是光学字符识别 (OCR)1.2 应用领域 2.项目背景介绍3.边缘检测3.1 原始图像读取3.2 预处理3.3 结果展示 3.轮廓检测4.透视变换5.OCR识别5.1 tesseract安装5.2 字符识别 1.引言 今天我们来看一个OCR相关的文…

开发工具IDEA的下载与初步使用【各种快捷键的设置,使你的开发事半功倍】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于IDEA的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.IDEA的简介以及优势 二.IDEA的下载 1.下…

释放马氏距离的力量:用 Python 探索多元数据分析

一、说明 马哈拉诺比斯距离&#xff08;Mahalanobis Distance&#xff09;是一种测量两个概率分布之间距离的方法。它是基于样本协方差矩阵的函数&#xff0c;用于评估两个向量之间的相似程度。Mahalanobis Distance考虑了数据集中各个特征之间的协方差&#xff0c;因此比欧氏距…

skynet 网络模块解析

文章目录 前言环境准备sneak peek线程数据结构会话对象&#xff1a;持有基础套接字&#xff0c;封装了套接字的基础操作。会话管理器&#xff1a;持有并管理会话池&#xff0c;给外部模块提供网络接口。 网络模块管理会话管理器的生命周期管理工作模式 总结技术点原子数据管道描…

漫话拥塞控制:BBR 是个单流模型

概要(便于检索主题)&#xff1a;单流&#xff0c;多流收敛&#xff0c;probe buffer 挤压带宽&#xff0c;maxbw-filter wnd。 我曾经经常说 BBR 是个单流模型&#xff0c;而不是多流收敛模型&#xff0c;也做过不少评论&#xff0c;最近在复听 IETF 的大会&#xff0c;在 IET…

SQL | 检索数据

1-检索数据 1.1-检索单个列 SELECT prod_name FROM Products; 上述SELECT语句从Products表中检索一个名为prod_name的列。 所要查找的列在select后面&#xff0c;from关键字指出从那个表查询数据。 输出如下&#xff1a; prod_name8 inch teddy bear12 inch teddy bear18…

linux鲁班猫代码初尝试[编译镜像][修改根文件系统重编译][修改设备树改屏幕为MIPI]

编译镜像 官方百度云盘资料:https://doc.embedfire.com/linux/rk356x/quick_start/zh/latest/quick_start/baidu_cloud/baidu_cloud.html 解压虚拟机压缩包:"鲁班猫\8-SDK源码压缩包\开发环境虚拟机镜像\ubuntu20.04.7z"后既可以用VMware打开,打开后可以看到已经有…

【前端】JQ生成二维码

提供两种方法&#xff0c;两种都是借助JQ插件生成。 所需文件&#xff1a;https://download.csdn.net/download/qq_25285531/88204985https://download.csdn.net/download/qq_25285531/88204985 方法一&#xff1a; <script type"text/javascript" src"/s…

【JavaEE基础学习打卡02】是时候了解JavaEE了

目录 前言一、为什么要学习JavaEE二、JavaEE规范介绍1.什么是规范&#xff1f;2.什么是JavaEE规范&#xff1f;3.JavaEE版本 三、JavaEE应用程序模型1.模型前置说明2.模型具体说明 总结 前言 &#x1f4dc; 本系列教程适用于JavaWeb初学者、爱好者&#xff0c;小白白。我们的天…

【刷题笔记8.11】LeetCode题目:二叉树中序遍历、前序遍历、后序遍历

LeetCode题目&#xff1a;二叉树中序遍历、前序遍历、后序遍历 题目1&#xff1a;二叉树中序遍历 &#xff08;一&#xff09;题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 &#xff08;二&#xff09;分析 二叉树中序遍历&#xff0c;遍历…

6-Ngnix配置反向代理

1.前提 虚拟机能连接外网 仿真http应用需在本虚拟机启用(原因&#xff1a;只有一台虚拟机做测试) http_8080和http_8081要启用&#xff08;http测试应用&#xff09; [rootcent79-2 ~]# ls -l http_* -rwxr-xr-x 1 root root 6391676 Jul 19 13:39 http_8080 -rwxr-xr-x 1 …

【C# Programming】C#第一课(自己学习的笔记)

目录 一、C# 介绍 1.1 托管代码(Manage Code ) &#xff1a; 1.2 基础类型库 (Base Class Library)&#xff1a; 1.3 程序集(Assembly)&#xff1a; 1.4 .NET 框架&#xff1a; 1.5 公共中间语言(Common Intermediate Language)&#xff0c;简称 IL。 1.6 C#编译器将源代…

RISC-V在快速发展的处理器生态系统中找到立足点

原文&#xff1a;RISC-V Finds Its Foothold in a Rapidly Evolving Processor Ecosystem 作者&#xff1a;Agam Shah 转载自&#xff1a;https://thenewstack.io/risc-v-finds-its-foothold-in-a-rapidly-evolving-processor-ecosystem/ 以下是正文 But the open source pr…

【用unity实现100个游戏之6】制作一个战旗自走棋类游戏(附源码)

文章目录 前言导入素材开始1. 设置瓦片间隙2. 放置全图瓦片3. 美化瓦片地图4. 添加树木障碍物5. 设定不同的排序图层6. 瓦片交互6. 瓦片交互优化6. 瓦片是否允许角色7. 添加角色8. 新增游戏管理脚本9. 角色移动范围逻辑10. 角色移动范围可视化11. 角色移动12. 重置瓦片颜色12. …

Spark(38):Streaming DataFrame 和 Streaming DataSet 转换

目录 0. 相关文章链接 1. 基本操作 1.1. 弱类型 api 1.2. 强类型 1.3. 直接执行 sql 2. 基于 event-time 的窗口操作 2.1. event-time 窗口理解 2.2. event-time 窗口生成规则 3. 基于 Watermark 处理延迟数据 3.1. 什么是 Watermark 机制 3.2. update 模式下使用 w…

【计算机视觉|生成对抗】条件生成对抗网络(CGAN)

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;Conditional Generative Adversarial Nets 链接&#xff1a;[1411.1784] Conditional Generative Adversarial Nets (arxiv.org) 摘要 生成对抗网络&#xff08;Generative Adversarial…

04_Hudi 集成 Spark、保存数据至Hudi、集成Hive查询、MergeInto 语句

本文来自"黑马程序员"hudi课程 4.第四章 Hudi 集成 Spark 4.1 环境准备 4.1.1 安装MySQL 5.7.31 4.1.2 安装Hive 2.1 4.1.3 安装Zookeeper 3.4.6 4.1.4 安装Kafka 2.4.1 4.2 滴滴运营分析 4.2.1 需求说明 4.2.2 环境准备 4.2.2.1 工具类SparkUtils 4.2.2.2 日期转换…