printf() and stdio in the Julia runtime
Libuv wrappers for stdio
julia.h
определяет libuv обертки для потоков stdio.h
:
uv_stream_t *JL_STDIN;
uv_stream_t *JL_STDOUT;
uv_stream_t *JL_STDERR;
... и соответствующие функции вывода:
int jl_printf(uv_stream_t *s, const char *format, ...);
int jl_vprintf(uv_stream_t *s, const char *format, va_list args);
Эти функции printf
используются файлами .c
в директориях src/
и cli/
везде, где требуется stdio, чтобы гарантировать, что буферизация вывода обрабатывается единообразно.
In special cases, like signal handlers, where the full libuv infrastructure is too heavy, jl_safe_printf()
can be used to write(2)
directly to STDERR_FILENO
:
void jl_safe_printf(const char *str, ...);
Interface between JL_STD* and Julia code
Base.stdin
, Base.stdout
и Base.stderr
связаны с библиотекой JL_STD*
потоков libuv, определенных в среде выполнения.
Функция __init__()
Julia (в base/sysimg.jl
) вызывает reinit_stdio()
(в base/stream.jl
), чтобы создать объекты Julia для Base.stdin
, Base.stdout
и Base.stderr
.
reinit_stdio()
использует ccall
для получения указателей на JL_STD*
и вызывает jl_uv_handle_type()
, чтобы проверить тип каждого потока. Затем он создает объект Julia Base.IOStream
, Base.TTY
или Base.PipeEndpoint
, чтобы представить каждый поток, например:
$ julia -e 'println(typeof((stdin, stdout, stderr)))'
Tuple{Base.TTY,Base.TTY,Base.TTY}
$ julia -e 'println(typeof((stdin, stdout, stderr)))' < /dev/null 2>/dev/null
Tuple{IOStream,Base.TTY,IOStream}
$ echo hello | julia -e 'println(typeof((stdin, stdout, stderr)))' | cat
Tuple{Base.PipeEndpoint,Base.PipeEndpoint,Base.TTY}
Методы Base.read
и Base.write
для этих потоков используют ccall
для вызова оберток libuv в src/jl_uv.c
, например:
stream.jl: function write(s::IO, p::Ptr, nb::Integer)
-> ccall(:jl_uv_write, ...)
jl_uv.c: -> int jl_uv_write(uv_stream_t *stream, ...)
-> uv_write(uvw, stream, buf, ...)
printf() during initialization
Потоки libuv, на которые полагается jl_printf()
и т.д., недоступны до середины инициализации времени выполнения (см. init.c
, init_stdio()
). Сообщения об ошибках или предупреждения, которые необходимо напечатать до этого, перенаправляются в стандартную библиотеку C fwrite()
с помощью следующего механизма:
В sys.c
указатели потока JL_STD*
статически инициализируются целочисленными константами: STD*_FILENO (0, 1 и 2)
. В jl_uv.c
функция jl_uv_puts()
проверяет свой аргумент uv_stream_t* stream
и вызывает fwrite()
, если поток установлен на STDOUT_FILENO
или STDERR_FILENO
.
Это позволяет единообразно использовать jl_printf()
на протяжении всего времени выполнения, независимо от того, доступен ли какой-либо конкретный фрагмент кода до завершения инициализации.
Legacy ios.c
library
Библиотека src/support/ios.c
унаследована от femtolisp. Она предоставляет кроссплатформенный буферизованный ввод-вывод файлов и временные буферы в памяти.
ios.c
все еще используется:
src/flisp/*.c
src/dump.c
– для сериализации файлового ввода-вывода и для буферов памяти.src/staticdata.c
– для сериализации файлового ввода-вывода и для буферов памяти.base/iostream.jl
– для ввода-вывода файлов (см.base/fs.jl
для эквивалента libuv).
Использование ios.c
в этих модулях в основном является самодостаточным и отделено от системы ввода-вывода libuv. Однако есть one place, где femtolisp вызывает jl_printf()
с помощью устаревшего потока ios_t
.
В ios.h
есть хак, который выравнивает поле ios_t.bm
с uv_stream_t.type
и гарантирует, что значения, используемые для ios_t.bm
, не пересекаются с допустимыми значениями UV_HANDLE_TYPE
. Это позволяет указателям uv_stream_t
указывать на потоки ios_t
.
Это необходимо, потому что вызывающему jl_printf()
jl_static_show()
передается поток ios_t
функцией fl_print()
из femtolisp. Функция Julia jl_uv_puts()
имеет специальную обработку для этого:
if (stream->type > UV_HANDLE_TYPE_MAX) {
return ios_write((ios_t*)stream, str, n);
}