【Linux】进程的详讲(中上)

news2025/4/4 5:04:02

目录

📖1.什么是进程?

📖2.自己写一个进程

📖3.操作系统与内存的关系

📖4.PCB(操作系统对进程的管理)

📖5.真正进程的组成

📖6.形成进程的过程

📖7、Linux环境下的进程知识

7.1 task_struct

7.2 task_struct内容分类标识符:描述一个进程的唯一标识符,用来区分不同进程。

7.3 操作系统如何组织进程

7.4 查看进程属性(先学会命令即可,具体用法下面讲)

7.5 proc目录

📖8.什么是进程PID 

📖9.通过系统调用接口查看进程PID 

📖10.父进程PPID

📖11.fork函数 (也叫做fork系统接口)

📖12.fork函数如何实现

📖13.最后来理解bash

📖14.完结


我从不放下,我偏要偏执!

 

📖1.什么是进程?

 上一篇文章中,我们提到过任何一个程序想要运行,必须先加载到内存中。

一个已经加载到内存中的程序并正在由CPU开始运行,就叫进程。


Windows操作系统下,我们可以通过任务管理器来查看计算机当前正在运行的进程。


Linux操作系统下,可以通过下面的指令来查看当前正在运行的进程信息

ps axj
top
//这两条指令都可以查看进程


📖2.自己写一个进程

我们上面提到过,进程就是加载到内存中并正在运行的程序,因此我们可以自己写一个.c源代码,通过编译来获得一个正在运行的程序

//process1.c
#include <stdio.h>  
#include <unistd.h>  
                                                                                                                                                                                              
int main()                              
{                                       
    while(1)                            
    {                                    
        printf("I am a process\n");  
        sleep(1);                       
    }                                   
    return 0;                           
}  

我们写的这个源代码是存储在磁盘上的,编译得到的可执行程序也是存储在磁盘上的,只有通过./mycode去执行 mycode 这个可执行文件,才能把它加载到内存中。加载到内存之后并由CPU开始运行,此时就会得到一个名为 mycode 的进程,可以通过下面这条指令来查看该进程的属性信息


ps axj | grep 进程名称
//ps axj 是查看当前的所有进程
//通过管道和grep将mycode进程的信息过滤出来,方便查找

可以发现有两个pro.exe,但是这里我们先不用管第一个,我们现在只需要知道,第一个的pro.exe是我们这个进程的属性信息即可

结束当前进程使用ctrl+c终止进程


📖3.操作系统与内存的关系

我们写的源代码,最终一定是由CPU来运行,而CPU只能从内存中拿数据,这就决定了我们的可执行程序和代码一定要先加载到内存当中。

此外,CPU作为计算机中的大脑去传达命令控制一切,又因为CPU只能与内存打交道,CPU为了控制一切,必须能让CPU控制操作系统,因为操作系统可以管理软硬件干什么,这就导致操作系统必须在内存中(因此,操作系统是位于内存中的)

简单回顾一下

操作系统->:  管理每一个软硬件该干什么

CPU->:  下达命令,处理数据

操作系统的内存管理包含将内存中的部分不常用数据转移到磁盘中,因为内存是高速临时存储,它在断电后可能丢失数据,而硬盘是低速持久存储断电后数据保留


小Tips:一个可执行程序本质上就是一个二进制文件,我们将一个可执行程序加载到内存,本质上就是将一个二进制文件加载到内存,这个二进制文件是由代码和数据两部分组成,无论是数据还是代码归根结底都可以被叫做数据,代码最终交给控制器去执行,数据最终交给运算器去运行。

一个计算机可以同时存在多个进程,给用户的主观感受就是,可以同时使用多个软件,即在我们的计算机上,聊天、听歌、打游戏可以同时进行。现在我们把这种可以同时运行多个进程的操作系统叫做多道操作系统


📖4.PCB(操作系统对进程的管理)

生成PCB的过程被定义为操作系统对进程的管理

任何可执行程序,在加载到内存前,操作系统要先创建PCB,即先描述出一个进程(可执行程序)的结构体。然后每一个进程创建一个结构体对象,这个结构体对象中存储了进程的各类基本信息(但是不包括可执行程序的代码和数据),这个结构体对象也被叫做进程控制块,本质上就是进程属性的集合。在课本上称之为 PCB,PCB 本质上一定是一个 struct 结构体,Linux操作系统下的PCB叫做:task_struct

(如果阅读完产生悖论思想可看一下目录五)

生成PCB的过程:

操作系统对软硬件的管理都是"先描述,再组织",最后都转化为数据结构的形式只要增删查改就可以

操作系统对进程管理就是如此,先描述,再组织

先描述:  每个进程都是要有一定的基础属性(所有进程的基础属性都相同),而操作系统了解到这一点后,就用一个结构体来管理这些属性

再组织:  由于每一个进程的基础属性都相同,所以操作系统就用结构体创建出对象,每一个进程代表一个结构体对象,这些结构体对象用数据结构的形式存储起来,在进程管理中是链表的形式,但无论是什么,最终都变成了对某个对象的增删改查


这里需要注意的是,PCB中有一个指针是可以指向代码和数据的,但是并不存储,只是指向,这一点很重要! ! ! ! ! ! ! !

小Tips同一操作系统下的所有进程,其 PCB 的属性类型是完全相同的


📖5.真正进程的组成

我们上面提到过进程就是内存中正在运行的可执行程序

因此,这个进程的组成一定是有可执行程序,因此,它一定有代码和数据

但是进程不单单由代码和数据组成

进程的组成为: PCB + 代码和数据

(你可能思考这样的问题,为什么生成PCB的过程叫做对进程的管理,没有PCB就不是进程怎么能叫做对进程的管理呢?

这里解答一下->:

进程管理不仅是 “管理已存在的进程”,更是对进程从无到有的全生命周期控制。)

如果说你是一个进程,那你就必须具备这两点,不能是单单的可执行程序就是进程

我们上面提到过操作系统如何管理进程,我们可以得知,操作系统管理进程并没有对代码和数据进行处理,可以看如下图->:

可执行程序中的代码和数据是由内存管理的,而我们的操作系统只需要生成PCB,并用PCB对进程进行更好地管理,这就是操作系统要干的事

PCB只存储进程的基本信息,不存储它的代码和数据


📖6.形成进程的过程

至此我们总结一下形成进程的过程

1️⃣ 先生成 PCB → 2️⃣ 再加载可执行程序到内存 → 3️⃣ 最后运行程序,成为进程


📖7、Linux环境下的进程知识

7.1 task_struct

task_struct 是 Linux操作系统 下的 PCB 结构,它是 Linux 内核的一种类型,会被装载到 RAM(内存)里,并且包含着进程的信息。


7.2 task_struct内容分类
标识符:描述一个进程的唯一标识符,用来区分不同进程。

标⽰符: 描述本进程的唯⼀标⽰符,⽤来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执⾏的下⼀条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下⽂数据: 进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器]。
I∕O状态信息: 包括显⽰的I/O请求,分配给进程的I∕O设备和被进程使⽤的⽂件列表。
记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。
其他信息
(声明: 不需要记住)
正如我们上面提到的,任意一个进程它的基础信息(PCB)都是相同的

7.3 操作系统如何组织进程

Linux 内核中,最基本的组织进程 task_struct 的方式是采用双向链表组织。但是一个 task_struct 对象不仅仅属于一个双链表,它可能存在多个数据结构中。对进程的管理,本质上就是将 task_struct 对象放到某个数据结构中。


7.4 查看进程属性(先学会命令即可,具体用法下面讲)

ps axj | head -1 && ps axj | grep 进程名称
//&&用来连接两个指令操作
//左边是只打印“PCB”信息
//右边是将mycode进程的相关信息过滤出来

我们的 ps axj | head -1 就是展示出来我们前面所讲的PCB属性名单,每一个进程的PCB属性都是相同的,但是属性里面的具体内容是不同的

小Tips:最后一行显示的其实是 grep 命令的进程,这里先留一个空,下面再讲grep


7.5 proc目录

ls /proc

ls展开proc目录,那么什么是proc目录?

我们先看一看展开proc目录的效果->:

(声明 : PID为PCB的一个属性,具体PID请看目录8,这里只是知道有pid这个东西即可)

这里一看确实乱码七糟,但是别急,听我讲解一下


proc 是一个 虚拟文件系统,它不占用实际磁盘空间,数据存储在内存中,主要用于动态展示系统运行时的信息,包括进程、硬件、内核状态等。以下是其核心特点和作用如下:

1.进程信息

(声明 : PID为PCB的一个属性,具体PID请看目录8,这里只是知道有pid这个东西即可)

每个运行的进程在 /proc 下都有一个以进程 PID 命名的目录

所以我们的进程pid是141405,那么在proc目录下其实就会有一个141405的目录,这个目录中储存着我们进程的各种信息(即PCB)

我们可以通过以下指令来查看PCB属性

ll /proc/进程的pid,如图中就是141405

2.系统硬件与内核信息

我们在上图中的右下角发现了一堆英文,这其实就是硬件,里面存放的是硬件的基础信息

需要注意的是,硬件可没有PCB这一说,PCB 聚焦于 进程个体的运行管理,而硬件属性是 系统层面的全局信息


小Tips:当我们终止掉 mycode 进程 proc 目录下的1624目录也会跟着被清理。其次,一个进程终止后再启动,它的 PID 大概率是会变化的。(为什么变化?下面讲)


📖8.什么是进程PID 

之前我们提到了PID,但是PID究竟是什么,我们还只限于知道,PID是PCB的其中一个属性

现在我们来彻底认识一下PID

PID 是用来唯一标识一个进程的属性,我们知道,进程的属性是PCB,而PCB是由操作系统生成的,操作系统管理着这么多的属性,怕有人乱搞,因此它们封装起来不让你直接接触,但是又不能彻底与世隔绝,因此操作系统设定了一个接口,用户可以通过这个接口去看到进程的属性,而这个接口就是PID

总结一下->:PID是一个接口,可以让用户访问到进程的属性(PCB)


📖9.通过系统调用接口查看进程PID 

  要获取进程的 PID 需要用到系统调用接口 getpid()。该函数会返回调用这个函数的进程的 PID。返回值类型是 pid_t。

#include <stdio.h>    
#include <unistd.h>  //sleep函数头文件  
#include <sys/types.h>//getpid函数头文件
int main()    
{    
    while(1)    
    {    
        printf("我的PID是:%d\n",getpid());                                                                                                                                                   
        sleep(1);    
    }    
    return 0;    
}


📖10.父进程PPID

如果我们展开PCB的属性,我们可以看到一个PPID的东西,如下图->:

(命令在7.4查看进程属性)

这个PPID叫做父进程,既然叫做父进程肯定和子进程有关系,我们再看下面内容


📖11.fork函数 (也叫做fork系统接口)

我们先来看一张图->:

诶?我们只写了一个hello world,怎么打印出两个了?难道是,bug!

不,这并不是bug,而是正常的,这是因为fork函数的功能


当我们的程序成功运行时,到fork函数执行时,操作系统会复制当前进程(父进程),生成一个子进程。因此,就存在了两个进程,而我们就看到了两个hello world


但是这是如何实现的?请看下文 


📖12.fork函数如何实现

知识点1:

我们知道,fork函数会导致复制出一个子进程,而这个子进程的代码实际上是与父进程共享的,也就说,父子进程共用一个代码

我们先总结一下->:1.父子进程共用一个代码


知识点2:

其次,虽然父子进程共用一个代码,但是数据是不同的,什么意思?

父子进程虽然代码相同,但是里面的变量是不同的,可以理解为子进程的变量是新开的一片空间,与父进程的变量不冲突(我们后面会演示,这里先记住)

再次总结一下->:2.父子进程变量的数据不冲突,因为子进程的数据单独开辟了空间


知识点3:

fork函数的返回值给父进程返回父进程的pid,给子进程返回0

返回不同的返回值,是为了区分不同的执行流,让不同的执行流去执行不同的代码块。简单来说就是为了区分父进程和子进程

让父进程和子进程去执行不同的任务,子进程创建出来的目的就是帮助父进程完成一些工作,如果返回值相同那 fork 函数后面的代码父进程和子进程都会执行,那么我们创建子进程就没有了意义

总结一下->:3.fork函数给父进程返回值是子进程的pid,给子进程返回的是0


问题1: fork函数是如何做到返回两次的?

首先,父子进程一共只会调用fork一次,不存在父进程调用一次,子进程调用一次,这也说明fork函数返回两次不是因为执行了两次

父进程的fork在被执行后,内核从 PID 池中找到一个未使用的整数作为子进程的 PID,我们知道PID是一个PCB的属性,也是一个目录。但是子进程最开始没有PCB,这就导致没有PID的存在,所以它会新开辟一段内存空间,这个内存空间会复制父进程的PCB,也就是为了创建子进程的PCB,接着父进程给这个PID返回给子进程,所以子进程就出现了(任何进程PID的分配都早于PCB的创建)

fork函数实际上执行了一次,返回了两次,因为子进程只会从fork函数的下一句开始执行,所以子进程的fork函数并没有被执行

但是子进程的fork函数被强制规定会返回,并且返回值为0


问题2:一个变量怎么会有不同的值?

我们看这样一段代码,后面是运行截图

可以发现,居然运行了两次,这就是因为我们的知识点2和知识点3,父子进程的数据是不同的,并且fork函数给父进程返回子进程的pid,子进程返回0


fork函数总结

  • fork 有两个返回值

  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)


问题3: 子进程创建后,父子进程谁先运行?

一般而言,一旦一个进程被创建出来,以打开QQ音乐为例,在操作系统层面该进程什么时候被运行我们作为用户是管不了的,我们只负责使用即可,操作系统在底层会去调度每一个进程,至于如何调度我们作为用户也无需关心。因此子进程被创建出来后,父进程和子进程究竟谁先执行是由调度器来决定的,所以谁先谁后并不一定。


小Tips:所有的进程在操作系统中会以链表的形式被组织起来,对进程的管理就变成了对链表的增删查改。挑选一个进程放到 CPU 中去运行,这个工作是由调度器去做的,调度器一般会做到公平调度。一般的计算机中只有一个 CPU,而进程却可能有很多个,这就注定了 CPU 是一个少量的资源,对所有的进程来说,运行的本质,就是把它放到 CPU 上,所以所有的进程,对 CPU 资源本质上是一种竞争关系,此时就必须要有调度器的存取,去保证每个进程被公平的调度。(具体可以看以后的文章,会讲到进程的优先级)


📖13.最后来理解bash

我们看这样一张图->:

我们可以看到父进程与子进程的pid,父进程的pid要早于子进程这是一个知识点


bash本质:一个能理解并执行用户输入命令的程序

(重要->:)

bash 的源代码实现中会调用 fork 这个函数去创建子进程,让自己继续去执行命令行解释。让创建出来的子进程去执行我们输入的指令。所以我们在 bash 命令行输入的所有指令,最终执行起来加载到内存变成进程后,都是 bash 进程的子进程。

因此->:(不太重要了解即可)

1.  如果一个进程是直接由bash启动的,那么bash的pid就是fork的父进程pid

2.  bash的子进程去调用fork,是由fork的子进程去生成另一个子进程


📖14.完结

   创作不易,留下你的印记!为自己的努力点个赞吧!

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

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

相关文章

优选算法的巧思之径:模拟专题

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 目录 一、模拟 二、例题讲解 2.1. 替换所有的问号 2.2. 提莫攻击 2.3. Z字形变换 2.4. 外观数列 2.5. 数青蛙 一、模拟 模拟算法说简单点就是照葫芦画瓢&#xff0c;现在草稿纸上模拟一遍算法过程&#xf…

【云服务器】在Linux CentOS 7上快速搭建我的世界 Minecraft 服务器搭建,并实现远程联机,详细教程

【云服务器】在Linux CentOS 7上快速搭建我的世界 Minecraft 服务器搭建&#xff0c;详细详细教程 一、 服务器介绍二、下载 Minecraft 服务端三、安装 JDK 21四、搭建服务器五、本地测试连接六、添加服务&#xff0c;并设置开机自启动 前言&#xff1a; 推荐使用云服务器部署&…

文本分析(非结构化数据挖掘)——特征词选择(基于TF-IDF权值)

TF-IDF是一种用于信息检索和文本挖掘的常用加权算法&#xff0c;用于评估一个词在文档或语料库中的重要程度。它结合了词频&#xff08;TF&#xff09;和逆文档频率&#xff08;IDF&#xff09;两个指标&#xff0c;能够有效过滤掉常见词&#xff08;如“的”、“是”等&#x…

【JavaSE】小练习 —— 图书管理系统

【JavaSE】JavaSE小练习 —— 图书管理系统 一、系统功能二、涉及的知识点三、业务逻辑四、代码实现4.1 book 包4.2 user 包4.3 Main 类4.4 完善管理员菜单和普通用户菜单4.5 接着4.4的管理员菜单和普通用户菜单&#xff0c;进行操作选择&#xff08;1查找图书、2借阅图书.....…

多线程(多线程案例)(续~)

目录 一、单例模式 1. 饿汉模式 2. 懒汉模式 二、阻塞队列 1. 阻塞队列是什么 2. 生产者消费者模型 3. 标准库中的阻塞队列 4. 自实现阻塞队列 三、定时器 1. 定时器是什么 2. 标准库中的定时器 欢迎观看我滴上一篇关于 多线程的博客呀&#xff0c;直达地址&#xf…

一个判断A股交易状态的python脚本

最近在做股票数据相关的项目&#xff0c;需要用到判断某一天某个时刻A股的状态&#xff0c;比如休市&#xff0c;收盘&#xff0c;交易中等&#xff0c;发动脑筋想了一下&#xff0c;这个其实还是比较简单的&#xff0c;这里我把实现方法分享给大家。 思路 当天是否休市 对于某…

闪记(FlashNote):让灵感快速成文的轻量级笔记工具

闪记&#xff08;FlashNote&#xff09;&#xff1a;让灵感快速成文的轻量级笔记工具 你是否经常遇到这样的情况&#xff1a;桌面上放了一大堆的新建123.txt&#xff0c;想记录一个想法&#xff0c;应该是一键开个一个快捷键然后瞬间记录就自动保存了&#xff0c;现在的很多笔记…

《大模型部署》——ollama下载及大模型本地部署(详细快速部署)

ollama Ollama 是一款开源跨平台的大语言模型&#xff08;LLM&#xff09;运行工具&#xff0c;旨在简化本地部署和管理 AI 模型的流程。 下载ollama 进入官网下载https://ollama.com/ 选择需要的系统下载 下载完成后直接进行安装 下载大模型 选择想要部署的模型&#…

Geotools结合SLD实现矢量中文标注下的乱码和可用字体解析

目录 前言 一、需求溯源 1、原始的SLD渲染 2、最初的效果 二、问题修复 1、还是字符编码 2、如何选择可用的字体 3、如何查看支持的字体库 三、总结 前言 随着地理信息系统&#xff08;GIS&#xff09;技术的不断发展&#xff0c;矢量数据的可视化和标注成为了地理信息展…

基于Python与CATIA V5的斐波那契螺旋线自动化建模技术解析

引言 斐波那契螺旋线&#xff08;Fibonacci Spiral&#xff09;作为自然界广泛存在的黄金比例曲线&#xff0c;在工业设计、产品造型、机械工程等领域具有重要应用价值。本文将以Python控制CATIA V5进行参数化建模为例&#xff0c;深入解析三维CAD环境中复杂数学曲线的自动化生…

动态规划(11.按摩师)

题目链接&#xff1a;面试题 17.16. 按摩师 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a; 状态表示&#xff1a; 对于简单的线性 dp &#xff0c;我们可以⽤「经验 题⽬要求」来定义状态表⽰&#xff1a; 以某个位置为结尾&#xff0c;巴拉巴拉&#xff1b;…

CentOS下安装Docker,Docker下安装JDK\MYSQL\REDIS\NGINX

先用VM安装好Centos8.5&#xff0c;可以选择安装迷你版&#xff0c;我安装的是UI版。 然后用MobaXterm_Portable_v23.0_cn连上去&#xff0c;互访成功就可以往下操作。 1. 修改文件&#xff1a;就是要把之前的mirror替换成现在的vault cd /etc/yum.repos.d/sed -i s/mirrorl…

demo.launch(inbrowser=True, share=True)无法生成共享网址

Gradio 的共享功能无法正常工作&#xff0c;原因是缺少一个名为 frpc_windows_amd64_v0.3 用到代码 app.demo.launch(show_errorTrue, inbrowserTrue, shareTrue) show_errorTrue&#xff1a;这个参数的作用是当应用在启动过程中出现错误时&#xff0c;会显示错误信息。这对于调…

翻译: 人工智能如何让世界变得更美好二

Basic assumptions and framework 基本假设和框架 To make this whole essay more precise and grounded, it’s helpful to specify clearly what we mean by powerful AI (i.e. the threshold at which the 5-10 year clock starts counting), as well as laying out a fram…

【vue】editor富文本输入全英文,谷歌浏览器:元素不会自动换行bug

【vue】editor富文本输入全英文&#xff0c;谷歌浏览器&#xff1a;元素不会自动换行bug 解决方案&#xff1a;给元素一个宽度 100% .editor {width: 100%; }

# OpenCV实现人脸与微笑检测:从图像到视频的实战应用

OpenCV实现人脸与微笑检测&#xff1a;从图像到视频的实战应用 在计算机视觉领域&#xff0c;人脸检测和微笑检测是两个非常有趣且实用的任务。它们广泛应用于智能监控、社交媒体分析、人机交互等多个场景。本文将通过两个代码示例&#xff0c;详细介绍如何使用OpenCV实现人脸…

Kubernetes可视化面板——KubePi(Kubernetes Visualization Panel - kubepi)

Kubernetes可视化管理面板——KubePi 在云计算和容器化的大潮下&#xff0c;Kubernetes 已成为管理容器集群的事实标准。然而&#xff0c;面对复杂的集群管理和运维工作&#xff0c;一个直观、易用的可视化工具显得至关重要。KubePi 正是为此而生——一款专为简化 Kubernetes …

【区块链安全 | 第二十三篇】单位和全局可用变量(一)

文章目录 单位和全局可用变量&#xff08;Units and Globally Available Variables&#xff09;以太单位&#xff08;Ether Units&#xff09;时间单位&#xff08;Time Units&#xff09;保留关键字 单位和全局可用变量&#xff08;Units and Globally Available Variables&am…

权重参数矩阵

目录 1. 权重参数矩阵的定义与作用 2. 权重矩阵的初始化与训练 3. 权重矩阵的解读与分析 (1) 可视化权重分布 (2) 统计指标分析 4. 权重矩阵的常见问题与优化 (1) 过拟合与欠拟合 (2) 梯度问题 (3) 权重对称性问题 5. 实际应用示例 案例1&#xff1a;全连接网络中的…

【现代深度学习技术】现代卷积神经网络06:残差网络(ResNet)

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…