一、 进程与线程
1. 进程和程序的区别?
- 程序:是静态的,指令和数据的集合,只是存放在磁盘上的一份文件。
- 进程:是程序的一次执行实例,是动态的,包含程序计数器、寄存器和变量的当前值。
2. 进程的基本状态及状态转换图?
进程常见 5 种状态:
- 新建(New)
- 就绪(Ready)
- 运行(Running)
- 阻塞(Blocked/Waiting)
- 终止(Terminated)
转换关系:
- 新建 → 就绪(完成创建)
- 就绪 ↔ 运行(获得/失去 CPU)
- 运行 → 阻塞(等待 I/O 等资源)
- 阻塞 → 就绪(等待事件完成)
- 运行/阻塞 → 终止(执行结束或被终止)
3. 进程控制块(PCB)包含哪些信息?
包括:
- 进程标识符(PID)
- 处理机状态(寄存器、程序计数器、PSW 等)
- 进程调度信息(优先级、调度状态、队列指针等)
- 内存管理信息(页表、段表)
- 文件管理信息(打开文件表等)
4. 线程和进程的区别?优缺点?
- 粒度:进程是资源分配的最小单位;线程是 CPU 调度的最小单位。
- 内存:进程独立内存空间,线程共享进程内存(代码段、数据段),但有独立栈和寄存器。
- 开销:进程创建/切换开销大,线程更轻量。
- 通信:进程间通信(IPC)复杂,线程共享内存通信简单。
- 优缺点总结:
- 进程:稳定、安全,但切换代价高。
- 线程:高效、共享方便,但可能影响稳定性(一个线程挂掉导致整个进程崩溃)。
5. 线程的上下文切换和进程上下文切换区别?
- 进程切换:保存和恢复 PCB,切换虚拟地址空间、页表、内核栈和硬件上下文 → 开销大。
- 线程切换:只切换寄存器、栈指针和 TCB,不需要切换地址空间 → 开销小。
6. 什么是协程?与线程的区别?
- 协程:用户态的轻量级线程,可以在函数内部挂起/恢复,不涉及内核调度。
- 区别:
- 协程切换无需陷入内核,开销比线程小。
- 协程属于单线程,不需要加锁即可共享数据。
- 一个线程可包含多个协程。
7. Linux 中创建线程的方法有哪些?
pthread_create()
(POSIX 线程库,最常用)- C++11 标准库
std::thread
(封装了 pthread) - 低层接口如
clone()
系统调用(Linux 内核使用)
(fork()
用于进程创建,但线程创建主要依赖 pthread。)
8. 孤儿进程、僵尸进程的原理和处理方法?
- 孤儿进程:父进程退出,但子进程仍在运行;会被
init
进程收养并负责回收。 - 僵尸进程:子进程结束,但父进程未调用
wait()
回收,子进程 PCB 残留。 - 处理方法:
- 父进程主动
wait()/waitpid()
回收。 - 设置
SIGCHLD
信号处理函数,自动回收。
- 父进程主动
9. 守护进程的特点和实现?
- 特点:
- 长期后台运行,无终端控制。
- 生命周期长,常用于服务。
- 实现步骤:
fork()
创建子进程,父进程退出。- 子进程
setsid()
创建新会话,脱离控制终端。 - 修改工作目录(通常
/
),重设文件权限掩码。 - 关闭/重定向标准输入输出。
10. 多线程编程中的共享数据如何保证安全?
常用同步手段:
- 互斥锁 (mutex):保证同一时间只有一个线程访问临界区。
- 读写锁 (rwlock):允许多个读者同时访问,但写者独占。
- 信号量 (semaphore):用于控制资源数量。
- 条件变量:线程间事件同步。
- 原子操作:如 C++11
std::atomic
。
二、进程调度与并发
1. 抢占式和非抢占式调度的区别?
- 非抢占式:进程一旦获得 CPU,就会一直运行到结束或阻塞才会释放处理机。实现简单,但可能导致长作业独占 CPU 。
- 抢占式:系统允许强行中断正在运行的进程,把 CPU 分配给更高优先级或更紧急的进程,响应及时,但开销更大。
2. 常见的调度算法(FCFS、SJF、优先级、RR、多级反馈队列等)?
- 先来先服务 (FCFS):按到达顺序调度,公平但对短作业不友好。
- 短作业优先 (SJF):优先调度运行时间短的作业,平均等待时间最小,但需要预估运行时间。
- 优先级调度:优先级高的作业先执行,可能导致饥饿。
- 时间片轮转 (RR):每个进程分配固定时间片,公平适合分时系统。
- 多级反馈队列 (MLFQ):结合多种策略,动态调整优先级,常用于通用操作系统。
3. 实时调度算法有哪些?
常见实时调度包括:
- Rate Monotonic (RM):周期短的任务优先。
- Earliest Deadline First (EDF):截止时间最早的任务优先。
- Least Laxity First (LLF):宽限时间最小的任务优先。
4. Linux 的调度策略 CFS、RT、Deadline 有何区别?
- CFS (Completely Fair Scheduler):Linux 默认调度器,基于时间片的公平调度,适合普通任务。
- RT (Real-Time):实时调度策略,包括 FIFO 和 RR,适合需要严格响应时间的任务。
- Deadline:基于任务的截止时间,结合 EDF 与 RT 特性,常用于实时系统。
5. 进程同步方式有哪些?(信号量、互斥锁、条件变量、自旋锁、读写锁等)
- 信号量 (semaphore):计数器机制,用于互斥或控制资源数量 。
- 互斥锁 (mutex):保证同一时刻只有一个线程/进程访问临界区:基于事件的线程同步 。
- 自旋锁 (spinlock):忙等方式,适合短时间锁竞争 。
- 读写锁 (rwlock):支持多读单写,效率高【7†C 面试宝典完整版.pdf†L8-L13】。
- 共享内存
- 管道 (pipe)
- 消息队列 (message queue)
- Socket
6. 线程通信的方式?(共享内存、管道、消息队列、socket 等)
线程通信方式常用的有共享内存、消息队列、条件变量、信号量等,其中共享内存最快,但需要同步机制保证安全;消息队列和条件变量常用于生产者-消费者模型;而管道和 socket 更适合跨进程或跨机器的通信。
7. 什么是临界区,如何避免冲突?
- 临界区:访问共享资源的一段代码。
- 避免冲突的方法:加锁机制(互斥锁、信号量、读写锁)、原子操作等 。
8. 什么是死锁?死锁产生的四个必要条件?
死锁:多个进程因资源竞争而互相等待,无法推进。
- 必要条件 :
- 互斥条件:资源不能同时被多个进程使用。
- 请求保持条件:已占有资源的进程提出新请求而不释放已有资源。
- 不可剥夺条件:进程已获得的资源不可强制剥夺。
- 循环等待条件:进程间形成环路等待。
9. 死锁的预防、避免、检测与解除方法?
- 预防:破坏四个必要条件之一(如资源一次性分配,允许剥夺,规定资源顺序) 。
- 避免:采用银行家算法。
- 检测:通过资源分配图、等待图检测环路。
- 解除:撤销或挂起部分进程,释放资源后重启。
10. 银行家算法的基本原理?
- 是一种避免死锁的算法,由 Dijkstra 提出。
- 核心思想:系统在分配资源时,先预判此次分配是否会使系统进入「不安全状态」。
- 如果会进入不安全状态,则拒绝分配,等待以后再分配;否则允许。
三、内存管理
1. 连续内存分配有哪些方式?(首次适应、最佳适应、最差适应)
- 首次适应 (First Fit):从低地址开始,找到第一个足够大的空闲分区就分配。实现简单,速度快,但容易产生低地址碎片。
- 最佳适应 (Best Fit):遍历所有空闲分区,找出最小但能满足需求的空闲块。碎片相对较少,但需要更多查找开销。
- 最差适应 (Worst Fit):选择最大的空闲分区分配,保留较多的大分区。可能造成大块内存浪费。
2. 分页和分段的区别?
- 分页 (Page):将进程逻辑地址空间划分为固定大小的页(Page),物理内存划分为页框。按页为单位分配,解决外部碎片,但页内可能有少量碎片。
- 分段 (Segment):按逻辑划分(代码段、数据段、堆栈段),每段大小不固定。方便共享与保护,但存在外部碎片问题。
- 本质区别:分页面向物理,分段面向逻辑。
3. 段页式内存管理的优缺点?
- 优点:结合分页和分段优点,既能提供逻辑分段方便保护与共享,又能通过分页避免外部碎片。
- 缺点:需要同时维护段表和页表,地址转换复杂,增加存取开销。
4. 什么是虚拟内存?
- 操作系统提供的抽象,每个进程看到的是独立的、连续的地址空间。
- 好处:扩大可用地址空间、内存保护、支持共享、减少碎片。
- 缺点:需要额外的数据结构,地址转换开销,缺页换入换出耗时。
5. 页表的作用?
- 页表是虚拟地址到物理地址的映射表,每个虚拟页对应一个物理页框基地址。
- 解决了“如果每个字节都映射,表太大”的问题,通过分页减少映射表大小。
6. 快表(TLB)的作用与命中率影响?
- TLB(Translation Lookaside Buffer)是页表的高速缓存。
- 命中时,虚拟地址能直接得到物理地址,访问快;未命中时仍需查页表,效率降低。
- 命中率越高,平均访问内存的时间越低;反之频繁缺页或TLB未命中会导致性能下降。
7. 多级页表为什么比单级页表好?
- 单级页表需要连续的大表空间,存储开销大(如 4GB 内存需约 32GB 表)。
- 多级页表按需分配页表,未使用的虚拟空间不用建立映射,节省内存。
8. 页面置换算法有哪些?(FIFO、LRU、LFU、OPT 等)
- FIFO:先进先出,简单但可能淘汰常用页。
- LRU:最近最少使用,基于局部性原理,常用。
- LFU:最不经常使用,考虑访问频率。
- OPT:最佳置换,淘汰未来最长时间不会使用的页,理想但无法实现,只能作为理论对比。
9. 什么是缺页中断?缺页中断的处理流程?
- 缺页中断:访问的页不在内存,CPU 触发异常。
- 流程:保护现场 → 查页表确认缺页 → 操作系统调入所需页面 → 更新页表 → 恢复执行。
- 特点:在指令执行期间发生,同一条指令可能触发多次。
10. 内存抖动(抖动现象)是什么?如何避免?
- 抖动:系统大部分时间都在进行页面置换,进程实际执行效率极低。
- 避免方法:增加物理内存、优化程序局部性、改进置换算法、适度限制多道程序并发数。
11. malloc 和 free 的实现原理?
- malloc:当分配空间小于 128KB,调用
brk()
;大于则调用mmap()
。通过内存池管理,减少碎片。 - free:将释放的内存块重新挂到空闲链表,可能触发合并。
12. 堆和栈的区别?内存分配策略?
- 堆:动态分配,手动申请释放,分配不连续,可能产生碎片。
- 栈:函数调用产生的局部变量,系统自动分配释放,连续内存,后进先出。
- 分配策略:堆向高地址增长,栈向低地址增长。
13. 堆溢出和栈溢出会带来什么问题?
- 堆溢出:不断分配不释放,导致 OutOfMemory。
- 栈溢出:深递归/死循环,导致 StackOverflow。
- 后果:程序崩溃,甚至被攻击者利用执行恶意代码。
14. Linux 内存分配函数(brk/sbrk/mmap)的区别?
- brk/sbrk:调整进程数据段(堆)的边界,适合小块内存分配。
- mmap:映射文件或匿名映射到内存,适合大块内存,支持共享和文件映射
15. 内核空间和用户空间的区别?为什么要区分?
- 用户空间:存放代码段、数据段、堆、栈等,用户程序运行。
- 内核空间:存放内核代码、驱动、内存管理数据结构。
- 区分原因:安全性(防止用户程序破坏系统)、稳定性(内核崩溃影响全局)、隔离资源管理。
四、文件系统与存储
1. 文件系统中 inode 和 dentry 的作用?
- inode:记录文件的元信息(大小、权限、时间戳、数据块指针等),不包含文件名。
- dentry(目录项):保存文件名与 inode 的映射关系,方便路径解析和缓存,加速文件查找。
2. 硬链接和软链接的区别?
- 硬链接:多个文件名指向同一个 inode,删除任意一个不会影响文件本身,不能跨文件系统,也不能对目录创建。
- 软链接:类似快捷方式,保存目标文件路径,可跨文件系统、可对目录创建。若原文件被删除,软链接失效(悬空链接)。
3. 文件描述符是什么?
- 内核为每个进程维护一个文件描述符表,文件描述符(FD)是这个表的索引,用于标识进程已打开的文件或 I/O 资源。标准输入/输出/错误对应 0、1、2。
4. 文件打开过程发生了什么?
- 查找文件路径,逐级通过 dentry 与 inode 定位文件。
- 检查权限(读/写/执行)。
- 创建并返回文件描述符(指向内核中文件对象)。
- 内核维护引用计数,确保文件共享与并发安全。
5. Linux 的缓冲区高速缓存(Page Cache)机制?
- Page Cache 缓存了磁盘文件的页数据,读操作优先访问缓存,减少磁盘 I/O;写操作先写缓存并标记脏页,再异步写回磁盘。
- 好处:提升读写性能,减少磁盘访问。缺点:崩溃可能丢失未刷新的数据。
6. mmap 的原理及应用场景?
- 原理:将文件映射到进程虚拟地址空间,进程可像访问内存一样读写文件,内核负责回写脏页。
- 应用场景:大文件读写、共享内存(进程间通信)、零拷贝优化。
7. 零拷贝技术(sendfile、mmap+write、splice)的原理?
- 传统 I/O:数据需在内核缓冲区和用户缓冲区之间多次拷贝。
- 零拷贝:减少或避免用户态与内核态之间的数据复制。
- sendfile:直接在内核空间完成文件到 socket 的传输。
- mmap+write:通过内存映射减少一次拷贝。
- splice:内核态直接在文件描述符之间移动数据,完全避免用户态拷贝。
8. 磁盘调度算法有哪些?(FCFS、SSTF、SCAN、C-SCAN、LOOK)
- FCFS:先来先服务,简单公平,但寻道效率低。
- SSTF:最短寻道时间优先,优先处理距离磁头最近的请求,可能导致饥饿。
- SCAN(电梯算法):磁头往一个方向移动,依次服务请求,到端点后再反向。
- C-SCAN:只在一个方向扫描,回到起点后再扫描,响应更均衡。
- LOOK/C-LOOK:与 SCAN/C-SCAN 类似,但只到最远请求位置再折返,避免无效扫描。
9. 日志型文件系统的原理?
- 在修改文件前,先把变更记录写入日志(顺序写,性能高),再执行实际数据写入。
- 系统崩溃时可通过日志回放恢复一致性。
- 优点:崩溃恢复快,保证一致性。缺点:日志写入有额外开销。
10. EXT4 与 NTFS 的区别?
- EXT4(Linux 常用):支持 extents(连续块)、延迟分配、日志功能,性能高,适合大规模文件系统。
- NTFS(Windows 常用):支持文件权限 ACL、加密、压缩、事务日志、备用数据流等,更复杂但功能全面。
- 总结:EXT4 强调性能和简单性;NTFS 强调功能和兼容性。
五、输入输出与设备管理
1. I/O 的三种方式:程序控制、中断驱动、DMA?
- 程序控制 I/O:CPU 主动轮询设备状态,直到设备准备好。实现简单,但 CPU 忙等效率低。
- 中断驱动 I/O:设备就绪时通过中断通知 CPU,CPU 再读写数据,避免无效等待。
- DMA(直接存储器访问):由 DMA 控制器直接在设备与内存间传输数据,仅在传输完成时通知 CPU,效率最高,适合大批量数据传输。
2. 阻塞 I/O、非阻塞 I/O、I/O 复用(select/poll/epoll)、异步 I/O 区别?
- 阻塞 I/O:调用 I/O 函数后进程阻塞,直到数据就绪。
- 非阻塞 I/O:立即返回,如果数据未就绪返回错误,应用需轮询检查。
- I/O 复用:
select/poll/epoll
等机制,允许一个进程同时等待多个 I/O 事件。就绪后再进行读写,常用于高并发网络服务器。 - 异步 I/O:应用发起请求后立即返回,内核完成 I/O 后主动通知应用,真正的异步模式。
3. epoll 为什么比 select/poll 高效?
- 减少拷贝:
select/poll
每次调用需拷贝整个 fd 集合,epoll 只需一次注册。 - 减少遍历:
select/poll
每次需遍历所有 fd,epoll 通过就绪链表直接返回就绪 fd。 - fd 数量限制:
select
默认最大 1024,poll 较大但仍需遍历;epoll 支持接近系统上限的 fd 数量。 - 触发机制:支持水平触发 (LT) 与边缘触发 (ET),ET 下事件通知更少,性能更优。
4. 信号驱动 I/O 和异步 I/O 的区别?
- 信号驱动 I/O:设备就绪时内核向进程发送
SIGIO
信号,进程再调用 read/write 完成数据传输,仍属“同步”范畴。 - 异步 I/O:应用提交请求后无需再管,内核直接完成数据拷贝并通知进程,可以直接使用数据。
- 区别本质:信号驱动 I/O 通知“可以读”,异步 I/O 通知“已经读完”。
5. Linux 常见的设备驱动模型?
- 字符设备驱动:按字节流读写,如串口、键盘、鼠标。
- 块设备驱动:以数据块为单位访问,支持随机访问,如硬盘、SSD。
- 网络设备驱动:处理报文收发,如网卡。
- Linux 设备驱动模型(LDM):统一抽象,基于 bus(总线)-device(设备)-driver(驱动) 三元模型,内核通过 sysfs 暴露设备层次,支持即插即用和模块化管理。
六、中断与系统调用
1. 什么是中断?和异常有什么区别?
- 中断:外部事件(I/O 完成、时钟信号、硬件请求等)打断 CPU 执行,转去执行相应处理程序。
- 异常:CPU 在执行指令时检测到的错误或特殊情况(如缺页、除零、非法指令)。
- 区别:中断来自外部设备,异常来自指令执行内部。
2. 硬中断和软中断的区别?
- 硬中断:由硬件设备产生(如键盘输入、磁盘完成 I/O)。
- 软中断:由软件触发(如
int
指令),常用于系统调用。
3. 系统调用的执行过程?
- 用户态程序调用库函数(如
read()
)。 - 执行软中断(陷入指令),进入内核态。
- 内核查系统调用号,定位对应的内核服务例程。
- 内核完成所需操作(如调度、I/O)。
- 返回结果,切回用户态继续执行。
4. 用户态和内核态的区别?什么时候进入内核态?
- 用户态:普通应用运行,权限低,不能直接操作硬件。
- 内核态:操作系统代码运行,权限高,可执行特权指令。
- 进入内核态的情况:
- 系统调用(主动)
- 异常(如缺页)
- 外部中断(如 I/O 完成)。
5. 常见的 Linux 系统调用有哪些?(fork、exec、wait、read、write、mmap 等)
- 进程管理:
fork()
、exec()
、wait()
、exit()
。 - 文件操作:
open()
、read()
、write()
、close()
、mmap()
。 - 内存管理:
brk()
、mmap()
。 - 设备/IPC:
ioctl()
、pipe()
、shmget()
、semop()
。
6. 系统调用和函数调用的区别?
- 系统调用:用户态进入内核态,需上下文切换,代价大。
- 函数调用:用户态内部的普通调用,无需切换,开销小。
7. 上下文切换的具体过程?
- 保存当前进程的寄存器、程序计数器、堆栈指针等上下文。
- 更新 PCB(进程控制块)。
- 选择下一个进程并恢复其上下文(寄存器、堆栈、程序计数器)。
- 切换页表等内存映射信息。
- 跳转到新进程继续执行。
七、并发与锁机制
1. 自旋锁和互斥锁的区别及应用场景?
- 自旋锁:等待时不断轮询,不释放 CPU;适合锁持有时间极短的场景(如内核)。
- 互斥锁:等待时阻塞,释放 CPU;适合长时间持锁的场景。
2. 读写锁的特点?
- 支持 多读单写:多个线程可同时读,但写操作独占。
- 适合读多写少的场景(如缓存)。
3. 原子操作的实现方式?(总线锁、缓存锁、CAS)
- 总线锁:CPU 发出 LOCK 信号锁住总线,防止其他处理器访问内存。
- 缓存锁:锁定缓存行,通过缓存一致性协议保证原子性。
- CAS(Compare-And-Swap):比较并交换,硬件原语,无需加锁即可实现原子操作。
4. 乐观锁和悲观锁的区别?
- 悲观锁:认为冲突频繁,先加锁再操作(如数据库行锁)。
- 乐观锁:认为冲突少,不加锁,通过版本号/CAS 检测冲突并重试。
5. 信号量的使用场景?
- 控制资源数量的并发访问,如数据库连接池、生产者-消费者模型。
- 可实现互斥(信号量=1)和同步(信号量>1)。
6. 条件变量的作用?
- 用于线程间同步,常与互斥锁配合使用。
- 线程在条件不满足时等待,条件满足时由其他线程发信号唤醒。
八、其他常见基础考点
1. 大端和小端的区别?如何判断?
- 小端:低有效字节存储在低地址(如 x86)。
- 大端:高有效字节存储在低地址(如网络字节序)。
- 判断方法:利用联合体存放整型和字符,检查低地址存的字节是否为低位数值。
2. 并发和并行的区别?
- 并发:单 CPU 通过快速切换运行多个任务,看似同时执行。
- 并行:多 CPU(或多核)上真正同时运行多个任务。
- 区别:并行是真同时,并发是假同时。
3. 临界区、互斥、同步、死锁的关系?
- 临界区:访问共享资源的代码片段。
- 互斥:确保同一时刻只有一个进程进入临界区。
- 同步:保证进程按照一定顺序执行。
- 死锁:多个进程因互相等待资源而永久阻塞,通常由互斥 + 请求保持 + 不可剥夺 + 环路等待四个条件导致。
4. Linux IPC(进程间通信)方式的优缺点对比?
- 管道:简单,但仅支持半双工,且无名管道需亲缘关系。
- 消息队列:可独立于进程存在,支持按类型读取,但开销较大。
- 信号量:用于互斥与同步,不适合传输大量数据。
- 共享内存:效率最高,但需配合同步机制避免冲突。
- Socket:支持跨主机通信,最灵活但开销大。
5. 常见 Linux 信号及作用(SIGKILL、SIGSTOP、SIGCHLD 等)?
- SIGKILL (9):强制终止进程,不可捕获和忽略。
- SIGSTOP (19):暂停进程(类似 Ctrl+Z),不可忽略。
- SIGCHLD (17):子进程结束时通知父进程。
- 其他常见信号:
SIGINT (2)
终止前台进程(Ctrl+C),SIGTERM (15)
请求进程正常退出。
6. fork、vfork 和 clone 的区别?
- fork:复制父进程,子进程拥有独立地址空间。
- vfork:子进程与父进程共享地址空间,直到
exec
或_exit
,效率更高但需谨慎。 - clone:Linux 特有,底层实现线程/轻量级进程,可选择性共享资源(内存、文件描述符等)。
7. Linux 内核模块(LKM)的特点?
- 内核代码可动态加载/卸载,不需重启内核。
- 提供驱动、文件系统等功能扩展。
- 内核态运行,效率高但错误可能导致系统崩溃。
8. COW(写时复制)机制?
- fork 时父子进程共享同一物理内存,只有在修改时才复制对应页。
- 优点:减少不必要的内存复制,提升 fork 效率。
9. 系统调用与库函数的区别?
- 系统调用:内核提供的接口,通过软中断陷入内核态(如
read()
)。 - 库函数:封装系统调用或纯用户态实现(如
printf()
可能调用write()
)。
10. 内核线程和用户线程的区别?
- 用户线程:由用户空间库实现,调度开销小,但若一个线程阻塞可能影响整个进程。
- 内核线程:由内核管理,可利用多核并行,但上下文切换开销大。
11. 高速缓存一致性问题(MESI 协议)?
- MESI 协议:缓存行有四种状态:
- Modified:已修改,仅本缓存有最新数据。
- Exclusive:与内存一致,仅本缓存有副本。
- Shared:与内存一致,多个缓存共享。
- Invalid:无效,需要从内存/其他缓存更新。
- 通过总线嗅探和状态转换,保证多核缓存一致性。
12. NUMA 架构下的内存管理?
- NUMA(非一致性内存访问):CPU 有本地内存,访问本地内存速度更快,访问远程内存延迟更高。
- 管理策略:
- 内存绑定:进程优先分配本地内存。
- 内存迁移:将远程内存页迁移到本地。
- 调度亲和性:让线程尽量运行在接近其数据的 CPU 上。