文章目录
- 🗞️ 冯诺依曼体系结构 🗞️
- 📃 为什么在计算机当中需要使用内存充当中间介质而不使CUP与外设直接进行交互?
- 📃 CPU如何读取数据
- 🗞️ 操作系统(Operating system) 🗞️
- 📃 操作系统如何对资源进行管理
- 🗞️ 进程 🗞️
- 📃 进程是如何被管理的?
- 📃 如何观察进程?
- 📃 以文件目录的形式观察进程
- 📃 进程标识符
- 📃 父进程
🗞️ 冯诺依曼体系结构 🗞️
数学家冯·诺依曼提出了计算机制造的三个基本原则,即采用二进制逻辑、程序存储执行以及计算机由五个部分组成运算器、控制器、存储器、输入设备、输出设备),这套理论被称为冯·诺依曼体系结构。
存储器:
在当前的计算机组成当中,存储方式分为内存存储与硬盘存储等;
与之不同的是硬盘为非易失性存储器(断电不易失),内存为易失性存储器(断电易失);
输入设备:
在生活当中, 键盘,摄像头,话筒,磁盘(读取文件),网卡等都为输入设备;
输出设备:
显示器,音响,磁盘(写入文件)网卡等都为输出设备;
CPU(中央处理器):
运算器
算逻运算 - 算术运算(±运算等)与逻辑运算(
if
,else
)控制器
CUP是可以相应外部事件的(协调外部就绪事件,例如将数据拷贝至内存当中)
📃 为什么在计算机当中需要使用内存充当中间介质而不使CUP与外设直接进行交互?
在各个存储介质当中,不同类型的存储介质的读取速度也不同
响应速度分别为:
CPU&&寄存器
>内存
>磁盘/SSD
>光盘
>磁带
在最早的计算机当中是不存在内存的;
也就是外设与CUP之间直接进行交互;
但是实际上在计算机当中,最影响计算机整体速度的并不是计算机中最快的,而是最慢的;
若是使外设直接与CPU直接进行交互将会导致外设的速度大大拖慢CPU的速度导致计算机整体效率变慢;
而若是在计算机当中添加存储器后,可以使得外设中的数据通过操作系统率先load(加载)至存储器中,而又由于存储器与CPU的读写速度相差跨度并不是特别大,所以当数据加载至存储器当中CPU可以直接访问存储器从而加快计算机整体的速度;
📃 CPU如何读取数据
以当前大环境而言,CPU一般读取数据(数据,代码)都是从内存之中进行读取;所以若是以数据的角度进行观察:CPU不和外设直接进行交互;
当CPU需要处理数据时会首先将外设中的数据加载到内存当中(外设只与内存进行交互);
以这种方式可以进行一种分类
input
将数据从外设(输入设备)加载至内存中;
output
将数据从内存加载至外设(输出设备)当中;
当数据需要处理时数据首先会加载至内存,通过CPU读取内存数据并作出相应处理;
当数据处理完毕之后并不会直接从CPU转至外设(输出设备),会先将处理后的数据存放回内存并让外设(输出设备)进行显示;
🗞️ 操作系统(Operating system) 🗞️
操作系统可以看作一个用来对软硬件资源进行管理的软件;
硬件
硬件是整个计算机之中最基本的,任何操作系统都是基于硬件之上;
而在一个机器当中的所有硬件都是按照冯诺依曼体系结构进行存放,不存在杂乱无章;
驱动程序
而紧接着在硬件之上就是驱动程序,驱动程序也是系统软件部分的一部分,它的主要功能就是对硬件进行操作;
主要提供软件级别的对硬件操作的接口;
操作系统的主要工作分为两个方面:
- 对上提供良好的使用环境;
- 对下通过管理软硬件资源使计算机保持更高的稳定性;
📃 操作系统如何对资源进行管理
操作系统对资源进行管理时主要按先描述后组织
的概念进行管理;
操作系统在整体中处于一个管理者的位置,而这里的先描述后组织的意思即为:
管理者将所有的被管理者以特定的结构进行描述,由于被管理者类型的不同,所以整体的结构也跟着不同;
由于大部分常见的操作系统都是由C/C++开发,所以可以将这个所谓的结构看成是一个结构体(被管理者可以看成类似实例化);
struct A{ /*属性*/ };
而管理者正式通过这些对象的属性,根据需要的条件对资源进行操作和管理;
🗞️ 进程 🗞️
由于操作系统需要对软硬件资源都进行管理,当然这里的资源也包括进程;
从之前的知识点中可以总结出一条结论:当一个程序被运行时,必须将该程序先加载到内存当中,这是因为由于冯诺依曼体系结构规定CPU访问数据时或者要对数据进行对应操作时首先必需从内存中访问;
当然这句话说的并不全面,对于"当一个程序被运行时,必须将改程序先加载到内存当中",事实是如此,但这仅仅只是以硬件的角度进行观察,真正意义上来说,当一个程序被运行之后,将不能叫做程序,应该叫做"进程的一部分";
以Windows11为例,当我Ctrl
+Alt
+del
并选择任务管理器时可以观察到许多进程;
根据用户的需要,用户可以通过这个任务管理器去结束相应的进程;
本质上操作系统并不具备直接管理程序的能力,但是操作系统中存在着一个进程管理;
当一个程序被运行起来之后,它将被加载进内存成为进程的一部分,而操作系统通过进程管理可以完成对这个进程的资源管理;
在Linux中也是如此,每当运行一个程序或者执行一条命令时都是将程序/命令加载进内存使其成为进程从而对该资源进行对应的管理;
📃 进程是如何被管理的?
在Linux中,可以同时运行多个程序或者同时执行多条命令的,当然这也意味着在Linux是可能同时存在大量的进程;
那么当Linux同时存在大量的进程时,操作系统该如何对进程这个资源进行管理;
其实很简单,在上文中我们提到的一个概念为 “先描述,后组织”;
操作系统对进程的管理也是如此;
在进程种有一种数据结构叫做PCB(Process Control Block),也可以称之为进程控制块,可以理解为进程属性的集合;
而在Linux下描述进程信息的PCB为task_struct
,是以结构体的形式进行实现的;
当一个程序加载进内存成为进程时,操作系统将自动为这个进程创建一个PCB结构体对象,实现对这些进程的描述;相应的这个结构体内存着对应进程的所有属性,而操作系统将用某种数据结构将这些结构体对象链接在一起,从而使得能够通过不同的属性对进程进行对应的资源管理;
这就可以理解了为什么说"当程序被加载进内存的时候就成为了进程的一部分",因为只有加载进内存的程序(代码与数据) + 该进程所对应的PCB结构体这个整体才能称为是一个进程;
在PCB种包含的结构体化的数据属性中包含了:
-
标识符
描述本进程的标识符;
-
状态
任务状态,退出代码等;
-
优先级
程序相对于其他进程的优先级;
-
程序计数器
程序中即将被执行的下一条指令的地址;
-
内存指针
程序对应的代码以及数据的位置信息;
-
上下文数据
进程执行时处理器的寄存器中的数据;
-
I/O状态
-
记账信息
可能包括处理器时间总和等各种信息;
-
其他
📃 如何观察进程?
在上文中我们提到了进程有关的概念,那么在Linux中该如何观察进程?
- 在Linux一般可以使用
ps
来观察进程;
但是ps
只能观察当前窗口(终端)的进程;
若是想要观察所有的进程的话需要使用指令ps axj
,当然也可以配合管道|
与grep
来查到对应所需要的进程;
那么该如何更直观的观察到进程?
假设有一个.cpp
文件,代码为:
#include<iostream>
using namespace std;
int main()
{
while(1) cout<<"进程1"<<endl;
}
并执行这段程序;
此时就已经是执行了一个进程,当然可以使用ps
命令的方式观察这个进程;
使用ps axj | head -1 && ps axj | grep myproc
查询该进程:
该进程就已经跑了起来;
此时在终端使用
ctrl
+C
结束该进程并再次查询该进程时发现该进程不存在;
- 在Linux中除了用
ps
命令以外还可以使用top
命令;
top
命令在Linux中可以看作是Windows下的任务管理器;
📃 以文件目录的形式观察进程
- 除了可以以上面的方式观察进程以外还可以以目录文件的形式观察进程;
以上面的方式我们再次运行那个程序,将进程跑起来;
-
先使用
ps
的方式查看进程的对应信息;将会看到PID与PPID;
我们首先观察
23460
的这个进程(下面的进程为使用grep
时的进程,在本次中可以不进行观察); -
再使用
ls
查看目录/proc
;可以发现
/proc
目录下的文件就是一些进程; -
再使用
ll
并使用grep
来找当前目录下的PID
为23460
的这个进程(此时进程还在运行当中);当我们手动使用
Ctrl
+C
将程序结束时再使用该指令查找该进程时将发现该进程已经不存在;
但这并不说明进程在Linux中是以目录的形式存在,实际上/proc
是Linux中的一个文件系统,而这个文件系统是一个伪文件系统,通过该文件系统可以访问进程的属性信息而已;
使用ll
观察23460
这个目录下的文件可以发现,在该目录下存在着两个文件;
这两个文件分别以cwd
与exe
开头;
-
exe
后所跟的路径为,当前进程所对应程序所在的路径; -
cwd
后所跟的路径为,当前进程的工作目录;当一个程序运行时,每个进程都将会有一个属性来保存当前自身所在的工作路径;
📃 进程标识符
在上面提到了关键字PID
,而实际上PID
即为进程的标识符;
每个进程所对应的PCB
都将会存储这个进程标识符;
进程标识符是每个进程中独有的属性;
当然每个进程在每次运行时其PID
都将不同;
在程序中也可以使用getpid()
函数来进行对当前进程PID
的获取;
其中pid_t
为操作系统中的一个数据类型,是一个无符号整型;
#include<iostream>
#include<sys/types.h>
using namespace std;
int main()
{
while(1) cout<<"进程1 "<<"PID : "<<getpid()<<endl;
}
从这里可以看到这里所获取的PID
与之前观察到的PID
都不同,可以验证在上面所说的"每个进程在每次运行时其PID
都将不同";
-
除了以
Ctrl
+C
的形式结束进程以外也可以使用kill -9 'PID'
的形式结束进程;
📃 父进程
除了进程以外还有父进程,也可以在程序中使用getppid()
函数来获取该进程的父进程的PID
;
而实际上PPID
即为bash
(Bash 是一种命令行解释器和 shell 程序);
Shell通过创建子进程的方式来完成对应的任务;