无话不谈|离 Linux 内核有多远?,Java( 四 )


FUNC(%esp)对应&start_thread ,
STACK(%esp)对应stackaddr ,
ARG(%esp)对应pd(新进程传递给start_thread的参数) 。
第1步 , 将新进程的栈stackaddr赋值给ecx , 确保它的值不为0 。 第2步 , 将pd、&start_thread和0存入新线程的栈 , 对当前进程的栈无影响 。 第3步 , 将当前进程的三个寄存器的值入栈 , esp寄存器的值相应减12 。 第4步 , 准备系统调用 , 其中将FLAGS+12(%esp)存入ebx , 对应clone_flags , 将clone的系统调用号存入eax 。 第5步 , 将clone_flags存入新进程的栈中 。 第6步 , 使用int指令发起系统调用 , 交给内核创建新线程 。 截止到此处 , 所有的代码都是当前进程执行的 , 新线程并没有执行 。 从第7步开始的代码 , 当前进程和新线程都会执行 。 对当前进程而言 , 程序将它第3步入栈的寄存器出栈 。 但对新线程而言 , 它是从内核的ret_from_fork执行的 , 切换到用户态后 , 它的栈已经成为stackaddr了 , 所以它的edi等于clone_flags , esi等于0 , ebx等于&start_thread 。 系统调用的结果由eax返回 , 第8步判断clone系统调用的结果 , 对当前进程而言 , clone系统调用如果成功返回的是新线程在它的pidnamespace中的id , 大于0 , 所以它执行ret退出__clone函数 。 对新线程而言 , clone系统调用的返回值等于0 , 所以它执行L(thread_start)处的代码 。 clone_flags的CLONE_VM标志被置位的情况下 , 会执行call*%ebx , ebx等于&start_thread , 至此start_thread得到了执行 , 它又调用了提供给pthread_create的start_routine , 结束 。 ”如此看来 , Java→JVM→glibc→内核 , 好像也没有多远 。


推荐阅读