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)
    ...
}

ruby_prog_init

相关推荐