【Linux我做主】深入探讨从冯诺依曼体系到进程
从冯诺依曼体系到进程
github地址
有梦想的电信狗
1. 前言
计算机系统的运行离不开硬件与软件的密切配合,其中,冯 · 诺依曼体系结构为现代计算机硬件设计奠定了基础,而操作系统则是管理软硬件资源、为用户程序提供执行环境的核心软件。本文从冯诺依曼体系结构(计算机硬件组成)和操作系统(计算机不可或缺的软件)出发,由浅入深带各位初识进程!
2. 计算机硬件
2.1 冯诺依曼体系结构
冯诺依曼体系结构由约翰·冯·诺依曼于1945年提出,是现代计算机系统普遍采用的一种结构模型。该模型由五个核心部分构成:
输入设备
输出设备
存储器(Memory):通常指内存(RAM),用于存储程序和数据
中央处理器(CPU):包括运算器(ALU)与控制器(CU)
总线系统(Bus):连接各个部分的通信媒介
目前为止,我们所能认识到的计算机,都是由一个个的硬件组成的,这些硬件被分成了以下几个单元:
外设
输入单元(输入设备):键盘、鼠标、扫描仪、摄像头、话筒、磁盘、网卡等
输出单元(输出设备):显示器、、磁盘、网卡、播放器硬件等
其中磁盘、网卡既是输入设备,也是输出设备
中央处理器(CPU):
运算器:对我们的数据进行计算任务(算术运算,逻辑运算)
控制器: 对我们的计算硬件流程进行一定的控制
其中每一个硬件都是独立的个体,各个硬件单元必须用 " 线 " 链接起来,连接的线称为总线!
系统总线:运算器和存储器之间的线
IO总线:存储器和输入输出设备之间的线
关键特性:
存储器特指内存(RAM),是CPU与各外设之间交换数据的唯一媒介
不考虑缓存情况,CPU能且只能对内存进行读写,不能直接访问外设(输入或输出设备)
所有外设的输入/输出,也都必须通过内存作为中转站,才能被CPU或其他设备读取或写入
总结,所有设备都只能直接和内存交互。
在冯诺依曼体系中,最核心的思想是:程序和数据以相同方式存储于存储器中,并通过 CPU顺序读取执行。
关于存储器,我们需要认识到:
存储是分级的
从上到下:
成本价格越来越低
存储容量越来越大
访问速度越来越慢
离CPU越来越远
程序要运行,必须先加载到内存中,这是由冯诺依曼结构体系决定的。
内存是硬件级别的缓存空间,具有核心地位
CPU计算好的数据,返回到内存中,本质就是一种缓存。进度条小程序的刷新,就是把内存中的数据(缓冲区内)刷新到输出设备显示器上。
2.2 冯诺依曼模型的三大要点
统一存储结构
所有程序与数据都存储于内存,CPU通过地址访问它们。CPU不会直接访问外设,所有设备都必须先与内存交互。
顺序执行机制
程序按照存储顺序执行,控制器通过**程序计数器(PC)**逐条指令读取。
程序可修改自身
由于指令与数据形式一致,程序可以动态修改自身代码(现代常规软件开发中不推荐,但该特性为人工智能领域带来了可能性)。
不过,如果我们对冯诺依曼的理解倘若仅仅停留在概念上,是不够的。我们需要深入到对软件数据流理解上。下面我们以登上qq开始和某位朋友发消息为例来体现下冯诺依曼结构体系中数据的流向。
2.3 从QQ聊天认识:冯诺依曼体系下数据是如何流动的?
假设你用QQ给朋友发消息,这个过程中背后的数据流大致如下:
发送方数据流动
输入设备触发操作
用户点击鼠标(输入设备)选择文件,触发中断信号。
控制器响应中断,通知操作系统调度QQ程序。
文件从磁盘加载到内存
**操作系统通过磁盘控制器(DMA)将文件数据从磁盘(外部存储器)直接读入内存(主存储器),**无需CPU逐字节处理。
CPU处理数据
运算器(ALU)对内存中的文件数据进行分块、加密和压缩。
控制器协调网络协议栈(TCP/IP)封装数据包,添加协议头部信息(如IP地址、端口号)。
数据发送至网卡
封装后的数据包**从内存通过DMA传输到网卡(输出设备)**的发送缓冲区。
网卡将数据转换为电信号,经物理网络(如网线、WiFi)发送。
接收方数据流动
网卡接收数据
网卡(输入设备)接收网络信号,将其转换为数字数据,并通过DMA直接写入内存的接收缓冲区,避免CPU参与原始数据搬运。
CPU解析数据包
控制器调度网络协议栈代码,运算器逐层解析数据包(如剥离TCP/IP头部),验证完整性。
解析后的文件数据暂存于内存的用户空间缓冲区。
数据写入磁盘
操作系统调用**磁盘控制器(DMA),将内存中的文件数据直接写入磁盘(外部存储器),**完成持久化存储。
关键硬件协作示意图
发送方:
磁盘 → DMA → 内存 → CPU处理 → 内存 → DMA → 网卡 → 网络
接收方:
网络 → 网卡 → DMA → 内存 → CPU处理 → 内存 → DMA → 磁盘
总结:在冯诺依曼体系中,所有外设读写都要经过内存。无论你是点击、输入、还是传输文件,实际的数据传输路径永远遵循“外设 ↔ 内存 ↔ CPU”。
3. 计算机软件的根基——操作系统
操作系统(Operating System,简称 OS)是介于硬件与应用程序之间的管理软件,本文针对以下三个问题展开讨论:
操作系统是什么?
为什么需要操作系统?
操作系统如何实现软硬件管理?
3.1 操作系统是什么?
操作系统(Operating System,简称 OS), 是一组控制和管理计算机硬件与软件资源的程序集合.
是用户与计算机硬件之间的桥梁。
从组成角度,操作系统通常包含:
内核(Kernel):处理硬件调度、进程管理、内存管理等核心任务。
系统调用接口:为用户程序提供访问硬件的安全通道。
Shell等工具:如 bash、zsh,用于人机交互。
系统服务与库函数:如 glibc、libc等,简化开发者使用系统调用的复杂性。
总结:操作系统是一款进行管理的软件,这里的管理既包括硬件也包括软件。
3.2 为什么需要操作系统?
在没有操作系统的年代,每一个程序都必须直接控制硬件,开发难度极高,效率低下。操作系统的引入使得:
用户不需要关心底层细节,只需操作人性化的界面或命令;
多个程序可以共享硬件资源(如CPU、内存、磁盘);
系统的安全性和稳定性得到保障;
提供统一的开发接口,促进软件生态发展。
一句话总结:操作系统是“硬件资源管理者 + 用户程序服务者”。
综合以上,我们就可以很好的回答我们为什么要有操作系统了。
操作系统通过管理好底层的软硬件资源(手段),为用户提供一个良好(稳定、高效、安全)的执行环境(目的)
3.3 如何管理?
概述
系统调用:用户访问操作系统的唯一入口(类似银行柜台)
管理方法:通过"先描述,再组织"的方式
先描述:用结构体(如C++中的类,C语言中的struct)描述资源
再组织:用链表,队列,红黑树,等数据结构将资源组织起来,统一管理。
组织起来后,对资源的管理,无非就是对这些数据结构的增删查改
所有访问操作系统的行为,都只能通过系统调用来完成,这是因为在操作系统里面会有各种各样的数据,但是操作系统不相信任何用户。
操作系统为了保证自己数据的安全,同时又为了保证给用户提供服务,操作系统以接口的方式为用户提供了调用的入口,方便用户来获取操作系统内部的数据。这些接口一般也是用C语言来实现的。
再来谈谈如何管理,所谓管理,无非是:先描述,再组织。
以C++为例,C++是一门面向对象的语言,其中有类和STL。
通过类的成员方法和成员变量,我们可以描述一个对象
通过STL中的各种容器(本质是数据结构),诸如vector,list等,可以将对象组织起来
有了描述和组织这两步之后,我们就可以很方便的进行管理了。
无论是添加一个对象,删除一个对象,还是对对象中的数据进行修改就都可以很方便的实现了。
而操作系统也恰恰是这么做的,它通过struct结构体把所有的资源信息描述出来,再通过链表或其他更高效的数据结构来组织软硬件资源,通过对数据结构的管理,实现对计算机软硬件资源的管理。
管理的本质,只需要对资源对应的数据进行管理,就能实现对资源的管理。操作系统通过驱动程序,获取到硬件的状态信息来管理硬件。
在操作系统中,管理任何对象,最终都可转化为对某种数据结构的增删查改。
总结核心方法:
核心方法一:结构化描述
操作系统首先用“结构体(struct)”来描述各种硬件或软件资源:
进程用task_struct描述;
文件用file结构体表示;
设备用device结构体封装。
核心方法二:高效组织
多个对象用“链表、红黑树、哈希表”等结构进行组织;
操作系统通过这些结构维护系统状态、调度顺序、访问权限等。
核心方法三:系统调用
用户程序无法直接访问内核空间;
通过系统调用(如fork()、read()、write())进入内核,完成实际资源操作。
3.4 系统调用与库函数
库函数和系统调用是上下级关系,库函数内封装了系统调用。操作系统不相信任何人,系统调用是操作系统对暴露的接口,供给开发使用
操作系统是硬件的管理者,想要访问硬件,必须经过操作系统也就是系统调用
系统调用:操作系统向上层程序暴露的接口,功能基础且对安全性要求高,例如 read()、write()、fork() 等;
库函数:开发者可在系统调用之上进行封装,形成更易用的 API,例如标准 C 库中的 fopen()、printf(),以降低调用难度,促进二次开发。
4. 由“先描述,再组织”初识进程
进程也是操作系统内的资源,同样遵从“先描述,再组织”的设计理念
进程概念初识
CPU只能从内存中读数据。电脑开机的过程,实际就是把操作系统加载到内存中的过程。
先说定义,大多数教材中给出的进程的定义如下:
一个已经加载到内存的程序,叫做进程。有时也把进程称作一个任务task
正在运行的程序,程序的一个执行实例,叫做进程
如上就是正在运行的一个进程
使用ps ajx | grep myprocess查看刚刚已经执行的进程。其中
ps ajx列出系统所有的进程
ps ajx | grep myprocess将所有进程通过管道输入,过滤myprocess进程
进程概念深入
认识进程
显而易见:
一个操作系统,不仅仅只能运行一个进程,是可以同时运行多个进程的!!!
因此操作系统必须将这多个进程管理起来。如何管理呢?先描述,再组织
首先,操作系统需要先认识进程!
思考?我们人是怎样认识一个事物或者对象的?
人是通过描述事物的属性来辨识事物的,也就是做面向对象的过程。当相关的属性足够多时,这一堆属性的集合,就是目标对象。人就是这么认识事物的。
那么操作系统认识进程,也需要足够多的进程的属性。由于操作系统是用C语言写的,因此操作系统必然采用struct来描述进程。
操作系统的行为
描述进程,要包含进程的属性的集合。结论如下:
任何一个进程,在加载到内存的时候,形成真正的进程时,操作系统要先创建描述进程属性的struct对象,
这个对象被称为——PCB。process ctrl blcok,进程控制块,是进程的属性的集合。
PCBstruct对象包含描述进程的所有属性,操作系统通过PCB进程控制块来辨识进程
基于以上内容,我们可以得出操作系统内核观点下的进程的概念:
单独加载到内存的可执行程序不叫进程,单独的PCB也不叫进程
描述进程的PCB结构体对象 和 加载到内存中的程序的代码和数据 合起来 才叫进程
PCB由操作系统内部自己维护的,代码和数据是开发者自己写的。
加载进程时,不止是把 code和 data加载到内存,操作系统还根据操作系统内的 描述进程的进程控制块,为当前进程创建PCB,把该进程的相关属性填充好,初始化,形成一个PCB,该结构体是操作系统自己形成的
操作系统管理进程,只需要对一个个的PCB对象做管理,就能管理好进程。对进程的管理,本质是对内核PCB数据结构对象的管理
进程的完全定义
进程的完全定义 = 内核PCB数据结构对象 + 自己的代码和数据。是担当分配系统资源(CPU时间,内存)的实体
内核PCB数据结构对象 包含描述这个进程的所有属性值
自己的代码和数据,就是自己在磁盘中形成的可执行程序
有了PCB结构体对象,操作系统内管理进程就变成了管理PCB结构体对象。操作系统内是用链表来组织PCB的,因此实现对进程的管理本质是对链表的增删查改
Linux下的进程
以上PCB是进程的设计理念,不同的操作系统对PCB的实现不完全相同,我们来看Linux下具体是如何实现的。
描述进程-PCB
进程所有的属性信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
一般称之为PCB(process control block),Linux操作系统下的具体的PCB是: task_struct
在Linux中描述进程的结构体叫做task_struct
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息,这个装载的过程就是task_struct的实例化
task_struct是一种自定义数据类型,Linux内核中,最基本的组织task_struct的方式,是采用双向链表组织的。
但每个PCB不只是双向链表内的一个结点
PCB结点可能同时也属于某个二叉树或者队列中的结点
task_struct中的主要属性分类
标示符: 描述本进程的唯一标示符,用来区别其他进程,这个标识符就是进程的PID
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。CPU要调度多个进程,就注定了多个进程要竞争CPU,因此需要设定进程的调度优先级
程序计数器(PC指针): 保存程序中即将被执行的下一条指令的地址和当前正在运行的指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针,方便CPU找到开发者程序的代码和数据
上下文数据: 进程执行时处理器的寄存器中的数据。
I/O状态信息: 包括显示的 I/O 请求,分配给进程的 I/O 设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息等
Linux下查看进程
通过命令查看
命令1
命令:ps
功能:查看系统下的进程
常用选项:ajx,用于查看系统下的所有进程
ps也是一个可执行程序,因此ps运行起来是一个进程,grep过滤时,会过滤出所有含有ps的所有内容
这里ps ajx 和 grep ps都含有ps,因此都会高亮显示。
常用用法 ps ajx | head -1 && ps ajx | grep processName
head -1 用于显示第一行,显示进程表头信息,
ps ajx | grep processName用于指定过滤进程precessName
可以看到显示多了进程的表头信息
命令2
命令:top
功能:查看系统CPU资源使用状态
常用选项:无
最左侧的PID就是进程的编号,由此达到查看进程的目的。
通过系统目录查看
进程的信息还可以通过/proc系统文件夹查看
每一个进程在系统运行期间,关掉后再重启,系统为进程重新分配的PID大部分情况是不一样的
ls /proc查看系统内所有的进程。
/proc目录是一个特殊的系统目录
关机时,目录内的所有数据都会消失
开机时,操作系统会创建该目录、及其文件和所有数据
/proc目录是系统将内存中的数据,通过可视化的方式,以目录的形式向我们呈现
该目录动态地包含系统内所有的进程,目录名大部分是数字名,代表进程的PID
查看当前进程的目录
通过当前进程的PID查看当前目录的内容:ls /proc/ 161157 -l,其中目录内有两个关键信息:
cwd:current working directory,表示当前进程的工作目录。
exe:指向当前可执行程序的存储路径
思考?
C语言使用fopen("log.txt", "w")创建文件时,没有指定文件在哪里创建,为什么会在当前目录下创建文件?
为什么touch test.c这个命令,一旦运行起来变成进程,我们并没有指定路径,却会自动在当前目录下创建文件?
这是因为
在特定目录下运行的进程,进程运行时所在的目录,就是当前进程对应的当前目录
进程启动时,记录下了启动时自己所处的目录,这个目录就是之后进程的工作目录cwd
进程在启动后,有了自己的工作目录cwd。调用fopen或touch时,用户传入的路径是"log.txt",在系统层面,会默认把cwd路径拼接到"log.txt"前面。即cwd/"log.txt"。cwd就叫当前路径
最终也就在当前目录下创建了文件
结语
通过本文的探讨,我们从冯诺依曼体系结构出发,深入理解了计算机硬件的核心组成与数据流动的本质。无论是键盘输入、屏幕显示,还是网络通信与文件传输,所有操作都严格遵循“外设 ↔ 内存 ↔ CPU”的协作逻辑。这一设计不仅奠定了现代计算机的基石,更揭示了内存作为数据中转枢纽的核心地位。
在此基础上,我们认识到操作系统是连接硬件与软件的桥梁。它通过==“先描述,再组织”==的哲学,以数据结构(如 task_struct)描述资源属性,并通过系统调用与高效算法实现资源管理。这种设计思想在进程管理中体现得尤为明显:进程作为程序执行的实例,其本质是内核数据结构(PCB)与代码数据的结合体,操作系统通过管理PCB的链表或树结构,完成对进程的调度、隔离与资源分配。
对于我们而言,理解进程的底层逻辑(如通过 /proc 目录查看进程信息、通过 ps 命令分析运行状态)是优化程序性能、排查问题的重要基础。而这一切的背后,始终贯穿着操作系统的核心使命——以安全、高效的方式,为程序提供执行环境。
以上就是本文的所有内容了,如果觉得文章写的不错,还请留下免费的赞和收藏,也欢迎各位大佬在评论区交流
————————————————
版权声明:本文为CSDN博主「有梦想的电信狗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2301_80064645/article/details/147552175