操作系统

371 阅读23分钟

基础

cpu执行一条指令

image.png

  • cpu要执行一条指令,首先就要将这条指令从内存中取出来,cpu一开始只知道这条指令的逻辑地址,通过该逻辑地址找到物理地址,cpu里面有个mmu,mmu里有一块地方保存了逻辑地址和物理地址的映射关系,这个映射关系内存中也有一份,通过查这个表找到物理地址(如果这个表中没有,就去内存中的mmap中找),硬件通过这个物理地址向主存要这条指令,主存找到这条指令就通过主线传给cpu,然后cpu执行
  • 需要确保程序所访问的地址空间时合法的
    • 由操作系统完成,操作系统建立和维护一张表map去限制程序访问地址范围,保证程序只能访问他们能访问的地址空间
    • 一个地址->起始地址:地址偏移
    • cpu要执行一条指令,就要查这个map,map会指出该逻辑地址是否越界,不越界就正常从物理内存取出来,越界cpu就出现map异常即内存访问异常(地址安全检测的过程)

什么是用户态、内核态

  • 为了避免操作系统和关键数据被用户程序破坏,将处理器执行状态分为内核态和用户态
  • 内核态是操作系统管理程序执行时时所处的状态,能够执行包含特权指令、IO指令在内的所有指令,能够访问系统内所有的存储空间
  • 用户态是用户程序执行时处理器所处的状态,不能执行特权指令、IO指令,只能访问用户地址空间
  • 用户程序运行在用户态,操作系统内核运行在内核态

如何实现内核态和用户态的切换

  • 处理器从用户态切换到内核态的方法有三种:系统调用、异常、和外部中断
    • 系统调用时操作系统的最小功能单位,是操作系统提供的用户接口,系统调用本身是一种软中断
    • 异常,也叫做内中断,是有错误引起的,如文件损坏、缺页故障等
    • 外部中断,是通过两根信号线来通知处理器外设的状态变化,是硬中断

并发和并行的区别

  • 并发:指宏观上看起来两个程序在同时运行(单核cpu交替执行任务),这种并发并不能提高计算机的性能,只能提高效率(有阻塞任务)
  • 并行:严格物理意义上的同时运行(多核cpu,两个任务分别运行在两个核上,两者互不影响),提高了计算机的性能和效率

进程的基本操作

  • 进程的创建:
pid_t fork(void);
- 新创建的子进程几乎但不完全与父进程相同。
- 子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。
- 子进程还获得与父进程任何打开文件描述符相同的副本
    - 这意味着当父进程fork时,子进程可以读写父进程中打开的任何文件。
- 子进程和父进程的最大区别是进程id不一样
- fork函数
    - 被调用一次返回两次
    - 一次在调用进程(父进程)中,返回的是子进程id
    - 另一次在新创建子进程中,返回0
    - 因为子进程的pid总是非零的,根据返回值就可以辨析程序是在父进程还是子进程中执行
  • 回收子进程
pid_t waitpid(pid_t pid, int *statusp, int options);
- 当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持一种已终止的状态,直到被他的父进程回收(reaped)。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程。一个进程可以通过调用waitpid函数来等待他的子进程终止或停止
  • 加载并执行程序
   int execve(const char *filename, const char *argv[], const char *envp[]);
 - execve函数在当前进程的上下文中加载并运行一个新程序
  • 进程终止
void exit(int status);

什么是进程

  • 进程是操作系统资源分配的基本单位,是独立运行的基本单位
  • 进程的经典定义就是执行中程序的实例。系统中每个程序都运行在某个进程的上下文中
    • 上下文由程序正确运行所需的状态组成。
      • 这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合
  • 进程组成成分
    • 进程控制块PCB,是进程存在的唯一标志,包含进程标识符PID,进程当前状态,程序和数据地址,进程优先级、CPU现场保护区(用于进程切换),占用的资源清单等
    • 程序段
    • 数据段

进程间通信方法

  • 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都是看不见的,所以进程之间要交换数据必须通过内核,在内核开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区读走。
  • 不同进程间的通信的本质:进程之间可以看到一份公共资源,而提供这份公共资源的形式和提供者不同,造成了通信方式不同
  • 进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket

进程如何通过管道进行通信

  • 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。通过pipe系统函数即可创建一个管道,有如下特质
    • 其本质是一个伪文件(实为内核缓冲区)
    • 两个文件描述符引用,一个表示读端,一个表示写端
    • 规定数据从管道的写端流入管道,从读端流出
  • 管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区实现
  • 管道局限性
    • 数据自己读不能自己写
    • 数据一旦被读走,便不再管道中存在,不可反复读取
    • 由于管道采用半双工通信方式,因此数据只能在一个方向上流动
    • 只能在公共祖先的进程间使用管道

进程如何通过共享内存通信

  • 这使得多个进程可以访问同一块共享内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新,这种方式需要依靠某种同步操作,互斥锁or信号量
  • 特点:
    • 共享内存是最快的一种IPC,避免了数据再用户空间和内核空间来回拷贝
    • 必须进行同步操作
    • 信号量和共享内存通常结合一起使用,信号量用来同步对共享内存的访问

什么是信号

  • 一个信号就是一条小消息,它通知进程系统中发生一个某种类型的事件。Linux系统上支持的30多种不同类型的信号。每种信号类型都对应于某种系统事件。底层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程而言是不可见的。信号提供了一种机制,通知用户进程发生了这些异常
    • 发送信号:内核通过更新目的进程上下文的某个状态,发送一个信号给目的进程。发送信号可以有两种原因
      • 内核检测到一个事件(比如除零错误、子进程终止)
      • 一个进程调用了kill函数,显示要求内核发送一个信号给目的进程。一个进程可以发送信号给自己
    • 接收信号:当目的进程被内核强迫以某种方式对信号的发送做出反应时,他就接收了信号。进程也可以忽略这个信号,终止或通过一个称为信号处理程序的用户层函数捕获这个信号。

如何编写正确且安全的信号处理函数

进程调度的时机

  • 当前运行的进程运行结束
  • 当前运行的进程由于某种原因处于阻塞
  • 执行完系统调用等系统程序后返回用户进程
  • 在使用抢占调度系统中,具有更高优先级的进程就绪时
  • 分时系统中,分给当前进程的时间片用完了

不能进行进程调度的情况

  • 中断处理程序执行时
  • 在操作系统的内核程序临界区内
  • 其他需要完全屏蔽中断的原子操作过程中

进程的调度策略

  • 先到先服务
  • 短作业优先
  • 优先级
  • 时间片轮转
  • 高响应比
  • 多级队列
  • 多级反馈队列

进程调度策略的基本设计指标

  • cpu利用率
  • 系统吞吐率,即单位时间内cpu完成的作业的数量
  • 响应时间
  • 周转时间(作业从提交到完成的时间间隔)
    • 平均周转时间
    • 带权周转时间
    • 平均带权周转时间

进程的状态与基本转换

  • 进程在运行时有三种基本状态
    • 就绪态:进程具备运行条件,等待系统分配处理器以便运行的状态。当进程已经分配到除cpu以外的所有必要资源后,只要再获得cpu即可立即执行,进程的这时状态称为就绪态。一个系统中处于就绪态的进程可能有多个,通常将他们排成一个队列,称为就绪队列
    • 运行态:进程占有处理器正在运行的状态。
    • 阻塞态:又称等待态或者睡眠态,指进程不具备运行条件,正在等待某个时间完成的状态
  • 各状态之间的装换
    • 就绪->执行:处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便有就绪态转换为执行态
    • 执行->就绪:1. 处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
    • 执行->阻塞:正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
    • 阻塞->就绪:处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。

孤儿进程?僵尸进程?

  • 孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被init进程(1号进程)收养,并由init进程对他们完成状态收集工作
  • 僵尸进程:进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的进程都是僵尸进程

什么是线程

  • 是进程划分的任务,是一个进程内可调度的实体,是cpu调度的基本单位,用于保证程序实时性,实现进程内部得并发
  • 线程是操作系统可识别的最小执行和调度单位。每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态
  • 每个线程完成不同的任务,但是属于同一个进程的不同线程之间共享一地址空间、打开文件队列、其他内核资源

简述线程和进程的区别和联系

  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少一个线程。线程依赖于进程存在
  • 进程在执行过程中拥有独立的地址空间,而多个线程共享进程的地址空间(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。)。但每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量
  • 进程是资源分配的最小单位,线程是cpu调度的最小单位
  • 通信:由于同一进程中的多个线程具有相同的地址空间,使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC线程间可以直接读写进程数据段(如全局变量)来进行通信(需要一些同步方法,以保证数据的一致性)。
  • 进程编程调试简单可靠性高,但是创建销毁开销大线程正相反,开销小,切换速度快,但是编程调试相对复杂
  • 进程间不会相互影响;一个进程中的某个线程挂掉将导致整个进程挂掉
  • 进程适应于多核、多机分步;线程适用于多核

进程同步方法

  • 有时候,需要多个进程来协同完成一些任务。 当多个进程需要对同一个内核资源进行操作时,这些进程便是竞争的关系,需要操作系统干预,在特定的同步点对所有进程进行同步。
  • 同步方法
    • 互斥锁
    • 读写锁
    • 条件变量
    • 记录锁
    • 信号量
    • 屏障

线程同步的方法

  • 同一进程内多个线程对同一数据进行修改需要加以线程同步
    • 互斥锁
    • 读写锁
    • 条件变量
    • 信号量
    • 自旋锁
    • 屏障

简述进程组

  • 进程组即多个进程的集合,进程组有一个组长,组长进程的PID等于进程组的PGID

简述协程

  • 协程:即用户线程。在linux下,线程有PCB,然后可以占用时间片去调度,但是在用户态线程中,该线程的执行不由内核做调度,由用户自己实现
  • 可以这么理解,在用户进程A中,再实现了个调度器,调度用户线程,这些线程不像之前的线程,内核是感知不到的,它们只能感知到A的存在,用户态线程之间时间片只能争取内核分给进程A的时间片。

死锁怎么产生?

  • 两个或两个以上的进程在执行过程中,因抢夺资源而造成的相互等待的现象。产生死锁需要满足以下四个条件
    • 互斥条件:进程对所分配的资源不允许其他进程访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放资源
    • 占有并等待条件:进程获得一定的资源后,又对其他资源发出请求,但是该资源可能被其他进程占有,此时请求阻塞,但该进程不会释放自己已经占有的资源
    • 非抢占条件:进程已获得的资源,在未使用完成之前,不可被剥夺,只能在使用后自己释放
    • 循环等待条件:进程产生死锁后,必然存在一个进程-资源之间的环形链

怎么解决死锁问题

  • 破坏产生死锁的四个必要条件
    • 资源一次性分配,这样就不会有请求了(破坏请求条件)
    • 只要有一个资源得不到分配,也不给这个进程分配其他资源(破坏占有并等待条件)
    • 可抢占资源:即当进程新的资源未得到满足时,释放已经获得的资源,从而破坏不可抢占的条件
    • 资源有序分配:系统给每类资源赋予一个序号,每个进程按编号递增的请求资源释放则相反,从而破坏环路等待的条件

什么是虚拟地址,什么是物理地址

  • 地址空间:进程可用于寻址内存的一套地址集合
  • 虚拟内存思想:每个程序拥有自己的地址空间,这个空间被分为多块,每一块称作一页或页面,每一页有连续的地址范围。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存的地址空间时,由硬件立刻执行必要的映射。当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理并重新执行失败的指令。
  • 在一个带虚拟内存的系统中,CPU 从一个有N=pow(2,n)个地址的地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间(virtual address space),现代系统通常支持 32 位或者 64 位虚拟地址空间。
  • 一个系统还有一个物理地址空间(physical address space),对应于系统中物理内存的M 个字节。
  • 主存中的每字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。

什么是虚拟内存

  • 为了更加有效管理内存并且少出错,现代系统提供了一种对主存的抽象概念,叫做虚拟内存。
  • 三个作用
    • 它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,高效使用了主存
    • 它为每个进程提供了一致的地址空间,从而简化了内存管理
    • 保护了每个进程的地址空间不被其他进程破坏

为什么要引入虚拟内存

  • 虚拟内存作为缓存的工具
  • 虚拟内存作为内存管理的工具。
  • 虚拟内存作为内存保护的工具

常见的页面置换算法

  • 当访问一个内存中不存在的页,并且内存已满,则需要从内存中调出一个页或将数据送至磁盘对换区,替换一个页,这种现象叫做缺页置换。当前操作系统最常采用的缺页置换算法如下:
    • 先进先出(FIFO)算法:

      • 思路:置换最先调入内存的页面,即置换在内存中驻留时间最久的页面。
      • 实现:按照进入内存的先后次序排列成队列,从队尾进入,从队首删除。
      • 特点:实现简单;性能较差,调出的页面可能是经常访问的
    • 最近最少使用(LRU)算法:

      • 思路: 置换最近一段时间以来最长时间未访问过的页面。根据程序局部性原理,刚被访问的页面,可能马上又要被访问;而较长时间内没有被访问的页面,可能最近不会被访问。

      • 实现:缺页时,计算内存中每个逻辑页面的上一次访问时间,选择上一次使用到当前时间最长的页面

      • 特点:可能达到最优的效果,维护这样的访问链表开销比较大

      • 当前最常采用的就是LRU算法。

    • 最不常用算法(Least Frequently Used, LFU

      • 思路:缺页时,置换访问次数最少的页面
      • 实现:每个页面设置一个访问计数,访问页面时,访问计数加1,缺页时,置换计数最小的页面
      • 特点:算法开销大,开始时频繁使用,但以后不使用的页面很难置换

写时复制

  • 如果有多个进程要读取它们自己的那部分资源的副本,那么复制是不必要的。每个进程只要保存一个指向这个资源的指针就可以了。只要没有进程要去修改自己的“副本”,就存在着这样的幻觉:每个进程好像独占那个资源。从而就避免了复制带来的负担。如果一个进程要修改自己的那份资源“副本”,那么就会复制那份资源,并把复制的那份提供给进程。不过其中的复制对进程来说是透明的。这个进程就可以修改复制后的资源了,同时其他的进程仍然共享那份没有修改过的资源。所以这就是名称的由来:在写入时进行复制。
    • 好处:如果进程从来就不需要修改资源,则不需要进行复制。惰性算法的好处就在于它们尽量推迟代价高昂的操作,直到必要的时刻才会去执行。
  • 在使用虚拟内存的情况下,写时复制(Copy-On-Write)是以页为基础进行的。所以,只要进程不修改它全部的地址空间,那么就不必复制整个地址空间。在fork()调用结束后,父进程和子进程都相信它们有一个自己的地址空间,但实际上它们共享父进程的原始页,接下来这些页又可以被其他的父进程或子进程共享。

优先级反转是什么?如何解决

  • 优先级反转:由于多进程共享资源,具有最高优先权的进程被低优先级进程阻塞,反而使具有中优先级的进程先于高优先级的进程执行,导致系统的崩溃。(优先级反转是指一个低优先级的任务持有一个被高优先级任务所需要的共享资源。高优先任务由于因资源缺乏而处于受阻状态,一直等到低优先级任务释放资源为止。而低优先级获得的CPU时间少,如果此时有优先级处于两者之间的任务,并且不需要那个共享资源,则该中优先级的任务反而超过这两个任务而获得CPU时间。如果高优先级等待资源时不是阻塞等待,而是忙循环,则可能永远无法获得资源,因为此时低优先级进程无法与高优先级进程争夺CPU时间,从而无法执行,进而无法释放资源,造成的后果就是高优先级任务无法获得资源而继续推进。)
  • 普遍使用的有2种方法:一种被称作优先级继承;另一种设置称作优先级上限
    • 优先级继承:一个高优先级进程等待一个低优先级进程持有的资源时,低优先级进程将暂时获得高优先级进程的优先级别,在释放共享资源后,低优先级进程回到原来的优先级别。
    • 优先级上限:指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级.((和优先级继承的区别)不管是否发生阻塞都升级)

简述select

  • slect是一种多路复用技术。其收到所有输入的文件描述符,返回哪些文件有新数据。
  • 可以设置阻塞和非阻塞状态,底层采用1024位bitmap实现,因此有文件描述符上限数

简述poll

  • poll是一种多路复用技术。其收到所有输入的文件描述符,返回哪些文件有新数据
  • 其通过链表代替了之前select的数据结构,使得没有上限限制

简述epoll

  • epoll是一种多路复用技术。
  • 其采用一个文件描述符管理多个输入的文件描述符,采用事件回调的方式,提高了程序运行效率

简述虚拟地址到物理地址转化的过程

  • 虚拟地址由虚拟页号和页偏移两部分组成。通过虚拟地址的页面号,首先在快表中查询是否有该映射,查询不成功,在页表中找到该页对应的物理地址。然后通过页物理地址+页偏移,得到真实的物理地址

简述页表

  • 页表用于存储虚拟地址中的虚拟页面号和物理页面号的映射关系。
  • 除此之外,有些页的读写有限制,页表也通过其他存储位,标记该页访问位、是否在内存中(可能被页面置换出去了)等等

简述多级页表

  • 多级页表用于减少内存的占用。以二级页表为例,虚拟地址被分为DIR,PAGE和offset三部分,通过顶级页表和DIR,寻找到该二级页表的起始位置,再通过二级页表的起始位置和PAGE,找到页物理地址,最后加上页偏移,即可得到最终的物理地址

简述快表

  • 快表也称为表高速缓存。其会存储一定数量的页表项,以此加快虚拟地址到物理地址的映射速度

简述MMU

  • MMU即内存管理单元,该硬件负责处理cpu的内存访问请求(处理虚拟地址到物理地址的转化工作)。快表(TLB)也存储在MMU上

IO模型有哪些

  • 在同步方式上:可分为同步IO和异步IO
  • 在阻塞方式上:可分为阻塞IO和非阻塞IO

简述阻塞IO

  • 阻塞和非阻塞描述的是调用方在获取消息的过程中的状态,阻塞等待还是立刻返回
  • 阻塞IO指的是调用方在获取消息过程中会挂起阻塞,直到获取到消息

简述非阻塞IO

  • 非阻塞IO指的是调用方在获取io的过程中会立刻返回而不进行挂起

简述同步IO

  • 同步和异步描述的是一种消息通知体制,主动等待消息返回还是被动接受消息
  • 同步IO指的是调用方通过主动等待获取调用返回的结果来获取消息

简述异步IO

  • 异步IO指的是被调用方通过某种方式(如,回调函数)来通知调用方获取消息

简述信号驱动IO

  • 信号驱动IO即,在内核中注册一个回调函数,当某个事件发生时,内核使用信号,使程序运行回调函数

简述IO多路复用

  • 即单线程可以监控多个文件描述符是否能进行IO操作的能力