printf() and stdio in the Julia runtime
Libuv wrappers for stdio
julia.h
は stdio.h
ストリームのための libuv ラッパーを定義しています:
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
関数は、出力バッファリングが統一された方法で処理されることを保証するために、src/
および cli/
ディレクトリ内の .c
ファイルで必要に応じて使用されます。
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__()
関数(base/sysimg.jl
内)は、reinit_stdio()
(base/stream.jl
内)を呼び出して、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
を使用して src/jl_uv.c
内の libuv ラッパーを呼び出します。例えば:
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
jl_printf()
などで使用される libuv ストリームは、ランタイムの初期化の途中まで利用できません(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
引数をチェックし、ストリームがSTDOUT_FILENO
またはSTDERR_FILENO
に設定されている場合にfwrite()
を呼び出します。
これにより、初期化が完了する前に特定のコードが到達可能かどうかに関係なく、ランタイム全体で jl_printf()
を一貫して使用できるようになります。
Legacy ios.c
library
src/support/ios.c
ライブラリはfemtolispから継承されています。これは、クロスプラットフォームのバッファ付きファイルIOとメモリ内一時バッファを提供します。
ios.c
はまだ以下で使用されています:
src/flisp/*.c
src/dump.c
– シリアル化ファイルIOおよびメモリバッファ用。src/staticdata.c
– シリアル化ファイルIOおよびメモリバッファ用。base/iostream.jl
– ファイル入出力用 (libuvの同等物についてはbase/fs.jl
を参照)。
ios.c
のこれらのモジュールでの使用は、主に自己完結型であり、libuv I/Oシステムから分離されています。しかし、one placeでは、femtolispがレガシーなios_t
ストリームを使用してjl_printf()
を呼び出します。
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()
がfemtolispのfl_print()
関数によってios_t
ストリームを渡されるために必要です。Juliaのjl_uv_puts()
関数はこれに特別な処理を行っています:
if (stream->type > UV_HANDLE_TYPE_MAX) {
return ios_write((ios_t*)stream, str, n);
}