Android随笔-Zygote中fork究竟是什么?

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,启动时间会从几百毫秒变成几秒。

相关推荐
Go-higher3 小时前
DriverTest 驾考知识卡片学习助手 —— 一款基于 Jetpack Compose 的现代 Android 学习APP
android·学习
安卓修改大师3 小时前
安卓修改大师APK控件修改实战教程
android
阿pin3 小时前
Android随笔-Zygote是什么?
android·zygote
小虎牙0073 小时前
Android kotlin图片库Coil源码详解
android·前端
AFinalStone4 小时前
Android 7系统网络(一)全景图与调用链路概览
android·网络·frameworks
用户86022504674724 小时前
Android DEX 内存 Dump 全流程实战:从 APK 提取到无特征内存盲扫
android
Android-Flutter7 小时前
android compose Brush 渐变和着色器 使用
android·kotlin·compose
杉氧7 小时前
兼容与共生:如何在旧项目中优雅地引入 Compose?
android·架构·android jetpack
Flynt8 小时前
Room 3.0 包名重构 + KMP 迁移:我把项目升级踩了个遍
android·数据库·kotlin