fork 是 Linux/Unix 系统的核心系统调用 ,用于创建一个新的进程。在 Android 中,它是 Zygote 孵化应用进程的底层机制。
一、fork 的本质
| 特性 | 说明 |
|---|---|
| 定义 | 从当前进程复制出一个几乎完全相同的子进程 |
| 返回值 | 在父进程中返回子进程的 PID(>0);在子进程中返回 0 |
| 内存机制 | Copy-on-Write(写时拷贝):初始共享父进程的内存页,修改时才复制 |
| 执行位置 | 子进程从 fork() 返回处开始执行,代码与父进程完全一致 |
二、fork 的执行流程

三、fork 的两次返回
这是 fork 最特殊的地方------一次调用,两次返回:
c
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork(); // 调用 fork
if (pid < 0) {
// 创建失败
printf("fork failed\n");
} else if (pid == 0) {
// 子进程执行这里
printf("I am child, pid = %d, my parent pid = %d\n", getpid(), getppid());
} else {
// 父进程执行这里
printf("I am parent, pid = %d, my child pid = %d\n", getpid(), pid);
}
return 0;
}
输出示例:
I am parent, pid = 1000, my child pid = 1001
I am child, pid = 1001, my parent pid = 1000
四、Copy-on-Write(写时拷贝)
这是 fork 高效的关键:
| 阶段 | 行为 | 内存状态 |
|---|---|---|
| fork 刚完成 | 子进程共享父进程的内存页 | 共享只读页,不额外占用内存 |
| 子进程读取数据 | 直接读共享页 | 仍共享,无拷贝 |
| 子进程修改数据 | 触发页错误,内核复制该页到子进程私有空间 | 仅修改的页被复制,其他仍共享 |
Android 中的应用:
- Zygote 预加载了 3000+ 个 framework 类
- 每个应用进程 fork 后,这些类在只读状态下被共享
- 只有当应用修改某个类/对象时,才会复制对应内存页
- 100 个应用共享一份 framework 类,极大节省内存
五、fork 在 Android 中的使用
1. Zygote fork SystemServer
java
// ZygoteInit.java
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run(); // 子进程(SystemServer)执行
return; // 父进程(Zygote)不会执行到这里
}
}
2. Zygote fork 应用进程
java
// ZygoteConnection.java --- 处理 AMS 的 fork 请求
private Runnable handleChildProc(ZygoteArguments parsedArgs, ...) {
// 关闭从父进程继承的 Socket
zygoteServer.closeServerSocket();
// 设置进程参数(UID、GID、资源限制等)
Zygote.setAppProcessName(parsedArgs, ...);
// 返回 Runnable,执行应用入口
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, ...);
}
3. 底层 native 调用
cpp
// Zygote.cpp --- 调用 Linux fork
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(...) {
pid_t pid = fork(); // Linux 系统调用
if (pid == 0) {
// 子进程
// 设置 SELinux 上下文、资源限制、UID/GID 等
SetSELinuxContext(...);
SetResourceLimits(...);
SetCredentials(...);
}
return pid;
}
六、fork 的限制与注意事项
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 多线程死锁 | 子进程只复制调用线程,其他线程的锁状态被继承但无人释放 | Zygote 用单线程 Socket,fork 前禁止创建线程 |
| 文件描述符泄漏 | 子进程继承父进程所有 fd | fork 后子进程主动关闭不需要的 fd |
| Binder 对象混乱 | 子进程复制 Binder 服务端对象,导致引用失效 | Zygote 不用 Binder,用 Socket |
| 内存膨胀 | 大量写操作触发 COW,复制大量内存页 | Zygote 预加载只读数据,减少写操作 |
七、总结
fork = 进程克隆 + 写时拷贝
- 它是 Linux 创建进程的唯一方式
- Android 中 Zygote 通过 fork 快速创建应用进程
- Copy-on-Write 让多个进程共享同一份只读数据,节省内存
- fork 与多线程不兼容,这是 Zygote 选择 Socket 而非 Binder 的根本原因
一句话:fork 是 Android 应用启动的"加速器",没有它,每个应用都要从头初始化 JVM,启动时间会从几百毫秒变成几秒。