Julia 运行时的初始化
Execution starts at main()
in ui/repl.c
.
main()
calls to set the C library locale and to initialize the “ios” library (see ios_init_stdstreams()
and ).
Next jl_parse_opts()
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 .
jl_parse_opts()
stores command line options in the global jl_options
struct.
julia_init()
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: 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 SIGINT
and calls jl_throw(jl_interrupt_exception)
.
_julia_init()
then returns and main()
calls true_main(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 .
true_main()
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
Base._start
calls which calls jl_parse_input_line("println("Hello World!")")
to create an expression object and to execute it.
Base.eval()
was by jl_init_primitives()
.
jl_f_top_eval()
calls , where ex
is the parsed expression println("Hello World!")
.
jl_toplevel_eval_in()
calls which calls eval()
in interpreter.c
.
The stack dump below shows how the interpreter works its way through various methods of and Base.print()
before arriving at which does ccall(jl_uv_write())
.
jl_uv_write()
calls uv_write()
to write “Hello World!” to JL_STDOUT
. See .:
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()
.
jlatexithook()
main()
calls . This calls _atexit
for each module, then calls jl_gc_run_all_finalizers()
and cleans up libuv handles.
Finally, main()
calls , which if requested on the command line, saves the runtime state to a new system image. See jl_compile_all()
and .