目录
寄存器
操作系统
进程(process)
CPU
pcb中关于进程调度相关的属性
寄存器
用来存储数据的单位,是CPU的一部分
寄存器,存储空间更小,访问速度更快,成本更高,掉电后数据会丢失
寄存器的速度和从内存速度的差距是极大的,3~4个数量级
寄存器用途:辅助CPU完成指令的执行,一条指令,不仅仅是指令本身,同时还有操作数
操作数通常是要在寄存器中保存的
由于寄存器和内存之间速度和空间上差异太大了,难以协调工作
CPU 往往又引入了 “缓存” (cache) 来调和寄存器和内存之间的速度
CPU 上要进行一系列运算,在运算的过程中,要反复用到一组内存中的数据,这些数据要频繁使用,又要频繁访问内存,同时这些数据又比较多,在寄存器中又存不下,就可以放到缓存中了
缓存的存储空间介于 寄存器 和 内存 之间
由于寄存器和内存之间差距太大了,搞一个缓存还不够用,后来又搞了一个
现代的CPU 都是这种典型的3级缓存的结构
缓存大小,对于有些场景,是非常影响到CPU 的性能的
操作系统
是一个搞管理的软件
操作系统存在的意义:
1、对下,要管理各种硬件设备
2、对上,要给应用程序提供一个稳定的运行环境
进程(process)
进程是程序的实例化,是一个正在运行中的程序、
这是一个所谓的程序,但是这个程序当前并没有正在运行
没有正在运行的程序,不叫进程
同一个程序,运行多次,就可能会产生多个进程
平时所说的程序,指的是 exe 的可执行文件,进程得是把程序跑起来
打开任务管理器,此时看到的这些,都是进程
程序是一个可执行文件,只是在硬盘上的一个东西(静态的)
如果双击程序,此时操作系统就会把可执行文件中的数据和指令,加载到内存中,并且让cpu去执行这里的指令,完成一系列相关的工作,运行起来的(动态的)进程
进程能够干活,说明进程能够消耗一定的硬件资源
可执行程序,只是占用了硬盘空间,而进程会消耗cpu资源、内存资源、硬盘、网络带宽.....
进程是系统分配硬件资源的基本单位
一台正在运行的计算机中,大概率是包含很多进程的,一旦东西多了,就需要套考虑“管理”
管理的前提是数量非常多,如果只需要考虑一两个进程,不用涉及到这么多
此时,针对计算机中“进程”的管理,核心思路是:先描述,再组织
1、会使用一个专门的结构体来记录一个进程里面各个属性
结构体就是一个低配的类
PCB(进程控制块) 是我们用来描述进程的一个结构体,Linux 中的PCB,在源码中是一个task_struck结构体
2、会使用一系列的数据结构,把多个进程进行一个有效的组织,随时方便进行遍历、查找、汇总数据....
通常是使用双向链表这样的方式来组织
当使用双向链表来组织的时候
a)查看进程的列表,本质上就是在遍历这个链表
b)插入一个进程,就是创建了一个PCB结构体,并且插入到链表上
c)销毁一个进程,就是把这个pcb结构体从链表上删除并释放
PCB中大概有哪些信息呢?
1、pid进程的标识
在同一个系统上,同一时刻中,每个进程的pid一定是不同的
有的时候,运行的是一个 .exe 但是实际上可能会涉及到多个进程
2、内存指针
表示了,该进程对应的内存资源是什么样的
内存资源中最主要要存储的就是从exe可执行文件中加载过来的指令和数据,此外还需要保存一些运行过程中的中间结果
指令是指二进制的指令,都是在程序员开发这个程序的时候,最终编译生成的结果,也就是程序员写的代码的逻辑
3、文件描述符表
硬盘是硬件,应用程序一般是没法直接接触到"硬件"这一层的
实际上是操作系统抽象成“文件”这样的概念,程序操作的是文件,而文件实际上是存储在硬盘上的
每个进程就会有一个“文件描述符表” 来记录当前这个进程正在使用哪些文件
操作系统打开一个文件,就会产生一个“文件描述符”(就相当于文件的身份标识一样,当然只是在进程的内部生效的),同时会使用文件描述符表(类似于数组)把文件描述符给组织起来
CPU
进程是需要在cpu上来执行指令的
进程如果是演员,cpu就是拍摄场地,每个进程要想执行cpu里面的命令,完成想要的任务,都需要在cpu上执行
一台机器上,进程同一个时刻有百八十个,但是cpu只有一个,这该怎么办呢?
轮着用就行了,也就是进程的调度
CPU是只有一个,但是舞台不只是一个,现在的cpu都是多核心的cpu
此时,我这个cpu是8个舞台,但是要拍戏的剧组有几十上百个,仍然是不够的,这个时候就要考虑到调度,调度的时候有下面两种情况:
1、并行
在同一时刻,两个进程同时运行在两个cpu逻辑核心上
2、并发
两个进程在同一个舞台上,轮着上
由于cpu切换进程速度极快,微观上,这两个进程是串行执行的,宏观上,看起来这两个进程是“同时”进行的
操作系统,在调度这些进程的时候,可能是按照并行的方式来调度,也可能是按照并发的方式来调度
在应用程序这一层,感知不到(在系统内核中能感知到),由于感知不到是哪种方式调动,并且这两种调动方式在宏观上的体现是一样的,通常也会用 “并发” 这个词代指 “并行” 和 “并发”
pcb中关于进程调度相关的属性
这些属性也就描述了进程对应的cpu资源的使用情况
(1)状态
就绪状态:一个进程已经随时做好了在cpu上执行的准备
阻塞状态 / 睡眠状态:有的时候,进程没有准备好被调度到cpu上
状态之间是可以相互转换的
实际上,进程在系统中状态还有很多种,其中最关键的就是 就绪 和 阻塞 状态
(2)优先级
系统给进程进行调度的时候,也不完全是公平的,也会根据优先级的不同,来决定时间分配的权限,就可以把系统资源调配给更重要的进程上了
(3)上下文
这些进程是轮着上的,一次运行不完,就需要保证下次上cpu的时候,能够从上次运行的位置,继续往后运行
对于操作系统来说,所记录的上下文就是该进程在执行过程中,cpu的寄存器中对应的数据
这些寄存器,有的是存一些中间结果,有的是才能一些特定含义的数据(比如下一条指令是在哪里,比如当前函数的调用关系)这些寄存器中的数据就需要在进程离开cpu之前,都保存好,保存到对应进程的pcb的上下文字段中(内存中)
下次该进程回到cpu执行,就可以把pcb中的上下文里的数据恢复到对应的寄存器中,这个时候,进程就是和上次执行的状态一模一样了
(4)记账信息
相当于是一个统计信息
会统计每个进程在cpu上都执行多久了,执行了多少指令了
是对于进度的调度工作进行一个“兜底”
例如:如果我发现给C排的时间太少了,接下来就给C多排一点时间
每个进程,都需要有一定的内存资源
早期的操作系统,就是直接把物理内存分配给进程,就带来一个严重问题:
一旦某个进程内部代码写出了bug,内存访问越界了,就可能影响到别的进程
C语言中有一个东西叫做指针(指针是个变量,存了个整数,这个整数就是内存的地址)
使用指针,尤其是解引用指针的时候,就需要保证当前这个指针指向的是有效的内存,否则解引用操作就可能引起 未定义行为
这个保证是由人工保证的,这就很容易出现指针越界,指向了不该指向的内存(野指针)的情况、
按照上述,直接分配物理内存的模型,此时一旦指针越界指向到其它位置,就会影响到别的进程的执行,会非常影响系统的稳定性
后来,操作系统就引入了 “虚拟地址空间” 这样的概念,有效解决上述的问题
虚拟地址,并非是在物理内存上真实存在的地址,只是在该进程中存在
不同的进程中,可以存在相同的虚拟地址,但是实际上对应到的是不同的物理地址
虚拟地址和物理地址,操作系统可以灵活的进行转换
但是对于应用程序来说,只能看到虚拟地址,看不到真实的地址
虚拟地址的好处:
这样设定之后,每个进程的有效的虚拟地址,都是固定范围
1、这样使用该虚拟地址的内存,都需要操作系统进行转换成物理地址的过程,这个转换过程中,就可以针对虚拟地址是否有效做出一个校验,将之前的人工保证变成了系统自动检查
2、当系统发现这里检查出了非法的地址的使用,就可以及时的通知这个进程,甚至直接让进程挂掉,避免影响到其它的进程,提高了整个系统的稳定性
在虚拟地址空间的加持下,进程就具有了独立性:每个进程都有自己的虚拟空间,一个进程无法直接访问或者修改其它进程虚拟地址空间的内容,强化了系统的稳定性
通过虚拟地址空间,把进程隔离开了,但是有的时候,还需要让进程之间,产生点配合 / 联系
进程间通信:就是在进程隔离性的进程上开一个口子,能够有限制的进行相互影响
进程之间具体用来通信的办法有很多,无论用哪种办法,本质都是一样的:找一个公共的区域,多个进程都能访问到的区域,借助公共区域来完成数据的交换