这份资料涵盖了 Linux 系统编程 的核心内容,包括进程管理、线程编程、IPC(进程间通信)、同步互斥机制等高频面试题。

为了方便记忆和复习,我将其重新梳理为 进程管理、进程间通信 (IPC)、多线程编程、同步互斥 四大板块,并保留了所有关键名词和细节。

搭配下面的播客使用效果更佳

📂 第一板块:进程管理

这部分考察对操作系统核心概念的理解,以及进程的生命周期。

问题 级别 详细解答
什么是进程 必问 进程是一个正在执行的程序,也是 CPU 资源分配的基本实体。
进程在系统的进程队列 (task list) 中调度,该队列由进程控制块 (task_struct) 组成。一个 task_struct 管理一个进程。系统会为进程分配一个虚拟内存空间,通过页表映射到物理内存。
进程控制块 (PCB) 包含哪些信息 了解 1. 身份信息:进程ID (PID)、用户ID (UID)、组ID (GID)。
2. 状态信息:程序计数器 (PC)、CPU 寄存器值、进程状态 (就绪/执行/阻塞等)。
3. 内存信息:描述虚拟内存分配的地址信息。
4. 文件信息:文件描述符表 (指向 file 结构体)、当前工作目录。
5. 资源控制:资源上限、控制终端信息。
进程有哪些状态 重点 1. 创建态:申请 PCB,分配资源,未完成则卡在此状态。
2. 就绪态:已准备好,只差 CPU 时间片。
3. 执行态:正在 CPU 上运行。
4. 睡眠态 (阻塞态):调用 sleep, wait, pause 等函数,等待事件。
5. 暂停态:收到暂停信号 (SIGSTOP)。
6. 僵尸态:进程退出但未被父进程回收。
7. 死亡态:进程退出且已被回收。
进程状态是如何切换的 就绪 → 运行:调度器选中。
运行 → 就绪:时间片用完或被高优先级抢占。
运行 → 阻塞:等待 I/O 或事件。
阻塞 → 就绪:事件完成。
运行 → 终止:执行结束。
进程的虚拟内存空间机制 必问 概念:物理 CPU 只有一个,但通过虚拟化让每个进程认为自己独占 CPU 和内存。
过程:进程加载时,内核创建 task_structmm_struct (虚拟地址描述符)。
地址空间:本质是内核中的一张表,将内存分区域。进程运行时,通过 MMU 和页表将虚拟地址映射到物理内存。
设计虚拟地址的好处 了解 1. 独立性:链接器可设定固定地址,无需关心实际物理地址。
2. 节省内存:共享代码 (如库文件) 在物理内存只存一份,映射给多进程。
3. 利用碎片:虚拟地址连续,但物理地址可以离散。
Linux中子进程从父进程继承了什么 了解 用户ID/组ID、环境变量、堆栈、共享内存、打开的文件描述符、当前工作目录、根目录、信号屏蔽字、资源限制、控制终端。
Linux中子进程独有的属性 了解 进程号 (PID)、父进程号 (PPID)、自己的文件描述符拷贝、运行时间数据、文件锁 (子进程不继承父进程的锁)。
fork() 与 vfork() 的区别 重点 fork():创建子进程,子进程复制父进程的地址空间 (写时复制)。
vfork():创建子进程,子进程与父进程共享地址空间。子进程必须先调用 execexit,否则父进程无法运行。效率高但危险。
system 和 exec 函数族 重点 exec 族 (如 execl):执行新程序,取代当前进程的代码段、数据段。调用后原进程代码不再执行(我变成你)。
system:在当前进程中调用 shell 执行命令,执行完后继续执行后面的代码(我找人帮你干活)。
exit、_exit、_Exit的区别 exit:标准 C 库函数。终止进程前会清理缓冲区 (如 printf 内容)、调用 atexit 注册的函数、关闭文件。
_exit / _Exit:系统调用。立即终止进程,不清理缓冲区。
进程调度的方法 重点 1. SCHED_FIFO (实时):先来先服务,一直运行直到退出或放弃。
2. SCHED_RR (实时):时间片轮转。
3. SCHED_OTHER (非实时):普通分时调度 (CFS)。
实时进程会抢占非实时进程。
守护进程及设计步骤 了解 定义:后台运行,脱离终端控制,通常用于系统服务。
步骤
1. fork() 创建子进程,父进程退出 (变孤儿)。
2. setsid() 创建新会话 (脱离控制终端)。
3. chdir("/") 改变工作目录 (防占用挂载点)。
4. umask(0) 重设文件掩码。
5. close() 关闭无用文件描述符 (0,1,2)。

📂 第二板块:进程间通信

这部分考察不同进程如何传递数据,是后端与嵌入式开发的重难点。

问题 级别 详细解答
进程的通信方式IPC有哪些 必问 1. 无名管道 (Pipe):半双工,仅限亲缘进程。
2. 有名管道 (FIFO):半双工,允许无亲缘进程,以文件形式存在。
3. 信号 (Signal):异步通信,通知事件。
4. 消息队列 (Message Queue):链表结构,支持按类型取消息。
5. 共享内存 (Shared Memory):映射同一块物理内存,速度最快
6. 信号量 (Semaphore):计数器,主要用于同步和互斥,而非传数据。
7. 套接字 (Socket):跨机器网络通信。
哪些通信方式需要借助内核? 了解 几乎所有:管道、FIFO、消息队列、信号量、共享内存、Socket 都需要内核支持。
管道通信机制 (Pipe vs FIFO) 必问 本质:内核中的缓冲区。
无名管道 (pipe):无文件名,通过文件描述符操作。读端 fd[0],写端 fd[1]。读空管道阻塞,写满管道阻塞。
有名管道 (mkfifo):有文件名,可用于任意进程。若文件已存在会报错 File exists,需先判断再打开。
信号机制 必问 定义:软件层次对中断的模拟,异步通信。
分类:非实时 (1-31),实时 (34-64)。
处理方式:1. 默认动作 (通常终止)。2. 捕获 (执行信号处理函数)。3. 忽略。
特殊SIGKILL (9) (立即处死)和 SIGSTOP (19) (立即冻结)不能被忽略或阻塞
阻塞:将信号加入屏蔽集,暂时挂起不处理。
按下ctrl+c,程序收到SIGINT信号
消息队列 必问 特点:链表结构,存放在内核。支持按类型读取消息(不一定要先进先出)。独立于进程,进程退出消息仍在。
共享内存机制 必问 原理:多个进程的虚拟地址映射到同一块物理内存。
优点最快的 IPC,因为无需内核与用户空间的数据拷贝。
注意:需配合信号量使用,解决同步竞争问题。

📂 第三板块:多线程编程

问题 级别 详细解答
进程与线程的区别 必问 1. 资源:进程是资源分配的基本单位 (独立地址空间);
线程是 CPU 调度的基本单位 (共享进程资源)。
2. 开销:进程切换开销大 (切页表/上下文);线程切换开销小。
3. 通信:进程需 IPC;线程共享全局变量,通信简单但需同步。
4. 健壮性:进程崩溃不影响其他进程;线程崩溃导致整个进程死掉。
5. 场景:多进程适合高隔离、高稳定;多线程适合高并发、高性能。
线程具有相同的堆栈吗 重点 不相同。每个线程有自己独立的栈空间 (Stack),但共享 (Heap)、全局变量和代码段。
线程有哪些属性 了解 作用域、栈尺寸、栈地址、优先级、分离状态 (detached)、调度策略。
线程如何回收 必问 默认是 joinable 状态,必须由其他线程调用 pthread_join() 回收资源,否则产生“僵尸线程”。
调用 pthread_detach() 可设置为分离态,线程结束后自动释放资源。
线程的相关函数名 创建: pthread_create
退出: pthread_exit
等待: pthread_join
取消: pthread_cancel
分离: pthread_detach
线程池原理 重点 组成:1. 任务队列 (链表)。2. 工作线程 (N个)。3. 管理者线程 (1个)。
工作流程
生产者将任务加入队列 → 唤醒条件变量中的工作线程 → 工作线程取任务执行 → 执行完继续等待。
管理者线程定期检测,根据任务量动态增删线程。
多线程与线程池区别 必问 多线程:来一个任务创建一个线程,销毁也需时间。并发极大时,频繁创建销毁开销巨大。
线程池:预先创建一组线程,复用这些线程处理任务。避免了频繁创建销毁的开销,防止资源耗尽。
协程 (Coroutine) 重点 定义:用户态轻量级线程。和普通线程由系统调度不同,协程由程序员控制切换,非抢占式。
优势:单线程内实现多任务并发,无锁机制,切换极快,适合高并发 IO 密集型任务。
可重入函数 重点 定义:被多个执行流重复调用时,结果依然正确。
条件:不使用全局/静态变量,不返回静态地址,仅依赖栈上变量。
重入讨论的是函数被中断后再进入的安全性。

📂 第四板块:同步与互斥

这部分是多线程编程的灵魂,解决资源竞争问题。

问题 级别 详细解答
线程的同步方式 必问 1. 互斥锁 (Mutex):保护临界区,独占访问。
2. 信号量 (Semaphore):控制并发数量,同步。
3. 条件变量 (Cond):等待通知,协调执行顺序。
4. 读写锁 (RWLock):读共享,写独占。
互斥锁 (Mutex) 必问 作用:保证同一时刻只有一个线程访问共享资源。
机制:抢不到锁的线程会阻塞 (睡眠)。
自旋锁 (Spinlock) 重点 作用:抢不到锁的线程会循环等待 (忙等),不睡眠。
对比:自旋锁不切换上下文,适合持有时间极短的场景;互斥锁会睡眠,适合持有时间长的场景。
读写锁 重点 特性:写独占,读共享。写锁优先级通常高于读锁。
场景:读多写少 (大量读取,少量修改)。
信号量 (Semaphore) 必问 本质:计数器。P操作 (减/等待),V操作 (加/释放)。
类型
1. 二值信号量:类似互斥锁 (0或1)。
2. 计数信号量:控制并发数 (如连接池)。
注意:信号量为0时 P 操作会阻塞。
线程的条件变量 特点:不能单独使用,必须配合互斥锁。
作用:让线程在满足特定条件前休眠,避免忙轮询。pthread_cond_wait 会原子性地释放锁并进入休眠。
死锁如何产生及4个必要条件 必问 4个条件 (缺一不可):
拿独木桥上的两个人举例子:
1. 互斥:资源独占,(你独占了你的桥面)
2. 占有且等待:拿了资源不放,还想要新的。(你独占了你的桥面,还想要别人的桥面)
3. 不可抢占:不能强行剥夺别人的资源。(你不能强行抢了别人的桥面)
4. 循环等待:A等B,B等A。(你想要别人的桥面,别人也想要你的桥面)
常见场景:同一线程重复上锁;两线程交叉上锁;持锁时被取消。
死锁处理方式 重点 预防:破坏上面的4种条件 (如一次性申请所有资源、按序申请)。
避免:银行家算法 (分配前评估安全性)。
检测与解除:剥夺资源、撤销进程。
同步方式的选择场景 实战 互斥锁:简单保护独占资源。
信号量:生产者-消费者模型,控制资源数量。
条件变量:等待某个事件发生 (如队列非空)。
内核链表 (kernel list) 定义:Linux 内核专用双向循环链表。
特点
1. 指针封装在 struct list_head 中。
2. 数据与链表分离:链表节点嵌入在用户数据结构中,通过 container_of 宏反推数据地址。
3. 提供丰富宏操作:list_add, list_del, list_for_each_entry
Lvgl是什么? 重点 LVGL (Light and Versatile Graphics Library):开源嵌入式 GUI 库。
特点:轻量 (适合 MCU)、控件丰富、跨平台、支持多输入设备。
场景:智能手表、工业仪表、物联网设备界面。