首页 > C++ 操作系统 常考面试题总结
头像
英杰速演
发布于 昨天 10:53 河北
+ 关注

C++ 操作系统 常考面试题总结

1. 进程通信中的管道实现原理

管道是半双工的进程间通信方式,核心基于内核缓冲区实现:

  1. 系统在内核中开辟一块临时的缓冲区,作为管道的载体;
  2. 管道以文件描述符的形式暴露给进程,进程通过读/写文件描述符,完成对内核缓冲区的读写操作;
  3. 数据按先进先出原则传输,写进程向缓冲区写入数据,读进程从缓冲区读取数据,内核自动维护读写位置;
  4. 分为匿名管道和命名管道:匿名管道基于pipe系统调用创建,仅能用于父子进程间通信;命名管道基于mkfifo创建,以文件形式存在于文件系统,可用于无亲缘关系的进程间通信。

2. 简述mmap的原理和使用场景

原理

mmap是内存映射文件机制,核心是将磁盘文件的部分或全部,直接映射到进程的虚拟地址空间:

  1. 进程调用mmap时,内核在进程虚拟地址空间中分配一段连续的虚拟内存区域;
  2. 内核建立该虚拟区域与磁盘文件的映射关系,但不立即加载文件数据到物理内存;
  3. 进程首次访问该虚拟内存时,触发缺页中断,内核才将对应文件数据加载到物理内存,并更新页表;
  4. 进程对映射区域的读写,会被内核同步到磁盘文件(或通过msync主动同步),实现内存与文件的双向绑定。

使用场景

  • 大文件高效读写:避免频繁read/write的用户态与内核态切换,减少数据拷贝;
  • 进程间通信:多个进程映射同一文件,实现共享内存式通信;
  • 高性能网络IO:结合sendfile实现零拷贝,提升数据传输效率。

3. 互斥量能不能在进程中使用?

可以,但需满足共享内存条件,分为两种情况:

  1. 线程级互斥量:默认基于进程内的局部内存创建,仅能用于同一进程内的线程同步,无法跨进程使用;
  2. 进程级互斥量:通过将互斥量创建在共享内存区(如mmap映射区、shmget共享内存),并设置进程共享属性(如POSIX互斥量的PTHREAD_PROCESS_SHARED属性),即可实现跨进程同步。

4. 协程是轻量级线程,轻量级表现在哪里?

协程的“轻量级”核心体现在调度和资源开销上,与操作系统线程对比差异显著:

  1. 调度开销:协程由用户态调度器管理,而非操作系统内核,切换时无需陷入内核、无需保存/恢复CPU寄存器、无需更新页表,切换耗时仅为线程的1/100甚至更低;
  2. 内存开销:线程默认栈大小为几MB,而协程栈为几KB到几十KB(可动态扩容),单个进程可创建数万个协程,远多于线程的创建上限;
  3. 资源依赖:协程基于线程实现(N:M模型),多个协程复用一个线程的内核资源,减少内核态资源的占用。

5. 常见信号有哪些,表示什么含义?

信号是操作系统向进程发送的异步通知,核心常见信号及含义如下:

  1. SIGINT (2):中断信号,由Ctrl+C触发,通知进程终止运行;
  2. SIGTERM (15):终止信号,kill命令默认发送,允许进程捕获并执行清理逻辑后退出;
  3. SIGKILL (9):强制终止信号,无法被捕获、阻塞或忽略,用于强制杀死进程;
  4. SIGSEGV (11):段错误信号,进程访问非法内存地址时触发;
  5. SIGPIPE (13):管道破裂信号,向读端已关闭的管道写入数据时触发;
  6. SIGCHLD (17):子进程状态变化信号,子进程退出或暂停时,内核向父进程发送该信号。

6. 线程同步方式有哪些?

线程同步的核心是解决临界区资源竞争问题,常见方式分为基础机制和高级机制:

基础同步机制

  1. 互斥锁:保证同一时间只有一个线程能进入临界区,适用于排他性资源访问;
  2. 读写锁:区分读操作和写操作,读共享、写排他,适合读多写少的场景;
  3. 信号量:分为二进制信号量(等价于互斥锁)和计数信号量,可实现多线程的资源计数与同步;
  4. 条件变量:配合互斥锁使用,实现线程间的“等待-唤醒”机制,解决线程间的依赖问题。

高级同步机制

  1. 屏障:等待所有线程到达指定点后,再继续执行,适用于批量任务协同;
  2. 自旋锁:线程获取锁失败时不阻塞,而是循环尝试,适合临界区执行时间极短的场景。

7. 什么是死锁,产生的条件,如何解决?

死锁定义

多个进程/线程因竞争资源,互相持有对方所需的资源,且均不释放已持资源,导致所有进程/线程陷入永久阻塞的状态。

产生的四个必要条件(缺一不可)

  1. 互斥条件:资源为排他性资源,同一时间仅能被一个进程/线程占用;
  2. 请求与保持条件:进程/线程持有部分资源时,又请求其他进程/线程持有的资源;
  3. 不可剥夺条件:进程/线程已持有的资源,无法被其他进程/线程强制剥夺,只能主动释放;
  4. 循环等待条件:多个进程/线程形成资源请求的闭环,即A等B的资源,B等C的资源,C等A的资源。

解决方法

  1. 预防死锁:破坏死锁的任一必要条件(如按序申请资源,破坏循环等待;资源预分配,破坏请求与保持);
  2. 避免死锁:通过银行家算法等,动态判断资源分配是否会导致死锁,仅在安全时分配资源;
  3. 检测与解除:定时检测系统是否存在死锁,若存在则通过剥夺资源、杀死进程等方式解除。

8. 有了进程,为什么还要有线程?

进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位,引入线程的核心目的是提升并发效率、降低开销:

  1. 创建与切换开销:进程创建时需分配独立的地址空间、页表、文件描述符等资源,切换时需刷新MMU、切换地址空间,开销极大;线程共享进程的地址空间和资源,创建仅需分配栈和寄存器,切换仅需保存线程上下文,开销远低于进程;
  2. 通信效率:进程间通信需借助内核(管道、共享内存等),存在用户态与内核态切换;线程共享进程的堆内存和全局变量,可直接通信,效率极高;
  3. 并发粒度:进程的并发粒度较粗,一个进程阻塞(如IO等待)会导致整个进程无法执行;线程阻塞时,同一进程的其他线程可继续占用CPU执行,提升了CPU利用率。

9. 单核机器上写多线程程序,是否要考虑加锁,为什么?

需要加锁,核心原因与CPU调度的时间片轮转机制和指令重排序有关:

  1. 时间片轮转:单核CPU通过时间片轮转调度线程,线程执行到临界区中间时,可能被操作系统挂起,切换到其他线程执行;若临界区资源未加锁,后续线程会读取到不完整的资源数据,导致数据竞争;
  2. 指令重排序:编译器和CPU为优化性能,会对指令进行重排序,多线程环境下,未加锁的临界区操作可能出现执行顺序错乱,破坏数据一致性;
  3. 示例:两个线程同时对全局变量i执行i++(非原子操作,分为读、加、写三步),若不加锁,可能出现两个线程同时读取i=0,最终i仅变为1,而非预期的2。

10. 多线程和多进程的不同?

11. 简述互斥锁的机制,互斥锁与读写锁的区别?

互斥锁机制

互斥锁基于原子操作和阻塞唤醒实现,核心逻辑:

  1. 互斥锁包含一个状态位(锁定/未锁定),通过原子指令(如test_and_set)实现锁的获取;
  2. 线程请求锁时,若锁为未锁定状态,原子性将其设为锁定,线程进入临界区;
  3. 若锁为锁定状态,请求线程会被阻塞,放入等待队列,直到持有锁的线程释放锁;
  4. 持有锁的线程执行完临界区代码后,释放锁并唤醒等待队列中的线程。

互斥锁与读写锁的区别

12. 什么是信号量,有什么作用?

信号量定义

信号量是一种计数器,基于PV操作(wait/signal)实现进程/线程间的同步与互斥,由操作系统保证PV操作的原子性。

核心作用

  1. 实现互斥:使用二进制信号量(值为0或1),等价于互斥锁,实现临界区的排他性访问;
  2. 实现同步:使用计数信号量(值为非负整数),通过计数控制资源的可用数量,实现线程间的依赖等待(如生产者-消费者模型中,控制缓冲区的空/满数量);
  3. 资源计数:限制同时访问某资源的进程/线程数量(如连接池的最大连接数限制)。

13. 进程、线程的中断切换过程是怎样的?

进程/线程的切换本质是上下文的保存与恢复,触发于中断(外部中断、异常中断、系统调用),核心步骤分为进程切换和线程切换,二者核心差异在于是否切换地址空间:

通用切换流程

  1. 中断触发:CPU执行指令时检测到中断,暂停当前进程/线程的执行;
  2. 保存上下文:将当前进程/线程的CPU寄存器值(程序计数器、栈指针、通用寄存器等)保存到内核栈中;
  3. 中断处理:内核执行中断服务程序,判断是否需要进行进程/线程调度;
  4. 选择下一个任务:调度器根据调度算法(如RR、CFS),从就绪队列中选择下一个要执行的进程/线程;
  5. 恢复上下文:从下一个进程/线程的内核栈中,恢复CPU寄存器值;
  6. 返回执行:CPU跳转到新进程/线程的指令地址,继续执行。

进程切换 vs 线程切换

  • 进程切换:额外增加地址空间切换步骤,内核需刷新MMU、切换页表、清空TLB,开销极大;
  • 线程切换:线程共享进程地址空间,无需切换页表和MMU,仅需保存/恢复线程专属的上下文,开销极低。

14. 简述自旋锁和互斥锁的使用场景

自旋锁核心特性

线程获取锁失败时,不阻塞、不放弃CPU,而是通过循环轮询的方式尝试获取锁,直到成功。

互斥锁核心特性

线程获取锁失败时,会被阻塞,放弃CPU,进入等待队列,直到被唤醒。

使用场景划分

  1. 自旋锁适用场景:临界区执行时间极短(微秒级),轮询的开销远小于线程阻塞唤醒的开销;多核CPU环境,避免单核下自旋导致的CPU资源浪费;内核态编程(如中断处理程序),无法进行阻塞操作的场景。
  2. 互斥锁适用场景:临界区执行时间较长(毫秒级及以上),自旋

全部评论

(1) 回帖
加载中...
话题 回帖

热门推荐