Ruby 2.x 源代码学习:bootstrap
初始化目的
准备 Ruby 执行环境,包括主线程,线程堆栈,垃圾搜集器
初始化Ruby 内置的类和方法,Ruby 内置类型是通过 C 语言实现的,"固化"在解释器里面
流程
main @ main.c ruby_sysinit @ ruby.c RUBY_INIT_STACK ruby_init @ eval.c ruby_setup @ eval.c
ruby_sysinit
ruby_sysinit 初始化 系统输入输出文件描述符
RUBY_INIT_STACK
RUBY_INIT_STACK 宏定义用于获取 线程堆栈信息,包括堆栈开始地址及其大小
#define RUBY_INIT_STACK \ VALUE variable_in_this_stack_frame; \ ruby_init_stack(&variable_in_this_stack_frame);
对于 linux 系统,ruby_init_stack 在 thread_pthread.c 文件中定义,根据不同的编译条件,使用不同的方法获取线程堆栈信息,native_main_thread 结构体用于存储线程堆栈信息
// thread_pthread.c static struct { rb_nativethread_id_t id; size_t stack_maxsize; VALUE *stack_start; #ifdef __ia64 VALUE *register_stack_start; #endif } native_main_thread; /* Set stack bottom of Ruby implementation. * * You must call this function before any heap allocation by Ruby implementation. * Or GC will break living objects */ void ruby_init_stack(volatile VALUE *addr #ifdef __ia64 , void *bsp #endif ) { native_main_thread.id = pthread_self(); #if MAINSTACKADDR_AVAILABLE if (native_main_thread.stack_maxsize) return; { void* stackaddr; size_t size; if (get_main_stack(&stackaddr, &size) == 0) { native_main_thread.stack_maxsize = size; native_main_thread.stack_start = stackaddr; reserve_stack(stackaddr, size); goto bound_check; } }
ruby_init
ruby_init 调用 ruby_setup 函数,所以真正干活的是 ruby_setup 函数
ruby_setup
如果 GET_VM 返回非 0 值,即 VM 已经已经初始化过,直接返回
初始化线程堆栈,对于从 main.c 函数进入 ruby_setup 函数来说,ruby_init_stack 什么也不做,RUBY_INIT_STACK 宏已经初始化过堆栈了
Init_BareVM(VM bootstrap: phase 1)
Init_heap,初始化垃圾搜集器(GC)
Init_vm_objects
rb_call_inits,调用 Init_XXX 方法,初始化内置类,模块 .etc
// eval.c int ruby_setup(void) { int state; if (GET_VM()) return 0; ruby_init_stack((void *)&state); Init_BareVM(); Init_heap(); Init_vm_objects(); PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { rb_call_inits(); ruby_prog_init(); GET_VM()->running = 1; } POP_TAG(); return state; }
Init_BareVM
// vm.c void Init_BareVM(void) { /* VM bootstrap: phase 1 */ // 创建 rb_vm_t 结构体,rb_vm_t 结构体是对 Ruby 虚拟机的抽象 rb_vm_t * vm = ruby_mimmalloc(sizeof(*vm)); // 创建 rb_thread_t 结构体,rb_thread_t 结构体是对 虚拟机线程的抽象 rb_thread_t * th = ruby_mimmalloc(sizeof(*th)); if (!vm || !th) { fprintf(stderr, "[FATAL] failed to allocate memory\n"); exit(EXIT_FAILURE); } MEMZERO(th, rb_thread_t, 1); rb_thread_set_current_raw(th); vm_init2(vm); // GC 相关初始化,创建 rb_objspace vm->objspace = rb_objspace_alloc(); ruby_current_vm = vm; Init_native_thread(); th->vm = vm; // 初始化(当前) Ruby 虚拟机线程 th_init(th, 0); ruby_thread_init_stack(th); }
th_init 函数很重要,它会初始化我们之前提到的 Ruby 虚拟机 栈帧(rb_control_frame_struct)
th_init
// vm.c static void th_init(rb_thread_t *th, VALUE self) { th->self = self; /* allocate thread stack */ #ifdef USE_SIGALTSTACK /* altstack of main thread is reallocated in another place */ th->altstack = malloc(rb_sigaltstack_size()); #endif /* th->stack_size is word number. * th->vm->default_params.thread_vm_stack_size is byte size. */ // 设置虚拟机线程堆栈大小 th->stack_size = th->vm->default_params.thread_vm_stack_size / sizeof(VALUE); // 分配虚拟机线程堆栈 th->stack = thread_recycle_stack(th->stack_size); // 初始化 th->cfp 指向虚拟机线程堆栈顶部 th->cfp = (void *)(th->stack + th->stack_size); // 重要!!!见下文 vm_push_frame(th, 0 /* dummy iseq */, VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_CFRAME /* dummy frame */, Qnil /* dummy self */, VM_BLOCK_HANDLER_NONE /* dummy block ptr */, 0 /* dummy cref/me */, 0 /* dummy pc */, th->stack, 0, 0); th->status = THREAD_RUNNABLE; th->errinfo = Qnil; th->last_status = Qnil; th->waiting_fd = -1; th->root_svar = Qfalse; th->local_storage_recursive_hash = Qnil; th->local_storage_recursive_hash_for_trace = Qnil; #ifdef NON_SCALAR_THREAD_ID th->thread_id_string[0] = '\0'; #endif #if OPT_CALL_THREADED_CODE th->retval = Qundef; #endif th->name = Qnil; }
Ruby 作为一个脚本语言,允许直接执行语句,而不像 Java,需要提供一个静态方法作为程序的入口点
所以 th_init 函数压入了一个初始的栈帧,这个栈帧的很多参数都为 0(dummy),栈帧中的 sp 等于 th->stack,即线程堆栈的基地址
Init_heap
Init_vm_objects
rb_call_inits
rb_call_inits 通过调用 Init_XXX 方法,初始化(加载) Ruby 内部类,XXX 是类名或模块名
// inits.c #define CALL(n) {void Init_##n(void); Init_##n();} void rb_call_inits(void) { ... // Init_String CALL(String); // Init_Hash CALL(Hash); // Init_Thread CALL(Thread) ... }