Ruby 2.x 源代碼學習:bootstrap

初始化目的

  • 準備 Ruby 執行環境,包括主線程,線程堆棧,垃圾蒐集器linux

  • 初始化Ruby 內置的類和方法,Ruby 內置類型是經過 C 語言實現的,"固化"在解釋器裏面bootstrap

流程

main @ main.c
    ruby_sysinit @ ruby.c
    RUBY_INIT_STACK
    ruby_init @ eval.c
        ruby_setup @ eval.c

ruby_sysinit

ruby_sysinit 初始化 系統輸入輸出文件描述符ruby

RUBY_INIT_STACK

RUBY_INIT_STACK 宏定義用於獲取 線程堆棧信息,包括堆棧開始地址及其大小jsp

#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 函數this

ruby_setup

  • 若是 GET_VM 返回非 0 值,即 VM 已經已經初始化過,直接返回spa

  • 初始化線程堆棧,對於從 main.c 函數進入 ruby_setup 函數來講,ruby_init_stack 什麼也不作,RUBY_INIT_STACK 宏已經初始化過堆棧了線程

  • Init_BareVM(VM bootstrap: phase 1)code

  • Init_heap,初始化垃圾蒐集器(GC)get

  • 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

相關文章
相關標籤/搜索