Initialization of the Julia runtime
Как выполняет среда выполнения Julia команду julia -e 'println("Hello World!")'
?
main()
Выполнение начинается с main()
in cli/loader_exe.c
, который вызывает jl_load_repl()
в cli/loader_lib.c
, который загружает несколько библиотек, в конечном итоге вызывая jl_repl_entrypoint()
in src/jlapi.c
.
jl_repl_entrypoint()
вызывает libsupport_init()
для установки локали C библиотеки и инициализации библиотеки "ios" (см. ios_init_stdstreams()
и Legacy ios.c
library).
Следующий jl_parse_opts()
вызывается для обработки параметров командной строки. Обратите внимание, что jl_parse_opts()
обрабатывает только параметры, которые влияют на генерацию кода или раннюю инициализацию. Другие параметры обрабатываются позже с помощью exec_options()
in base/client.jl
.
jl_parse_opts()
хранит параметры командной строки в global jl_options
struct.
julia_init()
julia_init()
in init.c
вызывается main()
и вызывает _julia_init()
in init.c
.
_julia_init()
начинает с вызова libsupport_init()
снова (он ничего не делает во второй раз).
restore_signals()
называется для обнуления маски обработчика сигнала.
jl_resolve_sysimg_location()
ищет настроенные пути для базового системного образа. См. Building the Julia system image.
jl_gc_init()
настраивает пулы выделения и списки для слабых ссылок, сохраненных значений и финализации.
jl_init_frontend()
загружает и инициализирует предварительно скомпилированное изображение femtolisp, содержащее сканер/парсер.
jl_init_types()
создает объекты описания типа jl_datatype_t
для built-in types defined in julia.h
. например.
jl_any_type = jl_new_abstracttype(jl_symbol("Any"), core, NULL, jl_emptysvec);
jl_any_type->super = jl_any_type;
jl_type_type = jl_new_abstracttype(jl_symbol("Type"), core, jl_any_type, jl_emptysvec);
jl_int32_type = jl_new_primitivetype(jl_symbol("Int32"), core,
jl_any_type, jl_emptysvec, 32);
jl_init_tasks()
создает объект jl_datatype_t* jl_task_type
; инициализирует глобальную структуру jl_root_task
; и устанавливает jl_current_task
на корневую задачу.
jl_init_codegen()
инициализирует LLVM library.
jl_init_serializer()
инициализирует 8-битные теги сериализации для встроенных значений jl_value_t
.
Если файл sysimg отсутствует (!jl_options.image_file
), то создаются модули Core
и Main
, и выполняется boot.jl
:
jl_core_module = jl_new_module(jl_symbol("Core"))
создает модуль Julia Core
.
jl_init_intrinsic_functions()
создает новый модуль Julia Intrinsics
, содержащий символы константы jl_intrinsic_type
. Эти символы определяют целочисленный код для каждого intrinsic function. emit_intrinsic()
переводит эти символы в инструкции LLVM во время генерации кода.
jl_init_primitives()
связывает функции C с символами функций Julia. Например, символ Core.:(===)()
связан с указателем функции C jl_f_is()
путем вызова add_builtin_func("===", jl_f_is)
.
jl_new_main_module()
создает глобальный модуль "Main" и устанавливает jl_current_task->current_module = jl_main_module
.
Примечание: _julia_init()
then sets jl_root_task->current_module = jl_core_module
. jl_root_task
является псевдонимом jl_current_task
в этот момент, поэтому current_module
, установленный jl_new_main_module()
выше, перезаписывается.
jl_load("boot.jl", sizeof("boot.jl"))
вызывает jl_parse_eval_all
, который многократно вызывает jl_toplevel_eval_flex()
для выполнения boot.jl
. <!– TODO – drill down into eval? –>
jl_get_builtin_hooks()
инициализирует глобальные указатели C для глобальных переменных Julia, определенных в boot.jl
.
jl_init_box_caches()
предварительно выделяет глобальные объекты значений целых чисел для значений до 1024. Это ускоряет выделение упакованных целых чисел позже. например:
jl_value_t *jl_box_uint8(uint32_t x)
{
return boxed_uint8_cache[(uint8_t)x];
}
_julia_init()
iterates над jl_core_module->bindings.table
, ища значения jl_datatype_t
и устанавливая префикс имени типа модуля на jl_core_module
.
jl_add_standard_imports(jl_main_module)
does "using Base" in the "Main" module.
Примечание: _julia_init()
теперь возвращается к jl_root_task->current_module = jl_main_module
, как это было до установки на jl_core_module
выше.
Специфические для платформы обработчики сигналов инициализируются для SIGSEGV
(OSX, Linux) и SIGFPE
(Windows).
Другие сигналы (SIGINFO, SIGBUS, SIGILL, SIGTERM, SIGABRT, SIGQUIT, SIGSYS
и SIGPIPE
) подключены к sigdie_handler()
, который выводит трассировку стека.
jl_init_restored_module()
вызывает jl_module_run_initializer()
для каждого десериализованного модуля, чтобы выполнить функцию __init__()
.
Наконец, sigint_handler()
подключен к SIGINT
и вызывает jl_throw(jl_interrupt_exception)
.
_julia_init()
затем возвращает back to main()
in cli/loader_exe.c
и main()
вызывает repl_entrypoint(argc, (char**)argv)
.
repl_entrypoint()
repl_entrypoint()
загружает содержимое argv[]
в Base.ARGS
.
Если файл ".jl" "программы" был передан в командной строке, то exec_program()
вызывает jl_load(program,len)
, который вызывает jl_parse_eval_all
, который многократно вызывает jl_toplevel_eval_flex()
для выполнения программы.
However, in our example (julia -e 'println("Hello World!")'
), jl_get_global(jl_base_module, jl_symbol("_start"))
looks up Base._start
and jl_apply()
executes it.
Base._start
Base._start
вызывает Base.exec_options
, который вызывает jl_parse_input_line("println("Hello World!")")
, чтобы создать объект выражения, и Core.eval(Main, ex)
, чтобы выполнить разобранное выражение ex
в контексте модуля Main
.
Core.eval
Core.eval(Main, ex)
вызывает jl_toplevel_eval_in(m, ex)
, который вызывает jl_toplevel_eval_flex
. jl_toplevel_eval_flex
реализует простую эвристику, чтобы решить, компилировать ли данный код или выполнять его интерпретатором. Когда ему передают println("Hello World!")
, он обычно решает выполнить код интерпретатором, в этом случае он вызывает jl_interpret_toplevel_thunk
, который затем вызывает eval_body
.
Стековый дамп ниже показывает, как интерпретатор проходит через различные методы Base.println()
и Base.print()
, прежде чем добраться до write(s::IO, a::Array{T}) where T
, который выполняет ccall(jl_uv_write())
.
jl_uv_write()
вызывает uv_write()
, чтобы записать "Hello World!" в JL_STDOUT
. См. Libuv wrappers for stdio.
Hello World!
Stack frame | Source code | Notes |
---|---|---|
jl_uv_write() | jl_uv.c | called though ccall |
julia_write_282942 | stream.jl | function write!(s::IO, a::Array{T}) where T |
julia_print_284639 | ascii.jl | print(io::IO, s::String) = (write(io, s); nothing) |
jlcall_print_284639 | ||
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.print(Base.TTY, String) |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.print(Base.TTY, String, Char, Char...) |
jl_apply() | julia.h | |
jl_f_apply() | builtins.c | |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.println(Base.TTY, String, String...) |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.println(String,) |
jl_apply() | julia.h | |
do_call() | interpreter.c | |
eval_body() | interpreter.c | |
jl_interpret_toplevel_thunk | interpreter.c | |
jl_toplevel_eval_flex | toplevel.c | |
jl_toplevel_eval_in | toplevel.c | |
Core.eval | boot.jl |
Поскольку в нашем примере есть только один вызов функции, который выполнил свою задачу, напечатав "Hello World!", стек теперь быстро разворачивается обратно к main()
.
jl_atexit_hook()
main()
вызывает jl_atexit_hook()
. Это вызывает Base._atexit
, затем вызывает jl_gc_run_all_finalizers()
и очищает дескрипторы libuv.
julia_save()
Наконец, main()
вызывает julia_save()
, который, если запрашивается в командной строке, сохраняет состояние выполнения в новое системное изображение. См. jl_compile_all()
и jl_save_system_image()
.