一.冯诺依曼体系结构
计算机大体可以说是由五个硬件组成:存储器,运算器,控制器,输入设备,输出设备
存储器又分为内存(掉电易失)和外存(永久性存储),所谓外存就是除内存以外具有永久性存储能力的存储设备(最常见的是磁盘)
运算器和控制器共同组成了中央处理器,也就是CPU
除此之外电脑中还存在一些输入输出设备,比如:网卡,磁盘等
在冯诺依曼中所说的存储器就是内存
冯诺依曼体系规定了这五个硬件之间是如何进行数据交流的:
通过观察发现,在数据层面上CPU只和内存打交道。
CPU是所有设备中最快的,而输入设备和输出设备作为外设来说是最慢的。如果每次CPU读取/写入数据都要和外设打交道,那么计算机的效率就是以外设作为基准了,因为CPU要等外设将数据拿出/载入,这也极大的浪费了CPU的性能。
越快的东西造价越高,为了不让计算机的价格太高,于是就在CPU和外设之间引入了内存(主存和高速缓存)。
让CPU只和内存打交道,那么整机的效率就由内存决定。内存比CPU要慢,但是比外设要快的多。
对所谓的打交道更详细点说就是:主存从磁盘读取数据并加载到L3高速缓存,L3到L1高速缓存逐级向上加载读取数据,最后将数据加载到寄存器中,CPU再到寄存器中读取数据。
以后聊天别发在吗,因为这个在吗要从你的键盘到你的内存然后还要到你的CPU,最后到你的网卡和显示器,再到我的网卡我的内存我的CPU,最后到我的显示器。太麻烦了,这边建议先叫老公(斜眼笑)
到这里我们可以得到第一个结论:为了提高整机效率,CPU只和内存打交道。
我在程序中写了一个printf("hello world\n");
于是屏幕上就输出了hello world,是因为我将打印hello world的指令写好点击运行加载到程序以后CPU从内存中读取到这个指令去帮我执行屏幕上才会输出hello world。
也就是说CPU不是天然就有数据的,CPU其实很笨它只会被动的执行我的指令或者操作系统的指令。那么要执行指令,CPU就要认识指令。在CPU内部有它自己的指令集,我写好程序以后这个程序要经过汇编等步骤生成可执行文件本质是将我所写的指令翻译成CPU所认识指令。
二.操作系统(operator system)
操作系统是一个做软硬件资源管理(进程管理,文件管理,内存管理,驱动管理)的软件。对下管理好软硬件(手段),对上提供良好的(稳定的,安全的,高效的)运行环境。
我们首先来理解一下什么是管理,当我们上了大学以后发现平常根本见不到校长,可是校长也能把学校管理好(没倒闭就是管理好了),对于你个人来说,校长怎么知道你给你评三好学生还是要勒令你退学呢?是因为大家的学校都有教务系统,你的一切信息都会在教务系统中实时更新。校长手握教务系统就可以知道你每个学期的成绩,你的综合表现。所以校长知道能知道是否要给你颁奖等。可以从上面的例子中知道,校长能管理好这个学校的根本原因是因为校长能一直拿到这个学校所有学生的数据。
这里可以得到第二个结论:管理的本质就是对数据的管理。
那么我的数据在教务系统中肯定不是以Excel表格的形式呈现的,因为Excel在面临几万项数据时会变得十分的难处理。
虽然我们每个人都是独一无二的,但是我们有共同的属性比如我们都有自己的姓名,家庭住址,电话号码等。也就是说我们的个人数据可以抽象为一个结构体或者类,只要使用特定的数据结构将这些结构体或者类管理起来,就能大大的减少管理的成本。
通过抽象描述和数据结构的组织,最终对我们每个人数据的管理就变成了对结构体或者类的管理,这也就为什么说面向对象是进步的表现。
这里可以得到第三个结论:管理的方法是先描述再组织
我是一个low炮,二十门考试挂了十八门,然后校长决定把我勒令退学。校长根据我的数据做了决策(将我退学),这时就需要有一个人执行校长所做的决策(帮我办理退学手续),这个人就是执行者,而我作为被管理者只能说好吧那我走。
也就是说:在管理者和被管理者之间还有一个执行者,管理者根据数据做决策,执行者更具决策做执行。
电脑是由各种遵循冯诺依曼体系结构的硬件组成的,操作系统是管理硬件的软件,也就是对于硬件来说操作系统就是管理者,那么还需要有一个执行者将管理者做的决策去执行这个决策,最后再收集被管理在这个决策下的数据交给管理者。对于硬件来说,这个执行者就是驱动。
那么到这里我们应该有一个这样的模型:
这就完了吗?肯定没有啊,回想一下我们在学习语言的时候,是不是第一步永远是敲hello world
,咔一下这个hello world
就打到显示器上了,是因为CPU帮我执行了打印的指令。CPU能执行我的指令不但是因为CPU认识我的指令,更是因为操作系统提供了系统调用的接口。
三.系统调用和库函数
操作系统作为计算机运行的根基,其中的数据是不能被随便更改的。也就是说操作系统并不能相信任何人(就怕群众里面有坏人),但是操作系统又必须要给用户服务。
所以操作系统为了保护自己和服务用户,就将自己封装起来通过给用户提供系统调用的接口来服务用户。为了给我们二次开发提供便利,大佬们就围绕系统调用创造了库函数。这些库函数帮我们去调用了系统调用,所以我们直接咔一个printf("hello world");
显示器上就打印了一个hello world。
那么到现在这个结构就能完全展现出来了:
可以看到虽然我们好像在做开发,但其实也是在用户层面做开发。
四.进程
操作系统是管理软硬件资源的软件,经过前面的学习我们已经知道操作系统通过驱动收集的数据对硬件做管理。一个程序想要运行就必须要加载到内存中,而在内存中的程序就是进程,所以操作系统通过对进程的管理来达到管理软件的目的。
1.进程控制块(PCB)
电脑一开机就会有程序被加载到内存中,这些是维护电脑本身所需要的,此外我们还会打开很多程序,也就是说在绝大多数情况下,进程的数量总是超过CPU的。
这就会导致一个CPU往往想要处理多个进程,虽然我们所写的程序本身又不具备进程属性,但一旦加载到内存中成为了进程,操作系统为了管理好进程,就会采用先描述再组织的方法给进程建立进程控制块并用合适的数据结构将这些进程控制块给管理起来。
进程控制块就是进程属性的集合,在Linux下叫做struct task_struct,在Windows下叫PCB。操作系统通过对进程控制块做管理来达到对进程做管理的目的。
总结:struct task_struct内核结构体->内核task_struct对象->将该结构和代码关联
进程=内核数据结构+对应的磁盘代码
2.查看进程
查看进程的指令为ps ajx
(查看所有进程)
写一个如下的简单程序:
当我在Linux下执行该程序以后,使用ps ajx|head -1 &&gerp 'proc'
就可以查看到进程proc并且可以清楚的知道有关proc的每一项属性代表的是什么意思
那么如果我不想该进程在我的机器上继续运行了就可以使用kill -9+pid
杀掉进程
如果在前面的学习过程中你有学过一些退出的骚操作,那么就可能会导致你的部分程序或者工具只是把它放在了后台并没有真正的结束掉这个进程或者工具,这就会导致你的服务器越来越卡。到今天你就可以使用ps ajx
查看所有的进程并使用kill -9+ pid
杀掉进程
当我的程序加载到内存中后,就成为了进程,这个进程从启动到终止可能中间有很长的一段时间,这就是为什么说进程具有动态属性。
也就是说进程在调度运行的时候,进程就具有动态属性。
3.系统相关的调用
地址是一块空间的唯一标识符,而进程的唯一标识就是id,使用getpid这个系统调用函数就可以拿到相关进程的id
接下来修改一下process.c在其中加入getpid函数
在Linux当中其实存在者一种特殊的目录,即内存级目录,这种目录都是以数字为名存放的都是进程的id
下面来证明一下这种数字文件是代表的是进程的id:
当我的程序在运行的时候我可以找到这个程序对应的id文件
当我杀掉这个程序以后,这个id文件就不存在了
到这里我们可以得到这样一个结论:在Linux下进程可以被当作一个文件来看待
拓展
当一个程序被加载到内存以后,这个程序就成为了进程。那么这个进程是否还要依赖本地的磁盘文件呢?下面我做一个实验
1.运行proc并找到这个程序的进程文件
可以发现这里有一个exe文件,这个exe文件就是pro可执行程序
2.将proc文件删除
当我执行
rm proc
指令删除掉了本地磁盘种对应的proc文件以后,虽然这个可执行文件开始警告,但是这个进程仍然在运行
所以可以得到这样一个结论,当一个程序被加载到内存以后,理论上这个进程就和对应的文件没关系了
4.fork介绍(并发引入)
fork是一个创建子进程的函数,它有两个返回值,对于子进程来说它的返回值是零,对于父进程来说它的返回值是子进程的pid
根据运行结果可以看到确实有两个proc进程,其中一个的ppid正好是另外一个的pid
而且子进程和父进程共同执行了fork后面的所有语句,也就是说fork后面的所有代码是子进程与父进程共享的
既然fork有两个返回值,那我们就可以通过以fork返回值作为条件来让父子进程分别执行不同的代码,这就是所谓并发。
让子进程休眠一秒,父进程休眠两秒,也就是当子进程打印两句时子进程打印一句
根据结果发现,在同一个程序中同时运行了两个死循环。这在我们以前是想都不敢想的,这就是子进程被创建的作用之一。
此外,我们还可以发现在子进程的父进程也有自己的父进程,并且这个父进程叫做bash。
bash就是Linux系统的内核,在我们启动Linux操作系统时这个bash就被加载了,此后我们所有的指令都是由bash创建的子进程来运行的。我们与bash交互,bash帮我们和操作系统交互。
总结一句话,fork()之后,会有父进程+子进程两个进程在执行后续代码,fork()后续的代码,被父子进程共享,通过返回值不同,让父子进程执行共享代码的一部分,这就是并发式编程。
五.总结
1.CPU不直接和外设打交道,只和内存直接打交道,这样可以提高整机效率。
2.操作系统是一个对软硬件资源进行管理的软件。
3.管理的本质是对数据的管理,管理的方法是先描述再组织
4.操作系统不相信任何人,它对外表现为一个整体,通过提供系统调用接口来服务用户。
5.进程=内核数据结构+对应的磁盘代码
6.fork之后的代码由父子进程共享,通过返回值的不同让父子进程各执行共享代码的一部分就叫做并发式编程。