并发:宏观意义上的同时执行,微观下还是多个程序交替执行
并行:严格意义上的同时执行
同步/异步关注的是消息通信机制
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
阻塞:在调用结果返回前,当前线程会被挂起,并在得到结果之后返回
非阻塞:如果不能立刻得到结果,则该调用者不会阻塞当前线程。因此对应非阻塞的情况,调用者需要定时轮询查看处理状态。
消息的传递有可能是阻塞的或非阻塞的 – 也被称为同步或异步的:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
什么是系统中断?中断做了什么?
保存当时的运行上下文,保存寄存器状态放入PCB,转到中断处理程序(内核态),恢复现场
中断分类
硬中断:由硬件造成的中断,会中断CPU
软中断:运行的进程造成的
硬中断可以递归完成(自己中断自己)
软中断得排序完成
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
什么是系统调用?
操作系统分为内核态和用户态
内核态就是操作系统所在的层面
用户态是应用程序所在的层面
为了保护操作系统不轻易的被外来程序破坏,有些文件,数据,硬件只有内核态的操作系统有访问操作权限,系统调用就是当应用程序中需要操作系统提供服务时,如请求 I/O 资源或执行 I/O 操作,应用程序必须使用系统调用命令。由操作系统捕获到该命令后,便将 CPU 的状态从用户态转换到系统态,然后执行操作系统中相应的子程序(例程),完成所需的功能。执行完成后,系统又将CPU 状态从系统态转换到用户态,再继续执行应用程序。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
内核
大内核,将操作系统作为一个紧密结合的整体放到内核
微内核,将操作系统的一部分功能移出内核,移到用户态,降低了操作系统的复杂性,但是操作系统在执行操作时会频繁的在内核态和用户态切换,会造成性能损失
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
线程和进程,协程
cpu运行程序是很快的,假设有程序正在用cpu,如果突然到了要IO的时候,这个时候不需要cpu了,需要IO资源的话,cpu如果选择等到该程序IO完成继续给他用的话,无疑会造成极大的资源浪费,你IO就去一边IO去,让其他IO好的程序运行不就好了吗,事实上我们也是这么做的,但是我们发现进程的切换会造成当前CPU环境的改变,比如后面的虚拟内存,这样也会导致资源的浪费,然后我们发现一个程序不是每个部分都需要IO的,于是就把一个进程分为了很多个线程,哪个线程IO,可以切换其他线程占用cpu,线程的切换比进程切换需要的资源就少多了,只是线程编写调试复杂,为了安全还需要各种锁机制,但无疑是能使cpu更加具有效率的用法,但是还不够,进程的切换可能会需要访问一些只有内核态才有权限的代码or空间,于是就会有用户态到内核态的切换,这个切换需要的资源也不是很小,于是出现了更小的单位,协程(python中的yield属于协程),协程就是程序自己模拟起一个线程,协程完全由程序自身控制,没有了线程切换的开销(用户态到内核态的切换,需要惊动OS),不需要加锁,执行效率更高
但是协程也失去了标准线程使用多CPU的能力。
(补充,用户态到内核态的切换,程序的运行本质上是建立了一个虚拟机,在虚拟机上运行,如果你想新起一个线程,你需要虚拟机向操作系统申请,这就是用户态到内核态的切换,如果是协程,虚拟机自己就可以起)
一个程序可以多线程,中间有线程切换,切换开销有用户态到内核态切换,可以使用多CPU,
一个程序也可以多协程,中间的切换程序自己控制,无法使用多cpu,没有用户态到内核态的切换
为什么用户态到内核态切换运行效率就慢呢?(主动去讲,加分的)(百度,我不会)
Linux | 用户态和内核态的切换耗费时间的原因 - 一秋复一秋 - 博客园
这个我喜欢
线程和进程的区别如下
进程是运行时程序的封装,是系统资源调度的最小单位
线程是cpu调度的最小单位
一个进程拥有多个线程,至少一个,每个线程都属于某个进程,一个线程挂掉整个进程都挂了
进程在创建时会分配资源,内存空间,IO设备等,该内存空间内部线程共享,每个线程有自己的寄存器,运行栈段,用于存储临时变量
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
原子操作:不会被线程调度机制打断的操作
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
通信
进程通信方式
管道:
普通管道:半双工(单向传输),字节流,有大小限制,只能在父子进程间使用
流管道:全双工
命名管道:可以在无关进程间通信
消息队列:
不止能传输字节流,而且没大小限制,类似链表的结构存储消息,独立于进程存在,实现了消息的随机读取,类似于发送邮件,但是不够及时
套接字:(ip地址:端口号)
可用于不同主机下进程的通信
共享内存:这就很快,但是得考虑线程安全问题,数据一致性问题
信号量:用于进程共享,就是俩进程都能访问,都能改变他,可多次使用
信号:用于单次传输,传完就没了,你想回就自己发个新信号过来
线程通信方式(不常问)
信号
信号量
临界区(可以共享的资源是临界资源,每个进程中访问临界资源的那段代码称为临界区)
互斥锁
共享内存
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
上下文切换
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
死锁
死锁是指两个或两个以上进程在执行过程中,因争夺资源而造成的相互等待现象
死锁产生的四个必要条件
处理死锁的基本方法
银行家算法
分配资源前,看分配该资源后是否存在一条关键路径可以完成所有进程
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
具体
读写锁:可以多个同时读,但一个写的时候其他人既不能写也不能读
互斥锁:每个时刻最多一个线程可以拥有该资源
自旋锁:想拥有资源,但是资源被别人拥有了,就会进入一个循环不断的访问资源是否空闲
概念
乐观锁:假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。
悲观锁:开启事务前把相关行都用互斥锁锁上先
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PCB常驻内存
逻辑地址和物理地址
相对位置和绝对位置
一个程序的编译运行过程
源文件 编译--------> 模块 (源代码编译为若干个模块) (将高级语言翻译为机器语言)
模块 链接--------->装入模块(目标模块及所需库函数链接在一起)
装入模块 装入------------->内存(用装入程序将装入模块放入内存运行)
逻辑地址到物理地址的三种转化方式
绝对装入:
编译期间算出地址,然后把装入程序中的地址替换掉。
(只适用于单道程序环境,因为只有一个程序时,地址才是固定的)
静态重定位:
假设程序起始地址在100位置,在装入期间将有逻辑地址的地方都+100
(程序就得一次性装入内存,内存不够的话作业将无法执行)
动态重定位:
用一个寄存器存放程序起始地址,在运行期间计算物理地址=逻辑地址+寄存器存放数据
(这样就允许程序在内存中移动)
链接也有三种方式:
静态链接:
在程序运行前就把所需的库函数和目标模块链接成一个完成的可执行文件
装入时动态链接:
边装入边链接
运行时动态链接:
需要使用时才对目标模块进行链接,优点是便于修改和更新,以便实现对模板的共享
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
内存空间的扩充
覆盖:按照程序执行顺序,让不同时间运行的进程共用同一块内存
交换:内存紧张时,将阻塞进程,优先度低的进程暂时换到外存,将外存中已经处于就绪队列的进程调入内存
连续分配内存方式:一个进程占用的内存空间为连续的
固定分区分配:
动态分区分配:
都会造成内存碎片问题
非连续分配内存方式(离散内存分配方式):一个进程占用的内存空间为非连续的
页式:将内存分为一个个相同大小的内存块,进程也分为一个个相同大小的内存块,这样只有最后一块可能会造成内存碎片。
页式存储方式地址转换问题:
物理地址=页面始址+页内偏移量
逻辑地址/页面长度=页号
逻辑地址%页面长度=页内偏移量
内存中还存放有页表:页表中存放了页号对应的块号(页面始址=块号*页面大小),每个进程都有一个页表。
段式:按照程序自身逻辑关系划分为若干个段,每个段都有一个段名,每个段都是从0地址开始编址
分段系统的逻辑地址结构是由段号(决定最多可以分为多少个段)和段内地址(每段最大长度)决定的
段表:<段号> <段长> <基址>
物理地址=段号(得到对应基址)+段内偏移量
优点 缺点
分页式 内存利用率高 不方便共享和保护
分段式 方便共享和保护 内存利用率低,容易产生大量的内存碎片
段页式:先分段,每一段再去分页
表:<段号> <页号> <页内偏移量>
多少个段 多少个页 页块大小
物理地址=段号(对应基址)+页号(对应页面始址)+页内偏移量
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*虚拟内存:
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
什么是页表
页表是一种特殊的数据结构,存放着各个虚拟页的状态,是否映射,是否缓存.。进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,这就需要用页表来记录。页表的每一个表项分为两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)。当进程访问某个虚拟地址,就会先去看页表,如果发现对应的数据不在物理内存中,则发生缺页异常。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
进程开始要访问一个地址,它可能会经历下面的过程
1.每次我要访问地址空间上的某一个地址,都需要把地址翻译为实际物理内存地址
2.所有进程共享这整一块物理内存,每个进程只把自己目前需要的虚拟地址空间映射到物理内存上
进程需要知道哪些地址空间上的数据在物理内存上,哪些不在(可能这部分存储在磁盘上),还有在物理内存上的哪里,这就需要通过页表来记录
3.页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)
4.当进程访问某个虚拟地址的时候,就会先去看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常
5.缺页异常的处理过程,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就找一个页覆盖,至于具体覆盖的哪个页,就需要看操作系统的页面置换算法是怎么设计的了。