冯诺依曼体系结构
在了解进程之前首先我们要先了解在硬件上的重要的体系结构—冯诺依曼体系结构 我将从下面这个图中的5个主要部件进行讲解
冯诺依曼体系结构五大部件
-
运算器: 计算机中执行各种算术和逻辑运算操作的部件。运算器的基本操作包括加、减、乘、除四则运算,与、或、非、异或等逻辑操作,以及移位、比较和传送等操作,亦称算术逻辑部件(ALU);
-
控制器: 程序计数器、指令寄存器、指令译码器、时序产生器和操作控制器组成,它是发布命令的“决策机构”,即完成协调和指挥整个计算机系统的操作。运算器和控制器统称中央处理器,也叫做CPU。中央处理器是电脑的心脏;
-
存储器: 存储器分为内存和外存。内存是电脑的记忆部件,用于存放电脑运行中的原始数据、中间结果以及指示电脑工作的程序。外存就像笔记本一样,用来存放一些需要长期保存的程序或数据,断电后也不会丢失,容量比较大,但存取速度慢。当电脑要执行外存里的程序,处理外存中的数据时,需要先把外存里的数据读入内存,然后中央处理器才能进行处理。外存储器包括硬盘、光盘和优盘
-
输入设别: 输入设备是向计算机输入数据和信息的设备。是计算机与用户或其他设备通信的桥梁。输入设备是用户和计算机系统之间进行信息交换的主要装置之一。键盘,鼠标,摄像头,扫描仪,光笔等都属于输入设备。
-
输出设备: 输出设备:是计算机硬件系统的终端设备,用于接收计算机数据的输出显示、打印、声音、控制外围设备操作等。也是把各种计算结果数据或信息以数字、字符、图像、声音等形式表现出来。常见的输出设备有显示器、打印机等。
冯诺依曼体系结构部件相关知识
-
CPU:由运算器和控制器组成
-
输入设备:话筒、摄像头、键盘、鼠标、磁盘、网卡等
-
输出设备: 声卡、显卡、网卡、磁盘、显示器、打印机等 —只做输入、只做输出、既做输入,又做输出(文件在磁盘上放着,当想把文件传到云盘上,经过网络传输出去,又可以将文件下载到磁盘上,磁盘既做输出,又做输入)
-
冯诺依曼的存储器指的是内存!!!内存 — 掉电易失 磁盘 — 永久存储
-
独立的设备是连接的 — 当两个人在聊天的时候一个人在键盘上输入一条消息时,消息会通过网络发送到另一个人的电脑上,在发送消息的过程中,信息从键盘到网卡,才能发送出去。
冯诺依曼体系结构中的数据流动
上面的冯诺依曼体系结构图中的红色的线代表的是设备之间的数据流动,那么我们会产生疑问为什么数据流动式按照上面图中所示的呢,数据流动本质 — 设备之间会进行数据的来回拷贝 。拷贝的整体速度,是决定计算机效率的重要指标!
首先我们知道数据在计算机可以通过输入设备传给CPU由CPU进行处理后在交给输出设备,但是这是错误的因为我们忽略了存储器的作用那么我们将会产生下面的疑问。
为什么在体系结构中要存在内存
首先我们要了解在计算机存储器中存在着一个存储金字塔
-
距离CPU越近的存储单元,效率越高,造价贵,单体容量越小
-
距离CPU越远的存储单元,效率越低,造价越便宜,单体容量大
因为输入,输出这些通常叫做设备,这些设备是离CPU较远,CPU的处理速度很快,但是输入和输出设备的速度和CPU的处理速度相差太大,这样会导致CPU会大量时间处于闲置状态,根据木桶原理,计算机的速度会受到最慢的那个设备的影响,会导致整个计算机的运算速度降低。我们引入内存,内存与CPU的处理速度的差距要更低一点,我们通过输入设备将数据输入到内存,在通过内存交给CPU处理,CPU处理完后将数据再次交给内存,从而提高速度。那么我们可能还存在疑问
- 最慢的设备还是外设,还存在。
- CPU本来可以直接通过输入设备得到数据,但是现在需要将数据从输入拷贝到内存,再从CPU拷贝到内存
但是如果我们可以在CPU还没有访问输入设备的数据前,提前将这些数据加载到内存中,CPU将处理完的数据放到内存后,CPU就不用管了,CPU在做其它事情时,将内存中的数据在送到输出设备。
我们可以把内存看做一个非常大的缓存介于设备和CPU之间,计算机的效率最终要以内存效率为主,引入内存把效率问题,转化成了软件问题。
解释现象
现象1:程序在运行的时候,必须先把程序加载到内存
首先我们知道一个程序是要被放在文件当中,而文件是放在磁盘当中的,磁盘是外设。然而一个程序中存放着大量的指令和数据,这些指令和数据是最终要让CPU执行的。然而我们有冯诺依曼体系结构可以知道,CPU是和内存之间进行直接交流,所以一个程序在运行之前要先加载到内存当中。
在数据层面,CPU和内存进行数据流动,外设和内存进行数据流动。
现象2:当甲和乙通过微信进行聊天时,当甲输入一句话时这句话是怎么流动的。
分析上图,甲先将要发送的数据,通过键盘输入设备,传输到存储器当中,然后再从存储器送入CPU进行分析,之后再传输到网卡当中,通过网络传输到乙,乙通过输入设备网卡,将数据传输到存储器当中,然后同过CPU的一系列操作,送回到存储器当中,最后传输给输出设备。
操作系统
概念
操作系统:是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境;它是计算机系统中最基本的、最接近系统硬件的系统软件。
那么到底什么时候管理,这是我们要重要探讨的问题,在文章的后面将会详细讲解。我们先了解一个问题 —为什么要有操作系统?
通过操作系统的感念我们可以知道操作系统的一个重要功能就是管理,那么我们可以把问题转换成为什么要有操作系统的管理?
因为
- 对下要管理好软硬件资源 — 手段
- 对上要提供一个良好(稳定,高效,安群)的运行环境 — 目的
计算机的层状结构
首先我们先了解一下计算机的层状结构
底层硬件
底层硬件是由冯诺依曼体系结构将他们组织起来
驱动程序
是计算机系统中的软件组件,它们与硬件设备进行通信和协调,使操作系统能够正确地与各种硬件设备进行交互。驱动程序充当操作系统和硬件之间的中间层,提供了一种标准化的接口,使操作系统可以了解和控制硬件设备的功能和特性。
驱动程序可以为硬件提供操作,但是驱动程序并不能决定什么时候打开什么时候关闭,什么时候读什么时候写等等。每一款硬件都要有与其对应的驱动程序。
管理概念
管理是指管理者在一定的环境条件下,通过实施计划,组织,领导,控制和创新等职能,以人为中心协调各种资源有效率和有效果地实现组织目标的过程。
一般一个人主要完成的无非两个方面,一个是做决策,一个是做执行。那么管理者 — 做决策的人,被管理者 — 做执行的人
管理者和被管理者并不需要见面
- 管理一个人的本质不在于和你见面
- 管理的本质不在于对人做管理,而在于对人的信息(数据做管理)
- 管理者核心工作是做决策,根据数据做决策
管理员和被管理者不见面,怎么拿数据
- 通过中间层进行获取数据
数据量太大,管理者如何管理
- 举例说明,假设要管理一个学校所有学生的信息,我们可以根据学生的学号,姓名,专业,年龄,性别等等定义成一个
struct student
结构体,将各个学生的结构体以链表的形式链接起来,那么当我们要增加,删除,查询,修改一个学生时,对学生的管理工作就变成了对链表的增删查改!!!
那么在整个计算机体系结构中,操作系统相当于管理者,硬件部分就相当于被管理者。
管理的核心就是6个字:先描述,在组织!!!
如何向上层提供良好的运行环境
我们都知道操作系统会向上层用户提供一个良好的运行环境,那么如何提供呢?通过下面银行体系进行解释。
银行体系
在银行体系中,首先银行必须要给用户提供服务,但是银行又害怕群众之中存在坏人,那么就假设所有人都是坏人,无法直接进入银行体系内部,那么群众如何获取到服务呢?
银行体系会对用户提供对应的柜员和窗口,这些服务窗口可以向上对用户提供了服务,有保护了银行的安全。
系统调用接口
系统调用提供了用户程序和操作系统之间的接口,应用程序通过系统调用实现与OS的通信并取得服务。由此可以看出系统调用的目的就是请求系统服务。
根据上面的例子,我们可以类比,用户是不能直接进入操作系统获取数据,这种行为是不安全的,用户只能通过系统调用接口来访问操作系统!!!
系统调用粗暴的理解,就是用C语言设计的函数,由操作系统就行提供,函数存在着输入和输出,用户输入数据,操作系统在通过系统函数输出。
用户操作接口
用户接口是用户与计算机系统交互的环境和方式。为了方便用户使用计算机系统,操作系统向用户提供了直接使用计算机系统的手段,通常称为用户接口。用户通过操作系统提供的接口与计算机系统交互,即用户通过一定的方式和途径,将自己的要求告诉计算机,而计算机根据用户不同的要求完成相应的操作和处理。
从银行体系例子进行分析,当老年人不会直接通过柜台进行操作,柜台的这些流程和操作对于老年人是不方便的,那么银行就会有大堂经理等职员,来服务老年人,给与更方便的服务。
Linux和windows是不同的两款操作系统但是C语言的打印都是printf
因为printf
是库函数,不同的操作系统的差异化屏蔽了,库函数的实现会根据不同的操作系统的系统调用接口进行实现。这就是语言跨平台性,可移植性!!!、
系统调用 VS 库函数
首先我们需要知道库函数就是用户操作接口中的一种,如我们C语言经常使用的,printf scanf
等。
库函数在上,系统调用在下!!!
结论
用户对软硬件的访问,不能越过操作系统,不能存在越级操作,必须从上贯穿到下。然而操作系统对上提供服务必须提供系统调用接口!!!,由于使用难度的问题,会提供用户操作接口。
进程
概念
课本概念:程序的一个执行实例,正在执行的程序等。
内核观点:担当分配系统资源(CPU时间、内存)的实体。
上述概念比较难以了解,下面将进行详细解释。
在计算机中我们可以启动多个程序,但是我们一定要将多个.exe
文件加载到内存当中。我们还知道,操作系统的一个功能时管理,那么操作系统要对多个加载到内存的程序进行管理。
由上面的讲解我们可以知道管理的核心就是,先描述在组织,程序最终都会成为二进制代码,操作系统并不认识。操作系统为了更好的描述这些加载进来的程序,会为这些进程创建描述这些进程的结构体变量!!!
这个结构体就是struct PCB
,那么程序加载到内存,变成进程之后,我们要给每一个进程形成一个PCB对象,便于操作系统的管理。所以 进程 = 内核数据结构 (内核PCB对象)+ 可执行程序 ,所有对进程的控制和操作,都只和进程的PCB有关,和进程的可执行程序没有关系!!!
描述进程 -PCB(进程控制块)
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
- PCB(process control block),Linux操作系统下的PCB是
task_struct
PCB内部属性
属性名称 | 解释 |
---|---|
标识符 | 描述本进程的唯一标示符,用来区别其他进程。 |
状态 | 任务状态,退出代码,退出信号等。 |
优先级 | 相对于其他进程的优先级。 |
程序计数器 | 程序中即将被执行的下一条指令的地址。 |
内存指针 | 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针 |
上下文数据 | 进程执行时处理器的寄存器中的数据。 |
I/O状态信息 | 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。 |
记账信息 | 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。 |
优先级
优先级存在的根本原因还是资源过少,CPU是少数的进程时多数的,进程必须在CPU进行排队,那么就通过优先级来判断谁先谁后的问题。
程序计数器
在CPU控制部件中的程序计数器(PC)的功能是用于存放指令的地址。程序执行时,PC的初值为程序第一条指令的地址,在顺序执行程序时,控制器首先按程序计数器所指出的指令地址从内存中取出一条指令,然后分析和执行该指令,同时将PC的值加1指向下一条要执行的指令
简单来说就是,当执行你的程序时,CPU怎么知道这个程序该执行到了哪一行代码?CPU中存在一个寄存器PC就是记录下一条要执行的指令。
内存指针
PCB中能找到对应程序的字段,叫做内存指针。
查看进程
我先通过运行一个程序来查询这个进程,下面是这个程序的源代码,这个代码放入到了一个myprocess.c文件当中
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("I am a process\n");
sleep(1);
}
return 0;
}
接下来我们通过查询命令来查询 ./myprocess
这个程序的进程信息
ps ajx | head -1 && ps ajx | grep myproces
但是图中./myprocess
是我们运行起来的程序产生的进程,但是grep
为什么也会创建进程?
因为grep
在进行过滤的时候本身是一个指令,指令也是一个程序,过滤的关键字中包含了myprocess
,grep
自己也变成了进程,所以过滤进程时,grep
中含有myprocess
也会被过滤出来了。
我们可以得到结论几乎所有的独立的指令,就是程序,运行起来也要变成进程!
如果不想要可以进行反向匹配
ps ajx | head -1 && ps ajx | grep myproces | grep -v grep
查看进程的另一种方式
Linux操作系统会将进程以文件的形式进行存储,放在/proc
这个目录下面(以pid
为名字)
ls /proc
当我们运行./myprocess
程序(获取程序的pid
),我们就可以根据相应的pid
在/proc
目录下找到这个进程的相关信息。
但是当我们程序运行的时候把可执行程序删掉,再次产看/proc会发现下图的现象
根据上图的现象我们会产生疑问,为什么我们把程序删掉后,为什么程序还在跑?因为运行一个程序的本质是,把程序拷贝到内存当中,此次运行过程中,删除程序只是删除的磁盘上的,本次运行的在内存上的仍在正常运行!!!
进程所对应的目录中除了exe
(可执行程序的目录)还有cwd
,那么cwd
是什么呢,下面将详细讲解。
cwd
— 当前进程所对应的当前工作目录
我们都知道在C语言中有着文件操作,当我们要打开一个文件时是fopen("file.txt","w")
,就会在当前路径下创建一个文件,那么什么是当前路径 ?
所谓当前路径就是cwd
所对应的!!!所以会和可执行程序创建在同一路径下
那么如何更改 当前进程的工作目录呢?
int chdir(const char *path)// 0成功 -1失败
通过这个系统接口就可以更改。
getpid /getppid
一个进程中的数据是在操作系统中维护的,PCB的所有数据是操作系统中的数据,要想获取必定调用系统调用。
getpid
用途
获取目前进程的标识符
头文件
#include<sys/types.h>
#include<unistd.h>
函数返回值以及形参类型
pid_t getpid(void); // pid_t类似一个整数类型
用法
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
__pid_t id = getpid();
while(1)
{
printf("I am a process pid: %d\n",id);
sleep(1);
}
return 0;
}
输出结果
getppid
一般在linux中,普通进程,都有他的父进程
用途
获取目前进程的父进程的标识符
头文件
#include<sys/types.h>
#include<unistd.h>
函数返回值以及形参类型
pid_t getppid(void); // pid_t类似一个整数类型
用法
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
__pid_t id = getpid();
__pid_t pid = getppid();
while(1)
{
printf("I am a process pid: %d ppid: %d\n",id, pid);
sleep(1);
}
return 0;
}
输出结果
我们发现两次程序运行pid
会发生变化,每次启动进程的pid
几乎都会变化,因为进程就是一个新的进程。但是我们的父进程一直都是68994没有发生变化。那么68994是什么。我们通过查询会发现68994是bash进程。
得出结论,命令行启动的所有程序最后转换成进程都是bash
(命令行解释器)的子进程!!!
本专栏为“小菜”linux学习之路
该文章仅供学习参考,如有问题,欢迎在评论区指出。