todo【面经】牛客网Linux和操作系统相关面经题目

1. 进程和线程的区别

  • 概念
    • 进程:
      对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;
    • 线程:
      进程的子任务,是CPU调度和分派的基本单位,实现进程内部的并发;
      线程是操作系统可识别的最小执行和调度单位。
      每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。
  • 区别
  1. 拥有资源
    进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。
  2. 调度
    线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
  3. 系统开销
    由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。
  4. 通信方面
    线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。

2. 什么是死锁

  • 产生条件
    1. 互斥:
      每个资源要么已经分配给了一个进程,要么就是可用的。
    2. 占有和等待:
      已经得到了某个资源的进程可以再请求新的资源。
    3. 不可抢占:
      已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
    4. 环路等待:
      有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
  • 解决策略
    1. 鸵鸟策略
      当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,直接忽略死锁
    2. 死锁预防
      通过破坏死锁产生的四个必要条件中的一个或多个,以避免发生死锁。
  • 破坏互斥:
    不让资源被一个进程独占,可通过假脱机技术允许多个进程同时访问资源;
  • 破坏占有和等待:有两种方案
    1. 已拥有资源的进程不能再去请求其他资源。一种实现方法是要求进程在开始执行前请求需要的所有资源。
    2. 要求进程请求资源时,先暂时释放其当前拥有的所有资源,再尝试一次获取所需的全部资源。
  • 破坏不可抢占:
    有些资源可以通过虚拟化方式实现可抢占;
  • 破坏循环等待:有两种方案
    1. 一种方法是保证每个进程在任何时刻只能占用一个资源,如果要请求另一个资源,必须先释放第一个资源;
    2. 另一种方法是将所有资源进行统一编号,进程可以在任何时刻请求资源,但要求进程必须按照顺序请求资源。
  1. 死锁避免
    它允许三个必要条件,但通过算法判断资源请求是否可能导致循环等待的形成并相应决策,来避免死锁点的产生。因此,其前提是知道当前资源使用的整体情况,以及申请资源线程本身所占有的资源细节。
  • 线程启动拒绝:如果一个线程的请求会引发死锁,则不允许其启动。
  • 资源分配拒绝:如果一个线程增加的资源请求会导致死锁,则不允许此申请。

3. 进程调度算法

  1. 批处理系统
    保证吞吐量和周转时间(从提交到终止的时间)
  1. 先来先服务 first-come first-serverd(FCFS)
    非抢占式的调度算法,按照请求的顺序进行调度。
    有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。
  1. 短作业优先 shortest job first(SJF)
    非抢占式的调度算法,按估计运行时间最短的顺序进行调度。
    长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
  1. 最短剩余时间优先 shortest remaining time next(SRTN)
    最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
  1. 交互式系统
    快速地进行响应
  1. 时间片轮转
    将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
  1. 优先级调度
    为每个进程分配一个优先级,按优先级进行调度。
    为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
  1. 多级反馈队列
    多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,…。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。
    每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。

4. 时间片轮转算法

见第3题

5. 进程间的通信方式,进程和线程的通信

  1. 管道
  2. 消息队列
  3. 信号量
  4. 信号
  5. 共享内存
  6. 套接字SOCKET
  • 线程间的同步方式(线程锁)
  1. 信号量
    它只取自然数值,并且只支持两种操作:
    P(SV): 如果信号量SV大于0,将它减一;如果SV值为0,则挂起该线程。
    V(SV): 如果有其他进程因为等待SV而挂起,则唤醒,然后将SV+1;否则直接将SV+1。
  2. 互斥量
    互斥量又称互斥锁,主要用于线程互斥,不能保证按序访问,可以和条件锁一起实现同步。当进入临界区 时,需要获得互斥锁并且加锁;当离开临界区时,需要对互斥锁解锁,以唤醒其他等待该互斥锁的线程。
  3. 条件变量
    条件变量,又称条件锁,用于在线程之间同步共享数据的值。条件变量提供一种线程间通信机制:当某个共享数据达到某个值时,唤醒等待这个共享数据的一个/多个线程。
  • 线程间通信
  1. 使用全局变量
    主要由于多个线程可能更改全局变量,因此全局变量最好声明为volatile
  2. 使用消息实现通信
    在Windows程序设计中,每一个线程都可以拥有自己的消息队列(UI线程默认自带消息队列和消息循环,工作线程需要手动实现消息循环),因此可以采用消息进行线程间通信sendMessage,postMessage。
  3. 使用事件CEvent类实现线程间通信
    Event对象有两种状态:有信号和无信号,线程可以监视处于有信号状态的事件,以便在适当的时候执行对事件的操作。

6. 弱引用和虚引用的区别

【在Java相关中】

7. 堆和方法区的区别

堆和栈的区别

  • 大小限制:
    栈底的地址和栈的最大容量是系统预先规定好的(2M/1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间, stack overflow。因此,能从栈获得的空间较小。堆是用链表来存储的不连续内存区域,大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
  • 申请效率:
    栈由系统自动分配,速度较快。但程序员是无法控制的。堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
  • 存储内容:
    栈存储返回地址,参数,局部变量。堆在这块内存空间中的首地址处记录本次分配的大小,具体内容由程序员安排。
  • 数据访问:
    存储在堆中的对象是全局可以被访问的,然而栈内存不能被其他线程所访问,且遵循LIFO原则。
  • 生命周期:
    栈内存的生命周期很短,而堆内存的生命周期从程序的运行开始到运行结束。

8. 什么时候会发生内存溢出

【todo】

9. linux如何查看cpu占用率

  1. top
    显示结果由两部分组成: 最上面是系统概要,下面是进程列表,以 CPU 的使用率排序。
    【todo】top

10. io多路复用了解吗

【todo放link】见其他文章

11. 深拷贝、浅拷贝

【见java相关】

12. 锁的粒度,什么时候使用

  • 锁的粒度越大,则并发性越低且开销大;锁的粒度越小,则并发性高且开销小。
    【todo】

13. 共享锁、排它锁

【todo】

14. Linux命令,查看打印日志;查看某端口被占用的进程;查看某文件夹下包含某个字符的文件有哪些;查看磁盘;查看CPU使用情况

【todo】

15. linux软连接

【todo】

16. linux修改个人权限

【todo】

17. 管道

【todo】

18. 协程

【todo】

19. linux修改文件名命令,杀死一个名为a的进程

【todo】

20. 内存泄露和内存溢出的区别

  • 内存溢出和内存泄漏
  1. 内存溢出
    指程序申请内存时,没有足够的内存供申请者使用。内存溢出就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误
  • 原因:
    内存中加载的数据量过于庞大,如一次从数据库取出过多数据 集合类中有对对象的引用,使用完后未清空,使得不能回收 代码中存在死循环或循环产生过多重复的对象实体 使用的第三方软件中的BUG 启动参数内存值设定的过小
  1. 内存泄漏
    内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
  • 分类:
  1. 堆内存泄漏 (Heap leak)。
    对内存指的是程序运行中根据需要分配通过malloc,realloc new等从堆中分配的一块内存,再是完成后必须通过调用对应的 free或者delete删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak。
  1. 系统资源泄露(Resource Leak)。
    主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。
  1. 没有将基类的析构函数定义为虚函数。
    当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露

21. 进程的死锁

【todo】

22. 生产者消费者模型怎么解决生产速度过快的问题

【todo】

23. 为什么线程比进程要快

  • 进程切换涉及虚拟地址空间的切换而线程不会。因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。
  • 进程都有自己的虚拟地址空间,把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用Cache来缓存常用的地址映射,这样可以加速页表查找,这个cache就是TLB(translation Lookaside Buffer,我们不需要关心这个名字只需要知道TLB本质上就是一个cache,是用来加速页表查找的)。由于每个进程都有自己的虚拟地址空间,那么显然每个进程都有自己的页表,那么当进程切换后页表也要进行切换,页表切换后TLB就失效了,cache失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢,而线程切换则不会导致TLB失效,因为线程线程无需切换地址空间。

24. 操作系统的多进程和多线程

【见第29题】

25. 进程、线程相关(切换,内存空间、调度,创建销毁)

进程状态转换

  1. 5个基本状态
    创建状态:进程正在被创建。
    就绪状态:进程被加入到就绪队列中等待CPU调度运行。
    执行状态:进程正在被运行。
    等待阻塞状态:进程因为某种原因,比如等待I/O,等待设备,而暂时不能运行。
    终止状态:进程运行完毕。
  2. 交换技术
    当多个进程竞争内存资源时,会造成内存资源紧张,并且,如果此时没有就绪进程,处理机会空闲,I/0速度比处理机速度慢得多,可能出现全部进程阻塞等待I/O。
    • 交换技术:换出一部分进程到外存,腾出内存空间。

在交换技术上,将内存暂时不能运行的进程,或者暂时不用的数据和程序,换出到外存,来腾出足够的内存空间,把已经具备运行条件的进程,或进程所需的数据和程序换入到内存。从而出现了进程的挂起状态:进程被交换到外存,进程状态就成为了挂起状态。

  • 虚拟存储技术:每个进程只能装入一部分程序和数据。
  • 进程的内存空间
  1. 进程的内存模型
    进程的内存模型

26. 在一个文件中查找某个字符在哪一行

【todo】

27. 僵尸进程是什么

  • 进程关系
    进程通常不是凭空独立的出现的,在类Unix系统中,所有的其他进程都是从 进程0 fork 出来的,每个进程都会拥有多个子进程。
  • fork()
  1. fork给父进程返回子进程pid,给其拷贝出来的子进程返回0,
  2. 这也是他的特点之一,一次调用,两次返回。
  3. 实质是在子进程的栈中构造好数据后,子进程从栈中获取到的返回值。

由于现代操作系统的写时复制机制,即使我们知道每个进程都拥有自己独立的地址空间,其实子进程指向的物理内存是和父进程相同的(代码段,数据段,堆栈都指向父亲的物理空间),只有子进程修改了其中的某个值时(通常会先调度运行子进程),才会给子进程分配新的物理内存,并根据情况把新的值或原来的值复制给子进程的内存。

  • 孤儿进程
  1. 一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。
  2. 孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。
  3. 由于孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。
  • 僵尸进程
  1. 一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。
  2. 僵尸进程通过 ps 命令显示出来的状态为 Z(zombie)。
  3. 系统所能使用的进程号是有限的,如果产生大量僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
  4. 要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 进程所收养,这样 init 进程就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。

28. 并发安全

【todo】

29. 多线程和多进程的区别

  • 多进程:进程是程序在计算机上的一次执行活动,即正在运行中的应用程序,通常称为进程。当你运行一个程序,你就启动了一个进程。每个进程都有自己独立的地址空间(内存空间),每当用户启动一个进程时,操作系统就会为该进程分配一个独立的内存空间,让应用程序在这个独立的内存空间中运行。
    在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多进程,也称多任务。现代的操作系统几乎都是多任务操作系统,能够同时管理多个进程的运行。
    多任务带来的好处是明显的,比如你可以边听mp3边上网,与此同时甚至可以将下载的文档打印出来,而这些任务之间丝毫不会相互干扰。
  • 多线程:线程是一个轻量级的子进程,是最小的处理单元;是一个单独的执行路径。可以说:线程是进程的子集(部分),一个进程可能由多个线程组成。
    线程是独立的。如果在一个线程中发生异常,则不会影响其他线程。它使用共享内存区域。
    多线程是一种执行模型,它允许多个线程存在于进程的上下文中,以便它们独立执行但共享其进程资源。
  • 区别:
    区别

【todo】

30. 乐观锁悲观锁

  • 乐观锁:乐观锁在操作数据时非常乐观,认为别人不会同时修改数据。因此乐观锁不会上锁,只是在执行更新的时候判断一下在此期间别人是否修改了数据:如果别人修改了数据则放弃操作,否则执行操作。
  1. CAScas机制
    缺点:cas缺点
  2. 版本号:
    除了CAS,版本号机制也可以用来实现乐观锁。版本号机制的基本思路是在数据中增加一个字段version,表示该数据的版本号,每当数据被修改,版本号加1。当某个线程查询数据时,将该数据的版本号一起查出来;当该线程更新数据时,判断当前版本号与之前读取的版本号是否一致,如果一致才进行操作
  • 悲观锁:悲观锁在操作数据时比较悲观,认为别人会同时修改数据。因此操作数据时直接把数据锁住,直到操作完成后才会释放锁;上锁期间其他人不能修改数据。
  • 适用场景:适用场景

31. 操作系统的内存分配方式,虚拟内存

虚拟内存

  • 基本内存管理(分段)
    在虚拟地址空间和物理地址空间之间做一一映射,这里映射的是一片连续的物理内存
  1. 虚拟内存解决了运行时内存地址不确定的问题
  2. 分段解决了内存空间的隔离,因为程序操作的是虚拟内存,而分段使得对应的物理内存不会重叠
  3. 因为分段映射的是一段连续的物理内存,所以没有解决内存利用率低的问题
  • 实现分段的策略
  1. 固定分区
    将内存分为几个固定的区域,每个区域的大小固定(通常不同),需要加载程序是选择一个闲置且容量足够大的分区进行加载
  2. 非固定分区
    非固定分区的思想在于除了划分给OS的空间之外,其余的内存空间是作为一个整体存在的。当一个程序需要占用内存空间时,就在该片空间里面分出一个大小刚刚满足程序所需的空间。再来一个程序时,则在剩下的空间里再这样分出一块来。在这种模式下,一个程序可以加载到任何地方,也可以和物理内存一样大。
    • 程序运行时的内存空间会增长,需要预留出增长空间
    • 预留太多会造成浪费,太小可能造成程序无法运行或者发生分区交换

交换分区swap
因为系统分配给程序的空间分段映射的是一段连续的物理内存,所以空间不够时可以将程序倒到外置存储中,再寻找足够的内存空间加载

  • 分页
    因为分段技术映射到物理内存上是一段连续的空间,所以无可避免的会造成内存利用率不高的问题, 为了增加内存的利用率,需要使用分页技术
  1. 概念:
    虚拟地址空间被分割成多个大小相同的页面(比如4k为一个页面),物理地址空间被分割成同样大小的页框。虚拟地址的页面通过一个页表映射物理内存的页框,页表中保存着两者的对应关系。

内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。操作系统为每个进程都维护一个页表。

  1. 优点
    • 分页的大小比较小,不会产生外部碎片
    • 一个进程占用的内存空间可以不是连续的,并且一个进程的虚拟页面在不需要的时候可以放在磁盘中
  2. 缺点
    • 页表大小很大
    • 一个进程只能占有一个虚拟地址空间。在此种限制下,一个程序的大小至多只能和虚拟空间一样大,其所有内容都必须从这个共同的虚拟空间内分配。
  • 逻辑分段
    逻辑分段将一个程序按逻辑关系分解为多个段, 每个逻辑单元可单独占用一个虚拟地址空间,这样使得编写程序的空间大为增长。
  • 段页式
    由程序员将程序分为多个逻辑段,如代码段、数据段,系统在每个段里面又进行分页, 采用多级页表,顶级为段表,次级为页表。由段号在段表里面获得所应该使用的页表,然后在该页表里面查找物理页面号
    image.png
  • 页面置换
    在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。
    页面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。
  1. 最近最久未使用
    在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。
    因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。
  2. 最近未使用
    每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类:
    • R=0,M=0
    • R=0,M=1
    • R=1,M=0
    • R=1,M=1
      当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。
      NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。
  3. 先进先出
    选择换出的页面是最先进入的页面。
    该算法会将那些经常被访问的页面换出,导致缺页率升高。
  4. 第二次机会
    当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。

32. 内核空间、用户空间,为什么要区分

在 CPU 的所有指令中,有些指令是非常危险的,如果错用,将导致系统崩溃,比如清内存、设置时钟等。如果允许所有的程序都可以使用这些指令,那么系统崩溃的概率将大大增加。
所以,CPU 将指令分为特权指令和非特权指令,对于那些危险的指令,只允许操作系统及其相关模块使用,普通应用程序只能使用那些不会造成灾难的指令。

33. 线程安全概念、方法

  • 概念:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。(《Java并发编程实践》中对线程安全的定义)
  • 不安全原因:在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如,同一内存区(变量,数组,或对象)、系统(数据库,web services等)或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。
  • 实现:实现线程安全

34. 进程的地址空间包含哪些内容

【todo】

35. 什么是系统调用?库函数和系统调用的区别?

【todo】

36.

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值