💣

操作系统面试指南

目录


  1. 进程与线程
  1. 进程调度
  1. 进程间通信
  1. 死锁
  1. 内存管理
  1. 文件系统
  1. IO 管理
  1. Linux 常用命令
  1. 高频面试题与答案

一、进程与线程

1.1 进程

定义:程序的一次执行过程,是资源分配的基本单位
进程控制块(PCB): - 进程标识符 (PID) - 进程状态 - 程序计数器 (PC) - CPU 寄存器 - 内存管理信息 - 打开文件列表
进程状态:新建 → 就绪 ⇄ 运行 → 阻塞 → 终止

1.2 线程

定义:CPU 调度的基本单位,共享进程资源
特性
进程
线程
资源
独立地址空间
共享进程地址空间
开销
创建/切换开销大
开销小
通信
IPC 机制
直接读写共享内存
安全性
隔离性好
一个崩溃影响整个进程

1.3 协程

  • 用户态轻量级线程,由程序自己调度
  • 切换开销极小(不涉及内核)
  • 协作式调度(主动让出)

1.4 上下文切换

进程切换:保存/恢复 PCB、更新页表、刷新 TLB 线程切换:同进程内只需保存/恢复寄存器 开销:进程 > 线程 > 协程

1.5 线程的实现方式

类型
说明
优点
缺点
用户级线程
用户空间实现,内核不感知
切换快、无系统调用
一个阻塞全部阻塞
内核级线程
内核管理和调度
可利用多核
切换开销大
混合模型
M:N 映射
兼顾两者优点
实现复杂

1.6 进程 vs 线程 vs 协程

特性
进程
线程
协程
调度
内核
内核
用户程序
切换开销
大(ms级)
中(μs级)
小(ns级)
内存
独立地址空间
共享进程空间
共享线程栈
通信
IPC
共享内存
直接访问
并行
可并行
可并行
单线程内不可

1.7 守护进程

特点: - 后台运行,无控制终端 - 生命周期长,随系统启动 - 典型:sshd、nginx、mysqld
创建步骤
pid_t pid = fork();
if (pid > 0) exit(0);      // 父进程退出
setsid();                   // 创建新会话
chdir("/");                 // 切换工作目录
umask(0);                   // 重设文件权限掩码
close(STDIN_FILENO);        // 关闭标准输入输出

1.8 exec 系列函数

// 替换当前进程映像
execl("/bin/ls", "ls", "-l", NULL);
execlp("ls", "ls", "-l", NULL);    // 使用 PATH
execv("/bin/ls", argv);
execvp("ls", argv);
特点: - 不创建新进程,PID 不变 - 替换代码段、数据段、堆栈 - 通常与 fork 配合使用

1.9 wait/waitpid 函数

pid_t wait(int *status);           // 阻塞等待任一子进程
pid_t waitpid(pid_t pid, int *status, int options);

// 获取退出状态
if (WIFEXITED(status)) {
    int exit_code = WEXITSTATUS(status);
}
waitpid 参数: - pid > 0:等待指定进程 - pid = -1:等待任一子进程 - WNOHANG:非阻塞

二、进程调度

2.1 调度算法

算法
特点
缺点
FCFS
先来先服务
护航效应
SJF
短作业优先
长作业饥饿
优先级
高优先级先执行
低优先级饥饿
RR
时间片轮转,公平
切换开销
多级反馈队列
兼顾响应和吞吐
实现复杂

2.2 多级反馈队列

高优先级队列 (时间片小) → 未完成降级
     ↓
中优先级队列 (时间片中) → 未完成降级
     ↓
低优先级队列 (时间片大/FCFS)

2.3 Linux CFS 调度器

完全公平调度器(Completely Fair Scheduler)
  • 使用红黑树管理可运行进程
  • 虚拟运行时间(vruntime)越小,优先级越高
  • 每个进程按权重分配 CPU 时间
vruntime = 实际运行时间 × (NICE_0_LOAD / 进程权重)
特点: - O(log n) 复杂度选择下一个进程 - 支持组调度(cgroups) - 自动平衡交互性和吞吐量

2.4 实时调度算法

算法
说明
SCHED_FIFO
先进先出,高优先级抢占
SCHED_RR
时间片轮转,同优先级轮换
SCHED_DEADLINE
基于截止时间调度(EDF)
实时进程优先级:1-99(高于普通进程)
struct sched_param param;
param.sched_priority = 50;
sched_setscheduler(pid, SCHED_FIFO, &param);

三、进程间通信

3.1 管道(Pipe)

int fd[2];
pipe(fd);  // fd[0] 读端,fd[1] 写端
  • 半双工,单向流动
  • 仅限父子进程
  • 命名管道(FIFO)可用于无关进程

3.2 消息队列

int msgid = msgget(key, IPC_CREAT | 0666);
msgsnd(msgid, &msg, sizeof(msg), 0);
msgrcv(msgid, &msg, sizeof(msg), type, 0);
  • 消息有类型,可选择性接收
  • 内核中存储,有大小限制

3.3 共享内存(最快)

int shmid = shmget(key, size, IPC_CREAT | 0666);
void* ptr = shmat(shmid, NULL, 0);
// 直接读写 ptr
shmdt(ptr);
  • 多进程直接访问同一内存
  • 需要同步机制(信号量)

3.4 信号量

sem_t sem;
sem_init(&sem, 1, 1);  // 进程间共享,初值1
sem_wait(&sem);  // P 操作
// 临界区
sem_post(&sem);  // V 操作

3.5 信号

signal(SIGINT, handler);  // 注册信号处理函数
kill(pid, SIGTERM);       // 发送信号

3.6 IPC 对比

方式
特点
适用场景
管道
简单,单向
父子进程
消息队列
有类型,异步
解耦通信
共享内存
最快
大数据量
信号量
同步/互斥
资源控制
Socket
跨主机
网络通信

3.7 线程同步机制

互斥锁(Mutex)

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_lock(&mutex);
// 临界区
pthread_mutex_unlock(&mutex);

自旋锁

pthread_spinlock_t spinlock;
pthread_spin_init(&spinlock, 0);

pthread_spin_lock(&spinlock);
// 临界区(应该很短)
pthread_spin_unlock(&spinlock);
特性
互斥锁
自旋锁
等待方式
阻塞睡眠
忙等待
适用场景
临界区长
临界区短、多核
开销
上下文切换
CPU 空转

读写锁

pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);

pthread_rwlock_rdlock(&rwlock);  // 读锁(共享)
pthread_rwlock_wrlock(&rwlock);  // 写锁(独占)
pthread_rwlock_unlock(&rwlock);

条件变量

pthread_mutex_t mutex;
pthread_cond_t cond;

// 等待方
pthread_mutex_lock(&mutex);
while (!condition) {
    pthread_cond_wait(&cond, &mutex);  // 原子释放锁并等待
}
// 处理
pthread_mutex_unlock(&mutex);

// 通知方
pthread_mutex_lock(&mutex);
condition = true;
pthread_cond_signal(&cond);  // 或 pthread_cond_broadcast
pthread_mutex_unlock(&mutex);

3.8 生产者消费者问题

#include<queue>
#include<mutex>
#include<condition_variable>

template<typename T>
class BlockingQueue {
    std::queue<T> queue;
    std::mutex mtx;
    std::condition_variable not_empty;
    std::condition_variable not_full;
    size_t capacity;

public:
    void push(T item) {
        std::unique_lock<std::mutex> lock(mtx);
        not_full.wait(lock, [this]{ return queue.size() < capacity; });
        queue.push(std::move(item));
        not_empty.notify_one();
    }

    T pop() {
        std::unique_lock<std::mutex> lock(mtx);
        not_empty.wait(lock, [this]{ return !queue.empty(); });
        T item = std::move(queue.front());
        queue.pop();
        not_full.notify_one();
        return item;
    }
};

3.9 读者写者问题

#include<shared_mutex>

class ReadWriteLock {
    std::shared_mutex rw_mutex;
    int data = 0;

public:
    // 读者:共享锁
    int read() {
        std::shared_lock<std::shared_mutex> lock(rw_mutex);
        return data;
    }

    // 写者:独占锁
    void write(int value) {
        std::unique_lock<std::shared_mutex> lock(rw_mutex);
        data = value;
    }
};
读者优先 vs 写者优先: - 读者优先:可能导致写者饥饿 - 写者优先:可能导致读者饥饿 - 公平策略:按到达顺序处理

3.10 哲学家就餐问题

问题:5 个哲学家围坐,每人需要两把叉子才能吃饭
解决方案: 1. 资源分级:按编号顺序获取叉子 2. 仲裁者:引入服务员协调 3. 限制人数:最多 4 人同时尝试
// 资源分级解法
void philosopher(int id) {
    int left = id;
    int right = (id + 1) % 5;

    // 总是先获取编号小的叉子
    if (left < right) {
        lock(fork[left]);
        lock(fork[right]);
    } else {
        lock(fork[right]);
        lock(fork[left]);
    }

    eat();

    unlock(fork[left]);
    unlock(fork[right]);
}

四、死锁

4.1 死锁条件(四个必要条件)

  1. 互斥:资源只能被一个进程使用
  1. 占有并等待:持有资源同时等待其他资源
  1. 不可剥夺:资源只能由持有者释放
  1. 循环等待:存在进程等待环

4.2 死锁处理

策略
方法
预防
破坏必要条件(资源有序分配等)
避免
银行家算法
检测
资源分配图检测环
恢复
终止进程/资源抢占

4.3 银行家算法

Available: 可用资源向量
Need = Max - Allocation

安全性检查:
1. 找到 Need <= Available 的进程
2. 假设其完成,释放资源
3. 重复直到所有进程完成(安全)或无法继续(不安全)

五、内存管理

5.1 内存分区

高地址
+------------------+
|     内核空间     |
+------------------+
||  ← 向下增长
||
||
||  ← 向上增长
+------------------+
|      BSS        |  ← 未初始化全局变量
+------------------+
|      Data       |  ← 已初始化全局变量
+------------------+
|      Text       |  ← 代码段
+------------------+
低地址

5.2 虚拟内存

  • 每个进程独立虚拟地址空间
  • 通过页表映射到物理内存
  • 优点:隔离、大地址空间、按需加载、共享

5.3 分页

虚拟地址 = 页号 + 页内偏移
         ↓ 页表查询
物理地址 = 帧号 + 页内偏移
多级页表:解决页表过大问题 TLB(快表):缓存常用页表项,加速地址转换

5.4 页面置换算法

算法
原理
特点
FIFO
淘汰最先进入的页
简单,可能 Belady 异常
LRU
淘汰最久未使用的页
效果好,实现复杂
LFU
淘汰使用次数最少的页
需要计数器
Clock
LRU 近似,访问位
实现简单,效果好
LRU 实现:哈希表 + 双向链表
class LRUCache {
    int capacity;
    list<pair<int, int>> cache;  // 双向链表
    unordered_map<int, list<pair<int,int>>::iterator> map;  // 哈希表
public:
    int get(int key) {
        if (map.find(key) == map.end()) return -1;
        cache.splice(cache.begin(), cache, map[key]);  // 移到头部
        return map[key]->second;
    }
    void put(int key, int value) {
        if (map.find(key) != map.end()) {
            map[key]->second = value;
            cache.splice(cache.begin(), cache, map[key]);
        } else {
            if (cache.size() == capacity) {
                map.erase(cache.back().first);
                cache.pop_back();
            }
            cache.push_front({key, value});
            map[key] = cache.begin();
        }
    }
};

5.5 内存分配

算法
说明
首次适应
找到第一个足够大的空闲块
最佳适应
找到最小的足够大的空闲块
伙伴系统
2^n 大小分配,便于合并
Slab
对象缓存,减少碎片

5.6 缺页中断处理

1. CPU 访问虚拟地址
2. MMU 查页表,发现页不在内存(缺页)
3. 触发缺页中断
4. 操作系统处理:
   a. 查找空闲物理页(或页面置换)
   b. 从磁盘读取页面
   c. 更新页表
   d. 重新执行指令
缺页类型: - 硬缺页:需要从磁盘读取 - 软缺页:页面在内存中(如共享页)

5.7 内存映射(mmap)

void* addr = mmap(NULL, length, PROT_READ | PROT_WRITE,
                  MAP_SHARED, fd, 0);
// 直接操作 addr
munmap(addr, length);
用途: - 文件映射:减少拷贝 - 匿名映射:大内存分配 - 进程间共享内存

5.8 分段机制

逻辑地址 = 段号 + 段内偏移
         ↓ 段表查询
物理地址 = 段基址 + 段内偏移
段表项:段基址、段长度、访问权限
分段 vs 分页: | 特性 | 分段 | 分页 | |——|——|——| | 大小 | 可变 | 固定 | | 可见性 | 用户可见 | 用户透明 | | 碎片 | 外部碎片 | 内部碎片 | | 共享 | 便于共享 | 较复杂 |

5.9 内存碎片

类型
说明
解决方案
内部碎片
分配单元内未使用空间
更小的分配粒度
外部碎片
空闲块太小无法分配
紧凑、伙伴系统

5.10 伙伴系统

分配 70KB:
1. 需要 128KB(2^7)块
2.256KB 块分裂:128KB + 128KB
3. 返回一个 128KB 块

释放时:
1. 检查伙伴块是否空闲
2. 若空闲则合并
3. 递归向上合并
优点:快速分配、减少外部碎片 缺点:内部碎片

5.11 Slab 分配器

Slab 缓存池
+------------------+
| task_struct 缓存 |[slab1][slab2]...
+------------------+
| inode 缓存       |[slab1][slab2]...
+------------------+
特点: - 预分配常用对象 - 避免频繁 malloc/free - 减少碎片

5.12 段页式内存管理

逻辑地址 = 段号 + 页号 + 页内偏移
         ↓ 段表查询
         ↓ 页表查询
物理地址 = 帧号 + 页内偏移
优点: - 结合分段和分页的优点 - 段用于逻辑划分(代码、数据、栈) - 页用于物理内存管理

5.13 系统调用过程

1. 用户程序调用库函数(如 read()2. 库函数将参数放入寄存器
3. 执行 syscall/int 0x80 指令
4. CPU 切换到内核态
5. 根据系统调用号查找处理函数
6. 执行内核函数
7. 返回结果,切换回用户态
系统调用 vs 函数调用: | 特性 | 系统调用 | 函数调用 | |——|———-|———-| | 特权级 | 需要切换 | 不需要 | | 开销 | 大 | 小 | | 安全检查 | 需要 | 不需要 |

5.14 中断与异常

类型
说明
示例
中断(外部)
外部设备触发
键盘、网卡、时钟
异常(内部)
CPU 执行指令时产生
除零、缺页、断点
陷阱
主动触发
系统调用
中断处理流程
1. 保存当前上下文
2. 根据中断号查找中断处理程序
3. 执行中断处理
4. 恢复上下文
5. 返回被中断的程序

六、文件系统

6.1 文件系统结构

+------------------+
|    超级块        |  ← 文件系统元信息
+------------------+
|   inode 表       |  ← 文件元数据
+------------------+
|   数据块位图     |
+------------------+
|   数据块         |  ← 文件内容
+------------------+

6.2 inode

存储文件元数据: - 文件大小、权限、时间戳 - 数据块指针(直接、间接、双重间接)

6.3 硬链接 vs 软链接

特性
硬链接
软链接
inode
相同
不同
跨文件系统
不可以
可以
链接目录
不可以
可以
原文件删除
仍可访问
失效
ln file hardlink      # 硬链接
ln -s file softlink   # 软链接

6.4 文件描述符

  • 进程打开文件的索引
  • 0: stdin, 1: stdout, 2: stderr
  • 指向内核文件表项

6.5 文件打开过程

1. 用户调用 open()
2. 内核分配文件描述符
3. 在文件表中创建表项
4. 查找/创建 inode
5. 返回文件描述符
三层结构
进程文件描述符表 → 系统文件表 → inode 
     (fd)           (偏移量)     (文件元数据)

6.6 虚拟文件系统(VFS)

应用程序
    ↓ 统一接口(open/read/write)
   VFS+-------+-------+-------+
| ext4  |  xfs  |  nfs  |
+-------+-------+-------+
VFS 对象: - 超级块对象:文件系统信息 - inode 对象:文件元数据 - 目录项对象:路径组件 - 文件对象:打开的文件

6.7 ext4 文件系统特点

特性
说明
最大文件
16 TB
最大分区
1 EB
日志功能
支持,保证一致性
延迟分配
提高性能
多块分配
减少碎片
在线碎片整理
支持

6.8 权限管理

# 权限表示
-rwxr-xr-x  1 user group  size date filename
 │││ │││ │││
 │││ │││ └── 其他用户权限
 │││ └── 组权限
 └── 所有者权限

# 数字表示
r=4, w=2, x=1
chmod 755 file  # rwxr-xr-x
chmod 644 file  # rw-r--r--

# 特殊权限
chmod u+s file  # SUID:以文件所有者权限执行
chmod g+s dir   # SGID:新文件继承目录组
chmod +t dir    # Sticky:只有所有者能删除

七、IO 管理

7.1 IO 模型

模型
说明
阻塞 IO
调用阻塞直到数据就绪
非阻塞 IO
立即返回,轮询检查
IO 多路复用
select/poll/epoll 监听多个 fd
信号驱动 IO
数据就绪时发送信号
异步 IO
内核完成 IO 后通知应用

7.2 同步 vs 异步,阻塞 vs 非阻塞

  • 同步:用户进程主动等待/查询 IO 结果
  • 异步:内核完成后通知用户进程
  • 阻塞:调用时进程挂起
  • 非阻塞:调用立即返回

7.3 零拷贝

传统 IO
磁盘 → 内核缓冲区 → 用户缓冲区 → Socket 缓冲区 → 网卡
      (4次拷贝,4次上下文切换)
sendfile
磁盘 → 内核缓冲区 → Socket 缓冲区 → 网卡
      (2次拷贝,2次上下文切换)
mmap + write
磁盘 → 内核缓冲区(用户空间映射)→ Socket 缓冲区 → 网卡

7.4 DMA 原理

直接内存访问:外设直接与内存交换数据,无需 CPU 参与
传统 IOCPU 控制数据传输,占用 CPU 时间

DMA IO1. CPU 发起 DMA 请求
2. DMA 控制器接管总线
3. 外设与内存直接传输
4. 传输完成,中断通知 CPU
优点:释放 CPU、提高效率

7.5 磁盘调度算法

算法
说明
特点
FCFS
先来先服务
公平,但寻道时间长
SSTF
最短寻道时间优先
效率高,可能饥饿
SCAN
电梯算法,来回扫描
公平,响应时间可预测
C-SCAN
单向扫描,返回起点
更均匀的等待时间
LOOK
SCAN 变体,不到边界
减少不必要移动

八、Linux 常用命令

8.1 进程管理

ps aux              # 查看所有进程
ps -ef | grep nginx # 查找进程
top / htop          # 实时监控
kill -9 PID         # 强制终止
kill -15 PID        # 优雅终止
nohup cmd &         # 后台运行

8.2 文件操作

ls -la              # 列出文件详情
find . -name "*.log" # 查找文件
grep -r "pattern" .  # 递归搜索内容
chmod 755 file      # 修改权限
chown user:group file # 修改所有者

8.3 网络工具

netstat -tunlp      # 查看端口监听
ss -tunlp           # 更快的 netstat
lsof -i :8080       # 查看端口占用
tcpdump -i eth0     # 抓包
curl -X POST url    # HTTP 请求

8.4 性能分析

vmstat 1            # 系统整体状态
iostat -x 1         # 磁盘 IO
free -h             # 内存使用
df -h               # 磁盘空间
sar -n DEV 1        # 网络流量
strace -p PID       # 跟踪系统调用

8.5 文本处理命令

# awk - 文本处理
awk '{print $1, $3}' file           # 打印第1、3列
awk -F: '{print $1}' /etc/passwd    # 指定分隔符
awk '$3 > 100 {print}' file         # 条件过滤
awk '{sum += $1} END {print sum}'   # 求和

# sed - 流编辑器
sed 's/old/new/g' file              # 替换
sed -n '10,20p' file                # 打印10-20行
sed -i 's/old/new/g' file           # 原地修改
sed '/pattern/d' file               # 删除匹配行

8.6 CPU 100% 排查流程

# 1. 找到高 CPU 进程
top -c

# 2. 找到高 CPU 线程
top -Hp <PID>

# 3. 线程 ID 转十六进制
printf "%x\n" <TID>

# 4. 查看线程堆栈
jstack <PID> | grep -A 30 <TID_HEX>

# 5. 或使用 perf
perf top -p <PID>
perf record -p <PID> -g -- sleep 30
perf report
常见原因: - 死循环 - 频繁 GC - 正则回溯 - 加密/压缩计算

8.7 内存泄漏排查流程

# 1. 查看内存使用
free -h
cat /proc/<PID>/status | grep -i mem

# 2. 查看进程内存映射
pmap -x <PID>
cat /proc/<PID>/smaps

# 3. 使用 valgrind(C/C++)
valgrind --leak-check=full ./program

# 4. Java 堆分析
jmap -heap <PID>
jmap -histo <PID>
jmap -dump:format=b,file=heap.bin <PID>

8.8 磁盘 IO 高排查流程

# 1. 查看整体 IO
iostat -x 1

# 2. 找到高 IO 进程
iotop -o

# 3. 查看进程打开的文件
lsof -p <PID>

# 4. 跟踪 IO 系统调用
strace -e trace=read,write -p <PID>

# 5. 使用 blktrace
blktrace -d /dev/sda -o - | blkparse -i -
关键指标: - %util:磁盘繁忙度 - await:IO 等待时间 - r/s, w/s:每秒读写次数

8.9 日志查看命令

# tail - 查看文件末尾
tail -f /var/log/syslog      # 实时跟踪
tail -n 100 file             # 最后 100 行

# less - 分页查看
less +F file                 # 类似 tail -f
less +G file                 # 跳到文件末尾

# journalctl - systemd 日志
journalctl -u nginx          # 查看服务日志
journalctl -f                # 实时跟踪
journalctl --since "1 hour ago"  # 时间过滤
journalctl -p err            # 按级别过滤

8.10 信号量 vs 互斥锁

特性
信号量
互斥锁
值范围
非负整数
0 或 1
用途
同步 + 互斥
仅互斥
所有权
有(谁加锁谁解锁)
典型场景
生产者消费者
临界区保护
// 信号量:可以由不同线程 P/V
sem_wait(&sem);  // 任意线程
// ...
sem_post(&sem);  // 可以是另一个线程

// 互斥锁:必须由同一线程加锁解锁
pthread_mutex_lock(&mutex);
// ...
pthread_mutex_unlock(&mutex);  // 必须是同一线程

九、高频面试题与答案

Q1: 进程和线程的区别?

答案
特性
进程
线程
定义
资源分配单位
CPU 调度单位
地址空间
独立
共享进程空间
开销
通信
IPC
共享内存
安全性
隔离好
相互影响

Q2: 进程间通信方式有哪些?

答案: 1. 管道:半双工,父子进程 2. 消息队列:内核中消息链表 3. 共享内存:最快,需同步 4. 信号量:同步/互斥 5. 信号:异步通知 6. Socket:跨主机通信

Q3: 什么是死锁?如何避免?

答案
死锁:多个进程互相等待对方持有的资源
四个必要条件:互斥、占有等待、不可剥夺、循环等待
避免方法: 1. 破坏必要条件(资源有序分配) 2. 银行家算法 3. 加锁顺序一致 4. 超时机制

Q4: 虚拟内存的作用?

答案: 1. 地址空间隔离:进程间互不干扰 2. 扩展内存:使用磁盘作为后备 3. 按需加载:只加载需要的页面 4. 内存共享:多进程共享代码段 5. 简化编程:统一的地址空间

Q5: 页面置换算法有哪些?

答案: 1. FIFO:先进先出 2. LRU:最近最少使用 3. LFU:最不经常使用 4. Clock:LRU 近似算法
LRU 实现:哈希表 + 双向链表,O(1) 操作

Q6: 用户态和内核态的区别?

答案
特性
用户态
内核态
权限
受限
完全访问
资源访问
不能直接访问硬件
可以访问所有资源
指令
非特权指令
所有指令
切换时机:系统调用、中断、异常

Q7: select、poll、epoll 的区别?

答案
特性
select
poll
epoll
fd 限制
1024
数据结构
数组
链表
红黑树+链表
拷贝
每次全量
每次全量
注册时一次
复杂度
O(n)
O(n)
O(1)

Q8: 什么是零拷贝?

答案: - 减少 CPU 拷贝次数的技术 - sendfile:内核直接发送,不经过用户空间 - mmap:用户空间映射内核缓冲区 - 应用:Kafka、Nginx

Q9: fork() 的原理?

答案: 1. 创建子进程 PCB 2. 复制父进程地址空间(写时复制 COW) 3. 复制文件描述符表 4. 父进程返回子进程 PID,子进程返回 0
写时复制(COW):fork 后父子共享内存页,写入时才复制

Q10: 什么是僵尸进程和孤儿进程?

答案
僵尸进程: - 子进程退出,父进程未调用 wait() 回收 - 占用 PID 和进程表项 - 解决:父进程 wait() 或 SIGCHLD 处理
孤儿进程: - 父进程先退出 - 被 init(1) 进程收养 - 无危害

最后更新:2026-01-09
你觉得这篇文章怎么样?
YYDS
比心
加油
菜狗
views

Loading Comments...