操作系统


别问 问就是为了面试豁出了老命

什么是操作系统

  • 操作系统本质上也是一个软件
  • 操作系统屏蔽了底层硬件的复杂性,提供简单的操作去操作硬件
    操作系统内核.jpg

    用户态和系统态

  • 用户态:就是用户调用的进程可以调用用户的各类数据
  • 系统态;几乎可以访问计算机上的任何资源

    系统调用

    一般来说,进程的运行都是在用户态运行的,但很多时候会发生一些系统态级别的操作,因此需要发生系统调用来和操作系统发生请求关系。

系统调用类型

  • 设备管理:启动或者关闭一些设备
  • 文件读取:文件的增删改查的操作
  • 进程控制:进程的创建,阻塞,销毁
  • 进程通信:完成进程之间的消息传递
  • 内存管理:内存的分配、回收以及获取作业占用内存区大小及地址等

线程和进程

  • 进程:就是用户所运行的一个完整的程序
  • 线程:一个进程下可以运行多个进程

单核cpu

关于一个单核的CPU的问题,一个单核cpu可以运行多个进程,只要内存空间足够,cpu也可以处理的过来;
一个单核cpu下的进程中的线程,每次只能运行一个线程

用jvm的角度去思考进程和线程

启动一个jvm则是一个进程,jvm中运行的线程则是对应操作系统中的线程

进程间的通信方式

进程通信的概念

进程之间的用户地址空间是相互独立的,但是不同进程之间免不了需要数据交换等操作,因此进程间的通信需要依靠于内核,准确的说是内核缓存区
进程A需要把变量读取到内核缓冲区,进程B再把数据从缓冲区中读取到进程中,这就是所谓的进程间通信(IPC,InterProcess Communication)

进程通信的方式

  • 无名管道
    • 半双工,数据只能单向的流动,也就是说可以是 进程A -> 管道 -> 进程B ,但是 进程B -> 管道 -> 进程A 是不允许的。
      无名管道.jpg
    • 管道的实质是 内核上中的一个缓冲区(在内存中) ,是一个队列,读取过后的数据则在队列中不存在
    • 当管道满的时候,则相应的读进程或者写进程进入等待队列
    • 无名管道的局限性
      • 进程之间必须有亲缘关系才可以
      • 数据是单向的
      • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
      • 管道的数据是无格式字节流,传输的时候需要双方确定好数据格式
    • 无名管道阻塞问题:无名管道无需显示打开,创建时直接返回文件描述符,在读写时需要确定对方的存在,否则将退出。如果当前进程向无名管道的一端写数据,必须确定另一端有某一进程。如果写入无名管道的数据超过其最大值,写操作将阻塞,如果管道中没有数据,读操作将阻塞,如果管道发现另一端断开,将自动退出。
  • 有名管道
    • 有名管道通过建立 路径名 来关联不同的进程 ,这样解决了无名管道只能有亲缘关系的进程通信的问题
    • 有名管道的管道名在文件系统(磁盘),但是传输的内容在内存
    • 有名管道数据是双向的
    • 有名管道阻塞问题:有名管道在打开时需要确实对方的存在,否则将阻塞。即以读方式打开某管道,在此之前必须一个进程以写方式打开管道,否则阻塞。此外,可以以读写(O_RDWR)模式打开有名管道,即当前进程读,当前进程写,不会阻塞。
  • 信号
    • 信号可以在任何时候发给某一进程,而无需知道该进程的状态
      常见信号.jpg
    • 信号可以在用户空间进程和内核之间直接交互,内核可以利用信号来通知用户空间的进程发生了哪些系统事件
    • 信号来源
      • 硬件来源:比如ctrl+c是复制
      • 软件来源:进程调用kill函数
    • 信号的生命周期
      信号的生命周期.jpg
  • 信号量
    • 信号量是一个计数器,信号量本质是为了多个进程之间的数据同步。
    • 为获取共享资源,信号量的操作可分为3步
      1. 创建信号量: 这要求调用者指定初始值,对于二值信号量来说,它通常是1,也可是0
      2. 等待信号量: 测试信号量的值,如果是小于0则阻塞,P操作
      3. 挂出信号量:信号量的值加一,V操作
    • 信号量的类型
      • Posix(可移植性操作系统接口)有名信号量(使用Posix IPC名字标识)
      • Posix基于内存的信号量(存放在共享内存区中)
      • System V信号量(在内核中维护)
    • 信号量和整形数据的区别
      • 信号量是一个非负的整型,除了初始化外,只能用wait(semap) , signal(semap)这两个原子命令 =》 P(测试)V(增加)操作 ;
  • 消息队列
    • 存放在内核中消息链表
    • 由于只存放在内存中,因此只有内核被重启,信息才会被删除
    • 消息队列在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达
    • 目前主要有两种类型的消息队列:POSIX消息队列以及System V消息队列
  • 共享内存
    • 存在于内核中的特定的一块内存区域供进程同步使用
    • 是最快的进程通信的手段
    • 为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率
  • 套接字(socket)
    • 由域、端口号、协议类型三个属性决定
        • AF_INET 指的是Internet网络
        • AF_UNIX 指的是UNIX文件系统
      1. 端口号
        • 每一个基于TCP/IP网络通讯的程序(进程)都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入/输出信息,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序
      2. 协议
        • 流套接字
          • 一个有序、可靠、双向字节流的连接
          • TCP/IP连接,UNIX文件系统使用
        • 原始套接字
          • 原始套接字允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现
        • 数据报套接字
          • 它不需要建立连接和维持一个连接,它们在域中通常是通过UDP/IP协议实现的。

线程同步

  1. 互斥量 : 互斥量值只能为0/1 => 互斥锁 => 加锁和解锁必须由同一线程分别对应使用
  2. 信号量 : 信号量值可以为非负整数 => 共享锁 => 可以由一个线程释放,另一个线程得到
  3. 事件: (Wait/Notify) 就是通过一些操作,对线程进行操作

进程的调度

  1. 先来先服务(FCFS)
  2. 短作业优先(SJF): 从就绪队列中选出一个估计运行时间最短的进程为之分配资源
  3. 时间片轮转法(RR): 为每个进程分配一个时间段,则进程在允许的时间中运行
  4. 优先级调度:为进程分配优先级,优先级高的先执行,优先级相同的按照先来先服务
  5. 多级反馈队列:设置多个不同优先级的队列,先执行高优先级队列内的进程

操作系统内存管理

原帖地址

  • 分区存储管理

    • 分区管理的分类
      • 固定分区:把内存固定的分成几部分,可以大小一样,也可以不一样,分区总数固定
        • 缺陷:固定了分区总数,那么并发数目会被限制;分区的大小不一定和进程完全匹配,则内碎片较多
      • 动态分区:根据装入程序动态的分配大小
        • 动态分区就是在程序装入时,在内存中找到比所需内存大于或等于的空间,如果是大于则会把空间切割成两份,其中一个被占用,另一个空闲
        • 分区算法:
          1. 最先适配法:按照分区在内存中的先后,找到第一个可以装入程序的分区
          2. 下次适配法:从上次找到装入程序的分区继续向下找可以装下当下这个程序的分区
          3. 最佳适配法:从头到尾查分区,找到与程序要求空间相差最少的
          4. 最坏适配法:按分区在内存的先后次序从头查找,找到最大的空闲分区进行分配。
      • 伙伴分区:由于固定分区限制多,动态分区算法复杂,占用内存多则使用伙伴分区
        1. 伙伴系统规定,无论已分配分区或空闲分区,其大小均为 2 的 k 次幂,k 为整数, l≤k≤m,不同分区大小存在不同的分区链表中
        2. 则2^1 表示分配的最小分区的大小,2^m 表示分配的最大分区的大小( 通常 2^m是整个可分配内存的大小)
        3. 当查找一个长度为n的空间时,一般会找一个 2^(i-1)<n<= 2^i的值,最好的情况是2^i = n
        4. 如果2^i的分区已经没有了,则找2^(i+1)的分区,如果存在,则把2^(i+1)的分区拆成两份,一份占用,一份存到2^i的分区链表中,则拆开的这两个分区就称作时伙伴分区,当然了如果2^(i+1)也没了,则找2^(i+2),以此类推
      • 内存紧缩:就是把空闲空间向一个方向移动,是的外碎片合并成一个大的空间,以供使用
      • 覆盖技术:把某程序中的重要部分先载入到内存,不需要的则可以先放入外存
        覆盖技术.jpg
      • 交换技术:多个程序并发执行时,可以将暂时不能执行的程序(进程)送到外存中,从而获得空闲内存空间来装入新程序(进程)
  • 页式存储管理

    • 把主存分配成固定大小一页一页的,划分的力度更大,也就是说页比块更小
    • 页式管理通过页表对应逻辑地址和物理地址
    • 缺陷:但是和块式管理一样,存在类似的问题
  • 段式存储管理
    • 段式管理把主存分为一段段的,每一段的空间又要比一页的空间小很多
    • 段是有实际意义的,每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。 段式管理通过段表对应逻辑地址和物理地址
  • 段页式存储管理(linux中)
    • 逻辑地址空间分成若干个段,之后每个段又被分成若干个页

快表(TLB)

页式存储管理的读取流程

一般来说,在页式存储管理中,页存储在主存中,当要读取某数据的时候,内核需要两次访问主存。第一次是,查找页表,逻辑地址转化成物理地址。第二次才是真正的读写操作。

快表.jpg

快表

将主存中的部分页表,放到cpu内部的关联存储器中(高速缓存区),当cpu给出有效可用的逻辑地址(有效地址)后,经过MMU(内存管理单元)找到页号,自动送到快表去查找,若找到匹配的页号,则直接返回物理地址,则直接只需要访问访问一次主存,就可以完成操作。

有了快表以后的读取流程

  1. 根据虚拟地址中的页号查快表;
  2. 如果该页在快表中,直接从快表中读取相应的物理地址;
  3. 如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;
  4. 当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。

小结:快表其实就是一个缓存区域,有的话直接快速拿走,没有的话则访问主存中的页表,再存入快表

多级页表

多级页表可以想想b+树,如果式一级的话可能只是n,但是如果是多级页表的话,可能相乘的结果会比相加更大。举个例子有空间5,如果不是多级页表,则最终可用的也就是5,多级页表可能是2*3=6,这样子就节约了内存。

虚拟内存

一般来说,一个程序的大小总会比真实的主存要大,如果全部填入主存是不够的,则虚拟内存就是把磁盘上的空间和内存作为一个整体,以供程序运行使用。
虚拟内存其实是定义了虚拟地址来维护磁盘上的虚拟空间和主存,虚拟空间会被主存按需调用,甚至有时候会把程序直接从主存全部移动到磁盘中

虚拟内存A.jpg
(图是PTE仅含有一个有效位标记的页表结构)

  • 在页表上规定了一个偏移量PTE(Page Table Entry, PTE),则在页表上每一个固定的偏移量下都会有一个PTE。
  • 在页表上,每一个PTE的ADDRESS对应一个虚拟内存和物理内存
  • 在页表上,有效位代表了虚拟空间和物理地址是否可以对应
  • 虚拟页VP 0、VP 4、VP 6、VP 7被缓存在物理内存中,虚拟页VP 2和VP 5被分配在页表中,但并没有缓存在物理内存,虚拟页VP 1和VP 3还没有被分配
  • 如果页未命重,则移交到内核,内核会找到一个牺牲页,如果牺牲页发生修改,则内核会把牺牲页复制到磁盘,之后更新PTE指定到牺牲页的位置

局部性原理

  1. 时间局部性 :如果程序中的某条指令一旦执行,不久以后该指令可能再次执行
  2. 空间局部性 :一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问

页面置换算法

  • OPT 页面置换算法(最佳页面置换算法): 淘汰在最长时间内不再被访问的页面
  • FIFO(First In First Out) 页面置换算法(先进先出页面置换算法):淘汰最先进入内存的页
  • LRU (Least Currently Used)页面置换算法(最近最久未使用页面置换算法): LRU算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 T,当须淘汰一个页面时,选择现有页面中其 T 值最大的
  • LFU (Least Frequently Used)页面置换算法(最少使用页面置换算法):使用最少的页面作为淘汰页