2021-03-17

<div id="article_content" class="article_content clearfix">
        <link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/ck_htmledit_views-b5506197d8.css">
                <div id="content_views" class="markdown_views prism-github-gist">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <p><strong>写在前面:</strong></p> 
<p>  找工作告一段落,期间经历了很多事情,也思考了许多问题,最后也收获了一些沉甸甸的东西 —— 成长和一些来自阿里、百度、京东(sp)、华为等厂的Offer。好在一切又回到正轨,接下来要好好总结一番才不枉这段经历,遂将此过程中笔者的一些笔试/面试心得、干货发表出来,与众共享之。在此特别要感谢CSDN以及广大朋友的支持,我将坚持记录并分享自己所学、所想、所悟,央请大家不吝赐教,提出您宝贵的意见和建议,以期共同探讨提高。</p> 
<hr> 
<p><strong>摘要:</strong></p> 
<p>  本文对面试/笔试过程中经常会被问到的一些关于操作系统的问题进行了梳理和总结,一方面方便自己温故知新,另一方面也希望为找工作的同学们提供一个复习参考。关于这块内容的初步了解和整体掌握,建议大家读一读《操作系统概念》(高教版)一书。</p> 
<hr> 
<p><strong>版权声明:</strong></p> 
<p>  本文原创作者:<a href="http://my.csdn.net/justloveyou_" target="_blank" rel="noopener noreferrer">书呆子Rico</a> <br>   作者博客地址:<a href="http://blog.csdn.net/justloveyou_/" target="_blank" rel="noopener noreferrer">http://blog.csdn.net/justloveyou_/</a></p> 
<hr> 
<p><strong>1、进程和线程以及它们的区别</strong></p> 
<ul><li><p>进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;</p></li><li><p>线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的 <font color="red">实时性</font>,实现进程内部的并发;</p></li><li><p>一个程序至少有一个进程,一个进程至少有一个线程,线程依赖于进程而存在;</p></li><li><p>进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。 <br>    <br> 更多关于 <strong>操作系统历史及进程/线程由来</strong> 的相关介绍请见笔者博文<a href="http://blog.csdn.net/justloveyou_/article/details/53448157" target="_blank" rel="noopener noreferrer">《Java 并发:并发背景》</a>。</p></li></ul> 
<hr> 
<p><strong>2、进程间的通信的几种方式</strong></p> 
<ul><li><p><font color="red">管道(pipe)及命名管道(named pipe)</font>:管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;</p></li><li><p><font color="red">信号(signal):</font>信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;</p></li><li><p><font color="red">消息队列:</font>消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息;</p></li><li><p><font color="red">共享内存:</font>可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等;</p></li><li><p><font color="red">信号量:</font>主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段;</p></li><li><p><font color="red">套接字:</font>这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。</p></li></ul> 
<hr> 
<p><strong>3、线程同步的方式</strong></p> 
<ul><li><p><font color="red">互斥量 Synchronized/Lock:</font>采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问</p></li><li><p><font color="red">信号量 Semphare:</font>它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量</p></li><li><p><font color="red">事件(信号),Wait/Notify:</font>通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作</p></li></ul> 
<hr> 
<p><strong>4、什么是死锁?死锁产生的条件?</strong></p> 
<p>1). 死锁的概念</p> 
<p>  在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲,就是两个或多个进程无限期的阻塞、相互等待的一种状态。</p> 
<hr> 
<p>2). 死锁产生的四个必要条件</p> 
<ul><li><p><font color="red">互斥:</font>至少有一个资源必须属于非共享模式,即一次只能被一个进程使用;若其他申请使用该资源,那么申请进程必须等到该资源被释放为止;</p></li><li><p><font color="red">占有并等待:</font>一个进程必须占有至少一个资源,并等待另一个资源,而该资源为其他进程所占有;</p></li><li><p><font color="red">非抢占:</font>进程不能被抢占,即资源只能被进程在完成任务后自愿释放</p></li><li><p><font color="red">循环等待:</font>若干进程之间形成一种头尾相接的环形等待资源关系 </p></li></ul> 
<hr> 
<p>3). 死锁的处理基本策略和常用方法</p> 
<p>  解决死锁的基本方法主要有 预防死锁、避免死锁、检测死锁、解除死锁 、鸵鸟策略 等。</p> 
<p>  (1). 死锁预防 <br>    <br>   死锁预防的基本思想是<font color="red"> 只要确保死锁发生的四个必要条件中至少有一个不成立,就能预防死锁的发生</font>,具体方法包括:</p> 
<ul><li><p>打破互斥条件:允许进程同时访问某些资源。但是,有些资源是不能被多个进程所共享的,这是由资源本身属性所决定的,因此,这种办法通常并无实用价值。</p></li><li><p>打破占有并等待条件:可以实行资源预先分配策略(进程在运行前一次性向系统申请它所需要的全部资源,若所需全部资源得不到满足,则不分配任何资源,此进程暂不运行;只有当系统能满足当前进程所需的全部资源时,才一次性将所申请资源全部分配给该线程)或者只允许进程在没有占用资源时才可以申请资源(一个进程可申请一些资源并使用它们,但是在当前进程申请更多资源之前,它必须全部释放当前所占有的资源)。但是这种策略也存在一些缺点:在很多情况下,无法预知一个进程执行前所需的全部资源,因为进程是动态执行的,不可预知的;同时,会降低资源利用率,导致降低了进程的并发性。</p></li><li><p>打破非抢占条件:允许进程强行从占有者哪里夺取某些资源。也就是说,但一个进程占有了一部分资源,在其申请新的资源且得不到满足时,它必须释放所有占有的资源以便让其它线程使用。这种预防死锁的方式实现起来困难,会降低系统性能。</p></li><li><p>打破循环等待条件:实行资源有序分配策略。对所有资源排序编号,所有进程对资源的请求必须严格按资源序号递增的顺序提出,即只有占用了小号资源才能申请大号资源,这样就不回产生环路,预防死锁的发生。</p></li></ul> 
<hr> 
<p>  (2). 死锁避免的基本思想 <br>    <br>   死锁避免的基本思想是动态地检测资源分配状态,以确保循环等待条件不成立,从而确保系统处于安全状态。所谓安全状态是指:如果系统能按某个顺序为每个进程分配资源(不超过其最大值),那么系统状态是安全的,换句话说就是,如果存在一个安全序列,那么系统处于安全状态。资源分配图算法和银行家算法是两种经典的死锁避免的算法,其可以确保系统始终处于安全状态。其中,资源分配图算法应用场景为每种资源类型只有一个实例(申请边,分配边,需求边,不形成环才允许分配),而银行家算法应用于每种资源类型可以有多个实例的场景。</p> 
<hr> 
<p>  (3). 死锁解除</p> 
<p>  死锁解除的常用两种方法为进程终止和资源抢占。所谓进程终止是指简单地终止一个或多个进程以打破循环等待,包括两种方式:终止所有死锁进程和一次只终止一个进程直到取消死锁循环为止;所谓资源抢占是指从一个或多个死锁进程那里抢占一个或多个资源,此时必须考虑三个问题:</p> 
<p>  (I). 选择一个牺牲品 <br>   (II). 回滚:回滚到安全状态 <br>   (III). 饥饿(在代价因素中加上回滚次数,回滚的越多则越不可能继续被作为牺牲品,避免一个进程总是被回滚)</p> 
<hr> 
<p><strong>5、进程有哪几种状态?</strong></p> 
<ul><li><p>就绪状态:进程已获得除处理机以外的所需资源,等待分配处理机资源;</p></li><li><p>运行状态:占用处理机资源运行,处于此状态的进程数小于等于CPU数;</p></li><li><p>阻塞状态: 进程等待某种条件,在条件满足之前无法执行;</p> <p>               <img src="http://static.zybuluo.com/Rico123/xilfs88fqtduey2lj5o3503z/%E8%BF%9B%E7%A8%8B%E7%9A%84%E4%B8%89%E7%A7%8D%E7%8A%B6%E6%80%81.jpg" alt="进程的三种状态.jpg-14.1kB" title=""></p></li></ul> 
<hr> 
<p><strong>6、线程有几种状态?</strong></p> 
<p>  在 Java虚拟机 中,线程从最初的创建到最终的消亡,要经历若干个状态:创建(new)、就绪(runnable/start)、运行(running)、阻塞(blocked)、等待(waiting)、时间等待(time waiting) 和 消亡(dead/terminated)。在给定的时间点上,一个线程只能处于一种状态,各状态的含义如下图所示:</p> 
<p>          <img src="https://img-blog.csdn.net/20171021161747787?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVzdGxvdmV5b3Vf/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描述" title=""></p> 
<hr> 
<p>  线程各状态之间的转换如下:</p> 
<p>             <img src="http://static.zybuluo.com/Rico123/ot7o6218591iwj9py999hs1u/%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81.jpg" alt="线程的状态.jpg-59.9kB" title=""></p> 
<p>  更多关于 <strong>线程状态及其转化</strong> 的相关叙述,请见笔者博文<a href="http://blog.csdn.net/justloveyou_/article/details/54347954" target="_blank" rel="noopener noreferrer">《 Java 并发:Thread 类深度解析》</a>。</p> 
<hr> 
<p><strong>7、分页和分段有什么区别(内存管理)?</strong></p> 
<p>  段式存储管理是一种符合用户视角的内存分配管理方案。在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)</p> 
<p>  页式存储管理方案是一种用户视角内存与物理内存相分离的内存分配管理方案。在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分离。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)。</p> 
<p><font color="red"><b><strong>两者的不同点:</strong></b></font></p> 
<ul><li><p><font color="red">目的不同:</font>分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;</p></li><li><p><font color="red">大小不同:</font>页的大小固定且由系统决定,而段的长度却不固定,由其所完成的功能决定;</p></li><li><p><font color="red">地址空间不同:</font> 段向用户提供二维地址空间;页向用户提供的是一维地址空间;</p></li><li><p><font color="red">信息共享:</font>段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;</p></li><li><p><font color="red">内存碎片:</font>页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)。</p></li></ul> 
<hr> 
<p><strong>8、操作系统中进程调度策略有哪几种?</strong></p> 
<ul><li><p><font color="red">FCFS(先来先服务,队列实现,非抢占的):</font>先请求CPU的进程先分配到CPU</p></li><li><p><font color="red">SJF(最短作业优先调度算法):</font>平均等待时间最短,但难以知道下一个CPU区间长度</p></li><li><p><font color="red">优先级调度算法(可以是抢占的,也可以是非抢占的):</font>优先级越高越先分配到CPU,相同优先级先到先服务,存在的主要问题是:低优先级进程无穷等待CPU,会导致无穷阻塞或饥饿;解决方案:<font color="red"><b>老化</b></font></p></li><li><p><font color="red">时间片轮转调度算法(可抢占的):</font>队列中没有进程被分配超过一个时间片的CPU时间,除非它是唯一可运行的进程。如果进程的CPU区间超过了一个时间片,那么该进程就被抢占并放回就绪队列。</p></li><li><p><font color="red">多级队列调度算法:</font>将就绪队列分成多个独立的队列,每个队列都有自己的调度算法,队列之间采用固定优先级抢占调度。其中,一个进程根据自身属性被永久地分配到一个队列中。</p></li><li><p><font color="red">多级反馈队列调度算法:</font>与多级队列调度算法相比,其允许进程在队列之间移动:若进程使用过多CPU时间,那么它会被转移到更低的优先级队列;在较低优先级队列等待时间过长的进程会被转移到更高优先级队列,以防止饥饿发生。</p></li></ul> 
<hr> 
<p><strong>9、说一说进程同步有哪几种机制</strong></p> 
<p>  原子操作、信号量机制、自旋锁管程、会合、分布式系统</p> 
<hr> 
<p><strong>10、什么是虚拟内存?</strong></p> 
<p>1).内存的发展历程</p> 
<p>  没有内存抽象(单进程,除去操作系统所用的内存之外,全部给用户程序使用) —&gt; 有内存抽象(多进程,进程独立的地址空间,交换技术(内存大小不可能容纳下所有并发执行的进程) <br> )—&gt; 连续内存分配(固定大小分区(多道程序的程度受限),可变分区(首次适应,最佳适应,最差适应),碎片) —&gt; 不连续内存分配(分段,分页,段页式,虚拟内存)</p> 
<hr> 
<p>2).虚拟内存</p> 
<p>  虚拟内存允许执行进程不必完全在内存中。虚拟内存的基本思想是:每个进程拥有独立的地址空间,这个空间被分为大小相等的多个块,称为页(Page),每个页都是一段连续的地址。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻进行必要的映射;当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的命令。这样,<font color="red"><b>对于进程而言,逻辑上似乎有很大的内存空间,实际上其中一部分对应物理内存上的一块(称为帧,通常页和帧大小相等),还有一些没加载在内存中的对应在硬盘上,如图5所示。</b></font> <br> 注意,请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。</p> 
<p>                   <img src="https://img-blog.csdn.net/20171021161259614?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVzdGxvdmV5b3Vf/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描述" title=""></p> 
<p>  由图5可以看出,虚拟内存实际上可以比物理内存大。当访问虚拟内存时,会访问MMU(内存管理单元)去匹配对应的物理地址(比如图5的0,1,2)。如果虚拟内存的页并不存在于物理内存中(如图5的3,4),会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出。</p> 
<hr> 
<p>3). 页面置换算法</p> 
<ul><li><p><font color="red">FIFO先进先出算法</font>:在操作系统中经常被用到,比如作业调度(主要实现简单,很容易想到);</p></li><li><p><font color="red">LRU(Least recently use)最近最少使用算法</font>:根据使用时间到现在的长短来判断;</p></li><li><p><font color="red">LFU(Least frequently use)最少使用次数算法</font>:根据使用次数来判断;</p></li><li><p><font color="red">OPT(Optimal replacement)最优置换算法</font>:理论的最优,理论;就是要保证置换出去的是不再被使用的页,或者是在实际内存中最晚使用的算法。</p></li></ul> 
<hr> 
<p>4). 虚拟内存的应用与优点</p> 
<p>  虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。虚拟内存的使用可以带来以下好处:</p> 
<ul><li><p>在内存中可以保留多个进程,系统并发度提高</p></li><li><p>解除了用户与内存之间的紧密约束,进程可以比内存的全部空间还大</p></li></ul> 
<hr> 
<p><strong>11、颠簸</strong></p> 
<p>  颠簸本质上是指频繁的页调度行为,具体来讲,进程发生缺页中断,这时,必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此,会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸(抖动)。</p> 
<p>  内存颠簸的解决策略包括:</p> 
<ul><li><p>如果是因为页面替换策略失误,可以修改替换算法来解决这个问题;</p></li><li><p>如果是因为运行的程序太多,造成程序无法同时将所有频繁访问的页面调入内存,则要降低多道程序的数量;</p></li><li><p>否则,还剩下两个办法:终止该进程或增加物理内存容量。</p></li></ul> 
<hr> 
<p><strong>12、局部性原理</strong></p> 
<p>(1). 时间上的局部性:最近被访问的页在不久的将来还会被访问;</p> 
<p>(2). 空间上的局部性:内存中被访问的页周围的页也很可能被访问。</p> 
<hr> 
<blockquote> 
 <h2 id="引用"><a name="t0"></a>引用</h2> 
 <p><a href="http://blog.csdn.net/bupt_tinyfisher/article/details/8939689" target="_blank" rel="noopener noreferrer"> 分段和分页内存管理</a> <br> <a href="http://www.imooc.com/article/11015" target="_blank" rel="noopener noreferrer">BAT面试之操作系统内存详解</a></p> 
</blockquote>
                </div><div><div></div></div>
                <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-d7a94ec6ab.css" rel="stylesheet">
                <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-f1c5feb645.css" rel="stylesheet">
        </div>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
embedcpp-2021-03是一个有关嵌入式C++编程的课程,于2021年3月举办。嵌入式C++编程是指在嵌入式系统使用C++编程语言进行开发的一种方法。 在嵌入式系统,资源通常是有限的,例如处理器速度、内存容量和存储空间等。因此,使用C++编程语言可以提供更高的灵活性和效率,帮助开发人员充分利用有限的资源。C++在嵌入式系统的应用范围广泛,例如物联网设备、汽车电子和工业自动化等领域。 embedcpp-2021-03课程旨在向学员介绍嵌入式C++编程的基础知识和技巧。课程内容通常包括以下方面: 1. C++语法和特性:介绍C++的基本语法、面向对象编程和泛型编程等概念,以及C++11、C++14和C++17的一些新特性。 2. 嵌入式系统概述:了解嵌入式系统的基本特点、硬件和软件组成,以及与传统桌面开发的区别。 3. 低级编程:学习如何与硬件交互,包括使用寄存器、配置外设和处理断等。还可以介绍使用汇编语言优化性能的技巧。 4. 内存管理:探讨嵌入式系统的内存管理技术,包括堆栈和堆的使用、动态内存分配和对象生命周期管理等。 5. 实时操作系统(RTOS):介绍嵌入式系统常用的实时操作系统,如FreeRTOS和µC/OS等,学习如何使用RTOS进行任务调度和资源管理。 除了理论知识,embedcpp-2021-03课程通常还包括实际的项目练习,以帮助学员将所学知识应用于实际场景。通过该课程,学员可以了解嵌入式C++编程的基础概念和实践技巧,为嵌入式系统开发提供了一定的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值