Skip to content

Latest commit

 

History

History
317 lines (179 loc) · 24.1 KB

File metadata and controls

317 lines (179 loc) · 24.1 KB

常见面试题

还有就是进程和线程的联系与区别,问的最多的就是线程之间的一些同步技术,如互斥体、信号量、条件变量等(Windows上还有事件、临界区等),这些东西你必须熟悉到具体的API函数使用的层面上来,从另外一个角度来说,这是咱们实际工作中编码最常用的东西,如果你连这个都不能熟练使用,那么你肯定不是一个合格的开发者;这类问题还可以引申为什么是死锁、如何避免死锁;进程之间通信的常用技术也需要掌握,常用的通信方式(linux下)有共享内存、匿名和具名管道、socket、消息队列等等,管道和socket是两个必须深入掌握的考察点(与上面网络通信有点重复);

进程与线程

进程有哪几种状态?

  • 运行状态:占用 CPU 资源正在运行;
  • 就绪状态:可运行,但因为其他进程正在运行而暂时停止;
  • 阻塞状态: 进程等待某种条件,在条件满足之前无法执行 (比如等待用户键盘输入)

进程之间的转换

进程的三种状态之间有四种可能的转换关系:

  • 1.运行->阻塞(进程等待输入而进入阻塞)
  • 4.阻塞->就绪(出现有效输入,但调度程度选择了另一个进程)
  • 3.就绪->运行(调度程序选择这个进程)
  • 2.运行->就绪(调度程序选择另一个进程)

进程与线程的区别

进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。可以认为进程是程序的一次执行过程,是资源分配的最小单位

线程是进程中执行运算的最小单位,是进程中的一个实体,是操作系统调度的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

【总结】

  • 进程是系统进行资源调度和分配的一个独立单位,是程序的一次执行过程,是资源分配的最小单位;

  • 线程是进程的实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位;

  • 一个进程可以有多个线程,多个线程也可以并发执行 ;

使用多线程的好处

    1. 易于调度。
    1. 提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。
    1. 开销少。由于线程比进程更轻量级,创建线程比创建进程要快,所需开销很少。。
    1. 利于充分发挥多处理器的功能。通过创建多线程进程,每个线程在一个处理器上运行,从而实现应用程序的并发性,使每个处理器都得到充分运行。

为什么有了多线程还要用多进程?

进程是一个程序实体,多个程序需要多个进程。此外,进程地址空间相互隔离,安全。

为什么有了多进程还要多线程?

  • 每个进程有一个地址空间和一个控制线程,多线程并行实体共享同一个地址空间和所有可用数据的能力,这是多进程模型(具有不同的地址空间)无法做到的。
  • 线程比进程更轻量级,比进程更容易创建,更容易撤销。
  • 性能方面,若多个线程都是 CPU 密集型的,则不能获得性能上的增强,若是存在大量的计算和 IO 处理,多线程允许这些活动彼此重叠进行,从而会加快程序执行的速度。

什么是CPU密集型、IO密集型?

进程、线程和协程之间的区别和联系

什么时候用进程?什么时候用线程?

进程与线程的选择取决以下几点:

  • 需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。
  • 线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应
  • 因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布用线程;
  • 并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求;
  • 需要更稳定安全时,适合选择进程;需要速度时,选择线程更好。

进程和线程的区别?什么时候用进程?什么时候用线程?

协程

  • **协程是一种比线程更加轻量级的存在。**正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
  • 最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
  • 协程基本上不能同步通讯,多采用异步的消息通讯,效率比较高

线程池的好处

  • 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能执行。
  • 第三:提高线程的可管理性,线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

进程与程序的区别

  • 进程是程序的一次运行活动,属于一种动态的概念。程序是一组有序的静态指令,是一种静态的概念。
  • 一个进程可以执行一个或多个程序。
  • 程序可以作为一种软件资源长期保持着,而进程则是一次执行过程,它是暂时的,是动态地产生和终止的。
  • 进程更能真实地描述并发,而程序不能。
  • 进程由程序和数据两部分组成,进程是竞争计算机系统有限资源的基本单位
  • 进程具有创建其他进程的功能;而程序没有。
  • 进程还具有并发性和交往性,这也与程序的封闭性不同

线程同步的方式

(对共享内存进行访问的程序片段称作临界区域)

  • 互斥量:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。

    互斥量只有两种状态:0,1,其中0表示解锁。当一个线程需要访问临界区时,如果该互斥量当前是解锁的(即临界区可用),则调用线程可进入临界区。

  • 信号量:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。

  • 事件(信号):(允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务)通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。

进程与线程之间的一种简单解释

进程同步的方式

信号量,互斥量,管程,文件锁

进程间的通信方式

(Inter Process Communication, IPC 几种通信方式)

  • 共享内存( shared memory ):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。 MySQL内部通信也使用了共享内存的方式,可以通过配置文件添加 --shared-memory实现

  • 信号量( semophore ): 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

  • 信号 ( sinal ):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。除了用于进程通信外,进程还可以发送信号给进程本身。

  • 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

    管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。

    管道有三种:

    ① 普通管道:有两个限制:一是只支持半双工通信方式,即只能单向传输;二是只能在具有亲缘关系间的进程(父子进程)之间使用;

    ② 流管道:去除第一个限制,支持双向传输;

    ③ 命名管道:去除第二个限制,可以在不相关进程之间进行通信。

    • 命名管道 (named pipe): 命名管道也是半双工的通信方式,它克服了管道没有名字的限制,并且它允许无亲缘关系进程间的通信。命令管道在文件系统中有对应的文件名,命名管道通过命令mkfifo或系统调用mkfifo来创建。 Microsoft SQL Server数据库默认安装后的本地连接使用的就是命名管道。 MySQL在 Window环境下,如果需要两个进程在同一台服务器上通信可以使用命名管道,通过 --enable-named-pipe选项设置。
  • 消息队列( message queue ): 消息队列是由消息的链表结构实现,存放在内核中并由消息队列标识符标识。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  • 套接字( socket ): 也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

进程间通信——几种方式的比较和详细实例

关于 fork 和父子进程的理解

什么是缓冲区溢出?有什么危害?其原因是什么?

缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。

危害有以下两点:

  • 程序崩溃,导致拒绝服务
  • 跳转并且执行一段恶意代码

造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。

缓冲区溢出攻击

作业(进程)调度算法

进程与作业的联系和区别

一、联系

  • 一个作业通常包括几个进程,几个进程共同完成一个任务,即作业。
  • 用户提交作业以后,当作业被调度,系统会为作业创建进程,一个进程无法完成时,系统会为这个进程创建子进程。

二、区别

  • 进程是一个程序在一个数据集上的一次执行,而作业是用户提交给系统的一个任务。

  • 一个作业可由多个进程组成,且必须至少由一个进程组成,反过来则不成立。

调度算法

  • 先来先服务调度算法(FCFS) 每次调度都是从后备作业队列中选择第一个进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。

  • 最短作业(进程)优先调度算法(SPF)(非抢占式) 最短作业优先(SJF)的调度算法总是选择剩余运行时间最短的那个作业(进程)进行。缺点:长作业的运行得不到保证。

  • 最短剩余时间优化算法(抢占式)

    先运行剩余时间最短的那个进程A,此时,如果来了一个新的进程B,剩余时间最短,当前运行的进程A就被挂起,而运行新的进程B。这种方式可以使新的短作业获得良好的服务。

  • 轮转调度(RR) 在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列。每个进程被分配一个时间段,称为时间片。每次调度时,把CPU分配给队首进程,并令其执行一个时间片。如果在时间片结束时,该进程还在运行,则将剥夺CPU并分配给另一个进程。如果该进程在时间片结束前阻塞或结束,则CPU立即进行切换。

  • 多级反馈队列调度算法 它是目前被公认的一种较好的进程调度算法。

    (1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。

    (2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。

    (3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。

几个常用的操作系统进程调度算法

死锁

死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

(在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态)

活锁:指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。

死锁产生的条件?

死锁产生的四个条件(四个条件必定是同时满足的,有一个条件不成立,则不会产生死锁)

  • 互斥条件:一个资源一次只能被一个进程使用;
  • 请求与保持条件(占有且等待):一个进程因请求资源而阻塞时,对已获得资源保持不放;
  • 不剥夺条件:进程获得的资源,在未完全使用完之前,不能被其它进程强行剥夺;
  • 循环等待条件:死锁发生时,系统中一定由两个或两个以上的进程组成的一个环路,该环路中的每一个进程都在都在等待着下一个进程所占有的资源

当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了 。

怎么解决死锁

四点:预防死锁、避免死锁、检测死锁、解除死锁

死锁预防

死锁预防——确保系统永远不会进入死锁状态。

产生死锁需要四个条件,那么,只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于互斥条件是非共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件。

破坏请求与保持(占有且等待)条件

方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源, 这样就不会再有请求了 。 优点:简单易实施且安全。

  • 缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。使进程经常发生饥饿现象。

方法2:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。

破坏不剥夺条件

当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。

该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作失效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。

破坏循环等待条件

可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程占有编号为 i 的资源时,那么它下一次申请资源只能申请编号大于 i 的资源。【哲学家就餐问题

这样虽然避免了循环等待,但是这种方法是比较低效的,资源的执行速度会变慢,并且可能在没有必要的情况下拒绝资源的访问,比如说,进程 3 想要申请资源 1,如果资源 1 并没有被其他进程占有,此时将它分配个进程 3 是没有问题的,但是为了避免产生循环等待,该申请会被拒绝,这样就降低了资源的利用率。

死锁的避免

死锁的避免—— 在使用前进行判断,只允许不会产生死锁的进程申请资源 。

银行家算法:该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可很快完成其计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。

死锁的检测

  1. 首先为每个进程和每个资源指定一个唯一的号码;
  2. 然后建立资源分配表和进程等待表。

略,面试不会问。

死锁的解除

一旦检测出死锁,就应立即釆取相应的措施,以解除死锁。 死锁解除的主要方法有:

  • 资源剥夺法。挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但应防止被挂起的进程长时间得不到资源,而处于资源匮乏的状态。

  • 撤销进程法。强制撤销部分、甚至全部死锁进程并剥夺这些进程的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行。

    终止所有的死锁进程。这种方式简单粗暴,但是代价很大,很有可能会导致一些已经运行了很久的进程前功尽弃。

    逐个终止进程,直至死锁状态解除。该方法的代价也很大,因为每终止一个进程就需要使用死锁检测来检测系统当前是否处于死锁状态。

  • 进程回退法。让一(多)个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。

死锁的四个必要条件和解决办法

4 个生活场景详解 BAT 面试中的死锁问题

什么是虚拟内存。

物理内存:在应用中,自然是顾名思义,物理上,真实的插在板子上的内存是多大就是多大了。而在CPU中的概念,物理内存就是CPU的地址线可以直接进行寻址的内存空间大小。

虚拟内存:它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

虚拟地址、逻辑地址、线性地址、物理地址的区别。

虚拟地址:指的是由程序产生的由段选择符和段内偏移地址两个部分组成的地址。为什么叫它是虚拟的地址呢?因为这两部分组成的地址并没有直接访问物理内存,而是要通过分段地址的变换机构处理或映射后才会对应到相应的物理内存地址。

逻辑地址:指由程序产生的与段相关的偏移地址部分。不过有些资料是直接把逻辑地址当成虚拟地址,两者并没有明确的界限。

线性地址:指的是虚拟地址到物理地址变换之间的中间层,是处理器可寻指的内存空间(称为线性地址空间)中的地址。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址可以再经过变换产生物理地址。若是没有采用分页机制,那么线性地址就是物理地址。

物理地址:指的是现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果!

分页和分段

  • 段是信息的逻辑单位,它是根据用户的需要划分的,因此段对用户是可见的 ;页是信息的物理单位,是为了管理主存的方便而划分的,对用户是透明的。
  • 段的大小不固定,有它所完成的功能决定;页大大小固定,由系统决定
  • 段向用户提供二维地址空间;页向用户提供的是一维地址空间
  • 段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制。

分段和分页

磁盘调度算法:

1.先来先服务(FCFS),按访问请求到达的先后顺序服务。简单,公平,但是效率不高,相临两次请求可能会造成最内到最外柱面寻道,使磁头反复移动,增加了服务时间,对机器不利。

2.最短寻道时间优先(SSTF),优先选择距当前磁头最近的访问请求进行服务,主要考虑寻道优先。改善了磁盘平均服务时间,但是造成某些访问请求长期等待得不到服务。

3.扫描算法(SCAN),当设备无访问请求时,磁头不动;当有访问请求时,磁头按一个方向移动,在移动过程中对遇到的访问请求进行服务,然后判断该方向上是否还有访问请求,如果有则继续.

4.循环扫描算法(CSCAN):循环扫描调度算法是在扫描算法的基础上改进的。磁臂改为单项移动,由外向里。当前位置开始沿磁臂的移动方向去选择离当前磁臂最近的哪个柱面的访问者。如果沿磁臂的方向无请求访问时,再回到最外,访问柱面号最小的作业请求。

节选自路人甲: 常见面试题整理--操作系统篇(每位开发者必备)

内核中各种同步机制(自旋锁大内核锁顺序锁等)