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

相关推荐
码农coding10 小时前
android12 开机动画分析
android
又见情义12 小时前
RK3568 Android 13 开机时间优化-禁用非必要系统服务
android
Android-Flutter12 小时前
Java Thread 和 Runnable 详解
android
mmsx12 小时前
osmdroid Overlay 体系详解:比例尺/罗盘/总绘图层有序叠放
android·源码·地图·osmdroid
又见情义14 小时前
RK3568 Android 13 开机时间优化-HAL 服务延时启动
android
v_348360876215 小时前
Android8 开机动画结束分析
android
美狐美颜SDK开放平台17 小时前
直播APP开发核心功能详解:美颜SDK、音视频、礼物等功能成本怎么计算?
android·人工智能·计算机视觉·音视频·直播美颜sdk
安河桥。17 小时前
Android视频流处理模块硬件加速器数据流转技术说明
android·图像处理·isp
hunterandroid17 小时前
[Android 从零到一] APK 体积优化实战:从 50MB 到 15MB 的瘦身之旅
android·前端