1、基础概念

1.1 进程与线程的区别

  • 资源:进程是资源分配的基本单位,但线程不拥有资源,线程能访问其所属进程的资源;
  • 调度:线程是独立调度的基本单位,同一进程中线程的切换不会引起进程的切换,而不同进程间线程的切换会引起进程的切换;
  • 系统开销:进程的新建和撤销时,系统需要为其分配和回收资源,如内存空间和I/O设备等,开销远大于线程的新建和撤销。进程的切换需要当前进程CPU环境的保护和新进程环境的设置,而线程的切换只需要保存和设置少量的寄存器内容,开销很小。因此,线程的系统开销远低于进程
  • 通信:线程可以直接读写进程数据进行通信,但进程需要IPC(进程间通信技术)进行通信(管道、消息队列、共享内存)。

1.2 多进程与多线程

多进程:

• 进程是一个程序在给定数据集上的一次运行;
• 多进程并发执行,共享物理内存、磁盘、IO、打印机等资源

多线程:

• 线程是一个基本的CPU执行单位,必须依靠进程存活。线程是一个执行上下文(execution context),是CPU运行的一串指令;
• 多线程并发执行,共享地址空间、打开的文件等父进程的全部资源;
• 主线程完成从进程创建到结束的全部操作,期间其他的线程被创建或退出

python多进程:

• 实现:multiprocessing.Process()
• 进程间通信:Queue多进程安全队列,pipe管道通信。

python多线程:

• 实现:threading.Thread()
• GIL(Global Interpreter Lock)全局解释器锁:GIL是python设计之初为了数据安全所加的,只有在官方的Cpython解释器里面才有。一个python进程只有一个GIL,只有获取了GIL的线程才被允许执行。每次释放GIL都会经历线程竞争和线程切换而耗费资源,因此多核CPU中python多线程反而更耗时。
• 加互斥锁保证多线程安全,即解决竞争,保证数据一致性【python中一般使用RLock可重入锁】。
import threading
import multiprocessing
import time
def print_time(counter, delay, name):print('start:', name)for _ in range(counter):time.sleep(delay)print('counting: ', name, time.ctime(time.time()))print('end:', name)
if __name__ == '__main__':t1 = threading.Thread(target = print_time, args = (3, 1, 'thread_1'))t2 = threading.Thread(target = print_time, args = (3, 1, 'thread_2'))# t1.start()# t2.start()p1 = multiprocessing.Process(target = print_time, args = (5, 1, 'process_1'))p2 = multiprocessing.Process(target = print_time, args = (3, 1, 'process_2'))# p1.start()# p2.start()

选择多进程还是多线程:
CPU密集型:偏重于计算,频繁使用CPU,适合多进程。如机器学习。
I/O密集型:经常输入输出,适合多线程。如爬虫。

1.3 协程

  • 协程是微线程,在一个线程中执行,执行过程中可以随时中断,由用户控制(进程和线程本质上是系统运行)。执行效率高,减少了线程切换和锁开销。
  • 协程失去了标准线程使用多核的能力。多进程+协程,既可以利用多核,又能利用协程的高效率。
  • 实现:asyncio(异步IO,即不用等待其结束就能进行其他操作),在yield处暂停等待指令。

协程相比线程的优势:协程执行效率高。

  • 协程由用户控制,不需要线程切换开销。与多线程相比,线程数量越多,协程的优势越明显;
  • 协程不需要锁机制。线程中需要用锁保护数据,而协程不需要写变量保护,只需要判断状态就好了。

1.4 进程、线程、协程堆栈区别

  • 进程:有独立的堆栈,不共享堆也不共享栈;由操作系统调度
  • 线程:有独立的栈,共享堆而不共享栈;由操作系统调度;
  • 协程:有独立的栈,共享堆而不共享栈;由程序员自己调度。

1.5 进程调度

  • 批处理系统(周转时间):先来先服务(短作业等待时间过长),短作业优先(饿死),最短剩余时间优先(短作业优先的抢占式版本)。
  • 交互式系统(快速响应):时间片轮转,优先级调度,多级反馈队列

1.6 进程同步

  • 对临界资源进行访问的代码为临界区,临界区需要互斥访问。
  • 同步与互斥:同步是进程因合作产生制约关系,需要按一定的顺序执行。互斥是不同进程不能同时访问临界资源。
  • 信号量:PV操作,P是当信号量大于0时-1,若为零进入休眠;V操作是唤醒休眠的进程,即+1。
  • 互斥量:当信号量只能取01时即为互斥量,0表示临界区加锁,1表示临界区解锁。

1.7 进程间通信方式

  • 管道通信
  • 消息队列
  • 共享内存
  • 信号量
  • 信号
  • 套接字(socket)
  1. 管道

管道分为有名管道和无名管道

无名管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系一般指的是父子关系。无明管道一般用于两个不同进程之间的通信。当一个进程创建了一个管道,并调用fork创建自己的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式。

有名管道也是一种半双工的通信方式,但是它允许无亲缘关系进程间的通信。

无名管道:优点:简单方便;缺点:1)局限于单向通信2)只能创建在它的进程以及其有亲缘关系的进程之间;3)缓冲区有限;

有名管道:优点:可以实现任意关系的进程间的通信;缺点:1)长期存于系统中,使用不当容易出错;2)缓冲区有限

  1. 信号量

信号量是一个计数器,可以用来控制多个线程对共享资源的访问.,它不是用于交换大批数据,而用于多线程之间的同步.它常作为一种锁机制,防止某进程在访问资源时其它进程也访问该资源.因此,主要作为进程间以及同一个进程内不同线程之间的同步手段.

优点:可以同步进程;缺点:信号量有限

  1. 信号

信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生.

  1. 消息队列

消息队列是消息的链表,存放在内核中并由消息队列标识符标识.消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点.消息队列是UNIX下不同进程之间可实现共享资源的一种机制,UNIX允许不同进程将格式化的数据流以消息队列形式发送给任意进程.对消息队列具有操作权限的进程都可以使用msget完成对消息队列的操作控制.通过使用消息类型,进程可以按任何顺序读信息,或为消息安排优先级顺序.

优点:可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便;缺点:信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合

  1. 共享内存

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问.共享内存是最快的IPC(进程间通信)方式,它是针对其它进程间通信方式运行效率低而专门设计的.它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步与通信.

优点:无须复制,快捷,信息量大;

缺点:1)通信是通过将共无法实现享空间缓冲区直接附加到进程的虚拟地址空间中来实现的,因此进程间的读写操作的同步问题;2)利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通信

  1. 套接字:可用于不同及其间的进程通信

优点:1)传输数据为字节级,传输数据可自定义,数据量小效率高;2)传输数据时间短,性能高;3) 适合于客户端和服务器端之间信息实时交互;4) 可以加密,数据安全性强

缺点:1) 需对传输的数据进行解析,转化成应用级的数据。

进程间通信串读

1.8 五种 I / 0 模型. epoll 、poll和selcet的区别

1.8.1 IO 复用模型

IO 模型,场景:

处理多个请求时,可以采用:IO 多路复用 或者 阻塞 IO +多线程

IO 多路复用:一个 线程,跟踪多个 socket 状态,哪个就绪,就读写哪个;
阻塞 IO + 多线程:每一个请求,新建一个服务线程

思考:IO 多路复用 和 多线程 的适用场景?

  • IO 多路复用:单个连接的请求处理速度没有优势
    大并发量:只使用一个线程,处理大量的并发请求,降低上下文环境切换损耗,也不需要考虑并发问题,相对可以处理更多的请求;
    消耗更少的系统资源(不需要线程调度开销)
    适用于长连接的情况(多线程模式长连接容易造成线程过多,造成频繁调度)
  • 阻塞IO + 多线程:实现简单,可以不依赖系统调用。
    每个线程,都需要时间和空间;
    线程数量增长时,线程调度开销指数增长

1.8.2 select、poll、epoll 区别

文件描述符:当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

IO多路复用是一种同步IO模型,实现一个进程可以监视多个文件句柄(socket、文件或者管道等等),一旦某个文件句柄就绪,就能够通知程序进行相应的读写操作。

select/poll 系统调用:

// select 系统调用
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
// poll 系统调用
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
  • select:
    查询 fd_set 中,是否有就绪的 fd,可以设定一个超时时间,当有 fd (File descripter) 就绪或超时返回;
    fd_set 是一个位集合,大小是在编译内核时的常量,默认大小为 1024
    特点:
    连接数限制,fd_set 可表示的 fd 数量太小了;
    线性扫描:判断 fd 是否就绪,需要遍历一边 fd_set;
    数据复制:用户空间和内核空间,复制连接就绪状态信息

  • poll:
    解决了连接数限制
    poll 中将 select 中的 fd_set 替换成了一个 pollfd 数组
    解决 fd 数量过小的问题
    数据复制:用户空间和内核空间,复制连接就绪状态信息

  • epoll:event 事件驱动
    事件机制:避免线性扫描
    为每个 fd,注册一个监听事件
    fd 变更为就绪时,将 fd 添加到就绪链表
    fd 数量:无限制(OS 级别的限制,单个进程能打开多少个 fd)

  1. epoll读到一半又有新事件来了怎么办?
      避免在主进程epoll再次监听到同一个可读事件,可以把对应的描述符设置为EPOLL_ONESHOT,效果是监听到一次事件后就将对应的描述符从监听集合中移除,也就不会再被追踪到。读完之后可以再把对应的描述符重新手动加上。

1.8.3 select、poll、epoll 时间复杂度区别

(1)select==>时间复杂度O(n)

它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

(2)poll==>时间复杂度O(n)

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,但是它没有最大连接数的限制,原因是它是基于链表来存储的.

(3)epoll==>时间复杂度O(1)

epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

1.9 死锁,举个例子,如何预防

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

  • 死锁四个必要条件:互斥、非抢占、拥有并等待、循环等待。

例子:哲学家问题。几个哲学家坐在圆桌上,每两个哲学家之间有一只筷子,一个哲学家只有同时拥有两只筷子才能开始进食。若每个哲学家拿起左边的筷子,那么所有人就陷入了等待右边筷子的死锁。【解决:按顺序给每支筷子设定优先级,每个哲学家首先拿起低优先级的筷子,这样,除了最后一位哲学家,所有人都会先拿起左边的筷子,因此打破了循环】

  • 死锁预防:破坏四个必要条件。
  • 死锁避免:
    • 安全状态:没有死锁发生时,即使所有进程突然请求最大资源需求数,也存在调度顺序可以使每个进程运行完毕。
    • 银行家算法:判断满足贷款需求是否会进入不安全状态,不安全就拒绝。

1.10 虚拟地址和物理地址

虚拟内存是为了将物理内存扩充为更大的逻辑内存。为了更方便地管理内存,操作系统将内存抽象成地址空间,每个程序有自己对应地地址空间。地址空间被分为很多块,一块被称为一页。

虚拟地址空间的页映射到物理地址空间,不用连续映射也不用全部映射。当引用的页没有映射到物理内存时,引发缺页中断,将其调入再重新执行命令即可。这使得一个程序可以在不完全装入内存地情况下执行。

内存管理单元(MMU)维护页表,保存程序虚拟地址空间与物理地址空间的映射关系。

虚拟地址 = 页面号 + 偏移量。

1.11 页面置换算法:(缺页率最低)

  • 最优:被置换出的页面最长时间不会再使用;
  • 最近最久未使用;
  • 最近未使用:每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。
  • 先进先出;

1.12 分段

分页的表在一维地址空间动态增长可能发生覆盖,分段会将每个表分开成不固定大小并且可以动态增长的独立地址空间。段页式先将表分段,再将段分页,既有分段系统的共享和保护,又有分页系统的虚拟内存。

1.13 堆栈的区别

堆和栈的区别?堆和栈的生命周期?

答:

一、堆栈空间分配区别:

1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;

2、堆(操作系统): 一般由程序员分配(申请一块内存空间)释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

二、堆栈缓存方式区别:

1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;

2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

三、堆栈数据结构区别:

堆(数据结构):堆可以被看成是一棵树,如:堆排序;

栈(数据结构):一种先进后出的数据结构。

数据结构中的stack我们叫做堆栈,其实是两种不同的数据结构,即堆和栈,堆实质上是满足一定性质的完全二叉树,而栈是“后进先出”的一种线性数据结构,它们与队列queue数据结构相对,queue是先进先出的线性数据结构,它们都是数据结构中的概念,或者可以叫做逻辑技术,与平台,语言等无关;

而我们经常所说的内存管理中的堆栈,其实应该分为“堆heap”和“栈stack”两个部分,即heap采用了堆的数据结构,栈采用了栈的数据结构,在内存管理中发挥不同的作用,以变量存储为例,变量的引用存储在栈区中,而该引用所指向的变量的值则存储在堆区中

  1. 操作系统的线程与进程的区别,线程的几种状态。
  2. 线程间通信的方式与进程间通信的方式。实际应用中哪些用到了线程通信和进程通信。
  3. 操作系统的僵死进程和孤儿进程的区别。
  4. 操作系统的虚拟内存,分段分页,缺页调度的流程。
  5. 操作系统的死锁的四个必要条件。且每个条件的含义。
  6. 进程的调度算法。
  7. 磁盘的寻道算法。
  8. 对待死锁的策略,死锁预防,死锁避免(银行家算法),死锁检测,死锁解除。
  9. 线程模型(go的调度模型,java的调度模型,python的调度模型)。
  10. 单核CPU中的线程会有线程安全问题吗?
  11. 前台进程和后台进程的区别。
  12. 五种 I / 0 模型. epoll 、poll和selcet的区别。
  13. linux的共享内存如何实现。
  14. fork命令是什么作用,开启一个shell界面对应什么操作。

操作系统系列常见八股文相关推荐

  1. 那些不能遗忘的知识点回顾——操作系统系列(笔试面试高频题)

    有那么一些零碎的小知识点,偶尔很迷惑,偶尔被忽略,偶然却发现它们很重要,也是各大笔试和面试高频出现考点.这段时间正好在温习这些,就整理在这里,一起学习一起提高!后面还会继续补充. --前言 1.进程和 ...

  2. 深度linux系统任务栏毛玻璃,操作系统中常见的「毛玻璃」效果是怎么设计出来的?...

    原标题:操作系统中常见的「毛玻璃」效果是怎么设计出来的? Matrix 首页推荐 Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考.我们会不定期挑选 Matrix ...

  3. 操作系统学习常见疑惑问与答

    主要看了下特权级这块,和上次看的保护模式下寻址,发现于渊的书有个问题,很多地方为了回避繁杂的理论,反而把一些要点略去了,最后自己还是不得不到网上到处查资料~ 以下内容貌似是一个即将毕业的学长做毕设所总 ...

  4. 操作系统系列七 —— 装载

    往期地址: 操作系统系列一 -- 操作系统概述 操作系统系列二 -- 进程 操作系统系列三 -- 编译与链接关系 操作系统系列四 -- 栈与函数调用关系 操作系统系列五 -- 目标文件详解 操作系统系 ...

  5. 电子电气架构车载网关系列——常见网关芯片特点

    电子电气架构车载网关系列--常见网关芯片特点 我是穿拖鞋的汉子,魔都中一位工程师! 老规矩分享一段喜欢的文字,避免成为高知识低文化的工程师! 见的人多了,自己得承认,那些身上自带稳定与秩序感得人是最吸 ...

  6. Windows Media Services 9 系列常见问题解答

    本文档提供了有关 Microsoft® Windows Media® Services 9 系列的常见问题的解答.要查看每个问题和答案的完整内容,请单击问题.要展开所有问题,请按 Shift+A.要折 ...

  7. 如何连接ipv6服务器_IPv6系列-常见困扰

    本文是<IPv6系列>文章的第二篇<常见困扰>,紧接<入门指南>,用于解答IPv6的10个常见困扰. 小慢哥的原创文章,欢迎转载 目录 ▪ 本文缘由 ▪ 困扰1. ...

  8. mysql 事务 查询 范围加锁_MySQL死锁系列-常见加锁场景分析

    本文我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能举一反三,灵活地分析真实开发过程中遇到的加锁问题. 如下图所示,数据库的隔离等级,SQL 语句和当前数据库 ...

  9. mysql常见死锁_MySQL死锁系列-常见加锁场景分析

    如下图所示,数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量. 下面,我们会首先讲解一下隔离等级.不同 SQL 语句 和 当前数据库数据对 ...

  10. 操作系统系列「一」OPERATING SYSTEMS THREE EASY PIECES 《操作系统导论》

    OPERATING SYSTEMS THREE EASY PIECES 全书中文版: https://github.com/remzi-arpacidusseau/ostep-translations ...

最新文章

  1. SpringBoot+Junt+Mock测试方法
  2. ASP.NET Core微服务(四)——【静态vue使用axios解析接口】
  3. 【活动(广州)】office365的开发者训练营
  4. 结构体变量和结构体指针变量作为函数参数传递问题
  5. vue - rimraf
  6. JDBC ResultSet分析
  7. Windows Server 2012 2R服务器版本域控制器的安装及域环境的搭建(内有镜像下载)...
  8. 使用svn merge 实现回退版本
  9. windows cmd命令结束进程(window使用kill命令)
  10. 机器学习大作业---文献综述
  11. Mapbox 绘制区域边界线 鼠标悬停效果 vue
  12. (学习笔记)【目标检测】YOLO系列简单归纳
  13. contiki学习笔记(九)文件系统CFS
  14. Android 关于佳博和汉印蓝牙热敏打印机开发
  15. linux nvme文件系统,Intel NVMe驱动器扇区大小不是4096的xfs文件系统的性能下降
  16. graphiz应用一例:欧洲上古和中世纪民族变迁
  17. python中的与或非 | ^
  18. CF1244C The Football Season
  19. 教程 2 || 10分钟成为简笔画达人,然后......
  20. 罗斯蒙特3051系列变送器的温度范围

热门文章

  1. Docker深入浅出系列
  2. 如何在win10下安装Docker
  3. sql服务器安全模式怎么修改,SQL Server 安全
  4. 实践 ArcGIS Web 3D
  5. bitbucket配置_用Bitbucket搭建博客初探
  6. 关于opencv的rows和cols的理解
  7. 行测:判断推理之图形推理
  8. 【联盛德W806上手笔记】四、PWM模块
  9. stripe海外支付php教程
  10. autojs 串口通信 替代无障碍 串口