【LInux】进程管理

ops/2025/4/1 2:25:05/

在这里插入图片描述

👦个人主页:Weraphael
✍🏻作者简介:目前正在学习c++和算法
✈️专栏:Linux
🐋 希望大家多多支持,咱一起进步!😁
如果文章有啥瑕疵,希望大佬指点一二
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍


目录

  • 一、进程概念
  • 二、进程管理 --- PCB
  • 三、在Linux中查看进程
      • 3.1 ps指令
      • 3.2 通过proc查看
  • 四、通过系统调用获取进程标识符pid
  • 五、父进程
  • 六、通过系统调用创建进程
      • 6.1 fork函数
      • 6.2 fork()为什么给父进程返回子进程的PID,给子进程返回0
      • 6.3 fork函数做了什么事情?
      • 6.4 fork函数是如何做到返回2次的
      • 6.5 pid变量是如何做到可以接收两个返回值

一、进程概念

由冯诺依曼体系结构可知,程序一旦运行起来,一定会先加载到内存,然后再通过CPU对其进行逐行的语句执行。而严格意义上,当一个程序被加载到内存中(正在运行的程序),它会被操作系统视为一个进程(或者称做任务)。

二、进程管理 — PCB

当你开机的时候启动的第一个程序就是操作系统(即操作系统是第一个加载到内存的)。常识告诉我们,系统可以同时运行多个进程,而操作系统是做管理工作的,而其中就包括了进程管理。那么操作系统是如何对进程进行管理的呢?

  • 这时就应该想到操作系统管理的六字真言:先描述,再组织

当一个进程出现时,操作系统会立即对其进行描述,并将这些描述信息组织起来以便管理。这种描述信息通常被放置在一个称为进程控制块PCB(process control block)的数据结构中(可以理解为进程属性的集合),并将这些PCB对象以双链表的形式组织起来。因此,操作系统对各个进程的管理就变成了对这条双链表的增、删、查、改等操作。

注意:每个操作系统都有其独特的方式来表示进程控制块PCBLinux下,PCB被表示为task_struct结构体task_struct结构体包含了Linux内核对进程的所有必要信息,比如:

struct task_struct
{进程标识符PID: 描述本进程的唯一标识符,用来区别其他进程。进程状态: 任务状态,退出代码,退出信号等。优先级: 相对于其他进程的优先级。程序计数器: 程序中即将被执行的下一条指令的地址。内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针上下文数据: 进程执行时处理器的寄存器中的数据I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。...
};

当一个进程被加载到内存中时,操作系统需要根据该进程的属性和需求,为其创建对应的PCB对象,除此之外,操作系统还需要将该进程的代码和数据加载到内存中。如果满足这两个要求(PCB对象和程序的代码数据相结合),就形成了一个完整的进程。

在这里插入图片描述

然而,操作系统在进行进程管理时,通常更关注的是进程的状态、资源分配和调度,以及对进程的控制(就好比学校系统,校长管理者和被管理者学生不需要见面,只需拿到学生信息就能达到某种意义上的管理)。因此,操作系统对进程的管理其实就是对进程的PCB对象进行管理。

三、在Linux中查看进程

3.1 ps指令

ps axj

测试代码如下:

在这里插入图片描述

当我们将以上程序运行起来,此时这个程序就变成了一个进程。而由于Linux下正在执行的程序会有点多,因此我们可以使用grep来过滤出我们想要看的进程信息

# head -1 -> 显示进程的属性字段
# && -> 一行执行两个指令
ps axj | head -1 && ps axj | grep '可执行程序'

在这里插入图片描述

如上图所示,明明要的是mytest进程,为什么会出现grep相关的进程呢?

我们自己写的代码,编译成为可执行程序,启动之后就是一个进程;同样的,基本指令本质上就是可执行文件, 这些指令在执行后也会成为进程,这也就是为什么上面会把grep显示出来。

当然也可以过滤掉grep命令的进程信息

ps axj | head -1 && ps axj | grep '可执行程序'' | grep -v grep

3.2 通过proc查看

进程的信息也可以通过/proc系统文件夹查看

ls /proc

在这里插入图片描述

每一个进程都会存在一个唯一的标识符,就如同每个学生都有学号一样,而这个唯一标识符在Linux称为pidprocess id),而上面蓝色的目录即为进程的pid。每当执行一个程序,/proc目录就会出现进程的pid

在这里插入图片描述

而当我们杀死进程,再运行同样的进程时,对应的pid是会改变的。

PID是由操作系统动态分配的,系统会确保每个进程的PID在系统中是唯一的。当一个进程启动时,操作系统会查找当前系统中未被使用的PID,并将其分配给新的进程。

# 杀死进程
kill -9 PID

在这里插入图片描述

四、通过系统调用获取进程标识符pid

想直接获取进程的信息,如进程标识符pid是不行的,因为进程控制块PCB是由操作系统管理的。而操作系统不相信用户,一般情况下不会直接将进程的信息提供给用户。相反,操作系统提供了系统调用接口来让用户程序与操作系统交互,从而获取进程信息。

getpid() # 获取进程pid

我们可以通过man指令来查看getpid()的详细描述

# 2号手册是专门查看系统调用
man 2 getpid

在这里插入图片描述

  • <sys/types.h> 是一个系统级别的头文件,它包含了许多基本的系统数据类型的定义。如果使用 pid_tuid_tgid_t 等,那么就要使用此头文件

  • <unistd.h> 中,则通常会包含一些系统调用的声明,例如 getpid()。因此,在使用 getpid() 函数时,需要包含 <unistd.h> 来获取函数的声明。

我们可以来测试一下,代码如下:

在这里插入图片描述

验证如下

在这里插入图片描述

五、父进程

在这里插入图片描述

从上图可以观察发现:每当运行一个程序时,对应的进程都会有一个父进程标识符ppidParent Process ID),并且每次终止进程,对应的pid就会发生改变,而ppid却始终没发生变化。

既然ppid始终不变,那么我们可以查查父进程到底是什么?

ps axj | head -1 && ps axj | grep 29062

在这里插入图片描述

图中可以看出父进程29062就是一个bash。因此,几乎我们在命令行上所执行的所有的指令,都是命令行bash进程的子进程!

除此之外,我们同样可以通过系统调用接口来获取一个进程的父进程的标识符ppid

getppid()

在这里插入图片描述

验证结果如下:

在这里插入图片描述

六、通过系统调用创建进程

6.1 fork函数

fork()是一个系统调用,用于创建子进程。

先来看看简单的代码样例:

在这里插入图片描述

运行结果如下:

在这里插入图片描述

为什么在fork之后的代码被执行了2次?这其实和它的返回值有关!

  • fork()创建成功,将子进程的pid作为父进程的fork函数的返回值,将0作为子进程的fork函数的返回值,
  • fork()创建失败,-1返回给父进程。不创建子进程

下面是一个简单的C语言示例,演示了如何检查fork()的返回值:

在这里插入图片描述

运行结果如下:

在这里插入图片描述

以上执行过程可以抽象成以下图:

在这里插入图片描述

简单来说:当程序执行到fork()函数时,操作系统会创建一个新的PCB对象,即子进程,该子进程与父进程几乎完全相同。然后父进程和子进程都会从fork()函数的调用处开始继续执行程序(代码共享),父子进程可以根据返回值的不同来执行不同的逻辑,以满足不同的需求。

6.2 fork()为什么给父进程返回子进程的PID,给子进程返回0

首先返回不同的返回值是为了让子进程和父进程可以执行不同的代码(干不同的事);其次

  • 父进程返回子进程的PID:父进程通常需要知道它创建的子进程的PID,以便管理和与子进程进行通信。所以,将子进程的PID作为fork()函数的返回值,使得父进程可以轻松地获取到子进程的标识符PID

  • 子进程返回0:在子进程中,通常需要知道它是子进程就行。因此将0作为fork()函数在子进程中的返回值,可以方便地用于条件判断。

6.3 fork函数做了什么事情?

进程 = PCB对象 + 数据和代码

  1. 为子进程创建新的进程控制块PCB

  2. 复制父进程的地址空间:fork()调用成功后,操作系统将父进程的地址空间复制到新进程中,包括代码段、数据段、堆栈等。这样新进程就拥有了父进程的所有数据和程序,成为了父进程的副本。

  3. 设置子进程的状态: 设置子进程的状态为就绪状态,使其可以被调度执行。

  4. 返回值处理

6.4 fork函数是如何做到返回2次的

在这里插入图片描述

fork()在内部创建完子进程后,就已经开始父子进程代码共享,会使得fork()被父进程执行一次,同时被子进程执行一次。这样就实现了fork()函数在逻辑上返回两次的效果。

6.5 pid变量是如何做到可以接收两个返回值

子进程共享父进程的代码和数据,而进程之间具有独立性,如果子进程修改了父进程的数据,那么势必会影响父进程,这也就是接下来我要回答:为什么变量pid可以接收两个fork()的返回值?这其实是因为系统对其做了写时拷贝

  • 在调用 fork() 函数创建子进程时,并不立即复制整个父进程的代码和数据,而是共享!这是因为如果复制整个数据段代码,而子进程在执行时不一定会对数据段的代码进行修改,那么就会导致资源浪费。

  • 如果在子进程执行期间需要修改共享的数据段,就会触发写时拷贝机制。这时,操作系统会为子进程分配一块新的内存空间,复制需要修改的数据,并且在新的内存空间上进行修改,而不会影响到父进程的数据。这样就确保了父子进程之间对内存的修改不会相互干扰,同时又避免了不必要的内存复制开销。

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

相关文章

算法打卡day52|单调栈篇03| 84.柱状图中最大的矩形

算法题 Leetcode 84.柱状图中最大的矩形 题目链接:84.柱状图中最大的矩形 大佬视频讲解&#xff1a;84.柱状图中最大的矩形视频讲解 个人思路 这题和接雨水是相似的题目&#xff0c;原理上基本相同&#xff0c;也是可以用双指针和单调栈解决&#xff0c;只是有些细节不同。…

Git--原理与使用

目录 一、课程目标二、初始Git三、安装Git3.1 Linux-centos 四、Git的基本操作4.1 创建Git本地仓库 五、配置Git六、认识工作区、暂存区、版本库七、添加文件八、查看.git九、修改文件十、版本回退十一、撤销修改11.1 情况一&#xff1a;对于工作区的代码&#xff0c;还有add11…

【Burpsuite靶场】XSS专题精讲

【个人】&#xff1a;NEUQ大一学生 【专业】&#xff1a;通信工程 (Communication Engineering) 【个人方向】&#xff1a;网安、开发双管齐下 【座右铭】&#xff1a;真正的英雄主义,就是看清生活的真相后依然热爱生活 -- 罗曼.罗兰 一、认识XSS&#xff08;跨站脚本攻击&…

[Unity]打包Android后xxx方法丢失。

记录一个坑&#xff1a; Editor下C#一段反射代码运行正常&#xff0c;但是打包后报错。最后发现是PlayerSettings里的Managed Stripping Level&#xff08;托管堆代码剥离级别&#xff09;导致的&#xff0c;项目默认的是Medium。改成Low确实好使&#xff0c;但是会造成包体大…

BUUCTF---misc---[SWPU2019]我有一只马里奥

1、下载附件是一个.exe文件 2、运行之后可以看到桌面生成了1.txt文件&#xff0c;文件里面有如下内容 3、经过信息搜索&#xff1a;NTFS&#xff08;New Technology File System&#xff09;是一种由Microsoft开发的专有日志文件系统。根据它的提示&#xff0c;应该是把flag.tx…

Unity之OpenXR+XR Interaction Toolkit快速监听手柄任意按键事件

前言 当我们开发一个VR时,有时希望监听一个手柄按键的点击事件,或者一个按钮的Value值等。但是每次有可能监听的按钮有不一样,有可能监听的值不一样,那么每次这么折腾,有点累了,难道就没有一个万能的方法,让我可以直接监听我想要的某个按钮的事件么? 答案是肯定的,今…

AndroidStudio中虚拟机(AVD)无法启动,出现unable to locate adb错误

1.检查Android SDK Platform-Tools是否安装(个人是通过这个方法解决的) 首先通过File-Project Structure-Project SDK检查SDK有没有被选中 步骤&#xff1a;打开file -> settings &#xff0c;搜索SDK 之后点击"-",在点击Apply进行安装 2.可能是驱动的问题 电脑…

F检验的步骤

F检验&#xff0c;也称为联合假设检验或方差齐性检验&#xff0c;主要用于检验两个或多个正态随机变量的总体方差是否相等。它还可以用于检验两个以上随机变量平均数差异的显著性。以下是F检验的一般使用步骤&#xff1a; 提出假设&#xff1a;首先&#xff0c;需要建立原假设&…