1. 冯诺依曼体系结构
如图,这是一个冯诺依曼体系结构简图
其中这里的存储器指的是内存!
用通俗的话来解释这个图,就是数据从输入设备进入,然后进入到存储器,CPU从存储器中拿到数据,处理完毕后,输出到输出设备。
- CPU与存储器与外设
其中CPU的功能是用来计算处理数据的,所需要的数据从存储器中来,并不是直接和外设打交道的。所处理完的数据之后,再交给存储器,外设再从存储器中拿到处理后的数据。
其实CPU很笨,只能被动的接受别人的指令或者别人的数据,然后去执行别人的指令,计算别人的数据。
一句话,所有的设备都只能直接同内存打交道。
- CPU如何认识别人的指令?
我们在编译代码的时候,本质就是形成一种二进制可执行程序。其中这种二进制可执行程序可以被CPU的指令集识别,从而CPU能够认识指令,计算数据。
- 操作系统的作用
当CPU处理完数据,执行完指令后,那么多余的数据,指令该怎么办呢?这个问题其实操作系统都会帮你解决。
2. 操作系统的作用
- 什么是操作系统
一句话简单说明什么是操作系统,操作系统就是一个进行软硬件资源管理的软件。
- 操作系统为什么要管理软硬件
一句话总结,操作系统通过合理的管理软硬件资源(手段),为用户提供良好的(稳定的,高效的,安全的)执行环境(目的)。
- 那么操作系统是如何管理硬件的呢?
你可以把操作系统想象成你的大学校长。那么在大学的时候,你的校长是怎么管理学生的呢?是不是中间还有一个人,这个人暂且称为辅导员。那么就有如下抽象图:
对于上面的图,可以这么理解。你的校长并不直接管理着学生,而是通过辅导员获取上来学生的数据来管理学生,再告知辅导员相应的决策来管理学生的。**那么同样的,操作系统也并不是直接去管理硬件的,而是通过驱动获取上来的数据来管理硬件,再通过数据告知相应的驱动去管理硬件的。**那么具体的图就变成了这样:
- 既然操作系统是通过数据来管理硬件的,那么数据多了怎么办?
还是举一个例子来说清楚问题。**假如一个大学里面有着2万名学生,那么就是有2万名学生的数据,那么校长是怎么管理的呢?其实每个学生都有着自己的一份信息表,校长就是通过这份信息表来管理着学生的。同样的,操作系统也是如此。当管理硬件的时候,每个硬件其实也是有着这样一份信息表的,用代码来表示,就是结构体。每当操作系统管理硬件的时候,都是通过这个结构体的信息来管理的。**如下图所示:
其中,校长有了学生的信息表之后,就可以开始管理学生,例如把学生信息放进一个链表来管理。同样的操作系统对硬件的管理也是如此,也是放进某种数据结构中来进行管理的。
所以操作系统管理的本质是:对数据做管理。
管理的方法:先描述,再组织。
- 用户是如何操作硬件的呢?
计算机的硬件其实就是一堆二极管,那为什么用户不能直接操作硬件完成相应的操作呢?这里用一个例子来说明一下。
在日常生活中,银行取钱很常见。面对银行大量的业务,为什么老百姓就不能自己操作呢?比如老百姓自己去存钱,自己去取钱,这样不是方便很多吗?其实原因很简单,银行并不信任任何人!!!所以设立很多小窗口来给老百姓提供业务,既保证了银行的安全,也实现了对老百姓的业务。
切换到计算机的世界,操作系统其实就相当于银行,操作系统并不相信任何用户,因为操作系统害怕用户不小心操作对操作系统本身造成了不可逆的伤害,所以操作系统也不允许用户直接操作硬件,而是提供了一系列的系统接口来供用户使用,从而实现了用户对硬件的操作,既保护了操作系统本身,也完成了对用户的服务。
- 操作系统概念图
3. 进程
- 进程的概念
对于进程的理解可以理解为:操作系统是怎么管理硬件的,就怎么管理软件的,而这里的软件就可以认为是进程。
在教科书上,进程的概念是,一个运行起来的程序叫做进程。可实际进程是什么样子的呢?下面来看一看图示:
- 当程序加载到内存中时,操作系统会对该程序生成相应的PCB
- CPU实际管理的并不是内存中的程序,而是管理程序对应的PCB
- PCB(程序控制块)
PCB全写是process control block。实际上可以认为是一个结构体,其中保存的是相应的程序的相关属性,例如优先级,代码地址,…
- 为什么会有PCB?
PCB实际上是对程序的描述,根据上面所讲过的内容,我们可以知道管理的实质其实就是先描述,再组织。操作系统对程序创建相应的结构体,对其进行了相应的描述,然后操作系统再根据相应的数据结构对这些PCB进行组织,例如链表。从而实现了对进程的管理。
- 所以进程是什么?
进程可以简单的认为是: 进程 = 内核数据结构(PCB) + 进程对应的磁盘代码。
下面来看看Linux中的进程。
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 int cnt = 0;
7 while(1)
8 {
9 printf("我是一个进程,%d\n",cnt++);
10 sleep(1);
11 }
12 return 0;
13 }
以这个代码为基础,创建一个进程。运行代码后,我们就可以看到该进程。
- 查看当前进程ID 和 父进程ID
这里可以调用系统接口来查看子进程ID和父进程ID
- 查看当前进程ID的系统接口是: getpid()
- 查看父进程ID的系统接口时: getppid();
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 // fork();
7 printf("我是一个进程!进程ID:%d, 父进程ID:%d\n",getpid(),getppid());
8 sleep(1);
9 return 0;
10 }
运行结果:
- 了解系统接口
fork()是一个系统接口,其主要功能是给当前进程创建一个子进程。可以通过man命令来查看fork的详细信息。
- 了解并发运行
当你的代码运行起来的时候,就已经是一个进程在运行了。那么此时我再创建一个子进程,那么后续的代码将会被两个进程(子进程和当前进程)所共享。例如下面这个代码:
运行结果: