硬件层
计算机由何组成?
我们现在手中的计算机,无论配置如何,是笔记本还是台式,都由三部分构成:
- 输入设备:键盘,鼠标...
- 中央处理器:cpu,显卡,磁盘...
- 输出设备:显示器...
在这三个设备里,已经有两个设备是我们天天看到使用的,输入设备和输出设备,但是我们了解甚少的,也就是中央处理器,是我们今天的主要话题:计算机是怎么通过这些中央处理器,操控我们的电脑的?
冯诺依曼体系
为了方便大家理解,我们用三体中的冯诺依曼来作为例子:三体中的冯诺依曼-CSDN博客https://blog.csdn.net/qq_74260823/article/details/139362105?spm=1001.2014.3001.5501 (一个关于三体冯诺依曼的扩写,详细讲了冯诺依曼体系的原理和构成,有兴趣可以看看帮助理解)
- 记录方阵,用来记录数据,我们把他叫做内存。
- 计算方阵,用来计算数据,我们把他叫做运算器。
- 皇帝,用来翻译指令,然后转化成机器指令,告诉内存和cpu应该干什么,cpu应该从内存的哪里拿什么数据,我们把他叫做控制器。
这些,便构成了现代的冯诺依曼体系:
但是我们很容易发现,所有的人干的事情,输入和输出,都只能和内存相互交流,没办法去直接和cpu交流,因为cpu听不懂你的话,你也看不懂cpu说的啥,就算你们直接交流了,也是语无伦次不了了之。
就像,你想和霍金说说话,直接找他是没办法的,你必须要通过其他渠道:
并且,这也是出于对cpu的一种保护机制,如果我们直接操作cpu里的数据,一会就给cpu干烧了
比如我们的数组越界,或者是野指针等问题,都是因为操作系统只允许我们操作安全范围内的内存。我们访问内存,实际上是操作系统在帮我们找内存,而非直接访问到cpu内的真实内存。当操作系统发现这个内存在安全范围内,我们便把数据首先交给操作系统,然后操作系统再把数据交给cpu,从而保护cpu内的数据不是被乱修改的。
软件层
什么是操作系统
操作系统是一款软件,要钱的。
我们为什么要选择Linux?因为Linux免费的。
但是,不同的操作系统,价格不同,其实现的基本准则是相同的。操作系统的任务和目标都只有一个:管理好所有的软硬件,只要能完成这个任务,无论这个操作系统是谁做的要多少钱,他都是一个好操作系统。
除了要钱以外,操作系统是一款专门负责管理的软件,他负责管理两件事:
- 与硬件交互,管理所有的软硬件资源
- 与用户交互,为用户的程序提供运行环境。
但是光凭这两句话,操作系统还是不知道要干什么。我们再把软硬件交互和用户交互细分,慢慢看看操作系统的管理原理:
硬件交互
键鼠硬盘
一个优秀的计算机,自然要有这几个熟知的硬件:
- 键盘
- 鼠标
- 网卡
- 硬盘
- ...
但是,计算机怎么知道,这些硬件被安装在了哪里,又怎么知道,这些硬件分别是干什么的?
cpu是没有办法直接读取这些信息值的,这些硬件也没有办法把内存里的二进制数01翻译成给人能看的懂的东西。所以,这个时候就必须要有一个翻译官:操作系统。
操作系统负责切换机械语言和偏人类语言:
当你在键盘敲下d的时候,由操作系统把d翻译成机器二进制码01放在内存的某个安全的位置,然后告诉显示器,从内存的哪个位置(具体的指针地址)拿到这个d,用哪种方式哪种语言打印在显示器的哪个位置,这便是操作系统对底层的任务。
不过,总有些神经病,喜欢改键位。R是放大招,可他偏不,他就喜欢A放大招,R改成平A,享受那种边走边R的快感。这个时候,键位发生了变化,如果直接去操作系统里改文件,
比如把#define R 大招 改成 #define A 大招
首先要找到键位对应的代码文件,然后找到某个键位对应的那一行,最后看看改了这些之后会不会对其他的代码产生影响,那也太麻烦了。
就算改了,对操作系统的负担也是相当大:
“原本我都写死了,现在你说要可修改,那我又要新加一堆功能,新写一堆接口,这个钱谁给我补呢?”
所以,为了避免这些麻烦,操作系统和硬件之间还有一层软件:驱动
驱动
驱动是介于操作系统和硬件之间的一个软件。有了驱动,操作系统就不再直接去硬件上读然后翻译了,他直接读取驱动已经翻译好了的东西,然后直接拿给内存。
比如还是放大招的问题。
按照原先的逻辑,操作系统首先看输入的是什么,是R,然后看R对应的是什么,是大招,于是就知道,我要放大招了。这用了两步翻译。
而有了驱动,驱动直接告诉操作系统,现在有一个大招的输入,操作系统就不再去看用户输入了哪个键,而是直接拿驱动告诉他的大招信息,然后知道,我要放大招了。只用了一步翻译。
但是,驱动什么时候向操作系统发出大招信息呢?这个就是驱动的实现,和操作系统没有关系了,操作系统只负责写出函数声明,然后让每家硬件公司在生产硬件的时候,必须配套一个驱动,驱动里要补充完整这些函数接口,让操作系统直接调用。至此,操作系统只负责接受各种已被驱动处理过的信息,而这些信息怎么来,怎么和用户进行交互,那就变成了驱动的事情。
但是,有人就要问了:
话是这个话,但是,驱动不是我操作系统写的,我只负责写好我的逻辑,当你一个公司产生一个产品的时候,你就把对应的驱动给写好给我,我直接用你的驱动,这样我的锅就变成了你的锅,岂不是完美?
用户交互
我们最直观的用户交互,便是我给钱,你给我发个安装包
太有交互了,我的Windows!
除此之外,我们按下CTRL+ALT+.,会蹦出来个任务管理器,这便是我们第二直观可以看到操作系统的交互:告诉我们每个进程的详细信息,杀掉一个进程,或是创造一个进程。
可是,我们有没有想过,为什么我们可以杀掉这些进程?为什么我们可以创建这些进程?
我们告诉操作系统这些任务,然后操作系统权量这些任务是否合理,合理的操作系统就帮我们完成,不合理的操作系统就会给我们警告,并不会实施这些操作。我们可能很疑惑,难道还有什么任务,是对操作系统不合理的吗?
要不你去直接修改cpu的数据试试?
要不你去直接修改操作系统的数据试试?
操作系统,同样对着底层和自身有着保护机制。我们能用操作系统去完成一些操作,完全是因为,操作系统给了我们一些操作指令,告诉我们那些是可以帮我们做的,这些指令叫做系统调用。比如printf,比如scanf,便是被封装起来的系统调用,我们可以用这些指令去访问底层,但是却无法直接访问底层的数据,因为他们都是被操作系统保护起来的。
所以,整个计算机的管理结构,可以用一张图来概括起来:
怎么管理?
对操作系统来说,所有的管理只遵从一句话:
先描述,再组织。
先描述,再组织。
先描述,再组织。
啥意思?
湖人球队
就比如你想管理湖人球队,此时来了一位年轻的球员,叫做科比,你对他并不熟悉,第一步应该干什么?把他的特点描述出来:
然后第二步,根据你的描述,把被描述的对象组织起来
而对所有球员,我们都这样安排,最后用一个册子把他们包含在内:
我们就完成了对一个球队的管理,需要的时候,我们从名单中一个个分析,一个个拿取,而球队中坠机了某位之后,我们只要把球员的信息表从册子里抽出来就可以了。
操作链表
而操作系统对任何东西的管理,也是通过此完成的。我们接下来要讲进程,就以进程举例,我们首先去描述一个进程,而在C语言中,描述一个进程最好的方法自然是结构体:
struct task//进程
{
char* name;//进程名
int pid;//进程id
...
}
而组织,便是把所有的进程串起来。完成这一操作,最简单的方法,自然也是链表:
struct task_listnode//进程链表
{
task* task;//指向的进程
task_listnode* next_task;//下一个进程
}
对一个进程的增删查改,就变成了对链表结点的增删查改,进程相当于球队的球员,而链表就相当于球队的名单,我们利用先描述再组织这一准则,完美把所有进程管理了起来。
当一个进程被创建的时候,我们把他放在链表的尾部;当一个进程退出的时候,我们直接用O(1)的时间把他删除掉。而在链表中的所有进程,便是正在运行的进程,我们想查看和管理进程的时候,直接遍历链表就可以找到这个进程。
而对文件,对内存,也都是先用结构体描述起每一个对象的特征,然后用链表把所有对象串起来,整个计算机也就通过这种巧妙的方法,完美地用几张链表管理了起来。