Julia 运行时的初始化

    Execution starts at , which calls jl_load_repl() in cli/loader_lib.c which loads a few libraries, eventually calling .

    repl_entrypoint() calls libsupport_init() to set the C library locale and to initialize the “ios” library (see and Legacy ios.c library).

    Next is called to process command line options. Note that jl_parse_opts() only deals with options that affect code generation or early initialization. Other options are handled later by process_options() in base/client.jl.

    jl_parse_opts() stores command line options in the .

    )

    julia_init() in task.c is called by main() and calls .

    _julia_init() begins by calling libsupport_init() again (it does nothing the second time).

    restore_signals() is called to zero the signal handler mask.

    searches configured paths for the base system image. See Building the Julia system image.

    sets up allocation pools and lists for weak refs, preserved values and finalization.

    jl_init_frontend() loads and initializes a pre-compiled femtolisp image containing the scanner/parser.

    creates jl_datatype_t type description objects for the built-in types defined in julia.h. e.g.

    creates the jl_datatype_t* jl_task_type object; initializes the global jl_root_task struct; and sets jl_current_task to the root task.

    jl_init_codegen() initializes the .

    jl_init_serializer() initializes 8-bit serialization tags for builtin jl_value_t values.

    If there is no sysimg file (!jl_options.image_file) then the Core and Main modules are created and boot.jl is evaluated:

    creates a new Julia module Intrinsics containing constant jl_intrinsic_type symbols. These define an integer code for each intrinsic function. translates these symbols into LLVM instructions during code generation.

    jl_init_primitives() hooks C functions up to Julia function symbols. e.g. the symbol Core.:(===)() is bound to C function pointer jl_f_is() by calling add_builtin_func("===", jl_f_is).

    creates the global “Main” module and sets jl_current_task->current_module = jl_main_module.

    Note: _julia_init() then sets jl_root_task->current_module = jl_core_module. jl_root_task is an alias of jl_current_task at this point, so the current_module set by jl_new_main_module() above is overwritten.

    calls jl_parse_eval_all which repeatedly calls to execute boot.jl. <!– TODO – drill down into eval? –>

    initializes global C pointers to Julia globals defined in boot.jl.

    jl_init_box_caches() pre-allocates global boxed integer value objects for values up to 1024. This speeds up allocation of boxed ints later on. e.g.:

    over the jl_core_module->bindings.table looking for jl_datatype_t values and sets the type name’s module prefix to jl_core_module.

    jl_add_standard_imports(jl_main_module) does “using Base” in the “Main” module.

    Note: _julia_init() now reverts to jl_root_task->current_module = jl_main_module as it was before being set to jl_core_module above.

    Platform specific signal handlers are initialized for SIGSEGV (OSX, Linux), and SIGFPE (Windows).

    Other signals (SIGINFO, SIGBUS, SIGILL, SIGTERM, SIGABRT, SIGQUIT, SIGSYS and SIGPIPE) are hooked up to which prints a backtrace.

    jl_init_restored_modules() calls for each deserialized module to run the __init__() function.

    Finally sigint_handler() is hooked up to and calls jl_throw(jl_interrupt_exception).

    _julia_init() then returns and main() calls repl_entrypoint(argc, (char**)argv).

    If there is a sysimg file, it contains a pre-cooked image of the Core and Main modules (and whatever else is created by boot.jl). See Building the Julia system image.

    deserializes the saved sysimg into the current Julia runtime environment and initialization continues after jl_init_box_caches() below…

    Note: jl_restore_system_image() (and staticdata.c in general) uses the .

    repl_entrypoint() loads the contents of argv[] into .

    If a .jl “program” file was supplied on the command line, then exec_program() calls which calls jl_parse_eval_all which repeatedly calls to execute the program.

    However, in our example (julia -e 'println("Hello World!")'), jl_get_global(jl_base_module, jl_symbol(“_start”)) looks up and jl_apply() executes it.

    Base._start

    calls Base.process_options which calls to create an expression object and Base.eval() to execute it.

    was mapped to jl_f_top_eval by jl_init_primitives().

    calls jl_toplevel_eval_in(jl_main_module, ex), where ex is the parsed expression println("Hello World!").

    calls jl_toplevel_eval_flex() which calls .

    The stack dump below shows how the interpreter works its way through various methods of Base.println() and before arriving at write(s::IO, a::Array{T}) where T which does ccall(jl_uv_write()).

    calls uv_write() to write “Hello World!” to JL_STDOUT. See Libuv wrappers for stdio.:

    Since our example has just one function call, which has done its job of printing “Hello World!”, the stack now rapidly unwinds back to main().

    jl_atexit_hook())

    main() calls . This calls Base._atexit, then calls jl_gc_run_all_finalizers() and cleans up libuv handles.

    Finally, calls , which if requested on the command line, saves the runtime state to a new system image. See jl_compile_all() and .