【面试题】操作系统面试题(第六篇)

1.线程的回收方式

join和detach较为常见且join和detach只适用于多线程编程。

其他方式还有线程取消(cancle)和线程池以及线程退出标志

1.join

  • 当一个线程通过join方式被回收时,主线程会等待该子线程执行完毕,然后才能继续执行。

  • 调用join方法会将当前线程阻塞,直到被调用的线程结束或超时。

  • join方法通常用于等待子线程完成并获取其运行结果,或确保所有子线程执行完毕后再进行下一步操作。

2.detach

  • 当一个线程通过detach方式被回收时,该线程将在后台运行,且与主线程分离,主线程不会等待它的结束。

  • 被detach的线程将变成守护线程(daemon thread),在主线程退出时会自动被系统回收。

  • detach方法通常用于不需要等待子线程的执行结果,且主线程不关心子线程的完整执行过程。

2.join和detach区别

  1. 执行顺序:

    • join方式:在主线程中调用子线程的join方法后,主线程会等待子线程执行完毕,然后才能继续执行。

    • detach方式:在主线程中调用子线程的detach方法后,子线程将与主线程分离,主线程不会等待子线程执行完毕。

  2. 线程状态:

    • join方式:被加入(join)的线程仍然处于可等待状态,主线程会等待该线程的结束以进行后续操作。

    • detach方式:被分离(detach)的线程将变成守护线程(daemon thread),主线程不会等待它的结束。

  3. 资源回收:

    • join方式:在join方法返回之前,线程的资源不会被释放,主线程需要等待子线程执行完毕后才能进行资源回收。

    • detach方式:被分离的线程将在后台运行,主线程不需要等待其结束,并且子线程的资源会在其结束时自动被系统回收。

  4. 错误处理:

    • join方式:由于主线程等待子线程执行完毕,可以通过检查子线程的返回值或捕获异常来处理子线程中的错误。

    • detach方式:对于分离的线程,主线程无法知道子线程的执行情况,也无法捕获其错误。

总的来说,join方式适合需要等待子线程完成并获取结果的情况,而detach方式则适用于主线程不关心子线程执行结果,且希望子线程在后台独立运行的情况。

3.线程通信方式

  1. 共享内存:多个线程共享同一块内存区域,通过读写该内存区域来进行通信。

    int shmget(key_t key, size_t size, int shmflg); key: 共享内存的标识符,用来标识一块共享内存,在操作系统中,共享内存的标识是不能重复的。可直接给一个32位的16进制的数字 size: 共享内存大小 shmflg:   IPC_CREAT:如果key标识的共享内存不存在,则创建   IPC_EXCL | IPC_CREAT:如果key标识的共享内存存在,则报错 权限: 按位或8进制数字 eg:0664(这个是共享内存自己的权限,自己的读写属性) 返回值: -1:失败 0:成功,返回的是共享内存的操作句柄,后续是通过操作句柄来进行操作共享内存的 查看共享内存的命令:ipcs -m 共享内存的生命周期跟随操作系统内核

  2. 信号量(Semaphore):通过一个计数器来控制多个线程的访问权限,当计数器为0时,线程需要等待;当计数器大于0时,线程可以继续执行。

  3. 互斥量(Mutex):用于保护共享资源,只允许一个线程访问该资源,其他线程需要等待。

  4. 条件变量(Condition Variable):用于线程之间的等待和唤醒操作,一个线程可以等待某个条件满足后再继续执行。

  5. 管道(Pipe):一种单向通信机制,用于在两个线程之间传输数据。命名管道,匿名管道。

4.进程间通信方式有哪些?

进程间通信(IPC,Inter-process Communication)指的是在不同进程之间交换信息的机制和方法。常见的进程间通信方式有以下几种:

  • 管道(Pipe):管道是一种半双工的通信方式,只能在具有亲缘关系的进程之间使用。管道可以实现进程之间的通信,但只能在父子进程或兄弟进程之间使用,因为管道是单向的,而且只能在具有公共祖先的进程之间使用。

  • 命名管道(Named Pipe):命名管道是一种有名的通信方式,可以实现无关进程之间的通信。它可以在不具有亲缘关系的进程之间传递数据,并且可以实现双向通信。

  • 信号量(Semaphore):信号量是一种计数器,允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。

  • 信号(Signal):信号是一种软件中断,用于通知进程发生了某个事件。它可以用于进程间的通信和同步。

  • 共享内存(Shared Memory):共享内存是一种高效的进程间通信方式,它可以在多个进程之间共享内存区域,从而实现数据的快速交换。

  • 消息队列(Message Queue):消息队列是一种进程间通信方式,可以实现不同进程之间的异步通信。

  • 套接字(Socket):套接字是一种网络通信方式,可以实现不同主机之间的进程间通信。

5.为什么匿名管道用来有亲缘关系的进程间通信?

父进程通过fork创建出子进程,二者的文件描述符表是相同的,文件描述符表中保存了pipe管道字段,因为有了相同的文件描述符表,所以二者可以通过该管道进行通信。而没有亲缘关系的进程文件描述符表不同,所以无法使用匿名管道通信。

6.匿名管道和命名管道的区别?

对于匿名管道,它的通信范围是存在父子关系的进程。因为管道没有实体,也就是没有管道文件,只能通过 fork 来复制父进程 fd 文件描述符,来达到通信的目的。

对于命名管道,它可以在不相关的进程间也能相互通信。因为命令管道,提前创建了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。

匿名管道:

a.读端关闭->如果写端继续写,则会收到系统发送的信号,写端被关闭。

b.读端存在->如果始终未读取数据,写端可以继续写入数据,写满后,写阻塞

c.写端关闭->如果读端读取剩余数据,可以。否则返回0。

d.写端存在->始终未写入数据,读阻塞。

命名管道:

a. 使用时,如果多个读端,则只有第一个读端阻塞读取,其他非阻塞读取。

b. 阻塞读取时,如果写入数据n > 管道缓存区 -> 写入的n字节是非原子的,直到n字节全部写完,write才返回。如果写入数据n <= 管道缓存区 -> 写入的n字节是原子的,如果没有n字节空间,则写阻塞

c. 非阻塞读取时,如果写入数据n > 管道缓存区 -> 如果管道满,返回-1,并设置EAGAIN,否则会随机写入1-n字节数据,具体写多少需要用户查看write返回值。

如果写入数据n <= 管道缓存区 -> 如果空间足够,则可以写入,否则返回-1

7.什么是信号

一般用来处理进程间通信的异常情况。 可以通过kill -l命令查看所有信号 信号的触发方式有两种:一种是键盘触发,另一种是命令发送。 比如CTRL+C是产生SIGINT信号,终止该进程,CTRL +Z是表示让该进程在后台运行。 通过kill -9 xxx 的形式可以给进程ID为xxx的进程发送终止信号,终止该进程。

8.让信号失效的方式

屏蔽  忽略 捕捉

9.操作系统中有哪些锁?

自旋锁(Spin Lock): 自旋锁是一种非阻塞锁,当一个线程尝试获取锁时,如果锁已被其他线程占用,它会一直自旋在一个忙等待的循环中,直到锁可用。自旋锁的底层实现通常使用原子操作,如测试和设置(Test-and-Set)或交换(Swap)指令,确保只有一个线程能够成功获取锁。

互斥锁(Mutex Lock): 互斥锁是一种阻塞锁,当一个线程尝试获取锁时,如果锁已被其他线程占用,它会被阻塞,等待锁可用。互斥锁的底层实现通常使用原子操作和等待队列,确保只有一个线程能够持有锁,其他线程必须等待。

信号量(Semaphore): 信号量是一种通用的同步机制,可以用于实现互斥和信号通知。信号量的底层实现包括一个计数器和一个等待队列。线程可以尝试获取信号量的许可,如果许可不足,则线程将被阻塞,直到有足够的许可可用。信号量的操作包括增加和减少计数器的值。

读写锁(Read-Write Lock): 读写锁允许多个线程同时读取共享数据,但只允许一个线程写入共享数据。读写锁的底层实现通常使用自旋锁、互斥锁和计数器等机制,以确保读操作之间不互斥,但写操作与读操作和其他写操作互斥。

条件变量(Condition Variable): 条件变量用于线程之间的条件同步,允许线程等待某个条件的发生。条件变量的底层实现通常包括一个等待队列和相关的互斥锁,线程可以等待条件的满足,并在条件满足时被唤醒。

10.进程调度算法

1.先来先服务(FCFS)

非抢占式的调度算法,先来后到,每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。

但是当一个长作业先运行了,那么后面的短作业等待的时间就会很长,不利于短作业。

FCFS 对长作业有利,适用于 CPU 繁忙型作业的系统,而不适用于 I/O 繁忙型作业的系统。

2.最短作业优先(Shortest Job First,SJF)

非抢占式,优先选择运行时间最短的进程来运行,这有助于提高系统的吞吐量。

这显然对长作业不利,很容易造成一种极端现象。

比如,一个长作业在就绪队列等待运行,而这个就绪队列有非常多的短作业,那么就会使得长作业不断的往后推,周转时间变长,致使长作业长期不会被运行。

3.高响应比优先(Highest Response Ratio Next, HRRN)

主要是权衡了短作业和长作业。

每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行

计算过程如下:

  • 如果两个进程的「等待时间」相同时,「要求的服务时间」越短,「响应比」就越高,这样短作业的进程容易被选中运行;

  • 如果两个进程「要求的服务时间」相同时,「等待时间」越长,「响应比」就越高,这就兼顾到了长作业进程,因为进程的响应比可以随时间等待的增加而提高,当其等待时间足够长时,其响应比便可以升到很高,从而获得运行的机会;

4.时间片轮转(Round Robin,RR)

每个进程被分配一个时间段,称为时间片(*Quantum*),即允许该进程在该时间段中运行。

  • 如果时间片用完,进程还在运行,那么将会把此进程从 CPU 释放出来,并把 CPU 分配另外一个进程;

  • 如果该进程在时间片结束前阻塞或结束,则 CPU 立即进行切换;

11.页面置换算法

当出现缺页异常,需调入新页面而内存已满时,选择被置换的物理页面,也就是说选择一个物理页面换出到磁盘,然后把需要访问的页面换入到物理页。

1.先进先出(FIFO)

选择在内存驻留时间很长的页面进行中置换,这个就是「先进先出置换」算法的思想。

跟最佳页面置换算法比较起来,性能明显差了很多。

2.最佳页面置换(OPT)

最佳页面置换算法基本思路是,置换在「未来」最长时间不访问的页面

所以,该算法实现需要计算内存中每个逻辑页面的「下一次」访问时间,然后比较,选择未来最长时间不访问的页面。

但是实际系统中无法实现,因为程序访问页面时是动态的,我们是无法预知每个页面在「下一次」访问前的等待时间。

3.最近最久未使用(LRU)

发生缺页时,选择最长时间没有被访问的页面进行置换

虽然 LRU 在理论上是可以实现的,但代价很高。为了完全实现 LRU,需要在内存中维护一个所有页面的链表,最近最多使用的页面在表头,最近最少使用的页面在表尾。

困难的是,在每次访问内存时都必须要更新「整个链表」。在链表中找到一个页面,删除它,然后把它移动到表头是一个非常费时的操作。

所以,LRU 虽然看上去不错,但是由于开销比较大,实际应用中比较少使用。

4.时钟页面置换

它跟 LRU 近似,又是对 FIFO 的一种改进。

该算法的思路是,把所有的页面都保存在一个类似钟面的「环形链表」中,一个表针指向最老的页面。

当发生缺页中断时,算法首先检查表针指向的页面:

  • 如果它的访问位位是 0 就淘汰该页面,并把新的页面插入这个位置,然后把表针前移一个位置;

  • 如果访问位是 1 就清除访问位,并把表针前移一个位置,重复这个过程直到找到了一个访问位为 0 的页面为止;

5.最不常用算法(LFU)

当发生缺页中断时,选择「访问次数」最少的那个页面,并将其淘汰

它的实现方式是,对每个页面设置一个「访问计数器」,每当一个页面被访问时,该页面的访问计数器就累加 1。在发生缺页中断时,淘汰计数器值最小的那个页面。

12.磁盘调度算法

1.先来先服务(FIFO)

根据磁道顺序进行处理。

如果大量进程竞争使用磁盘,请求访问的磁道可能会很分散,那先来先服务算法在性能上就会显得很差,因为寻道时间过长。

2.最短寻到优先(SSF)

优先选择从当前磁头位置所需寻道时间最短的请求。

最短寻道时间优先算法会产生饥饿的原因在于:磁头有可能再一个小区域内来回得移动

3.扫描算法

磁头在一个方向上移动,访问所有未完成的请求,直到磁头到达该方向上的最后的磁道,才调换方向,这就是扫描(Scan)算法

这种算法也叫做电梯算法,比如电梯保持按一个方向移动,直到在那个方向上没有请求为止,然后改变方向。

但是存在这样的问题,中间部分的磁道会比较占便宜,中间部分相比其他部分响应的频率会比较多,也就是说每个磁道的响应频率存在差异。

4.循环扫描算法

只有磁头朝某个特定方向移动时,才处理磁道访问请求,而返回时直接快速移动至最靠边缘的磁道,也就是复位磁头,这个过程是很快的,并且返回中途不处理任何请求,该算法的特点,就是磁道只响应一个方向上的请求

5.LOOK与C-LOOK算法

针对扫描算法和循环扫描的优化,磁头在移动到「最远的请求」位置,然后立即反向移动

不同的是,LOOK在返回途中进行响应;而C-LOOK返回途中不进行响应。

13.动态分区分配算法

动态分区分配算法是一种用于管理操作系统中内存分区的算法。在动态分区分配算法中,内存空间被划分为多个不同大小的分区,每个分区可以被分配给进程使用。

  1. 首次适应算法(First-Fit):根据进程的内存需求,从空闲分区链表中找到第一个足够大的分区进行分配,分配后的剩余空间形成新的空闲分区。

  2. 最佳适应算法(Best-Fit):根据进程的内存需求,从空闲分区链表中找到最小的足够大的分区进行分配,以便最小化剩余空闲分区的大小。

  3. 最坏适应算法(Worst-Fit):根据进程的内存需求,从空闲分区链表中找到最大的足够大的分区进行分配,以便最大化剩余空闲分区的大小。

  4. 循环首次适应算法(Next-Fit):类似于首次适应算法,但从上次分配的位置开始搜索空闲分区,以减少搜索的开销。

  • 41
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的小猴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值