android中调用native的fork函数后的现象观察

2,689 阅读2分钟

疑问

android的app运行过程中,如果通过jni调用到了native层的fork()函数,那么java层的各对象都会复制一份吗?

建工程做实验

建立一个包含C++的android工程,在工程中的native-lib.cpp中改成这个

#include <jni.h>
#include <string>
#include <unistd.h>

extern "C" JNIEXPORT jint JNICALL
Java_mainpackage_MainActivity_invokeFork(
        JNIEnv *env,
        jobject /* this */) {
    return fork();
}

然后在kotlin中建立一个class

package mainpackage

class SomeBody(var name: String) {
    override fun toString(): String {
        return "($name ${System.identityHashCode(this).toHex()})"
    }

    private fun Int.toHex() = Integer.toHexString(this)
}

然后在MainActivity中写一个函数

    fun studyFork() {
        val sb = SomeBody("张三")
        Log.i("fork", "before fork >> sb: $sb")
        val forkRet = invokeFork()
        Log.i("fork", "after fork >> forkRet: $forkRet pid: ${Process.myPid()} sb: $sb")    
    }

并且在MainActivity被create之后,调用studyFork() 发现输出是:

before fork >> sb: (张三 3e8825b)
after fork >> forkRet: 21281 pid: 21262 sb: (张三 3e8825b)
after fork >> forkRet: 0 pid: 21281 sb: (张三 3e8825b)

观察结果,得出自己的判断,对象sb在主进程和子进程中是同一个,即主进程和子进程共享同一个对象(后面的实验推翻了这个结论)。

新的困惑

主进程和子进程共享虚拟机中同一个堆空间吗?两个进程操作这个对象,观察到的结果是什么呢? 继续做实验,修改了studyFork()函数如下:

    fun studyFork() {
        val sb = SomeBody("张三")

        Log.i("fork", "before fork >> sb: $sb")
        val forkRet = invokeFork()
        Log.i("fork", "after fork >> forkRet: $forkRet pid: ${Process.myPid()} sb: $sb")

        if (forkRet == 0) { // 子进程
            sb.name = "李四"
            Log.i("fork", "sub process >> forRet: $forkRet modified-sb: $sb")
        } else { // 父进程
            Thread.sleep(1000)
            Log.i("fork", "parent process >> forRet: $forkRet sb: $sb")
        }
    }

让子进程创建后立刻修改sb的name为“李四”,然后父进程1s之后观察这个sb并打印日志。日志如下:

2019-04-03 18:33:24.208  I/fork: before fork >> sb: (张三 3e8825b)
2019-04-03 18:33:24.214  I/fork: after fork >> forkRet: 0 pid: 22143 sb: (张三 3e8825b)
2019-04-03 18:33:24.214  I/fork: after fork >> forkRet: 22143 pid: 22123 sb: (张三 3e8825b)
2019-04-03 18:33:24.215  I/fork: sub process >> forRet: 0 modified-sb: (李四 3e8825b)
2019-04-03 18:33:25.219  I/fork: parent process >> forRet: 22143 sb: (张三 3e8825b)

于是得出自己的判断:子进程修改了sb对象的name,主进程感知不到。进而得出这样的结论,主进程和子进程不共享同一个对象,只是各自持有一份sb拷贝。进而,两个进程不共享虚拟机的同一个堆空间。