当前位置: 首页 > news >正文

【Linux】理解进程,从这三件事开始:冯诺依曼、操作系统、PCB

目录

1 冯诺依曼体系结构

1.1 为什么软件运行要加载到内存?

1.2 为什么一定要有内存?

2 理解数据流动

3 操作系统(Operator System)

3.1 概念

​编辑3.2 设计OS的目的

3.3 理解操作系统

4 进程

4.1 什么是进程?

4.2 查看进程

4.3 通过系统调用获取进程标识符

4.4 通过系统调用创建进程:fork()


本文的重点:

  • 认识冯诺依曼系统
  • 操作系统概念与定位,理解“管理”
  • 深入理解进程概念,了解PCB

1 冯诺依曼体系结构

我们的计算机是由硬件构成,显示器,键盘......所有计算机都是按组织结构将硬件划分起来,遵守冯诺依曼体系结构。

  • 输入设备:键盘,鼠标,网卡,磁盘,话筒......
  • 输出设备:显示器,磁盘,网卡......
  • CPU=运算器+控制器,运算器主要是帮助运算,控制器帮助CPU获取指令,分析指令,执行指令
  • 存储器:内存
  • 输入设备和输出设备都称为外设,当我们访问磁盘时,读写动作称为Input/Output->IO
  • 站在内存角度理解IO,外设把数据给内存(I),内存把数据给输出设备(O)

软件运行,必须先加载到内存,程序运行之前在哪里?在磁盘。

1.1 为什么软件运行要加载到内存?

这次重点先放在数据信号上。从冯诺依曼体系结构看,CPU获取写入只能从内存中来进行!CPU只认内存,所以程序运行时,必须先加载到内存,这是体系结构决定的。

数据流动在体系结构中是从一个设备“拷贝”到另一个设备。体系结构的效率:由设备的“拷贝”效率决定。

最终结论:CPU在数据层面,只和内存打交道,外设只和内存打交道。

1.2 为什么一定要有内存?

为什么一定要有内存,我把内存去掉行吗,输入完别把数据直接给CPU,CPU处理好数据给输出设备不就行了吗?

假设输入输出设备处理数据的速度是毫秒级别的,CPU处理的速度是纳秒级别的。这样CPU处理数据还要等输入输出设备,CPU处理的快,处理完了可能输入设备都还没输送数据呢,这样的体系下的效率完全就依赖输入输出设备的效率。这就好比木桶原理:一个木桶能装多少水取决于最短的木片有多长。

并且寄存器虽然速度快,但是成本很高。所以为了在价格和效率之间找平衡,在计算机体系架构中引入了内存。有了内存,对CPU和外设之间速度不匹配的问题进行适配。这样就可以用合适的钱获得一台较好的计算机。把输入输出设备全内置寄存器,理论上可以,但是相当贵。所以当代计算机可以说是性价比的产物。冯诺依曼体系的意义就是让人们以较低价格买一台性能不错的计算机。后来由于芯片技术和摩尔定律,计算机效率越来越高,价格越来越低,所以平常人也能买得起计算机。

2 理解数据流动

对冯诺依曼的理解,不能只停留在概念上,要深入到对软件数据流理解上。所以假设两个人通过QQ发消息,一个人发你好,是如何展现在朋友的手机上的?

本质是两台冯诺依曼体系在聊天。一个用户输入设备是键盘,登录QQ时,本质是将QQ可执行程序加载到内存。数据“你好”经过运算器转为乱码结构,然后加载到内存经过网卡这个输入设备,交给网络,朋友的输入设备网卡得到数据,经过内存交给CPU解释成“你好”,然后通过输出设备显示在屏幕上。

软件本质都是在处理CPU和内存的关系。站在硬件角度,聊天是把数据从用户键盘经过体系结构转发到对方显示器的工作。发送文件的本质时把文件从本地磁盘经过体系结构拷贝至对方系统的工程。

3 操作系统(Operator System)

3.1 概念

任何计算机系统都包含一个基本的程序集合,叫做操作系统。

操作系统是一款进行软硬件管理的软件

操作系统包含:内核(进程管理,内存管理,文件管理,驱动管理)、其他程序(函数库,shell程序等)。


3.2 设计OS的目的

第一个意义:向下,进行软硬件资源的管理(这不是目的,是手段,目的是为了让用户良好执行)。

第二个意义:向上,为用户程序(应用程序)提供一个良好的执行环境,这是目的。

子问题:

(1)软硬件体系结构,是层状结构

(2)引入新概念,访问操作系统,必须使用系统调用,其实就是函数,只不过是系统提供的。

(3)我们的程序,只要你判断出它访问了硬件,那么它必须贯穿整个软硬件体系结构!(经过操作系统,驱动)。

(4)库可能在底层封装了系统调用。

(5) 理解系统调用:操作系统不相信任何人,操作系统要向用户提供对应的服务,但又不允许任何人进入内部,所以操作系统提供了一个叫系统调用(本质是函数调用)来访问操作系统。

(6)Linux/windows/macos都是用C语言写的,给的系统调用也就是C风格的C函数,只要是函数就有可能要有输入参数和返回值,输入参数是用户给操作系统的,返回值是操作系统给用户的。

3.3 理解操作系统

操作系统的定位是:一款纯正的"搞管理"的软件。

如何理解管理?假设场景在一个学校,那么学生就是被管理者,校长就是管理者。管理者有决策权,由辅导员去执行。那么操作系统就相当于校长决策,辅导员相当于驱动程序,学生相当于底层硬件。

子问题:

(1)要管理,管理者和被管理者,可以不用见面。

(2)那怎么管理呢?答案是根据“数据”进行管理。例如学校要选择一个篮球队,校长就先从身高数据为190cm以上的人进行筛选。

(3)如何得到数据呢?根据上面的例子,就是由辅导员获取。对应操作系统就是由中间层获取。操作系统要管理各种硬件,不需要直接和硬件打交道,是基于数据管理,数据怎么拿?驱动程序拿。

(4)操作系统怎么对硬件管理?操作系统对硬件管理,定义个struct device结构体,每个设备都要对应个struct device对象,操作系统管理硬件转化成对结构体对象的增删查改。

4 进程

4.1 什么是进程?

课本概念:进程是程序的一个执行实例,正在执行的程序。

内核观点:担当分配系统资源(CPU时间,内存)的实体。

可执行程序真正执行时就会被加载到内存中,把一个二进制文件从磁盘加载到内存,这个东西就叫做进程吗?实际上有很多程序被加载到内存,加载到什么位置呢?操作系统必然要对多个被加载到内存中的程序进行管理,如何管理?

操作系统会给每一个代码数据构建一个struct结构体对象,这个结构体内部有代码地址、数据地址、id、优先级等等。

操作系统的统一叫法,把这个结构体对象叫PCB,中文叫进程控制块。

在内核中,它给我们设计一个数据结构,每加载进来一个程序到内存,操作系统为这个程序用该类型创建一个对象,将信息填进类中,产生一个节点。每个节点都有一个指向内存中程序的指针,这个节点包含这个代码和数据的所有指针。与此同时节点之间也用指针连接起来,进而操作系统内形成了一个加载到内存中的程序列表,我们把这个程序列表叫做进程列表所以,进程不仅仅是将代码和数据加载到内存,而是进程=内核数据结构对象+代码和数据。换句话说,进程=PCB(task_struct)+自己的代码和数据。进程的所有属性,都可以直接或间接通过task_struct找到。

这样设计的好处

  • 操作系统不需要执行代码就能管理进程(只看PCB)
  • CPU不需要知道管理信息就能执行指令(只跑代码)
  • 分离关注点,各司其职

任何一个进程加载进内存的时候,除了将代码和数据加载到内存里,操作系统还要在自己的内部为该代码和数据创建task_struct结构体对象,然后这个结构体对象可以找到内存中的代码和数据,并且所有task_struct在操作系统内部以链表形式将所有PCB管理起来,所以操作系统内管理进程,就变成了对链表的增删查改。

所以CPU未来调度时,不是直接拿代码和数据,而是找到PCB,然后根据PCB找到与之匹配的代码和数据。然后代码运行完了,把代码和数据释放掉;在操作系统内部,将PCB节点释放掉,此时操作系统完成对进程的删除。

为什么一个进程加载时操作系统要创建PCB的结构体对象呢?因为操作系统要管理进程,必须要先描述进程的task_struct,然后再组织管理成特定的数据结构,操作系统就会把进程管理转化成对数据结构的增删查改。

一个结论:linux中执行的所有指令,工具,自己的程序,运行起来,全部都是进程。

4.2 查看进程

ps指令查进程:

ps axj | grep 可执行程序

ls /proc可以通过文件的方式查看进程,proc是process的缩写。

proc目录里记录的是当前系统中所有进程的信息,该目录里所有文件的每个数字目录代表就是特定进程的pid,每个数字目录里的内容包含这个进程运行时的动态属性,一旦进程退出,该目录会被系统动态移除。

随便查看一个数字进程目录:

这里重点属性有两个:(1)exe这个路径是进程对应的可执行文件,也就是说一个进程在启动时知道自己从哪里来的,PCB会记录下来这个进程对应的可执行程序的绝对路径加文件名。若删掉这个exe文件,进程还是会跑,因为删的是磁盘上的文件。进程启动时,这个文件的拷贝已经在内存了。(2)cwd是所处路径。进程记录了当前程序的所处路径,如果程序中有fopen类似代码,后在这个路径下拼接,会在此路径下创建文件。

4.3 通过系统调用获取进程标识符

进程id(PID)

父进程id(PPID)

系统调用getpid()获取进程id,getppid()获取父进程id。linux中所有进程都是被它父进程创建的,子进程由父进程创建。

知识点:

bash:命令行解释器,本质是一个进程,操作系统会给每一个登录用户分配一个bash。命令行中输入的所有命令都是以字符串的方式给bash,bash拿到命令就分析,像执行的ls、pwd、mkdir等等他们的父进程都是bash,那么一个进程bash怎么做到创建子进程的呢?这就要引出系统调用fork()

4.4 通过系统调用创建进程:fork()

fork()是系统调用,可以创建子进程,有两个返回值,返回值大于等于0.

子进程默认会指向父进程的代码和数据,子进程默认没有自己的代码和数据(可以有),因为默认没有程序新加载。

写一个程序:

fork之后通常要用if分流

运行结果:

fork()被调用一次,但在两个进程中各返回一次,父进程返回一次,子进程返回一次。

进程ret的值说明
父进程子进程的 PIDfork()在父进程中返回子进程的 PID
子进程0fork()在子进程中返回 0
问题答案
fork()返回几次?两次——一次在父进程,一次在子进程
返回值一样吗?不一样:父进程得子PID,子进程得0
为什么这样设计?让父子进程能区分自己,执行不同逻辑
代码会被执行几次?fork()之前的代码只执行一次,之后的代码父子各执行一次

子问题:

(1)为什么fork给父子不同的返回值?

在linux中,父进程:子进程=1:n。父进程要通过不同的pid区分不同的子进程,所以要将子进程pid返回给父进程。设置不同的返回值是方便对子进程做管理。子进程可以通过返回值 0 知道“我是子进程”,然后执行子进程特有的逻辑。

(2)为什么fork()会返回两次?

fork()执行后,系统申请新的PCB,拷贝父进程的PCB给子进程,子进程的PCB放入进程list中,甚至放入调度队列中,此时子进程已经创建甚至可能被调度了。父进程和子进程代码共享,父进程和子进程以各自的执行周期各自执行。父进程执行,子进程也执行,所以返回两次。

注意:

  • 返回值不是子进程被调度后才产生的
  • 而是在内核里就已经设置好了(父进程的返回值 = 子进程 PID,子进程的返回值 = 0)
  • 当进程(无论是父进程还是子进程)获得 CPU 并从内核态返回用户态时,它就看到那个返回值。

(3)为什么在上面的代码例子中,一个变量ret既等于0,又大于0,导致if else同时成立?

一个结论:进程具有独立性,一个进程挂了,不会影响其他进程,父子进程在数据层面上默认是共享的,但是一但有人尝试修改数据,那么在系统内部就会把被修改的数据在底层拷贝一份,让目标进程修改这个拷贝,这叫写时拷贝。ret既等于0又大于0,是因为父子进程各自有一份独立的ret变量副本,子进程看到的是0,父进程看到的是子进程的 PID。所以是两个进程,各自执行自己的if else判断。

父子进程保证独立性:

  • 数据结构独立:进程=内核数据结构+代码和数据
  • 代码共享,代码只读,数据以写实拷贝方式各自私有一份。

以上就是有关进程概念的相关内容,希望对你有所帮助,如果这篇文章对你有用,可以点点赞哦,你的支持就是我写下去的动力,后续会不断更新其他知识。

http://www.jsqmd.com/news/478078/

相关文章:

  • 如何用MMDetection3D训练自定义点云数据集?PointPillars实战教程
  • AIGlasses_for_navigation应用:微信小程序开发集成实时导航功能
  • 基于YOLOv5的火灾检测:中文文献综述(2016-2026)摘要本文对过去十年(2016-2026)基于YOLOv5的火灾检测中文文献进行了系统性综述。研究发现,YOLOv5作为单阶段目标检测
  • 鼎捷T100 R报表开发实战:从规格档定制到SQL优化的全流程解析
  • OpenClaw本地部署及飞书接入完整指南总结
  • 从模型损坏到代理冲突:深度解析OllamaEmbeddings两大高频错误的底层原因
  • Does Your Reasoning Model Implicitly Know When to Stop Thinking?
  • 青龙面板配置避坑指南:让你的GitHub爬虫脚本稳定运行(Python3.8+实测)
  • 毛玻璃效果实战:跨浏览器兼容的CSS3 backdrop-filter解决方案
  • AI Agents as Universal Task Solvers: It’s All About Time
  • Unsloth实战演练:从零开始微调一个中文对话模型全过程
  • Pico UnityXR中的手柄射线交互优化与事件封装
  • Midjourney vs Dall·E 3实战测评:电商产品图生成该选哪个AI工具?
  • The Trinity of Consistency as a Defining Principle for General World Models
  • 小白友好!Qwen3Guard-Gen-WEB实战教程:快速搭建多语言内容审核系统
  • UCIe开源生态全景图:从伯克利研究到企业级解决方案(2023最新)
  • Scikit-learn模型部署超简单
  • MusePublic艺术创作引擎效果展示:这些惊艳人像作品,都是用AI生成的
  • Windows下用Anaconda一键搞定LabelImg安装(附Python3.8兼容方案)
  • DAMO-YOLO与Java SpringBoot集成:构建企业级手机检测API
  • Qwen-Image-2512-Pixel-Art-LoRA真实案例:从提示词输入到PNG下载的端到端效果演示
  • #第七届立创电赛# 基于N32G430与INA199的USB功率计设计与RGB彩灯扩展实战
  • 我在非洲修电站,靠松鼠备份给家人“直播”我的生活——断网环境下的生存智慧
  • 小白友好:Face Fusion镜像参数详解与效果调优指南
  • GTE文本向量模型快速部署:中文情感分析与文本分类实战指南
  • 避开Dify模型配置的3个大坑:Ollama本地部署与Docker网络联调实战
  • 飞牛fnOS实战:如何用旧笔记本搭建家庭NAS(Debian内核+VMware详细配置)
  • 霜儿-汉服-造相Z-Turbo与计算机网络原理:理解模型API调用的HTTP/HTTPS协议细节
  • C++ 状态机模式 解读
  • containerd安装后必做的5项配置:从镜像加速到systemd驱动