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

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui