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

相关推荐
执明wa7 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio
404_coder8 小时前
源码视角下的 Android 开机流程:从 Zygote、SystemServer 到 Launcher
android
二流小码农8 小时前
鸿蒙开发:以登录案例了解代码架构MVVM
android·ios·harmonyos
用户69371750013849 小时前
从代码生产者到 AI 协作者:软件工程师的角色重构
android·前端·后端
GitLqr9 小时前
别在 Flutter 的 main() 里乱锁屏幕方向,小心 iPad 分屏功能被你搞没了
android·flutter·ios
sg_knight9 小时前
MySQL 存储过程详解:从入门到实战
android·数据库·mysql·database·dba·关系型数据库·db
爱笑鱼9 小时前
Binder(四):ioctl(BINDER_WRITE_READ) 之后,事务怎样到达目标进程?
android
AFinalStone9 小时前
Android 7系统休眠唤醒(二)开机全链路—BootROM到Launcher
android·电源管理·休眠唤醒
Mr YiRan10 小时前
Android NDK开发之统计到未被回收的图片
android