初始化目的
准备 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)
...
}