2024年Python最新【面试重点系列】操作系统常见面试重点题(万字图解)(1),科学技术协会面试题

在这里插入图片描述

感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:

① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

    • 🌻互斥锁
  • 🌻自旋锁

  • 🌻读写锁

  • 2.悲观锁和乐观锁分别适用于什么场景?

  • 2.乐观锁的两种实现方式?

    • 🌻一、版本号机制
  • 🌻二、CAS算法

  • 六、进程和线程的通信方式

    • 1.那你说说进程间的通信方式吧?
    • 🌻进程间通信的工作原理
  • 🌻管道

    • 🌼匿名管道
  • 🌼命名管道FIFO

  • 🌻消息队列

  • 🌻共享内存

  • 🌻信号量

  • 🌻信号

  • 🌻 Socket

  • 2.再来谈谈线程间的通信方式吧!

    • 🌻1、为什么需要线程通信
  • 🌻2、线程通信的方式

  • 总结

  • 面试重点系列文章

  • 参考

前言


本文的目的是学习、记录和分享一些常见的 操作系统 知识。同时会以面试场景的形式给大家带来详细的介绍。希望本篇文章能给屏幕前的你有所帮助。如果文章哪个地方有什么 错误,也恳请大佬们 斧正,博主将不胜感激!

学习长路漫漫,你我同行。共进步,同成长!


一、线程和进程的区别


1、进程和线程的区别

🌸

👨‍💼面试官:OK,我们开始了,首先来说一下进程和线程的一些区别吧!

🙍‍♂️小宝:好的面试官。

首先我们需要知道什么是 线程?什么又是 进程

  • 线程: 是操作系统能够进行运算调度的最小单位。

  • 进程: 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础

我们通过上述对两者的介绍我们能大概的知道了两者的 根本区别

线程是进程当中的⼀条执⾏流程,也是是独立调度的基本单位。

进程是操作系统进行资源分配的基本单位。

我们在从其他方面对这两者进行对比:

  • 地址空间:

  • 进程:进程包含独立的地址空间。

  • 线程:线程没有自己独立的地址空间,它共享所属进程的空间。

  • 开销:

  • 进程:进程之间的切换会有较大的开销。

  • 线程:线程之间的切换的开销比较小。

  • 资源:

  • 进程:系统在运行的时候会为每个进程分配资源,而不会为线程分配资源。

  • 线程:线程所使用的资源来自其所属进程的资源。

  • 通信:

  • 线程之间通信比进程之间通信更方便。

  • 包含关系:

  • 线程是一个轻量级进程,是进程的一部分。

2、进程和线程分别适用于什么场景呢?

🌸

👨‍💼面试官:那么两者的分别适用于什么场景呢?

🙍‍♂️小宝:

进程适用的场景:需要安全稳定的场景。

线程适用的场景

  • 频繁创建和销毁的场景。

  • 计算量大,切换频繁的场景。

  • 需要速度的场景。

3、进程切换为什么比线程更消耗资源?

🌸

👨‍💼面试官:进程切换为什么比线程更消耗资源?

🙍‍♂️小宝: 进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;线程切换时只需要切换硬件上下文和内核栈。

TLB补充: 英文:Translation Lookaside Buffer。通过翻译:旁路转换缓冲。TLB 就是负责将虚拟内存地址翻译成实际的物理内 存地址,而 CPU 寻址时会优先在 TLB 中进行寻址。处理器的性能就和寻址的命中率有很大的关系。

上下文补充: 在一个程序运行起来时进程会有很多中状态,状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开的文件描述符的集合,这个状态叫做上下文(Context)。

上下文切换:

  • 进程: 进程是由内核管理和调度的,所以说进程切换只能发生再内核态
  • 线程: 当两个线程不是属于同一个进程时,则和进程的上下文切换是一样的;当两个线程是属于同一个进程时,因为虚拟机内存是共享的,所以再切换时,虚拟内存这些资源就会保持不变,只需要切换线程的私有数据,寄存器等不共享的数据。

线程存在于进程中,一个进程可以有一个或多个线程。线程是运行在进程上下文中的逻辑流,这个线程可以独立完成一项任务。同样线程有自己的上下文,包括唯一的整数线程ID, 栈、栈指针、程序计数器、通用目的寄存器和条件码。可以理解为线程上下文是进程上下文的子集。

由于保存线程的上下文明显比进程的上下文小,因此系统切换线程时,必然开销更小。

所以这个只是线程比进程消耗资源小的其中一个方面。

4、发生进程上下文切换有哪些场景?

🌸

👨‍💼面试官:发生进程上下文切换有哪些场景?

🙍‍♂️小宝:进程上下文的切换场景有:

  • 例如某个进程时间片耗光,然后从 运行状态 转化为 就绪状态 然后调度程序就再从 就绪队列 中选取新的进程到运行状态。

  • 资源不足,进程会被挂起。

  • 通过睡眠函数 sleep 将自己挂起,自然也会有重新调度。

  • 来了高优先级的进程时,当前进程被挂起。

  • 硬件中断时,CPU 上的进程会被中断挂起,转而执行内核的中断服务程序;


二、进程的状态


1、有了解进程的一些状态吗?

🌸

👨‍💼面试官:有了解进程的一些状态吗?

🙍‍♂️小宝:知道一些的面试官。

首先 进程 有三个最基本的状态 —— 就绪状态、运行状态和阻塞状态。当然除了这三个还有两个基本的状态——创建状态和结束状态

我们再来说一下基本状态的作用。

  • 创建状态: 是一个进程的创建。

  • 就绪状态: 是进程等待到 CPU也就是 运行状态 的前一个状态,进程调度器 也就是在这里选择进程运行状态。简的来说:可运⾏,由于其他进程处于运⾏状态⽽暂时停⽌运⾏;

  • 运行状态: 代表在 运行状态进程占用 CPU进行一些操作。

  • 阻塞状态: 因为一些操作(如IO)让进程暂停运行。这时CPU给他控制权,他也无法运行!

  • 结束状态: 表示该 进程任务完成或出错时,需要从系统中消失。

🌸下面是小宝画的流程图(有点手残希望大家别建议)

流程图:

在这里插入图片描述

2、知道挂起状态吗?

🌸

👨‍💼面试官:回答挺不错的,那你知道挂起状态吗?

🙍‍♂️小宝:

挂起状态: 因为如果在上述中有很多的 阻塞状态进程就会占用物理空间,这是非常浪费资源的。所以我们会把这些 阻塞状态 的进程 转移到外存(硬盘)中。当再次运行时则会把该进程从外存转入到 物理内存

挂起状态还分为:

  • 就绪挂起状态:进程在外盘中,进入内存立即运行。

  • 阻塞挂起状态:进程在外盘中,并等待某个事件的出现。

流程图:(手残党来啦)

在这里插入图片描述

补充:

导致挂起的不仅不仅只有阻塞进程过多消耗物理内存,还有下面一些情况:

  • 通过 sleep 让进程间歇性挂起,其⼯作原理是设置⼀个定时器,到期后唤醒进程。

  • ⽤户希望挂起⼀个程序的执⾏,⽐如在 Linux 中⽤ Ctrl+Z 挂起进程;

3、知道挂起和阻塞的区别吗?

🌸

👨‍💼面试官:知道挂起和阻塞的区别吗?

🙍‍♂️小宝:通过上面对挂起和阻塞状态的分析我们先来说一下两者的 共同点

  • 都是一种暂停状态。

  • 不会占用 CPU

接下来是 不同点:

  • 占用资源不同: 阻塞是在物理内存中,而挂起是在外存中。

  • 发生时机不同阻塞一般在进程等待资源(IO资源、信号量等)时发生;而挂起是由于用户和系统的需要,例如,终端用户需要暂停程序研究其执行情况或对其进行修改、OS为了提高内存利用率需要将暂时不能运行的进程(处于就绪或阻塞队列的进程)调出到磁盘 。

  • 恢复时机不同阻塞要在等待的资源得到满足(例如获得了锁)后,才会进入就绪状态,等待被调度而执行;被挂起的进程由将其挂起的对象(如用户、系统)在时机符合时(调试结束、被调度进程选中需要重新执行)将其主动激活。


三、进程的调度


1、了解进程调度机制吗?

🌸

👨‍💼面试官:了解进程调度机制吗?

🙍‍♂️小宝:在进程的生命周期中,当进程从一个运行状态转变为另外一种状态时,就会触发一次调度

我们来看下图:

在这里插入图片描述

通过图片我们能看见一个 调度程序 包含有排队器、分派器和上下文切换器

先来看看它们的功能分别是什么?

  • 排队器: 每当有一个进程转换为就绪状态时,排队器就会将它插入到相对应的就绪队列中。

  • 分派器: 分派器依据进程调度程序的所选定的程序,将其从就绪队列中取出,然后进行从分派器到新选出的进程间的上下文切换,将处理机分配给新选出的进程。

  • 上下文切换器:CPU 切换时会发生两对上下文的切换操作:

  • 第一对:上下文切换时,OS会将保存当前的进程的上下文,即把当前进程的处理机寄存器内存保存到该进程的进程控制块内的相应单元,再装入分派到程序的上下文,以便分派程序的运行。

  • 第二对:上下文切换是移除分派程序的上下文,而把新进程的CPU现场信息装入到处理机的各个相应的寄存器中,以便新选择进程运行。

上面是进程调度机制中三个基本部分。(为了更好的了解进程的调度机制先做个大概的了解)

调度时机:

以下状态的变化都会触发操作系统的调度:

  • 从就绪状态—》运行状态;

  • 从运行状态 —》阻塞状态;

  • 运行状态 —》结束状态;

调度原则:

  • 一、为了提高CPU的利用率,在这种发送I/O事件致使CPU空闲的情况下,调度程序需要从就绪队列中选择一个进程来运行。

  • 二、要提高进程的吞吐率

2、了解进程调度算法吗?

🌸

👨‍💼面试官:你了解哪些进程调度的算法呢?

🙍‍♂️小宝:OK,接下来来说一下我们最常见的几种 调度算法

  • 先来先服务调度算法。

  • 最短作业优先调度算法

  • 高响应比优先调度算法

  • 时间片轮转调度算法

  • 最高优先级调度算法

  • 多级反馈队列调度算法

🌻

🌻先来先服务

FCFS:从字面意思我们就能知道它的具体功能就是谁先来 CPU 就先处理谁,就和排队买饭一样,谁先来谁就先买饭。

图解:

在这里插入图片描述

缺点: 不利于短服务,可能会一直存在长任务一直无法调度的情况。


🌻最短优先作业

我们也能从字面意思来了解,这个算法就是一直会调用 就绪队列 中时间短的序列来进行处理。

图解:

在这里插入图片描述

缺点:

这样对运行时长比较长的任务是比较不利的,可能长作业会一直得不到调度发生 饥饿状态


🌻高响应比优先

在批处理系统中,前两种算法都没能很好的处理不同时机的作业。而 高响应比优先 则是既考虑作业的 等待时间 又考虑了作业 运行时间 ,因此既照顾了短作业,又不致使长作业的等待时间过长。

公式:

在这里插入图片描述

🌻

🌻时间片轮转

本算法是在 FCFS 的基础上,每一次调度到的进程只能使用一个时间片的 CPU进程 执行完毕或时间片用尽后,将时间片交给队列中的下一个进程,并返回到队列尾。

在这里插入图片描述

缺点:

  • 如果时间片设得太短会导致过多的进程上下文切换,降低了 CPU 效率

  • 如果设得太长又可能引起对短作业进程的响应时间变长。

通常时间片设为 20ms~50ms 通常是一个比较合理的折中值。

🌻最高优先级

最高优先级的调度就是开始时给每一个进程分配一个优先级,然后优先级高的进程先调度。

我们这里来了解一下 静态优先级动态优先级

  • 静态优先级:创建进程时候,就已经确定了优先级了,然后整个运行时间优先级都不会变化;

  • 动态优先级:根据进程的动态变化调整优先级,比如如果进程运行时间增加,则降低其优先级,如果进程等待时间(就绪队列的等待时间)增加,则升高其优先级,也就是随着时间的推移增加等待进程的优先级

该算法也有两种处理优先级高的方法,非抢占式和抢占式:

  • 非抢占式调度算法: 挑选一个进程,然后让该进程运行直到被阻塞,或者直到该进程退出,才会调用另外一个进程也就是说,不会理时钟中断这个事情。

  • 抢断式调度算法: 让进程只运行某段时间,如果运行时间结束,则该进程就会被挂起,再从就绪队列中挑选另外一个进程。

缺点:

可能会导致 低优先级 永远不会被运行。(解决: 可以每隔一段时间为队列中等待的 进程 增加优先级。)

🌻

🌻多级反馈队列

本算法相当于结合了 时间片轮转优先级调度 的结合,有多层 FCFS 队列,且为不同的队列分配不同的 优先级

过程:

优先级越高的时间片越短,当一个新的进程就绪时,将其放入 优先级最高 的队列中,如果时间片用完后,将其放入到第二个队列,以次类推,知道该进程运行完成。

注: 优先级低的队列必须等优先级高的队列中没有任务时才能分配时间片。

图解:

在这里插入图片描述

可以发现,对于短作业可能可以在第一级队列很快被处理完。对于长作业,如果在第一级队列处理不完,可以移入下次队列等待被执行,虽然等待的时间变长了,但是运行时间也会更长了,所以该算法很好的兼顾了长短作业,同时有较好的响应时间。


四、死锁


🌸

1、死锁的概念

👨‍💼面试官:有了解死锁吗?来说一下它的概念。

🙍‍♂️小宝: 死锁 是两个或多个并发的进程占用了 对方 继续执行需要获取的资源,导致任意一个都无法继续执行。

为了更加了解 死锁 我们可以通过 哲学家的晚餐这个故事来了解。当一群哲学家坐在餐桌上面准备吃饭时,他们左右各有一只筷子。当所有的哲学家都拿起自己右手边的筷子时,此时桌子上的筷子是都被拿起(每个哲学家手里一只),但是吃饭需要两只筷子,一只筷子没办法吃,所以在座的所有哲学家都没办法吃饭。这其实就是一个 死锁问题

在这里插入图片描述

🌸

2、形成死锁的必要条件

👨‍💼面试官:那你知道怎么样形成死锁?

🙍‍♂️小宝:形成 死锁 有四个必要的条件:

  • 1、互斥条件。

  • 2、占有并保持条件。

  • 3、非抢占条件。

  • 4、循环等待条件。

互斥条件:

进程占用的资源必须是互斥的,即需要等待对方释放资源才能获取。

图解:

在这里插入图片描述

占用并保持:

进程尝试获取下一个资源时不会释放已经占用的资源。

图解:

在这里插入图片描述

非抢占条件:

只有进程执行完才能释放资源。

图解:

在这里插入图片描述

循环等待条件:

就和哲学家进餐一样,若干个进程相互占用形成一个环。

图解:

在这里插入图片描述

🌸

3、死锁的解决策略

👨‍💼面试官:既然形成了死锁,那有办法解决死锁这个问题吗?

🙍‍♂️小宝:死锁的解决策略有以下四种:

  • 1、死锁预防

  • 2、死锁避免

  • 3、死锁解除

  • 4、鸵鸟策略

🌻一、死锁预防

我们在预防 死锁 形成时,我们只需要让 死锁 四个条件其中一个不满足即可。

  • 破环互斥条件

  • 出现的问题: 无法实现对共享资源的操作同步,无实用价值。

  • 破快占有并保持条件: 可以提前对资源进行分配,即在程序开始前,就分配好所有资源。

  • 出现问题: 部分情况下,程序是在运行过程中动态获取资源的、预分配会给进程分配一些极少使用的资源。降低了资源的利用率。

  • 破坏非抢占条件: 进程无法获取到 继续执行 所需要的资源时,释放自己已有的资源,并重新尝试执行。

  • 出现问题: 性能开销大,效率低。

  • 破坏循环等待: 使用资源有序分配法,来破环环路等待条件。

资源有序分配法补充:

线程 A 和 线程 B 获取资源的顺序要一样,当线程 A 是先尝试获取资源 A,然后尝试获取资源 B 的时候,线程 B 同样也是先尝试获取资源 A,然后尝试获取资源 B。也就是说,线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源。

我们使用资源有序分配法的方式来修改前面发生死锁的代码,我们可以不改动线程 A 的代码。

我们先要清楚线程 A 获取资源的顺序,它是先获取互斥锁 A,然后获取互斥锁 B。

所以我们只需将线程 B 改成以相同顺序的获取资源,就可以打破死锁了。

图解:

在这里插入图片描述


🌻二、避免死锁

当允许四个条件中的前三个条件存在,如果系统能按某个顺序给进程分配资源,则代表系统就是安全的。

则通过 银行家算法等死锁规避算法 ,计算每一次为进程分配资源后进程是否安全,如果不处于,则让进程 等待并恢复资源分配矩阵。

死锁检测算法补充:

通过计算资源分配图中是否存在环判断系统,进程间是否存在死锁。

🌻三、死锁解除

通过终止进程的方式来释放资源,可终止所有进程,也可以终止一个进程,直到打破循环为止。

🌻四、鸵鸟策略

一般的鸵鸟策略就是大部分的操作系统都不会主动去处理死锁,而是忽略死锁。


🌸

4、活锁

👨‍💼面试官:既然说了死锁,那么你知道活锁吗?来说一下活锁的概念。

🙍‍♂️小宝:活锁就是当两个进程出现死锁时,都通过释放资源来解决。若释放资源保持相同步长,则会造成 活锁 ,即没有发送阻塞,但程序无法向下执行。意思就是想着相互谦让,A让B,B让A。这样就会形成谁也不去获取资源。

图解:

在这里插入图片描述


五、悲观锁和乐观锁


🌸

1、知道悲观锁和乐观锁的基本概念吗?

👨‍💼面试官:知道悲观锁和乐观锁的基本概念吗?

🙍‍♂️小宝:

悲观锁:

悲观锁 (Pessimistic Concurrency Control) 如其名,这个锁看起来就就很悲观。一般 悲观 是我们人类的一种情绪表现,它代表我们人类情感方面的消极一面,认为什么事情都是不好的。而这个悲观锁也是一样的,每次 悲观锁 去拿数据都会认为别人要去修改其数据,则就会相对应的加上 ,让其访问的进程变为 阻塞状态 。一般的互斥锁、自旋锁、读写锁都是 悲观锁

乐观锁:

乐观锁 (Optimistic Concurrency Control) 如其名,乐观代表的是一种积极向上的态度。即乐观锁也是表示 “数据变动不会太平凡” 同时允许多个事务对数据进行修改。那可能就会有小伙伴问 “这个没有加锁 乐观锁 有没有不都是一样的吗?那为什么还需要使用 乐观锁 ?“

为了回答上面的问题,我们先来了解 乐观锁 工作原理:先修改完共享资源,然后发现此段时间里有没有发生冲突,如果没有则表示此次修改完成。如果发现有线程对这个资源进行修改,那么则放弃本次修改。 在这个过程中其实 乐观锁 就是没有加锁,它也被称为 无锁编程

OK,我们再来看一看 悲观锁 的详细介绍。

上述中我们知道了一般悲观锁有 互斥锁、自旋锁、读写锁。接下来我们在对这几个来详细的介绍一下。


🌻互斥锁

互斥锁 是一种比较底层的锁,很多高级的锁都是基于 互斥锁 实现 。我们知道当我们给共享资源加锁时,就是为了保证只能有 一个 线程来进行访问,避免多线程同时操作导致数据出现问题。

互斥锁 的特点就是: 一次只能一个线程加锁,其余只能等待。且在一些线程加锁失败后 线程会释放 CPU 给其他 线程来操作。 且会 内核 会让线程陷入 阻塞状态

图解:

在这里插入图片描述

而我们知道一般任务从保存到再加载的过程就是一次上下文切换。 上下文切换 则就表示会有资源的开销。

则开销成本就是两次 上下文切换

  • 当加锁失败时, 内核 会把线程从运行态转化为睡眠状态,然后把 CPU 转化给其他线程

  • 当锁被释放时,之前的睡眠状态的线程就会转化为就绪状态,然后 内核 会在合适的时间,把 CPU 切换给线程运行!

🌻自旋锁

互斥锁 一样,自旋锁 也是一种底层的锁,很多高级的锁都是基于 自旋锁则其特点:

如果你也是看准了Python,想自学Python,在这里为大家准备了丰厚的免费学习大礼包,带大家一起学习,给大家剖析Python兼职、就业行情前景的这些事儿。

一、Python所有方向的学习路线

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

二、学习软件

工欲善其必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

三、全套PDF电子书

书籍的好处就在于权威和体系健全,刚开始学习的时候你可以只看视频或者听某个人讲课,但等你学完之后,你觉得你掌握了,这时候建议还是得去看一下书籍,看权威技术书籍也是每个程序员必经之路。

四、入门学习视频

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

五、面试资料

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

成为一个Python程序员专家或许需要花费数年时间,但是打下坚实的基础只要几周就可以,如果你按照我提供的学习路线以及资料有意识地去实践,你就有很大可能成功!
最后祝你好运!!!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值