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__() في base/sysimg.jl باستدعاء reinit_stdio() (في base/stream.jl) لإنشاء كائنات جوليا لـ Base.stdin، Base.stdout و Base.stderr.

reinit_stdio() يستخدم ccall لاسترجاع مؤشرات إلى JL_STD* ويستدعي jl_uv_handle_type() لفحص نوع كل دفق. ثم ينشئ كائن 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() في فمتوليسب. تحتوي دالة jl_uv_puts() في جوليا على معالجة خاصة لذلك:

if (stream->type > UV_HANDLE_TYPE_MAX) {
    return ios_write((ios_t*)stream, str, n);
}