LIBUV(1) libuv API documentation LIBUV(1)

libuv - کتابخانه پشتیبانی از ورودی/خروجی ناهمگام و رویدادمحور چندسکویی

کتابخانه libuv یک کتابخانه پشتیبانی چندسکویی با تمرکز بر ورودی/خروجی ناهمگام است. این کتابخانه در ابتدا برای استفاده در Node.js https://nodejs.org توسعه داده شد، اما توسط Luvit https://luvit.io، Julia https://julialang.org، uvloop https://github.com/MagicStack/uvloop و پروژه‌های دیگر https://github.com/libuv/libuv/blob/v1.x/LINKS.md نیز استفاده می‌شود.

نکته:

در صورت مشاهده خطا در این مستندات، می‌توانید با ارسال درخواست‌های پول (Pull Requests) در https://github.com/libuv/libuv به بهبود آن کمک کنید!

  • حلقه رویداد کامل با پشتیبانی از epoll، kqueue، IOCP و event ports.
  • سوکت‌های ناهمگام TCP و UDP
  • تفکیک نام دامنه (DNS) به‌صورت ناهمگام
  • عملیات ناهمگام فایل و سیستم فایل
  • رویدادهای سیستم فایل
  • پایانه TTY کنترل‌شده با کدهای گریز ANSI
  • ارتباط بین‌پردازشی (IPC) همراه با اشتراک‌گذاری سوکت، با استفاده از سوکت‌های دامنه یونیکس یا لوله‌های نام‌گذاری‌شده (ویندوز)
  • فرآیندهای فرزند (Child processes)
  • استخر ریسه‌ها (thread pool)
  • مدیریت سیگنال‌ها
  • ساعت با دقت بالا (High resolution clock)
  • مفاهیم پایه‌ای ریسه‌بندی و همگام‌سازی

کتابخانه libuv یک کتابخانه پشتیبانی چندسکویی است که در ابتدا برای Node.js https://nodejs.org نوشته شد. این کتابخانه بر اساس مدل ورودی/خروجی ناهمگام و رویدادمحور طراحی شده است.

این کتابخانه امکاناتی بسیار فراتر از یک لایه انتزاعی ساده بر روی سازوکارهای مختلف پایش (polling) ورودی/خروجی ارائه می‌دهد: «هندل‌ها» (handles) و «جریان‌ها» (streams) یک انتزاع سطح بالا برای سوکت‌ها و دیگر موجودیت‌ها فراهم می‌کنند؛ همچنین قابلیت‌های چندسکویی ورودی/خروجی فایل و کار با ریسه‌ها نیز در میان سایر موارد ارائه شده است.

نمودار زیر بخش‌های گوناگون تشکیل‌دهنده libuv و زیرسیستم مربوط به هر یک را نشان می‌دهد: [image]

کتابخانه libuv در ترکیب با حلقه رویداد، ۲ مفهوم انتزاعی برای کار در اختیار کاربران قرار می‌دهد: هندل‌ها (handles) و درخواست‌ها (requests).

هندل‌ها نشان‌دهنده اشیایی با طول عمر بالا هستند که قادرند در زمان فعال بودن عملیات خاصی را انجام دهند. چند مثال:

  • یک هندل آماده‌سازی (prepare handle) که در زمان فعال بودن، کالبک آن یک بار در هر تکرار حلقه فراخوانی می‌شود.
  • یک هندل سرور TCP که هر بار با برقراری یک اتصال جدید، کالبک اتصال آن فراخوانی می‌شود.

درخواست‌ها معمولاً نشان‌دهنده عملیاتی با طول عمر کوتاه هستند. این عملیات می‌توانند روی یک هندل انجام شوند: درخواست‌های نوشتن برای نوشتن داده روی یک هندل استفاده می‌شوند؛ یا به‌صورت مستقل عمل کنند: درخواست‌های getaddrinfo نیازی به هندل ندارند و مستقیماً روی حلقه رویداد اجرا می‌شوند.

رهنمودهای کار با هندل‌ها و درخواست‌ها:

1.
اگر uv_foo_init() در مقداردهی اولیه هندل موفق شد، باید uv_close() <#c.uv_close> را فراخوانی کنید. اگر تابع مقداردهی اولیه هندل با خطا مواجه شود، نیازی به انجام کاری ندارید.
2.
فقط هندل‌ها بسته می‌شوند، نه درخواست‌ها. برای مثال، uv_tcp_t <#c.uv_tcp_t> یک هندل است و uv_write_t <#c.uv_write_t> یک درخواست است.
3.
حافظه هندل فقط از داخل کالبک uv_close_cb <#c.uv_close_cb> یا پس از آن قابل آزادسازی یا استفاده مجدد است، نه قبل از آن.
4.
بیشتر هندل‌ها دارای توابع init + start/stop هستند؛ برخی هندل‌ها این‌گونه نیستند. مثال: uv_tcp_t <#c.uv_tcp_t> در مقایسه با uv_process_t <#c.uv_process_t>؛ تابع uv_spawn() <#c.uv_spawn> مقداردهی اولیه هندل و شروع فرآیند را در یک تابع ترکیب می‌کند.
5.
درخواست‌ها پس از تکمیل، یا در صورت لغو شدن با uv_cancel() <#c.uv_cancel>، به‌صورت خودکار بسته می‌شوند.
6.
هیچ پاک‌سازی اضافه‌ای لازم نیست مگر برای درخواست‌های uv_fs_t <#c.uv_fs_t> و uv_getaddrinfo_t <#c.uv_getaddrinfo_t>. برای uv_fs_t <#c.uv_fs_t>، پس از اتمام کار تابع uv_fs_req_cleanup() <#c.uv_fs_req_cleanup> را فراخوانی کنید؛ برای uv_getaddrinfo_t <#c.uv_getaddrinfo_t>، این تابع uv_freeaddrinfo() <#c.uv_freeaddrinfo> است.
7.
حافظه درخواست فقط از آن نقطه به بعد قابل آزادسازی یا استفاده مجدد است.

حلقه ورودی/خروجی (یا حلقه رویداد) بخش مرکزی libuv است. این حلقه بستر تمام عملیات ورودی/خروجی را مشخص می‌کند و باید به یک ریسه (thread) منفرد متصل باشد. تا زمانی که هر حلقه در ریسه‌ای جداگانه اجرا شود، می‌توان چندین حلقه رویداد را همزمان اجرا کرد. حلقه رویداد libuv (یا هر API دیگری که شامل حلقه یا هندل‌ها باشد) به جز در مواردی که صراحتاً خلاف آن ذکر شده باشد، از نظر همروندی ریسه‌ها امن نیست (thread-safe نیست).

حلقه رویداد از رویکرد رایج ورودی/خروجی ناهمگام تک‌ریسه‌ای پیروی می‌کند: تمام ورودی/خروجی‌های (شبکه) روی سوکت‌های مسدودنشدنی (non-blocking) انجام می‌شوند که با استفاده از بهترین سازوکار موجود در سکوی مربوطه پایش (poll) می‌شوند: epoll در لینوکس، kqueue در OSX و دیگر سیستم‌های BSD، event ports در SunOS و IOCP در ویندوز. به عنوان بخشی از یک تکرار حلقه، حلقه منتظر فعالیت ورودی/خروجی روی سوکت‌هایی می‌ماند که به پایشگر (poller) اضافه شده‌اند و کالبک‌هایی اجرا می‌شوند که وضعیت سوکت را مشخص می‌کنند (خواندنی، نوشتنی، قطع اتصال) تا هندل‌ها بتوانند عملیات خواندن، نوشتن یا ورودی/خروجی مورد نظر را انجام دهند.

برای درک بهتر نحوه عملکرد حلقه رویداد، نمودار زیر تمام مراحل یک تکرار حلقه را نشان می‌دهد: [image]

1.
مفهوم زمان جاری («اکنون» یا now) در حلقه مقداردهی اولیه می‌شود.
2.
اگر حلقه با UV_RUN_DEFAULT اجرا شده باشد، تایمرهای سررسیدشده اجرا می‌شوند. کالبک‌های تمام تایمرهای فعالی که زمان‌بندی آن‌ها قبل از زمان now حلقه باشد، فراخوانی می‌شوند.
3.
اگر حلقه زنده (alive) باشد یک تکرار آغاز می‌شود، در غیر این صورت حلقه فوراً پایان می‌یابد. چه زمانی یک حلقه زنده در نظر گرفته می‌شود؟ اگر حلقه‌ای دارای هندل‌های فعال و دارای ارجاع (ref'd)، درخواست‌های فعال یا هندل‌های در حال بسته شدن باشد، زنده در نظر گرفته می‌شود.
4.
کالبک‌های معوق (Pending) فراخوانی می‌شوند. در بیشتر موارد، تمام کالبک‌های ورودی/خروجی بلافاصله پس از پایش (polling) ورودی/خروجی فراخوانی می‌شوند. با این حال، مواردی وجود دارد که فراخوانی یک کالبک به تکرار بعدی حلقه موکول می‌شود. اگر تکرار قبلی کالبک ورودی/خروجی را به تعویق انداخته باشد، در این مرحله اجرا خواهد شد.
5.
کالبک‌های هندل‌های بیکار (idle handles) فراخوانی می‌شوند. بر خلاف نام نه‌چندان مناسبشان، هندل‌های بیکار در صورت فعال بودن در تمام تکرارهای حلقه اجرا می‌شوند.
6.
کالبک‌های هندل‌های آماده‌سازی (prepare handles) فراخوانی می‌شوند. کالبک‌های هندل‌های آماده‌سازی دقیقاً پیش از آنکه حلقه برای ورودی/خروجی مسدود شود، فراخوانی می‌شوند.
7.
مهلت زمانی پایش (Poll timeout) محاسبه می‌شود. پیش از مسدود شدن برای ورودی/خروجی، حلقه مدت‌زمان مسدود ماندن را محاسبه می‌کند. قوانین محاسبه این مهلت زمانی به شرح زیر است:
  • اگر حلقه با فلگ UV_RUN_NOWAIT اجرا شده باشد، مهلت زمانی 0 است.
  • اگر قرار باشد حلقه متوقف شود (تابع uv_stop() <#c.uv_stop> فراخوانی شده باشد)، مهلت زمانی 0 است.
  • اگر هیچ هندل یا درخواست فعالی وجود نداشته باشد، مهلت زمانی 0 است.
  • اگر هرگونه هندل بیکار (idle) فعالی وجود داشته باشد، مهلت زمانی 0 است.
  • اگر هندلی در انتظار بسته شدن باشد، مهلت زمانی 0 است.
  • اگر هیچ‌یک از موارد بالا مطابقت نداشته باشد، مهلت زمانی نزدیک‌ترین تایمر در نظر گرفته می‌شود، یا در صورت نبود تایمر فعال، بی‌نهایت در نظر گرفته می‌شود.
8.
حلقه برای ورودی/خروجی مسدود می‌شود. در این مرحله حلقه برای مدتی که در گام پیشین محاسبه شد، برای ورودی/خروجی مسدود می‌ماند. تمام هندل‌های مرتبط با ورودی/خروجی که یک توصیف‌کننده فایل (file descriptor) را برای عملیات خواندن یا نوشتن تحت نظر داشتند، در این مرحله کالبک‌هایشان فراخوانی می‌شود.
9.
کالبک‌های هندل‌های بررسی (check handles) فراخوانی می‌شوند. کالبک‌های هندل‌های بررسی بلافاصله پس از آنکه حلقه از حالت مسدود برای ورودی/خروجی خارج شد، فراخوانی می‌شوند. هندل‌های بررسی در واقع همتای هندل‌های آماده‌سازی هستند.
10.
کالبک‌های بستن فراخوانی می‌شوند. اگر هندلی با فراخوانی uv_close() <#c.uv_close> بسته شده باشد، کالبک بستن آن فراخوانی خواهد شد.
11.
مفهوم زمان جاری («اکنون» یا now) در حلقه به‌روزرسانی می‌شود.
12.
تایمرهای سررسیدشده اجرا می‌شوند. توجه داشته باشید که «اکنون» تا تکرار بعدی حلقه مجدداً به‌روزرسانی نمی‌شود. بنابراین اگر تایمری در حین پردازش تایمرهای دیگر سررسید شود، تا تکرار بعدی حلقه رویداد اجرا نخواهد شد.
13.
تکرار پایان می‌یابد. اگر حلقه در حالت‌های UV_RUN_NOWAIT یا UV_RUN_ONCE اجرا شده باشد، تکرار به پایان می‌رسد و تابع uv_run() <#c.uv_run> بازمی‌گردد. اگر حلقه با UV_RUN_DEFAULT اجرا شده باشد و هنوز زنده باشد از ابتدا ادامه می‌یابد، در غیر این صورت آن نیز پایان می‌یابد.

مهم:

کتابخانه libuv از یک استخر ریسه‌ها (thread pool) برای ممکن ساختن عملیات ناهمگام ورودی/خروجی فایل استفاده می‌کند، اما ورودی/خروجی شبکه همواره در یک ریسه منفرد (ریسه اختصاصی هر حلقه) انجام می‌شود.

نکته:

با وجود تفاوت در سازوکار پایش (polling)، libuv مدل اجرایی یکدستی را در سیستم‌های یونیکس و ویندوز ارائه می‌دهد.

برخلاف ورودی/خروجی شبکه، هیچ سازوکار پایه ورودی/خروجی فایل وابسته به سکویی وجود ندارد که libuv بتواند به آن متکی باشد، بنابراین رویکرد کنونی اجرای عملیات مسدودکننده ورودی/خروجی فایل در استخر ریسه‌ها (thread pool) است.

برای توضیحات جامع درباره وضعیت ورودی/خروجی فایل در سکوهای گوناگون، این مطلب را بررسی کنید: https://blog.libtorrent.org/2012/10/asynchronous-disk-io.

در حال حاضر libuv از یک استخر ریسه سراسری استفاده می‌کند که همه حلقه‌ها می‌توانند کارهای خود را در آن صف‌بندی کنند. در حال حاضر ۳ نوع عملیات در این استخر اجرا می‌شوند:

  • عملیات سیستم فایل
  • توابع DNS (مانند getaddrinfo و getnameinfo)
  • کدهای مشخص‌شده توسط کاربر از طریق uv_queue_work() <#c.uv_queue_work>

هشدار:

برای جزئیات بیشتر بخش زمان‌بندی کار در استخر ریسه‌ها <#threadpool> را ببینید، اما به یاد داشته باشید که اندازه استخر ریسه‌ها کاملاً محدود است.

در libuv، خطاها ثابت‌هایی با اعداد منفی هستند. به عنوان یک قاعده کلی، هر جا یک پارامتر وضعیت وجود دارد، یا یک تابع API یک عدد صحیح برمی‌گرداند، عدد منفی به معنای بروز خطا خواهد بود.

هنگامی که تابعی با دریافت کالبک دچار خطا می‌شود، کالبک هرگز فراخوانی نخواهد شد.

نکته:

جزئیات پیاده‌سازی: در یونیکس، کدهای خطا همان errno منفی‌شده (یا -errno) هستند، در حالی که در ویندوز توسط libuv به اعداد منفی دلخواه تعریف شده‌اند.

لیست آرگومان‌ها بیش از حد طولانی است
دسترسی غیرمجاز است
آدرس قبلاً استفاده شده است
آدرس در دسترس نیست
خانواده آدرس پشتیبانی نمی‌شود
منبع موقتاً در دسترس نیست
خانواده آدرس پشتیبانی نمی‌شود
شکست موقت
مقدار ai_flags نامعتبر است
مقدار نامعتبر برای hints
درخواست لغو شد
شکست دائمی
مقدار ai_family پشتیبانی نمی‌شود
کمبود حافظه
بدون آدرس
گره یا سرویس ناشناخته است
سرریز بافر آرگومان
پروتکل تفکیک‌شده ناشناخته است
سرویس برای نوع سوکت در دسترس نیست
نوع سوکت پشتیبانی نمی‌شود
اتصال از قبل در حال انجام است
توصیف‌کننده فایل نامعتبر است
منبع مشغول یا قفل است
عملیات لغو شد
نویسه یونیکد نامعتبر است
قطع اتصال ناشی از نرم‌افزار
اتصال رد شد
اتصال توسط طرف مقابل بازنشانی شد
آدرس مقصد الزامی است
فایل از قبل وجود دارد
آدرس نامعتبر در آرگومان فراخوانی سیستمی
فایل بیش از حد بزرگ است
میزبان در دسترس نیست
وقفه در فراخوانی سیستمی
آرگومان نامعتبر است
خطای ورودی/خروجی
سوکت از قبل متصل است
عملیات غیرمجاز روی دایرکتوری
برخورد با پیوندهای نمادین بیش از حد
تعداد فایل‌های باز بیش از حد است
پیام بیش از حد طولانی است
نام بیش از حد طولانی است
شبکه قطع است
شبکه در دسترس نیست
سرریز جدول فایل‌ها
فضای بافر در دسترس نیست
چنین دستگاهی وجود ندارد
چنین فایل یا دایرکتوری وجود ندارد
حافظه کافی نیست
رایانه در شبکه نیست
پروتکل در دسترس نیست
فضایی روی دستگاه باقی نمانده است
تابع پیاده‌سازی نشده است
سوکت متصل نیست
دایرکتوری نیست
دایرکتوری خالی نیست
عملیات سوکت روی غیرسوکت
عملیات روی سوکت پشتیبانی نمی‌شود
مقدار برای نوع داده تعریف‌شده بیش از حد بزرگ است
عملیات مجاز نیست
لوله شکسته است
خطای پروتکل
پروتکل پشتیبانی نمی‌شود
نوع پروتکل برای سوکت نادرست است
نتیجه بیش از حد بزرگ است
سیستم فایل فقط‌خواندنی است
ارسال پس از خاموش شدن نقطه پایانی انتقال امکان‌پذیر نیست
جستجوی موقعیت نامعتبر است
چنین فرآیندی وجود ندارد
مهلت اتصال به پایان رسید
فایل متنی مشغول است
پیوند بین‌دستگاهی مجاز نیست
خطای ناشناخته
پایان فایل
چنین دستگاه یا آدرسی وجود ندارد
تعداد پیوندها بیش از حد است
دستور ioctl برای دستگاه نامناسب است
نوع فایل یا قالب آن نامناسب است
توالی بایت غیرمجاز
نوع سوکت پشتیبانی نمی‌شود
درایور پروتکل متصل نیست

ماکرویی که به مجموعه‌ای از فراخوانی‌های iter_macro برای هر یک از ثابت‌های خطای فوق بسط می‌یابد. ماکروی iter_macro با دو آرگومان فراخوانی می‌شود: نام ثابت خطا بدون پیشوند UV_، و رشته متنی پیام خطا.
پیام خطای مربوط به کد خطای داده‌شده را برمی‌گرداند. در صورت فراخوانی با یک کد خطای ناشناخته، چند بایت حافظه نشت می‌کند.
پیام خطای مربوط به کد خطای داده‌شده را برمی‌گرداند. پیام خاتمه‌یافته با صفر در بافر buf ارائه‌شده توسط کاربر با حداکثر طول buflen بایت ذخیره می‌شود.

در نسخه 1.22.0 اضافه شد.

نام خطای مربوط به کد خطای داده‌شده را برمی‌گرداند. در صورت فراخوانی با یک کد خطای ناشناخته، چند بایت حافظه نشت می‌کند.
نام خطای مربوط به کد خطای داده‌شده را برمی‌گرداند. نام خاتمه‌یافته با صفر در بافر buf ارائه‌شده توسط کاربر با حداکثر طول buflen بایت ذخیره می‌شود.

در نسخه 1.22.0 اضافه شد.

کد خطای معادل در libuv را برای کد خطای وابسته به سکوی داده‌شده برمی‌گرداند: کدهای خطای POSIX در یونیکس (مواردی که در errno ذخیره می‌شوند)، و کدهای خطای Win32 در ویندوز (مواردی که توسط GetLastError() یا WSAGetLastError() برگردانده می‌شوند).

اگر sys_errno از قبل یک خطای libuv باشد، صرفاً همان برگردانده می‌شود.

در نسخه 1.10.0 تغییر کرد: تابع به‌صورت عمومی اعلام شد.

از نسخه 1.0.0 به بعد، libuv از طرح نسخه‌بندی معنایی https://semver.org پیروی می‌کند. این بدان معناست که در طول چرخه عمر یک انتشار اصلی می‌توان رابط‌های برنامه‌نویسی جدیدی اضافه کرد. در این بخش تمامی ماکروها و توابعی را خواهید یافت که امکان نوشتن یا کامپایل شرطی کد را برای کار با چندین نسخه مختلف libuv فراهم می‌کنند.

شماره اصلی نسخه libuv.
شماره فرعی نسخه libuv.
شماره وصله نسخه libuv.
تنظیم روی 1 برای نشان دادن نسخه انتشار پایدار libuv، و 0 برای نسخه در حال توسعه.
پسوند نسخه libuv. برخی از نسخه‌های در حال توسعه مانند کاندیداهای انتشار (Release Candidates) ممکن است دارای پسوندی مانند "rc" باشند.
نسخه libuv را در قالب یک عدد صحیح فشرده برمی‌گرداند. ۸ بیت برای هر مؤلفه استفاده می‌شود که شماره وصله در ۸ بیت کم‌ارزش‌تر ذخیره می‌شود. برای مثال برای libuv 1.2.3 این مقدار 0x010203 خواهد بود.

در نسخه 1.7.0 اضافه شد.

مقدار UV_VERSION_HEX را برمی‌گرداند.
شماره نسخه libuv را به صورت یک رشته برمی‌گرداند. برای نسخه‌های غیر انتشار، پسوند نسخه نیز گنجانده می‌شود.

حلقه رویداد (Event loop) بخش مرکزی و هسته اصلی قابلیت‌های libuv است. این حلقه وظیفه سرکشی (polling) برای ورودی/خروجی (I/O) و زمان‌بندی اجرای توابع بازخوانی (callbacks) را بر اساس منابع مختلف رویداد بر عهده دارد.

نوع داده حلقه رویداد.
گزینه‌های تکمیلی حلقه رویداد. ببینید: uv_loop_configure().
typedef enum {
    UV_LOOP_BLOCK_SIGNAL = 0,
    UV_METRICS_IDLE_TIME,
    UV_LOOP_USE_IO_URING_SQPOLL
} uv_loop_option;
حالت مورد استفاده برای اجرای حلقه با uv_run().
typedef enum {
    UV_RUN_DEFAULT = 0,
    UV_RUN_ONCE,
    UV_RUN_NOWAIT
} uv_run_mode;
تعریف نوع تابع بازخوانی ارسال‌شده به uv_walk().

فضایی برای نگهداری داده‌های دلخواه تعریف‌شده توسط کاربر. libuv از این فیلد استفاده نمی‌کند و دست به آن نمی‌زند.

ساختار uv_loop_t داده‌شده را مقداردهی اولیه می‌کند.
اضافه‌شده در نسخه 1.0.2.

تنظیم گزینه‌های تکمیلی حلقه رویداد. معمولاً باید این تابع را قبل از اولین فراخوانی uv_run() صدا بزنید مگر اینکه خلاف آن ذکر شده باشد.

در صورت موفقیت مقدار 0 و در صورت شکست کد خطای UV_E* برمی‌گرداند. برای مدیریت خطای UV_ENOSYS آماده باشید؛ این خطا به این معنی است که گزینه حلقه توسط پلتفرم پشتیبانی نمی‌شود.

گزینه‌های پشتیبانی‌شده:

  • UV_LOOP_BLOCK_SIGNAL: مسدود کردن یک سیگنال هنگام سرکشی (polling) برای رویدادهای جدید. آرگومان دوم به uv_loop_configure() شماره سیگنال است.

    این عملیات در حال حاضر فقط برای سیگنال‌های SIGPROF پیاده‌سازی شده است تا از بیدارباش‌های غیرضروری هنگام استفاده از نمونه‌بردار کارایی (sampling profiler) جلوگیری شود. درخواست سایر سیگنال‌ها با خطای UV_EINVAL مواجه خواهد شد.

  • UV_METRICS_IDLE_TIME: انباشت مقدار زمان بی‌کاری که حلقه رویداد در ارائه‌دهنده رویداد (event provider) سپری می‌کند.

    این گزینه برای استفاده از uv_metrics_idle_time() <#c.uv_metrics_idle_time> ضروری است.

  • UV_LOOP_USE_IO_URING_SQPOLL: فعال‌سازی نمونه SQPOLL io_uring برای مدیریت عملیات ناهمگام سیستم فایل.

تغییریافته در نسخه 1.39.0: گزینه UV_METRICS_IDLE_TIME اضافه شد.

تغییریافته در نسخه 1.49.0: گزینه UV_LOOP_USE_IO_URING_SQPOLL اضافه شد.

تمام منابع داخلی حلقه را آزاد می‌کند. این تابع را تنها زمانی فراخوانی کنید که اجرای حلقه به پایان رسیده باشد و تمام هندل‌ها و درخواست‌های باز بسته شده باشند، در غیر این صورت مقدار UV_EBUSY را برمی‌گرداند. پس از بازگشت این تابع، کاربر می‌تواند حافظه تخصیص‌یافته برای حلقه را آزاد کند.
حلقه پیش‌فرض مقداردهی‌شده را برمی‌گرداند. در صورت شکست در تخصیص حافظه ممکن است NULL برگرداند.

این تابع صرفاً روشی مناسب برای داشتن یک حلقه سراسری در سرتاسر برنامه است، حلقه پیش‌فرض به هیچ وجه تفاوتی با حلقه‌های مقداردهی‌شده توسط uv_loop_init() ندارد. به همین ترتیب، حلقه پیش‌فرض می‌تواند (و باید) با uv_loop_close() بسته شود تا منابع مرتبط با آن آزاد شوند.

هشدار:

این تابع از نظر ریسه‌ها امن نیست (thread-safe نیست).
این تابع حلقه رویداد را اجرا می‌کند. رفتار آن بسته به حالت مشخص‌شده متفاوت خواهد بود:
  • UV_RUN_DEFAULT: حلقه رویداد را تا زمانی که هیچ هندل یا درخواست فعال و ارجاع‌شده‌ای باقی نمانده باشد اجرا می‌کند. اگر uv_stop() فراخوانی شده باشد و همچنان هندل‌ها یا درخواست‌های فعالی وجود داشته باشند، مقدار غیرصفر برمی‌گرداند. در سایر موارد مقدار صفر برمی‌گرداند.
  • UV_RUN_ONCE: یک بار برای ورودی/خروجی سرکشی (poll) می‌کند. توجه داشته باشید که اگر هیچ تابع بازخوانی معلقی وجود نداشته باشد، این تابع مسدود (block) می‌شود. در صورت اتمام (عدم وجود هندل یا درخواست فعال باقی‌مانده) مقدار صفر، یا اگر انتظار بازخوانی‌های بیشتری می‌رود (به این معنی که باید حلقه رویداد را در آینده دوباره اجرا کنید) مقدار غیرصفر برمی‌گرداند.
  • UV_RUN_NOWAIT: یک بار برای ورودی/خروجی سرکشی می‌کند اما اگر هیچ تابع بازخوانی معلقی وجود نداشته باشد مسدود نمی‌شود. در صورت اتمام (عدم وجود هندل یا درخواست فعال باقی‌مانده) مقدار صفر، یا اگر انتظار بازخوانی‌های بیشتری می‌رود مقدار غیرصفر برمی‌گرداند.

تابع uv_run() بازگشت‌پذیر مجدد (reentrant) نیست. نباید آن را از درون یک تابع بازخوانی فراخوانی کرد.

اگر هندل‌های فعال دارای ارجاع، درخواست‌های فعال یا هندل‌های در حال بسته شدن در حلقه وجود داشته باشند، مقدار غیرصفر برمی‌گرداند.
حلقه رویداد را متوقف می‌کند و باعث می‌شود uv_run() در سریع‌ترین زمان ممکن به پایان برسد. این اتفاق زودتر از تکرار (iteration) بعدی حلقه رخ نخواهد داد. اگر این تابع قبل از مسدود شدن برای ورودی/خروجی فراخوانی شده باشد، حلقه در این تکرار برای ورودی/خروجی مسدود نخواهد شد.
اندازه ساختار uv_loop_t را برمی‌گرداند. برای نویسندگان بایندرهای FFI که مایل به دانستن ساختار درونی نیستند مفید است.
توصیف‌کننده فایل (file descriptor) بک‌اند را دریافت می‌کند. فقط kqueue، epoll و event ports پشتیبانی می‌شوند.

این تابع می‌تواند همراه با uv_run(loop, UV_RUN_NOWAIT) برای سرکشی در یک ریسه (thread) و اجرای توابع بازخوانی حلقه رویداد در ریسه‌ای دیگر استفاده شود؛ برای مشاهده مثال به test/test-embed.c مراجعه کنید.

نکته:

جاسازی fd مربوط به kqueue در مجموعه سرکشی (pollset) دیگر kqueue روی همه پلتفرم‌ها کار نمی‌کند. افزودن fd خطا نیست اما هرگز رویدادی تولید نمی‌کند.
مهلت زمانی سرکشی (poll timeout) را برمی‌گرداند. مقدار برگشتی به میلی‌ثانیه است، یا -1 برای عدم وجود مهلت زمانی.
برچسب زمانی فعلی را به میلی‌ثانیه برمی‌گرداند. این برچسب زمانی در ابتدای تیک حلقه رویداد ذخیره (cache) می‌شود؛ برای جزئیات و دلایل آن به uv_update_time() مراجعه کنید.

برچسب زمانی به صورت یکنواخت (monotonically) از یک نقطه زمانی دلخواه افزایش می‌یابد. هیچ فرضی درباره نقطه شروع در نظر نگیرید.

نکته:

اگر به دقت زیر میلی‌ثانیه نیاز دارید از uv_hrtime() <#c.uv_hrtime> استفاده کنید.
مفهوم «اکنون» (now) را در حلقه رویداد به‌روزرسانی می‌کند. Libuv زمان فعلی را در ابتدای تیک حلقه رویداد ذخیره (cache) می‌کند تا تعداد فراخوانی‌های سیستمی مرتبط با زمان کاهش یابد.

معمولاً نیازی به فراخوانی این تابع نخواهید داشت مگر اینکه توابع بازخوانی داشته باشید که حلقه رویداد را برای مدت طولانی مسدود کنند، جایی که «طولانی» تا حدی نسبی است اما معمولاً در مقیاس یک میلی‌ثانیه یا بیشتر است.

پیمایش لیست هندل‌ها: تابع walk_cb با آرگومان arg داده‌شده اجرا خواهد شد.
اضافه‌شده در نسخه 1.12.0.

مقداردهی مجدد هرگونه وضعیت کرنل لازم در فرآیند فرزند پس از یک فراخوانی سیستمی fork(2) https://man7.org/linux/man-pages/man2/fork.2.html.

پایشگرهایی (watchers) که قبلاً شروع شده‌اند، در فرآیند فرزند نیز به کار خود ادامه خواهند داد.

فراخوانی صریح این تابع روی هر حلقه رویدادی که در فرآیند والد ایجاد شده و قصد ادامه استفاده از آن در فرآیند فرزند را دارید ضروری است، از جمله حلقه پیش‌فرض (حتی اگر دیگر در والد از آن استفاده نکنید). این تابع باید قبل از فراخوانی uv_run() یا هر تابع API دیگر که از حلقه در فرزند استفاده می‌کند فراخوانی شود. عدم انجام این کار منجر به رفتار تعریف‌نشده خواهد شد، از جمله احتمال تحویل رویدادهای تکراری به هر دو فرآیند والد و فرزند یا متوقف شدن غیرمنتظره فرآیند فرزند.

در صورت امکان، ترجیح داده می‌شود به جای استفاده مجدد از حلقه‌ای که در والد ایجاد شده، یک حلقه جدید در فرآیند فرزند ایجاد شود. حلقه‌های جدیدی که در فرآیند فرزند پس از fork ایجاد می‌شوند نباید از این تابع استفاده کنند.

این تابع روی ویندوز پیاده‌سازی نشده است و در آنجا مقدار UV_ENOSYS را برمی‌گرداند.

احتیاط:

این تابع آزمایشی است. ممکن است دارای باگ باشد و ممکن است تغییر کند یا حذف شود. پایداری API و ABI تضمین نمی‌شود.

نکته:

در Mac OS X، اگر از هندل‌های رویداد سیستم فایل (FS event) پوشه در فرآیند والد برای هر حلقه رویدادی استفاده شده باشد، فرآیند فرزند دیگر قادر به استفاده از کارآمدترین پیاده‌سازی FSEvent نخواهد بود. در عوض، استفاده از هندل‌های FS event پوشه در فرآیند فرزند به همان پیاده‌سازی استفاده‌شده برای فایل‌ها و سایر سیستم‌های مبتنی بر kqueue تنزل خواهد یافت.

احتیاط:

در AIX و SunOS، هندل‌های رویداد سیستم فایل که در زمان انشعاب (forking) از قبل در فرآیند والد شروع شده بودند، در فرآیند فرزند رویدادی تحویل نخواهند داد؛ آنها باید بسته شده و مجدداً راه‌اندازی شوند. در تمام پلتفرم‌های دیگر، آنها بدون نیاز به اقدام دیگری به طور عادی به کار خود ادامه می‌دهند.

احتیاط:

هر مقدار بازگشتی قبلی از uv_backend_fd() اکنون نامعتبر است. آن تابع باید دوباره فراخوانی شود تا توصیف‌کننده فایل بک‌اند صحیح مشخص گردد.
مقدار loop->data را برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

مقدار loop->data را روی data تنظیم می‌کند.

اضافه‌شده در نسخه 1.19.0.

ساختار uv_handle_t نوع پایه برای تمامی انواع هندل‌ها در libuv است.

ساختارها به گونه‌ای تراز شده‌اند که هر هندل libuv را می‌توان به uv_handle_t تبدیل (cast) کرد. تمامی توابع API تعریف‌شده در اینجا با هر نوع هندلی کار می‌کنند.

هندل‌های Libuv غیرقابل جابه‌جایی (non-movable) هستند. اشاره‌گرهای ساختار هندل که به توابع ارسال می‌شوند باید در طول مدت عملیات درخواستی معتبر باقی بمانند. هنگام استفاده از هندل‌های تخصیص‌یافته در پشته (stack) دقت کنید.

نوع هندل پایه در libuv.
نوع هندل libuv.
typedef enum {
  UV_UNKNOWN_HANDLE = 0,
  UV_ASYNC,
  UV_CHECK,
  UV_FS_EVENT,
  UV_FS_POLL,
  UV_HANDLE,
  UV_IDLE,
  UV_NAMED_PIPE,
  UV_POLL,
  UV_PREPARE,
  UV_PROCESS,
  UV_STREAM,
  UV_TCP,
  UV_TIMER,
  UV_TTY,
  UV_UDP,
  UV_SIGNAL,
  UV_FILE,
  UV_HANDLE_TYPE_MAX
} uv_handle_type;
اجتماع (Union) تمام انواع هندل‌ها.
تعریف نوع تابع بازخوانی ارسال‌شده به uv_read_start() <#c.uv_read_start> و uv_udp_recv_start() <#c.uv_udp_recv_start>. کاربر باید حافظه تخصیص دهد و ساختار uv_buf_t <#c.uv_buf_t> داده‌شده را پر کند. اگر مقدار NULL به عنوان پایه بافر (base) یا 0 به عنوان طول آن تعیین شود، خطای UV_ENOBUFS در بازخوانی uv_udp_recv_cb <#c.uv_udp_recv_cb> یا uv_read_cb <#c.uv_read_cb> فعال خواهد شد.

هر بافر فقط یک بار استفاده می‌شود و کاربر مسئول آزادسازی آن در تابع بازخوانی uv_udp_recv_cb <#c.uv_udp_recv_cb> یا uv_read_cb <#c.uv_read_cb> است.

یک اندازه پیشنهادی (در حال حاضر در بیشتر موارد 65536) ارائه می‌شود، اما این صرفاً یک پیشنهاد است و ارتباطی با داده‌های معلق برای خواندن ندارد. کاربر در تخصیص میزان حافظه دلخواه خود آزاد است.

به عنوان مثال، برنامه‌هایی با الگوهای تخصیص سفارشی مانند استفاده از freelistها، استخرهای تخصیص (allocation pools) یا تخصیص‌دهنده‌های مبتنی بر slab ممکن است تصمیم بگیرند از اندازه متفاوتی متناسب با قطعات حافظه‌ای که از قبل در اختیار دارند استفاده کنند.

مثال:

static void my_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
  buf->base = malloc(suggested_size);
  buf->len = suggested_size;
}
تعریف نوع تابع بازخوانی ارسال‌شده به uv_close().

اشاره‌گر به uv_loop_t <#c.uv_loop_t> که هندل روی آن در حال اجرا است. فقط‌خواندنی.
مقدار uv_handle_type که نوع هندل زیرین را مشخص می‌کند. فقط‌خواندنی.
فضایی برای نگهداری داده‌های دلخواه تعریف‌شده توسط کاربر. libuv از این فیلد استفاده نمی‌کند.

ماکرویی که به مجموعه‌ای از فراخوانی‌های iter_macro برای هر یک از انواع هندل باز می‌شود. ماکروی iter_macro با دو آرگومان فراخوانی می‌شود: نام عنصر uv_handle_type بدون پیشوند UV_، و نام نوع ساختار متناظر بدون پیشوند uv_ و پسوند _t.
در صورتی که هندل فعال باشد مقدار غیرصفر و اگر غیرفعال باشد مقدار صفر را برمی‌گرداند. معنای «فعال» به نوع هندل بستگی دارد:
  • یک هندل uv_async_t همیشه فعال است و نمی‌توان آن را غیرفعال کرد، مگر با بستن آن از طریق uv_close().
  • هندل‌های uv_pipe_t، uv_tcp_t، uv_udp_t و غیره - اساساً هر هندلی که با ورودی/خروجی سروکار دارد - زمانی فعال است که کاری مرتبط با ورودی/خروجی انجام می‌دهد، مانند خواندن، نوشتن، اتصال، پذیرش اتصالات جدید و غیره.
  • هندل‌های uv_check_t، uv_idle_t، uv_timer_t و غیره زمانی فعال هستند که با فراخوانی uv_check_start()، uv_idle_start() و غیره راه‌اندازی شده باشند.

قاعده سرانگشتی: اگر یک هندل از نوع uv_foo_t تابعی به نام uv_foo_start() دارد، از لحظه فراخوانی آن تابع فعال است. به طور مشابه، uv_foo_stop() مجدداً هندل را غیرفعال می‌کند.

در صورتی که هندل در حال بسته شدن یا بسته شده باشد مقدار غیرصفر و در غیر این صورت صفر برمی‌گرداند.

نکته:

این تابع فقط باید بین مقداردهی اولیه هندل و رسیدن بازخوانی بستن (close callback) استفاده شود.
درخواست بسته شدن هندل. تابع close_cb پس از این فراخوانی به صورت ناهمگام صدا زده خواهد شد. این تابع الزماً باید قبل از آزادسازی حافظه روی هر هندل فراخوانی شود. علاوه بر این، حافظه فقط در close_cb یا پس از بازگشت آن قابل آزادسازی است.

هندل‌هایی که توصیف‌کننده‌های فایل را در بر می‌گیرند بلافاصله بسته می‌شوند اما close_cb همچنان به تکرار بعدی حلقه رویداد موکول می‌شود. این به شما فرصت می‌دهد تا هرگونه منبع مرتبط با هندل را آزاد کنید.

درخواست‌های در حال انجام، مانند uv_connect_t یا uv_write_t، لغو می‌شوند و بازخوانی‌های آن‌ها به صورت ناهمگام با وضعیت status=UV_ECANCELED فراخوانی می‌شوند.

در مواردی که پاک‌سازی یا آزادسازی حافظه لازم نیست، close_cb می‌تواند NULL باشد.

ارجاع به هندل داده‌شده اضافه می‌کند. ارجاع‌ها خودتوان (idempotent) هستند، یعنی اگر هندلی از قبل ارجاع داده شده باشد، فراخوانی مجدد این تابع اثری نخواهد داشت.

ببینید: شمارش ارجاع (Reference counting).

ارجاع از هندل داده‌شده را حذف می‌کند. ارجاع‌ها خودتوان هستند، یعنی اگر هندل ارجاعی نداشته باشد، فراخوانی مجدد این تابع اثری نخواهد داشت.

ببینید: شمارش ارجاع (Reference counting).

اگر هندل دارای ارجاع باشد مقدار غیرصفر و در غیر این صورت صفر برمی‌گرداند.

ببینید: شمارش ارجاع (Reference counting).

اندازه نوع هندل داده‌شده را برمی‌گرداند. برای نویسندگان بایندرهای FFI که مایل به دانستن ساختار درونی نیستند مفید است.

توابع API زیر یک آرگومان uv_handle_t دریافت می‌کنند اما فقط برای برخی از انواع هندل کار می‌کنند.

اندازه بافر ارسالی که سیستم‌عامل برای سوکت استفاده می‌کند را دریافت یا تنظیم می‌کند.

اگر *value == 0 باشد، مقدار *value را روی اندازه فعلی بافر ارسال تنظیم می‌کند. اگر *value > 0 باشد، از *value برای تنظیم اندازه جدید بافر ارسال استفاده می‌کند.

در صورت موفقیت صفر و در صورت بروز خطا یک مقدار منفی برمی‌گرداند.

این تابع برای هندل‌های TCP، پایپ و UDP در یونیکس و برای هندل‌های TCP و UDP در ویندوز کار می‌کند.

نکته:

لینوکس دو برابر اندازه را تنظیم می‌کند و دو برابر مقدار تنظیم‌شده اصلی را برمی‌گرداند.
اندازه بافر دریافتی که سیستم‌عامل برای سوکت استفاده می‌کند را دریافت یا تنظیم می‌کند.

اگر *value == 0 باشد، مقدار *value را روی اندازه فعلی بافر دریافت تنظیم می‌کند. اگر *value > 0 باشد، از *value برای تنظیم اندازه جدید بافر دریافت استفاده می‌کند.

در صورت موفقیت صفر و در صورت بروز خطا یک مقدار منفی برمی‌گرداند.

این تابع برای هندل‌های TCP، پایپ و UDP در یونیکس و برای هندل‌های TCP و UDP در ویندوز کار می‌کند.

نکته:

لینوکس دو برابر اندازه را تنظیم می‌کند و دو برابر مقدار تنظیم‌شده اصلی را برمی‌گرداند.
معادل توصیف‌کننده فایل وابسته به پلتفرم را دریافت می‌کند.

هندل‌های زیر پشتیبانی می‌شوند: TCP، پایپ‌ها، TTY، UDP و poll. ارسال هر نوع هندل دیگر با خطای UV_EINVAL شکست خواهد خورد.

اگر هندلی هنوز توصیف‌کننده فایل متصل نداشته باشد یا خود هندل بسته شده باشد، این تابع UV_EBADF را برمی‌گرداند.

هشدار:

هنگام استفاده از این تابع بسیار مراقب باشید. libuv فرض می‌کند کنترل توصیف‌کننده فایل در اختیار خودش است، بنابراین هرگونه تغییر در آن ممکن است منجر به اختلال در عملکرد شود.
مقدار handle->loop را برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

مقدار handle->data را برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

مقدار handle->data را روی data تنظیم می‌کند.

اضافه‌شده در نسخه 1.19.0.

مقدار handle->type را برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

نام ساختار معادل را برای یک نوع هندل داده‌شده برمی‌گرداند، مثلاً "pipe" (همانند uv_pipe_t <#c.uv_pipe_t>) برای UV_NAMED_PIPE.

اگر چنین نوع هندلی وجود نداشته باشد، مقدار NULL برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

حلقه رویداد libuv (اگر در حالت پیش‌فرض اجرا شود) تا زمانی اجرا می‌شود که هیچ هندل فعال و دارای ارجاعی باقی نمانده باشد. کاربر می‌تواند با حذف ارجاع از هندل‌های فعال، حلقه را وادار به خروج زودهنگام کند؛ برای مثال با فراخوانی uv_unref() پس از فراخوانی uv_timer_start() <#c.uv_timer_start>.

یک هندل می‌تواند ارجاع‌دار یا بدون‌ارجاع باشد، طرح شمارش ارجاع از یک شمارنده استفاده نمی‌کند، بنابراین هر دو عملیات خودتوان (idempotent) هستند.

تمام هندل‌ها هنگام فعال بودن به طور پیش‌فرض ارجاع دارند؛ برای توضیح دقیق‌تر درباره اینکه فعال بودن شامل چه مواردی است به uv_is_active() مراجعه کنید.

ساختار uv_req_t نوع پایه برای تمامی انواع درخواست‌ها در libuv است.

ساختارها به گونه‌ای تراز شده‌اند که هر درخواست libuv را می‌توان به uv_req_t تبدیل (cast) کرد. تمامی توابع API تعریف‌شده در اینجا با هر نوع درخواستی کار می‌کنند.

ساختار پایه درخواست libuv.
اجتماع (Union) تمام انواع درخواست‌ها.
نوع درخواست libuv.
typedef enum {
    UV_UNKNOWN_REQ = 0,
    UV_REQ,
    UV_CONNECT,
    UV_WRITE,
    UV_SHUTDOWN,
    UV_UDP_SEND,
    UV_FS,
    UV_WORK,
    UV_GETADDRINFO,
    UV_GETNAMEINFO,
    UV_REQ_TYPE_MAX,
} uv_req_type;

فضایی برای نگهداری داده‌های دلخواه تعریف‌شده توسط کاربر. libuv از این فیلد استفاده نمی‌کند.
مقدار uv_req_type که نوع درخواست را مشخص می‌کند. فقط‌خواندنی.

ماکرویی که به مجموعه‌ای از فراخوانی‌های iter_macro برای هر یک از انواع درخواست باز می‌شود. ماکروی iter_macro با دو آرگومان فراخوانی می‌شود: نام عنصر uv_req_type بدون پیشوند UV_، و نام نوع ساختار متناظر بدون پیشوند uv_ و پسوند _t.
لغو یک درخواست معلق. اگر درخواست در حال اجرا باشد یا اجرای آن به پایان رسیده باشد با شکست مواجه می‌شود.

در صورت موفقیت مقدار 0 و در صورت شکست کد خطای < 0 برمی‌گرداند.

در حال حاضر فقط لغو درخواست‌های uv_fs_t <#c.uv_fs_t>، uv_getaddrinfo_t <#c.uv_getaddrinfo_t>، uv_getnameinfo_t <#c.uv_getnameinfo_t>، uv_random_t <#c.uv_random_t> و uv_work_t <#c.uv_work_t> پشتیبانی می‌شود.

توابع بازخوانی درخواست‌های لغوشده در زمانی در آینده فراخوانی خواهند شد. آزادسازی حافظه مرتبط با درخواست تا قبل از فراخوانی تابع بازخوانی امن نیست.

نحوه گزارش لغو به تابع بازخوانی به شرح زیر است:

  • فیلد req->result در یک درخواست uv_fs_t <#c.uv_fs_t> روی UV_ECANCELED تنظیم می‌شود.
  • تابع بازخوانی یک درخواست uv_work_t <#c.uv_work_t>، uv_getaddrinfo_t <#c.uv_getaddrinfo_t>، uv_getnameinfo_t <#c.uv_getnameinfo_t> یا uv_random_t <#c.uv_random_t> با مقدار status == UV_ECANCELED فراخوانی می‌شود.
اندازه نوع درخواست داده‌شده را برمی‌گرداند. برای نویسندگان بایندرهای FFI که مایل به دانستن ساختار درونی نیستند مفید است.
مقدار req->data را برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

مقدار req->data را روی data تنظیم می‌کند.

اضافه‌شده در نسخه 1.19.0.

مقدار req->type را برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

نام ساختار معادل را برای یک نوع درخواست داده‌شده برمی‌گرداند، مثلاً "connect" (همانند uv_connect_t <#c.uv_connect_t>) برای UV_CONNECT.

اگر چنین نوع درخواستی وجود نداشته باشد، مقدار NULL برمی‌گرداند.

اضافه‌شده در نسخه 1.19.0.

هندل‌های تایمر برای زمان‌بندی فراخوانی توابع بازخوانی در آینده استفاده می‌شوند.

تایمرها یا تک‌باره (single-shot) هستند یا تکرارشونده. تایمرهای تکرارشونده سربار زمانی را تنظیم نمی‌کنند بلکه نسبت به مفهوم «اکنون» در حلقه رویداد مجدداً آماده‌باش (rearm) می‌شوند.

Libuv مفهوم «اکنون» خود را درست پیش از اجرای بازخوانی‌های تایمر و بلافاصله پس از بیدار شدن از انتظار برای ورودی/خروجی به‌روزرسانی می‌کند. همچنین ببینید: uv_update_time() <#c.uv_update_time>.

مثال: یک تایمر تکرارشونده با بازه 50 میلی‌ثانیه‌ای که اجرای بازخوانی آن 17 میلی‌ثانیه طول می‌کشد، 33 میلی‌ثانیه بعد دوباره اجرا می‌شود. اگر کارهای دیگر بیشتر از 33 میلی‌ثانیه طول بکشند، بازخوانی تایمر در سریع‌ترین زمان ممکن اجرا خواهد شد.

نوع هندل تایمر.
تعریف نوع تابع بازخوانی ارسال‌شده به uv_timer_start().

موردی وجود ندارد (N/A)

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

راه‌اندازی تایمر. مقادیر timeout و repeat به میلی‌ثانیه هستند.

اگر timeout صفر باشد، تابع بازخوانی در تکرار بعدی حلقه رویداد اجرا می‌شود. اگر repeat غیرصفر باشد، بازخوانی ابتدا پس از timeout میلی‌ثانیه و سپس به صورت مکرر پس از هر repeat میلی‌ثانیه اجرا می‌شود.

نکته:

مفهوم «اکنون» در حلقه رویداد را به‌روزرسانی نمی‌کند. برای اطلاعات بیشتر به uv_update_time() <#c.uv_update_time> مراجعه کنید.

اگر تایمر از قبل فعال باشد، صرفاً به‌روزرسانی می‌شود.

متوقف کردن تایمر، تابع بازخوانی دیگر فراخوانی نخواهد شد.
تایمر را متوقف می‌کند، و اگر تکرارشونده باشد آن را با استفاده از مقدار repeat به عنوان مهلت زمانی (timeout) مجدداً راه‌اندازی می‌کند. اگر تایمر قبلاً هرگز شروع نشده باشد مقدار UV_EINVAL برمی‌گرداند.
مقدار بازه تکرار را به میلی‌ثانیه تنظیم می‌کند. تایمر برای اجرا در بازه زمانی داده‌شده زمان‌بندی خواهد شد، صرف‌نظر از مدت زمان اجرای تابع بازخوانی، و در صورت عبور از برهه زمانی (time-slice overrun) از معناشناسی عادی تایمر پیروی خواهد کرد.

نکته:

اگر مقدار تکرار از درون بازخوانی تایمر تنظیم شود، بلافاصله اعمال نمی‌شود. اگر تایمر قبلاً غیرتکرارشونده بوده باشد، متوقف خواهد شد. اگر تکرارشونده بوده باشد، مقدار تکرار قدیمی برای زمان‌بندی مهلت بعدی استفاده خواهد شد.
دریافت مقدار تکرار تایمر.
زمان باقی‌مانده تا سررسید تایمر یا 0 را در صورت منقضی شدن برمی‌گرداند. زمان نسبت به uv_now() <#c.uv_now> است.

اضافه‌شده در نسخه 1.40.0.

همچنین ببینید:

توابع API ساختار uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

هندل‌های آماده‌سازی (Prepare) تابع بازخوانی داده‌شده را یک بار در هر تکرار حلقه، درست قبل از سرکشی (poll) برای ورودی/خروجی اجرا می‌کنند.

نوع هندل آماده‌سازی (Prepare).
تعریف نوع تابع بازخوانی ارسال‌شده به uv_prepare_start().

موردی وجود ندارد (N/A)

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

مقداردهی اولیه هندل. این تابع همیشه با موفقیت انجام می‌شود.
بازگشتی‌ها
0
راه‌اندازی هندل با تابع بازخوانی داده‌شده. این تابع همیشه با موفقیت انجام می‌شود، مگر زمانی که cb مقدار NULL باشد.
بازگشتی‌ها
0 در صورت موفقیت، یا UV_EINVAL زمانی که cb == NULL باشد.
متوقف کردن هندل، تابع بازخوانی دیگر فراخوانی نخواهد شد. این تابع همیشه با موفقیت انجام می‌شود.
بازگشتی‌ها
0

همچنین ببینید:

توابع API ساختار uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

هندل‌های بررسی (Check) تابع بازخوانی داده‌شده را یک بار در هر تکرار حلقه، درست بعد از سرکشی برای ورودی/خروجی اجرا می‌کنند.

نوع هندل بررسی (Check).
تعریف نوع تابع بازخوانی ارسال‌شده به uv_check_start().

موردی وجود ندارد (N/A)

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

مقداردهی اولیه هندل. این تابع همیشه با موفقیت انجام می‌شود.
بازگشتی‌ها
0
راه‌اندازی هندل با تابع بازخوانی داده‌شده. این تابع همیشه با موفقیت انجام می‌شود، مگر زمانی که cb مقدار NULL باشد.
بازگشتی‌ها
0 در صورت موفقیت، یا UV_EINVAL زمانی که cb == NULL باشد.
متوقف کردن هندل، تابع بازخوانی دیگر فراخوانی نخواهد شد. این تابع همیشه با موفقیت انجام می‌شود.
بازگشتی‌ها
0

همچنین ببینید:

توابع API ساختار uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

هندل‌های بی‌کار (Idle) تابع بازخوانی داده‌شده را یک بار در هر تکرار حلقه، درست قبل از هندل‌های uv_prepare_t <#c.uv_prepare_t> اجرا می‌کنند.

نکته:

تفاوت قابل توجه با هندل‌های آماده‌سازی این است که وقتی هندل‌های بی‌کار فعالی وجود دارند، حلقه به جای مسدود شدن برای ورودی/خروجی، یک سرکشی بدون مهلت زمانی (zero timeout poll) انجام می‌دهد.

هشدار:

علیرغم نام آن، هندل‌های بی‌کار بازخوانی‌های خود را در هر تکرار حلقه دریافت می‌کنند، نه زمانی که حلقه واقعاً «بی‌کار» (idle) است.

نوع هندل بی‌کار (Idle).
تعریف نوع تابع بازخوانی ارسال‌شده به uv_idle_start().

موردی وجود ندارد (N/A)

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

مقداردهی اولیه هندل. این تابع همیشه با موفقیت انجام می‌شود.
بازگشتی‌ها
0
راه‌اندازی هندل با تابع بازخوانی داده‌شده. این تابع همیشه با موفقیت انجام می‌شود، مگر زمانی که cb مقدار NULL باشد.
بازگشتی‌ها
0 در صورت موفقیت، یا UV_EINVAL زمانی که cb == NULL باشد.
متوقف کردن هندل، تابع بازخوانی دیگر فراخوانی نخواهد شد. این تابع همیشه با موفقیت انجام می‌شود.
بازگشتی‌ها
0

همچنین ببینید:

توابع API ساختار uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

هندل‌های ناهمگام (Async) به کاربر اجازه می‌دهند حلقه رویداد را «بیدار» (wakeup) کنند و بازخوانی فراخوانی‌شده‌ای از ریسه دیگر دریافت کنند.

نوع هندل ناهمگام (Async).
تعریف نوع تابع بازخوانی ارسال‌شده به uv_async_init().

موردی وجود ندارد (N/A)

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

مقداردهی اولیه هندل. ارسال تابع بازخوانی NULL مجاز است.
بازگشتی‌ها
0 در صورت موفقیت، یا کد خطای < 0 در صورت شکست.

نکته:

برخلاف سایر توابع مقداردهی اولیه هندل، این تابع هندل را بلافاصله راه‌اندازی می‌کند.
بیدار کردن حلقه رویداد و فراخوانی تابع بازخوانی هندل ناهمگام.
بازگشتی‌ها
0 در صورت موفقیت، یا کد خطای < 0 در صورت شکست.

نکته:

فراخوانی این تابع از هر ریسه‌ای امن است. تابع بازخوانی روی ریسه حلقه رویداد فراخوانی خواهد شد.

نکته:

تابع uv_async_send() از نظر سیگنال‌های ناهمگام ایمن است (async-signal-safe) https://man7.org/linux/man-pages/man7/signal-safety.7.html. فراخوانی این تابع از درون یک گرداننده سیگنال (signal handler) ایمن است.

هشدار:

کتابخانه libuv فراخوانی‌های uv_async_send() را ادغام (coalesce) می‌کند، به این معنی که هر فراخوانی از آن لزوماً منجر به یک بار اجرای تابع بازخوانی نخواهد شد. به عنوان مثال: اگر uv_async_send() 5 بار متوالی قبل از فراخوانی بازخوانی صدا زده شود، بازخوانی تنها یک بار اجرا خواهد شد. اگر پس از اجرای بازخوانی، uv_async_send() دوباره فراخوانی شود، بازخوانی مجدداً صدا زده خواهد شد.

همچنین ببینید:

توابع API ساختار uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

قلاب‌های نظرسنجی (Poll handles) برای پایش توصیف‌کننده‌های فایل از نظر قابلیت خواندن، قابلیت نوشتن و قطع اتصال، مشابه با هدف تابع poll(2) https://man7.org/linux/man-pages/man2/poll.2.html استفاده می‌شوند.

هدف از قلاب‌های نظرسنجی، امکان یکپارچه‌سازی کتابخانه‌های خارجی است که برای آگاه‌سازی از تغییرات وضعیت سوکت به حلقه رویداد وابسته‌اند، مانند c-ares یا libssh2. استفاده از uv_poll_t برای هر هدف دیگری توصیه نمی‌شود؛ uv_tcp_t <#c.uv_tcp_t>، uv_udp_t <#c.uv_udp_t> و غیره پیاده‌سازی سریع‌تر و مقیاس‌پذیرتری را نسبت به آنچه با uv_poll_t به دست می‌آید ارائه می‌دهند، به‌ویژه در ویندوز.

این احتمال وجود دارد که قلاب‌های نظرسنجی گاهی سیگنال خوانایی یا نوشتنی بودن توصیف‌کننده فایل را ارسال کنند، حتی اگر در واقعیت چنین نباشد. بنابراین کاربر باید همیشه آمادگی مدیریت خطای EAGAIN یا معادل آن را هنگام تلاش برای خواندن از یا نوشتن در توصیف‌کننده فایل داشته باشد.

داشتن چند قلاب نظرسنجی فعال برای یک سوکت واحد مجاز نیست؛ این امر می‌تواند باعث ایجاد حلقه بی‌نهایت (busyloop) در libuv یا اختلال در عملکرد آن شود.

کاربر نباید یک توصیف‌کننده فایل را زمانی که توسط یک قلاب نظرسنجی فعال پایش می‌شود ببندد. این کار می‌تواند باعث گزارش خطا توسط قلاب شود، اما همچنین ممکن است پایش سوکت دیگری را آغاز کند. با این حال، بستن توصیف‌کننده فایل بلافاصله پس از فراخوانی uv_poll_stop() یا uv_close() <#c.uv_close> کاملاً ایمن است.

نکته:

در ویندوز تنها سوکت‌ها را می‌توان با قلاب‌های نظرسنجی پایش کرد. در یونیکس هر توصیف‌کننده فایلی که توسط poll(2) https://man7.org/linux/man-pages/man2/poll.2.html پذیرفته شود قابل استفاده است.

نکته:

در سیستم‌عامل AIX، پایش برای قطع اتصال (disconnection) پشتیبانی نمی‌شود.

نوع داده قلاب نظرسنجی (Poll handle).
تعریف نوع برای تابع بازخوانی ارسال‌شده به uv_poll_start().
انواع رویدادهای نظرسنجی (Poll event types)
enum uv_poll_event {
    UV_READABLE = 1,
    UV_WRITABLE = 2,
    UV_DISCONNECT = 4,
    UV_PRIORITIZED = 8
};

موردی وجود ندارد (N/A)

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

راه‌اندازی اولیه قلاب با استفاده از یک توصیف‌کننده فایل (file descriptor).

تغییریافته در نسخه 1.2.2: توصیف‌کننده فایل روی حالت غیرمسدودکننده (non-blocking) تنظیم می‌شود.

راه‌اندازی اولیه قلاب با استفاده از یک توصیف‌کننده سوکت. در یونیکس این تابع کاملاً معادل uv_poll_init() است. در ویندوز یک قلاب SOCKET دریافت می‌کند.

تغییریافته در نسخه 1.2.2: سوکت روی حالت غیرمسدودکننده (non-blocking) تنظیم می‌شود.

آغاز نظرسنجی توصیف‌کننده فایل. پارامتر events یک ماسک بیتی متشکل از UV_READABLE، UV_WRITABLE، UV_PRIORITIZED و UV_DISCONNECT است. به محض تشخیص یک رویداد، تابع بازخوانی با مقدار 0 برای status فراخوانی می‌شود و رویدادهای دریافت‌شده در فیلد events تنظیم می‌گردند.

رویداد UV_PRIORITIZED برای پایش وقفه‌های sysfs یا پیام‌های خارج از باند (out-of-band) پروتکل TCP استفاده می‌شود.

رویداد UV_DISCONNECT از این نظر اختیاری است که ممکن است گزارش نشود و کاربر آزاد است آن را نادیده بگیرد، اما می‌تواند به بهینه‌سازی مسیر خاموش‌سازی کمک کند زیرا ممکن است از یک فراخوانی اضافی خواندن یا نوشتن جلوگیری نماید.

در صورت بروز خطا هنگام نظرسنجی، مقدار status کمتر از 0 خواهد بود و با یکی از کدهای خطای UV_E* مطابقت دارد (به مدیریت خطاها <#errors> مراجعه کنید). کاربر نباید سوکت را در زمانی که قلاب فعال است ببندد. اگر کاربر با این حال چنین کاری انجام دهد، تابع بازخوانی ممکن است برای گزارش وضعیت خطا فراخوانی شود، اما این امر تضمین‌شده نیست. اگر status == UV_EBADF باشد، نظرسنجی برای قلاب فایل متوقف می‌شود و هیچ رویداد دیگری گزارش نخواهد شد. کاربر در این حالت باید uv_close() <#c.uv_close> را روی قلاب فراخوانی کند.

نکته:

فراخوانی uv_poll_start() روی قلابی که از قبل فعال است اشکالی ندارد. انجام این کار ماسک رویدادهایی را که پایش می‌شوند به‌روزرسانی می‌کند.

نکته:

اگرچه UV_DISCONNECT را می‌توان تنظیم کرد، اما این قابلیت در AIX پشتیبانی نمی‌شود و بنابراین در فیلد events در تابع بازخوانی تنظیم نخواهد شد.

نکته:

اگر یکی از رویدادهای UV_READABLE یا UV_WRITABLE تنظیم شده باشند، تا زمانی که توصیف‌کننده/سوکت مربوطه همچنان به ترتیب خواندنی یا نوشتنی باقی بماند، تابع بازخوانی مجدداً فراخوانی خواهد شد؛ به‌ویژه در هر یک از سناریوهای زیر:
  • تابع بازخوانی به دلیل خواندنی/نوشتنی شدن سوکت فراخوانی شده است و اصلاً عملیات خواندن/نوشتن روی این سوکت انجام نداده است.
  • تابع بازخوانی عملیات خواندن را روی سوکت انجام داده، اما همه داده‌های موجود را نخوانده است (زمانی که UV_READABLE تنظیم شده است).
  • تابع بازخوانی عملیات نوشتن را روی سوکت انجام داده، اما سوکت پس از آن نیز همچنان قابل نوشتن باقی مانده است (زمانی که UV_WRITABLE تنظیم شده است).
  • سوکت پیش از فراخوانی uv_poll_start() روی قلاب نظرسنجی مرتبط با آن، از قبل خواندنی/نوشتنی شده و از آن زمان وضعیت سوکت تغییر نکرده است.

در تمامی سناریوهای فوق، سوکت خواندنی یا نوشتنی باقی می‌ماند و بنابراین تابع بازخوانی مجدداً فراخوانی خواهد شد (بسته به رویدادهای تنظیم‌شده در ماسک بیتی). این رفتار به عنوان تحریک سطحی (level triggering) شناخته می‌شود.

تغییریافته در نسخه 1.9.0: رویداد UV_DISCONNECT اضافه شد.

تغییریافته در نسخه 1.14.0: رویداد UV_PRIORITIZED اضافه شد.

توقف نظرسنجی توصیف‌کننده فایل؛ تابع بازخوانی دیگر فراخوانی نخواهد شد.

نکته:

فراخوانی uv_poll_stop() بلافاصله اثر می‌کند: هرگونه تابع بازخوانی در انتظار نیز لغو می‌شود، حتی اگر اعلان تغییر وضعیت سوکت پیش‌تر آماده شده باشد.

همچنین ببینید:

توابع رابط برنامه‌نویسی uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

قلاب‌های سیگنال، مدیریت سیگنال به سبک یونیکس را بر پایه هر حلقه رویداد پیاده‌سازی می‌کنند.

دریافت برخی سیگنال‌ها شبیه‌سازی شده است:

  • سیگنال SIGINT معمولاً زمانی تحویل داده می‌شود که کاربر کلیدهای CTRL+C را فشار دهد. با این حال، مانند یونیکس، در صورت فعال بودن حالت خام ترمینال (terminal raw mode) این سیگنال تولید نمی‌شود.
  • سیگنال SIGBREAK هنگامی که کاربر کلیدهای CTRL + BREAK را فشار دهد تحویل داده می‌شود.
  • سیگنال SIGHUP زمانی تولید می‌شود که کاربر پنجره کنسول را ببندد. در زمان SIGHUP به برنامه تقریباً ۱۰ ثانیه فرصت داده می‌شود تا پاک‌سازی را انجام دهد. پس از آن ویندوز بدون قید و شرط آن را خاتمه می‌دهد.
  • سیگنال SIGWINCH هر زمان که libuv تغییر اندازه کنسول را تشخیص دهد برانگیخته می‌شود. هنگامی که یک برنامه libuv درون یک شبیه‌ساز کنسول اجرا می‌شود، یا هنگامی که یک برنامه ۳۲ بیتی libuv روی یک سیستم ۶۴ بیتی در حال اجراست، SIGWINCH شبیه‌سازی خواهد شد. در چنین مواردی سیگنال‌های SIGWINCH ممکن است همیشه به موقع تحویل داده نشوند. برای یک قلاب uv_tty_t <#c.uv_tty_t> قابل نوشتن، libuv تنها زمانی تغییرات اندازه را تشخیص می‌دهد که مکان‌نما حرکت کند. هنگامی که از یک قلاب uv_tty_t <#c.uv_tty_t> قابل خواندن استفاده می‌شود، تغییر اندازه بافر کنسول تنها در صورتی تشخیص داده می‌شود که قلاب در حالت خام (raw mode) باشد و در حال خواندن باشد.
  • دیده‌بان‌ها (watchers) برای سایر سیگنال‌ها با موفقیت ایجاد می‌شوند، اما این سیگنال‌ها هرگز دریافت نمی‌شوند. این سیگنال‌ها عبارتند از: SIGILL، SIGABRT، SIGFPE، SIGSEGV، SIGTERM و SIGKILL.
  • فراخوانی‌های raise() یا abort() برای برانگیختن برنامه‌نویسی‌شده سیگنال توسط libuv تشخیص داده نمی‌شوند؛ این فراخوانی‌ها دیده‌بان سیگنال را تحریک نخواهند کرد.

تغییریافته در نسخه 1.15.0: پشتیبانی از SIGWINCH در ویندوز بهبود یافت.

تغییریافته در نسخه 1.31.0: پشتیبانی libuv نسخه ۳۲ بیتی از SIGWINCH در ویندوز ۶۴ بیتی به پیاده‌سازی قدیمی بازگردانده شد.

  • گرفتن سیگنال‌های SIGKILL و SIGSTOP غیرممکن است.
  • مدیریت SIGBUS، SIGFPE، SIGILL یا SIGSEGV از طریق libuv منجر به رفتار تعریف‌نشده می‌شود.
  • سیگنال SIGABRT در صورتی که توسط abort() تولید شود (مثلاً از طریق assert())، توسط libuv گرفته نخواهد شد.
  • در لینوکس، سیگنال‌های SIGRT0 و SIGRT1 (سیگنال‌های ۳۲ و ۳۳) توسط کتابخانه NPTL pthreads برای مدیریت نخ‌ها استفاده می‌شوند. نصب دیده‌بان برای این سیگنال‌ها منجر به رفتار غیرقابل پیش‌بینی می‌شود و اکیداً منع شده است. نسخه‌های آینده libuv ممکن است به سادگی آن‌ها را رد کنند.

نوع داده قلاب سیگنال (Signal handle).
تعریف نوع برای تابع بازخوانی ارسال‌شده به uv_signal_start().

سیگنالی که توسط این قلاب پایش می‌شود. فقط‌خواندنی.

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

شروع کار قلاب با تابع بازخوانی مشخص‌شده، در حال پایش سیگنال داده‌شده.
افزوده‌شده در نسخه 1.12.0.

عملکرد مشابه با uv_signal_start() دارد اما کنترل‌کننده سیگنال به محض دریافت سیگنال بازنشانی می‌شود.

توقف قلاب؛ تابع بازخوانی دیگر فراخوانی نخواهد شد.

همچنین ببینید:

توابع رابط برنامه‌نویسی uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

قلاب‌های فرایند یک فرایند جدید را اجرا (spawn) می‌کنند و به کاربر اجازه می‌دهند آن را کنترل کرده و با استفاده از جریان‌ها (streams) کانال‌های ارتباطی با آن برقرار کند.

نوع داده قلاب فرایند (Process handle).
گزینه‌ها برای اجرای فرایند (ارسال‌شده به uv_spawn()).
typedef struct uv_process_options_s {
    uv_exit_cb exit_cb;
    const char* file;
    char** args;
    char** env;
    const char* cwd;
    unsigned int flags;
    int stdio_count;
    uv_stdio_container_t* stdio;
    uv_uid_t uid;
    uv_gid_t gid;
} uv_process_options_t;
تعریف نوع برای تابع بازخوانی ارسال‌شده در uv_process_options_t که وضعیت خروج و سیگنالی را که در صورت وجود باعث خاتمه فرایند شده است، نشان می‌دهد.
فلگ‌هایی که روی فیلد flags از uv_process_options_t تنظیم می‌شوند.
enum uv_process_flags {
    /*
    * Set the child process' user id.
    */
    UV_PROCESS_SETUID = (1 << 0),
    /*
    * Set the child process' group id.
    */
    UV_PROCESS_SETGID = (1 << 1),
    /*
    * Do not wrap any arguments in quotes, or perform any other escaping, when
    * converting the argument list into a command line string. This option is
    * only meaningful on Windows systems. On Unix it is silently ignored.
    */
    UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2),
    /*
    * Spawn the child process in a detached state - this will make it a process
    * group leader, and will effectively enable the child to keep running after
    * the parent exits. Note that the child process will still keep the
    * parent's event loop alive unless the parent process calls uv_unref() on
    * the child's process handle.
    */
    UV_PROCESS_DETACHED = (1 << 3),
    /*
    * Hide the subprocess window that would normally be created. This option is
    * only meaningful on Windows systems. On Unix it is silently ignored.
    */
    UV_PROCESS_WINDOWS_HIDE = (1 << 4),
    /*
    * Hide the subprocess console window that would normally be created. This
    * option is only meaningful on Windows systems. On Unix it is silently
    * ignored.
    */
    UV_PROCESS_WINDOWS_HIDE_CONSOLE = (1 << 5),
    /*
    * Hide the subprocess GUI window that would normally be created. This
    * option is only meaningful on Windows systems. On Unix it is silently
    * ignored.
    */
    UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6),
    /*
     * On Windows, if the path to the program to execute, specified in
     * uv_process_options_t's file field, has a directory component,
     * search for the exact file name before trying variants with
     * extensions like '.exe' or '.cmd'.
     */
    UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME = (1 << 7)
};
ظرف نگه‌دارنده برای هر قلاب stdio یا توصیف‌کننده فایل ارسالی به فرایند فرزند.
typedef struct uv_stdio_container_s {
    uv_stdio_flags flags;
    union {
        uv_stream_t* stream;
        int fd;
    } data;
} uv_stdio_container_t;
فلگ‌هایی که نحوه انتقال stdio به فرایند فرزند را مشخص می‌کنند.
typedef enum {
    /*
    * The following four options are mutually-exclusive, and define
    * the operation to perform for the corresponding file descriptor
    * in the child process:
    */
    /*
    * No file descriptor will be provided (or redirected to
    * `/dev/null` if it is fd 0, 1 or 2).
    */
    UV_IGNORE = 0x00,
    /*
    * Open a new pipe into `data.stream`, per the flags below. The
    * `data.stream` field must point to a uv_pipe_t object that has
    * been initialized with `uv_pipe_init(loop, data.stream, ipc);`,
    * but not yet opened or connected.
    /*
    UV_CREATE_PIPE = 0x01,
    /*
    * The child process will be given a duplicate of the parent's
    * file descriptor given by `data.fd`.
    */
    UV_INHERIT_FD = 0x02,
    /*
    * The child process will be given a duplicate of the parent's
    * file descriptor being used by the stream handle given by
    * `data.stream`.
    */
    UV_INHERIT_STREAM = 0x04,
    /*
    * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE
    * determine the direction of flow, from the child process' perspective. Both
    * flags may be specified to create a duplex data stream.
    */
    UV_READABLE_PIPE = 0x10,
    UV_WRITABLE_PIPE = 0x20,
    /*
    * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the
    * handle in non-blocking mode in the child. This may cause loss of data,
    * if the child is not designed to handle to encounter this mode,
    * but can also be significantly more efficient.
    */
    UV_NONBLOCK_PIPE = 0x40
} uv_stdio_flags;

شناسه فرایند (PID) فرایند اجراشده. پس از فراخوانی uv_spawn() تنظیم می‌شود.

نکته:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.
تابع بازخوانی که پس از خروج فرایند فراخوانی می‌شود.
مسیری که به برنامه اجرایی اشاره می‌کند.
آرگومان‌های خط فرمان. آرگومان args[0] باید مسیر برنامه باشد. در ویندوز از CreateProcess استفاده می‌شود که آرگومان‌ها را درون یک رشته الحاق می‌کند و این امر می‌تواند خطاهای عجیبی ایجاد کند. به فلگ UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS در uv_process_flags مراجعه کنید.
متغیرهای محیطی برای فرایند جدید. اگر NULL باشد از محیط فرایند والد استفاده می‌شود.
دایرکتوری کاری جاری برای زیرفرایند.
فلگ‌های گوناگونی که نحوه رفتار uv_spawn() را کنترل می‌کنند. به uv_process_flags مراجعه کنید.
فیلد stdio به آرایه‌ای از ساختارهای uv_stdio_container_t اشاره می‌کند که توصیف‌کننده‌های فایل قابل دسترسی برای فرایند فرزند را توصیف می‌کنند. قرارداد استاندارد این است که stdio[0] به ورودی استاندارد (stdin)، توصیف‌کننده 1 به خروجی استاندارد (stdout) و توصیف‌کننده 2 به خطای استاندارد (stderr) اختصاص دارد.

نکته:

در ویندوز، توصیف‌کننده‌های فایل بزرگتر از ۲ تنها در صورتی برای فرایند فرزند در دسترس هستند که فرایند فرزند از زمان اجرای MSVCRT استفاده کند.
کتابخانه libuv می‌تواند شناسه کاربر/گروه فرایند فرزند را تغییر دهد. این تغییر تنها زمانی رخ می‌دهد که بیت‌های مناسب در فیلدهای flags تنظیم شده باشند.

نکته:

این ویژگی در ویندوز پشتیبانی نمی‌شود و uv_spawn() با شکست مواجه شده و خطا را روی UV_ENOTSUP تنظیم می‌کند.
فلگ‌هایی که نحوه ارسال ظرف stdio به فرایند فرزند را مشخص می‌کنند.
اجتماعی (Union) شامل stream یا fd برای ارسال به فرایند فرزند.

وراثت توصیف‌کننده‌های فایل / قلاب‌هایی را که این فرایند از والد خود به ارث برده است غیرفعال می‌کند. اثر این کار این است که فرایندهای فرزندی که توسط این فرایند اجرا می‌شوند به طور تصادفی این قلاب‌ها را به ارث نمی‌برند.

توصیه می‌شود این تابع را در ابتدای برنامه در سریع‌ترین زمان ممکن فراخوانی کنید، پیش از آنکه توصیف‌کننده‌های فایل به ارث رسیده بتوانند بسته یا تکثیر شوند.

نکته:

این تابع بر مبنای حداکثر تلاش عمل می‌کند: هیچ تضمینی وجود ندارد که libuv بتواند تمام توصیف‌کننده‌های فایل به ارث رسیده را کشف کند. به طور کلی عملکرد آن در ویندوز بهتر از یونیکس است.
قلاب فرایند را راه‌اندازی اولیه کرده و فرایند را اجرا می‌کند. اگر فرایند با موفقیت اجرا شود، این تابع مقدار 0 را برمی‌گرداند. در غیر این صورت، کد خطای منفی متناظر با دلیل عدم اجرای فرایند بازگردانده می‌شود.

دلایل احتمالی برای عدم موفقیت در اجرا شامل مواردی مانند (اما نه محدود به) عدم وجود فایل اجرایی، نداشتن مجوزهای لازم برای استفاده از setuid یا setgid مشخص‌شده، یا عدم وجود حافظه کافی برای تخصیص به فرایند جدید است.

تغییریافته در نسخه 1.24.0: فلگ‌های UV_PROCESS_WINDOWS_HIDE_CONSOLE و UV_PROCESS_WINDOWS_HIDE_GUI اضافه شدند.

تغییریافته در نسخه 1.48.0: فلگ UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME اضافه شد.

سیگنال مشخص‌شده را به قلاب فرایند داده‌شده ارسال می‌کند. برای بررسی پشتیبانی از سیگنال‌ها، به‌ویژه در ویندوز، به مستندات uv_signal_t --- Signal handle <#signal> مراجعه کنید.
سیگنال مشخص‌شده را به شناسه فرایند (PID) داده‌شده ارسال می‌کند. برای بررسی پشتیبانی از سیگنال‌ها، به‌ویژه در ویندوز، به مستندات uv_signal_t --- Signal handle <#signal> مراجعه کنید.
مقدار handle->pid را برمی‌گرداند.

افزوده‌شده در نسخه 1.19.0.

همچنین ببینید:

توابع رابط برنامه‌نویسی uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

قلاب‌های جریان انتزاعی از یک کانال ارتباطی دوطرفه (duplex) را فراهم می‌کنند. uv_stream_t یک نوع انتزاعی است؛ کتابخانه libuv سه پیاده‌سازی مختلف از جریان را در قالب‌های uv_tcp_t <#c.uv_tcp_t>، uv_pipe_t <#c.uv_pipe_t> و uv_tty_t <#c.uv_tty_t> ارائه می‌دهد.

نوع داده قلاب جریان (Stream handle).
نوع درخواست اتصال (Connect request).
نوع درخواست خاموش‌سازی/بستن اتصال (Shutdown request).
نوع درخواست نوشتن (Write request). هنگام استفاده مجدد از اشیاء متعلق به این نوع، باید دقت فراوان داشت. زمانی که یک جریان در حالت غیرمسدودکننده (non-blocking) قرار دارد، درخواست‌های نوشتن ارسال‌شده با uv_write در صف قرار می‌گیرند. استفاده مجدد از اشیاء در این مقطع زمانی رفتاری تعریف‌نشده به همراه دارد. استفاده مجدد از شیء uv_write_t تنها پس از برانگیخته شدن تابع بازخوانی ارسال‌شده به uv_write ایمن است.
تابع بازخوانی هنگام خوانده شدن داده‌ها روی یک جریان.

اگر داده‌ای در دسترس باشد، مقدار nread بزرگتر از 0 و در صورت بروز خطا کوچکتر از 0 خواهد بود. هنگام رسیدن به پایان فایل (EOF)، مقدار nread برابر با UV_EOF خواهد بود. زمانی که nread کوچکتر از 0 باشد، پارامتر buf ممکن است به بافر معتبری اشاره نکند؛ در این حالت buf.len و buf.base هر دو برابر با 0 تنظیم می‌شوند.

نکته:

مقدار nread ممکن است 0 باشد، که به معنی خطا یا پایان فایل نیست. این معادل با EAGAIN یا EWOULDBLOCK در فراخوانی read(2) است.

فراخواننده مسئول توقف/بستن جریان هنگام بروز خطا از طریق فراخوانی uv_read_stop() یا uv_close() <#c.uv_close> است. تلاش برای خواندن مجدد از جریان رفتاری تعریف‌نشده دارد.

فراخواننده مسئول آزادسازی بافر است، libuv از آن مجدداً استفاده نمی‌کند. بافر ممکن است در زمان بروز خطا یک بافر تهی (که در آن buf->base == NULL و buf->len == 0 باشد) باشد.

تابع بازخوانی پس از نوشته شدن داده‌ها روی جریان. در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.
تابع بازخوانی پس از اتمام اتصالی که توسط uv_connect() آغاز شده است. در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.
تابع بازخوانی پس از تکمیل یک درخواست خاموش‌سازی (shutdown). در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.
تابع بازخوانی هنگامی که یک سرور جریان، اتصال ورودی جدیدی را دریافت می‌کند. کاربر می‌تواند با فراخوانی uv_accept() اتصال را بپذیرد. در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.

شامل حجم بایت‌های در صف انتظار برای ارسال. فقط‌خواندنی.
اشاره‌گر به جریانی که این درخواست اتصال روی آن در حال اجراست.
اشاره‌گر به جریانی که این درخواست خاموش‌سازی روی آن در حال اجراست.
اشاره‌گر به جریانی که این درخواست نوشتن روی آن در حال اجراست.
اشاره‌گر به جریانی که با استفاده از این درخواست نوشتن ارسال می‌شود.

همچنین ببینید:

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

خاموش‌سازی سمت خروجی (نوشتن) یک جریان دوطرفه (duplex). این تابع منتظر تکمیل درخواست‌های نوشتن معلق می‌ماند. پارامتر handle باید به یک جریان راه‌اندازی‌شده اشاره کند. پارامتر req باید یک ساختار درخواست خاموش‌سازی راه‌اندازی‌نشده باشد. پس از اتمام خاموش‌سازی، تابع cb فراخوانی می‌شود.
آغاز گوش دادن به اتصالات ورودی. پارامتر backlog تعداد اتصالات قابل قرارگیری در صف توسط هسته سیستم‌عامل را مشخص می‌کند، مشابه با listen(2) https://man7.org/linux/man-pages/man2/listen.2.html. هنگامی که یک اتصال ورودی جدید دریافت شود، تابع بازخوانی uv_connection_cb فراخوانی می‌گردد.
این فراخوانی همراه با uv_listen() برای پذیرش اتصالات ورودی استفاده می‌شود. این تابع را پس از دریافت uv_connection_cb جهت پذیرش اتصال فراخوانی کنید. قبل از فراخوانی این تابع، قلاب کلاینت باید راه‌اندازی اولیه شده باشد. مقدار بازگشتی کمتر از 0 نشان‌دهنده خطاست.

هنگامی که تابع بازخوانی uv_connection_cb فراخوانی می‌شود، تضمین می‌گردد که این تابع برای بار اول با موفقیت به پایان برسد. اگر بیش از یک بار تلاش به استفاده از آن کنید، ممکن است شکست بخورد. پیشنهاد می‌شود این تابع را فقط یک بار به ازای هر فراخوانی uv_connection_cb اجرا کنید.

نکته:

پارامترهای server و client باید قلاب‌هایی باشند که روی یک حلقه رویداد یکسان اجرا می‌شوند.
خواندن داده‌ها از یک جریان ورودی. تابع بازخوانی uv_read_cb چندین بار فراخوانی خواهد شد تا زمانی که داده دیگری برای خواندن نماند یا uv_read_stop() فراخوانی شود.

تغییریافته در نسخه 1.38.0: اکنون تابع uv_read_start() در صورت فراخوانی دوباره همواره UV_EALREADY و در صورت بسته شدن جریان مقدار UV_EINVAL را برمی‌گرداند. در نسخه‌های قدیمی‌تر libuv، مقدار UV_EALREADY روی ویندوز برمی‌گشت اما روی یونیکس خیر، و UV_EINVAL روی یونیکس برمی‌گشت اما روی ویندوز خیر.

توقف خواندن داده‌ها از جریان. تابع بازخوانی uv_read_cb دیگر فراخوانی نخواهد شد.

این تابع خودتوان (idempotent) است و فراخوانی آن روی جریانی که قبلاً متوقف شده ایمن است.

این تابع همواره با موفقیت انجام می‌شود؛ از این رو بررسی مقدار بازگشتی آن ضرورتی ندارد. مقدار بازگشتی غیرصفر نشان می‌دهد که اتمام آزادسازی منابع ممکن است به رویداد ورودی بعدی روی آن TTY در ویندوز موکول شود و به معنای شکست عملیات نیست.

نوشتن داده‌ها روی جریان. بافرها به ترتیب نوشته می‌شوند. مثال:
void cb(uv_write_t* req, int status) {
    /* Logic which handles the write result */
}
uv_buf_t a[] = {
    { .base = "1", .len = 1 },
    { .base = "2", .len = 1 }
};
uv_buf_t b[] = {
    { .base = "3", .len = 1 },
    { .base = "4", .len = 1 }
};
uv_write_t req1;
uv_write_t req2;
/* writes "1234" */
uv_write(&req1, stream, a, 2, cb);
uv_write(&req2, stream, b, 2, cb);

نکته:

حافظه‌ای که بافرها به آن اشاره می‌کنند باید تا زمان فراخوانی تابع بازخوانی معتبر باقی بماند. این قاعده برای uv_write2() نیز صادق است.
تابع نوشتن پیشرفته برای ارسال قلاب‌ها از طریق یک لوله (pipe). لوله باید با ipc == 1 راه‌اندازی شده باشد.

نکته:

پارامتر send_handle باید یک قلاب TCP، لوله (pipe) و UDP در یونیکس، یا یک قلاب TCP در ویندوز باشد که به عنوان سرور یا یک اتصال (در وضعیت گوش دادن یا متصل) عمل کند. سوکت‌ها یا لوله‌های مقیدشده (bound) به عنوان سرور در نظر گرفته می‌شوند.
همانند uv_write() عمل می‌کند، اما اگر نوشتن بلافاصله تکمیل نشود، درخواست نوشتن را در صف قرار نمی‌دهد.

یکی از مقادیر زیر را برمی‌گرداند:

  • بزرگتر از 0: تعداد بایت‌های نوشته‌شده (می‌تواند کمتر از اندازه بافر ارائه‌شده باشد).
  • کوچکتر از 0: کد خطای منفی (در صورتی که هیچ داده‌ای نتواند بلافاصله ارسال شود، UV_EAGAIN بازگردانده می‌شود).
مشابه uv_try_write() و تابع نوشتن گسترش‌یافته برای ارسال قلاب‌ها از طریق یک لوله همانند c:func:uv_write2 است.

تلاش برای ارسال یک قلاب در ویندوز پشتیبانی نمی‌شود و در آنجا مقدار UV_EAGAIN را برمی‌گرداند.

افزوده‌شده در نسخه 1.42.0.

اگر جریان قابل خواندن باشد مقدار 1 و در غیر این صورت 0 را برمی‌گرداند.
اگر جریان قابل نوشتن باشد مقدار 1 و در غیر این صورت 0 را برمی‌گرداند.
فعال یا غیرفعال کردن حالت مسدودکننده (blocking mode) برای یک جریان.

هنگامی که حالت مسدودکننده فعال باشد، تمام عملیات نوشتن به صورت همگام تکمیل می‌شوند. با این حال رابط کاربری بدون تغییر باقی می‌ماند، مثلاً تکمیل یا عدم موفقیت در عملیات همچنان از طریق تابع بازخوانی که به صورت ناهمگام فراخوانی می‌شود گزارش خواهد شد.

هشدار:

اتکای بیش از حد به این رابط برنامه‌نویسی توصیه نمی‌شود؛ احتمال دارد در آینده تغییرات قابل توجهی در آن ایجاد شود.

در حال حاضر در ویندوز تنها برای قلاب‌های uv_pipe_t <#c.uv_pipe_t> کار می‌کند. در پلتفرم‌های یونیکس، تمام قلاب‌های uv_stream_t پشتیبانی می‌شوند.

همچنین libuv در حال حاضر هیچ تضمینی در مورد ترتیب اجرا هنگام تغییر حالت مسدودکننده پس از ارسال درخواست‌های نوشتن ارائه نمی‌دهد. بنابراین توصیه می‌شود حالت مسدودکننده بلافاصله پس از باز کردن یا ایجاد جریان تنظیم شود.

تغییریافته در نسخه 1.4.0: پیاده‌سازی یونیکس اضافه شد.

مقدار stream->write_queue_size را برمی‌گرداند.

افزوده‌شده در نسخه 1.19.0.

همچنین ببینید:

توابع رابط برنامه‌نویسی uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

قلاب‌های TCP برای نمایش هر دو مورد جریان‌ها و سرورهای TCP استفاده می‌شوند.

ساختار uv_tcp_t یک زیرکلاس از uv_stream_t <#c.uv_stream_t> است.

نوع داده قلاب TCP (TCP handle).
فلگ‌های استفاده‌شده در uv_tcp_bind().
enum uv_tcp_flags {
    /* Used with uv_tcp_bind, when an IPv6 address is used. */
    UV_TCP_IPV6ONLY = 1,
    /* Enable SO_REUSEPORT socket option when binding the handle.
     * This allows completely duplicate bindings by multiple processes
     * or threads if they all set SO_REUSEPORT before binding the port.
     * Incoming connections are distributed across the participating
     * listener sockets.
     *
     * This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
     * FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
     */
    UV_TCP_REUSEPORT = 2,
};

موردی وجود ندارد (N/A)

همچنین ببینید:

اعضای uv_stream_t <#c.uv_stream_t> نیز اعمال می‌شوند.

راه‌اندازی اولیه قلاب. تا این لحظه هیچ سوکتی ایجاد نشده است.
راه‌اندازی اولیه قلاب با فلگ‌های مشخص‌شده. در حال حاضر تنها ۸ بیت پایینی پارامتر flags به عنوان دامنه سوکت (socket domain) استفاده می‌شوند. یک سوکت برای دامنه مشخص‌شده ایجاد خواهد شد. اگر دامنه مشخص‌شده AF_UNSPEC باشد هیچ سوکتی ایجاد نمی‌شود، دقیقاً مانند uv_tcp_init().

افزوده‌شده در نسخه 1.7.0.

باز کردن یک توصیف‌کننده فایل یا SOCKET موجود به عنوان یک قلاب TCP.

تغییریافته در نسخه 1.2.1: توصیف‌کننده فایل روی حالت غیرمسدودکننده (non-blocking) تنظیم می‌شود.

نکته:

توصیف‌کننده فایل یا SOCKET ارسال‌شده از نظر نوع بررسی نمی‌شود، اما لازم است که یک سوکت جریان (stream socket) معتبر را نشان دهد.
فعال کردن TCP_NODELAY که الگوریتم Nagle را غیرفعال می‌کند.
فعال / غیرفعال کردن قابلیت TCP keep-alive. پارامتر delay تأخیر اولیه بر حسب ثانیه است و زمانی که enable برابر صفر باشد نادیده گرفته می‌شود.

پس از رسیدن به زمان delay، ۱۰ کاوش پیاپی با فاصله ۱ ثانیه از یکدیگر انجام خواهد شد. اگر در پایان این روند اتصال همچنان برقرار نشود، قلاب با خطای UV_ETIMEDOUT ارسالی به تابع بازخوانی مربوطه نابود می‌شود.

اگر delay کمتر از ۱ باشد، مقدار UV_EINVAL برگردانده می‌شود.

تغییریافته در نسخه 1.49.0: اگر delay کمتر از ۱ باشد، مقدار UV_EINVAL` بازگردانده می‌شود.

فعال / غیرفعال کردن TCP keep-alive با تمام گزینه‌های سوکت: TCP_KEEPIDLE، TCP_KEEPINTVL و TCP_KEEPCNT. پارامتر idle مقدار مربوط به TCP_KEEPIDLE، پارامتر intvl مقدار مربوط به TCP_KEEPINTVL و پارامتر cnt مقدار مربوط به TCP_KEEPCNT است که در صورت صفر بودن on نادیده گرفته می‌شوند.

با فعال بودن TCP keep-alive، پارامتر idle مدت زمانی است (به ثانیه) که اتصال باید پیش از شروع ارسال کاوش‌های keep-alive توسط TCP بی‌کار بماند. پارامتر intvl زمان (به ثانیه) بین کاوش‌های متوالی است. در صورتی که پروتکل TCP تعداد cnt کاوش را بدون دریافت هیچ پاسخی از سوی طرف مقابل ارسال کند، اتصال را قطع کرده و سپس قلاب با ارسال خطای UV_ETIMEDOUT به تابع بازخوانی مربوطه از بین می‌رود.

اگر یکی از مقادیر idle، intvl یا cnt کمتر از ۱ باشد، مقدار UV_EINVAL بازگردانده می‌شود.

تغییریافته در نسخه 1.52.0: پشتیبانی از تنظیم گزینه‌های سوکت TCP_KEEPINTVL و TCP_KEEPCNT اضافه شد.

نکته:

اطمینان حاصل کنید که گزینه‌های سوکت توسط سیستم‌عامل زیربنایی پشتیبانی می‌شوند. پلتفرم‌های تحت پشتیبانی در حال حاضر:
  • AIX
  • DragonFlyBSD
  • FreeBSD
  • illumos
  • Linux
  • macOS
  • NetBSD
  • Solaris
  • Windows
فعال / غیرفعال کردن درخواست‌های پذیرش همگام ناهمگام (simultaneous accepts) که توسط سیستم‌عامل هنگام گوش دادن به اتصالات جدید TCP در صف قرار می‌گیرند.

این تنظیم برای تنظیم کارایی سرور TCP به عملکرد مطلوب استفاده می‌شود. فعال بودن پذیرش همزمان می‌تواند سرعت پذیرش اتصالات را به طور قابل توجهی افزایش دهد (که به همین دلیل به طور پیش‌فرض فعال است)، اما ممکن است منجر به توزیع بار نامتوازن در محیط‌های چندفرایندی شود.

اتصال (bind) قلاب به یک آدرس و پورت.

هنگامی که پورت از قبل گرفته شده باشد، می‌توان انتظار مشاهده خطای UV_EADDRINUSE از uv_listen() <#c.uv_listen> یا uv_tcp_connect() را داشت، مگر اینکه UV_TCP_REUSEPORT را در flags برای تمام سوکت‌های در حال اتصال مشخص کنید. به عبارت دیگر، فراخوانی موفقیت‌آمیز این تابع تضمین نمی‌کند که فراخوانی uv_listen() <#c.uv_listen> یا uv_tcp_connect() نیز با موفقیت انجام شود.

پارامترها
  • handle -- قلاب TCP. باید با uv_tcp_init() راه‌اندازی شده باشد.
  • addr -- آدرس جهت اتصال. باید به یک struct sockaddr_in یا struct sockaddr_in6 راه‌اندازی‌شده اشاره کند.
  • flags -- فلگ‌هایی که رفتار اتصال سوکت را کنترل می‌کنند. UV_TCP_IPV6ONLY می‌تواند در flags قرار گیرد تا پشتیبانی دوپشته‌ای (dual-stack) غیرفعال شده و تنها از IPv6 استفاده شود. UV_TCP_REUSEPORT می‌تواند در flags گنجانده شود تا گزینه سوکت SO_REUSEPORT با قابلیت توزیع بار فعال شود و اتصالات ورودی بین تمام سوکت‌های شنونده در چند فرایند یا نخ توزیع گردند.
مقادیر بازگشتی
0 در صورت موفقیت، یا کد خطای کوچکتر از 0 در صورت شکست.

تغییریافته در نسخه 1.49.0: فلگ UV_TCP_REUSEPORT اضافه شد.

نکته:

فلگ UV_TCP_REUSEPORT در حال حاضر فقط در Linux 3.9+، DragonFlyBSD 3.6+، FreeBSD 12.0+، Solaris 11.4 و AIX 7.2.5+ در دسترس است. در سایر پلتفرم‌ها این تابع خطای UV_ENOTSUP را برمی‌گرداند.
دریافت آدرس جاری که قلاب به آن متصل است. پارامتر name باید به یک قطعه حافظه معتبر و به اندازه کافی بزرگ اشاره کند؛ ساختار struct sockaddr_storage برای پشتیبانی از IPv4 و IPv6 توصیه می‌شود.
دریافت آدرس طرف متصل به قلاب (peer). پارامتر name باید به یک قطعه حافظه معتبر و به اندازه کافی بزرگ اشاره کند؛ ساختار struct sockaddr_storage برای پشتیبانی از IPv4 و IPv6 توصیه می‌شود.
برقراری یک اتصال TCP از نوع IPv4 یا IPv6. یک قلاب TCP راه‌اندازی‌شده و یک ساختار uv_connect_t <#c.uv_connect_t> راه‌اندازی‌نشده فراهم کنید. پارامتر addr باید به یک struct sockaddr_in یا struct sockaddr_in6 راه‌اندازی‌شده اشاره کند.

در ویندوز اگر addr برای اشاره به یک آدرس نامشخص (0.0.0.0 یا ::) راه‌اندازی شده باشد، برای اشاره به localhost تغییر داده می‌شود. این کار برای هماهنگی با رفتار سیستم‌های لینوکس انجام شده است.

تابع بازخوانی زمانی که اتصال برقرار شد یا خطایی در برقراری ارتباط رخ داد، فراخوانی می‌شود.

تغییریافته در نسخه 1.19.0: نگاشت 0.0.0.0 و :: به localhost اضافه شد.

همچنین ببینید:

توابع رابط برنامه‌نویسی uv_stream_t <#c.uv_stream_t> نیز اعمال می‌شوند.
بازنشانی یک اتصال TCP با ارسال یک بسته RST. این کار با تنظیم گزینه سوکت SO_LINGER با بازه ماندگاری (linger interval) صفر و سپس فراخوانی uv_close() <#c.uv_close> انجام می‌شود. به دلیل برخی ناهماهنگی‌های پلتفرم، ترکیب فراخوانی‌های uv_shutdown() <#c.uv_shutdown> و uv_tcp_close_reset() مجاز نیست.

افزوده‌شده در نسخه 1.32.0.

ایجاد یک جفت سوکت متصل‌به‌هم با مشخصات تعیین‌شده. قلاب‌های حاصل را می‌توان به uv_tcp_open ارسال کرد، با uv_spawn استفاده نمود، یا برای هر منظور دیگری به کار برد.

مقادیر معتبر برای flags0 و flags1 عبارتند از:

•
UV_NONBLOCK_PIPE: قلاب سوکت مشخص‌شده را برای کاربرد ورودی/خروجی OVERLAPPED یا FIONBIO/O_NONBLOCK باز می‌کند. این مورد برای قلاب‌هایی که توسط libuv استفاده خواهند شد توصیه می‌شود، و در موارد دیگر معمولاً پیشنهاد نمی‌گردد.

معادل socketpair(2) https://man7.org/linux/man-pages/man2/socketpair.2.html با دامنه AF_UNIX.

افزوده‌شده در نسخه 1.41.0.

هندل‌های پایپ یک لایه انتزاعی روی فایل‌های استریم در یونیکس (شامل سوکت‌های دامنه محلی، پایپ‌ها و FIFOها) و پایپ‌های نام‌گذاری‌شده در ویندوز فراهم می‌کنند.

uv_pipe_t یک «زیرکلاس» از uv_stream_t <#c.uv_stream_t> است.

نوع هندل پایپ.

مشخص می‌کند که آیا این پایپ برای انتقال هندل بین پردازه‌ها مناسب است یا خیر. تنها پایپ متصلی که قرار است هندل‌ها را انتقال دهد باید این فلگ را تنظیم داشته باشد، نه پایپ شنونده‌ای که uv_accept روی آن فراخوانی می‌شود.

همچنین ببینید (See also):

اعضای uv_stream_t <#c.uv_stream_t> نیز اعمال می‌شوند.

راه‌اندازی یک هندل پایپ. آرگومان ipc یک مقدار بولی است که نشان می‌دهد آیا این پایپ برای انتقال هندل بین پردازه‌ها استفاده خواهد شد یا خیر (که ممکن است بایت‌های ارسالی روی خط را تغییر دهد). تنها پایپ متصلی که قرار است هندل‌ها را انتقال دهد باید این فلگ را تنظیم داشته باشد، نه پایپ شنونده‌ای که uv_accept روی آن فراخوانی می‌شود.
باز کردن یک توصیف‌گر فایل یا HANDLE موجود به عنوان پایپ.

تغییریافته در نسخه 1.2.1: توصیف‌گر فایل روی حالت غیرمسدودکننده (non-blocking) تنظیم می‌شود.

نکته:

نوع توصیف‌گر فایل یا HANDLE پاس‌داده‌شده بررسی نمی‌شود، اما لازم است که نمایانگر یک پایپ معتبر باشد.
متصل کردن پایپ به یک مسیر فایل (در یونیکس) یا یک نام (در ویندوز).

برخلاف uv_pipe_bind2()، از سوکت‌های فضای نام انتزاعی (abstract namespace) لینوکس پشتیبانی نمی‌کند.

نام مستعار برای uv_pipe_bind2(handle, name, strlen(name), 0).

نکته:

مسیرها در یونیکس به sizeof(sockaddr_un.sun_path) بایت کوتاه‌سازی (truncate) می‌شوند که معمولاً بین ۹۲ تا ۱۰۸ بایت است.
متصل کردن پایپ به یک مسیر فایل (در یونیکس) یا یک نام (در ویندوز).

flags باید صفر یا UV_PIPE_NO_TRUNCATE باشد. برای فلگ‌های پشتیبانی‌نشده بدون انجام عملیات bind مقدار UV_EINVAL برمی‌گرداند.

از سوکت‌های فضای نام انتزاعی لینوکس پشتیبانی می‌کند. namelen باید بایت nul ابتدایی را شامل شود، اما بایت nul انتهایی را خیر.

افزوده شده در نسخه 1.46.0.

نکته:

مسیرها در یونیکس به sizeof(sockaddr_un.sun_path) بایت کوتاه‌سازی می‌شوند که معمولاً بین ۹۲ تا ۱۰۸ بایت است، مگر اینکه فلگ UV_PIPE_NO_TRUNCATE مشخص شده باشد که در این صورت خطای UV_EINVAL برگردانده می‌شود.
اتصال به سوکت دامنه یونیکس یا پایپ نام‌گذاری‌شده در ویندوز.

برخلاف uv_pipe_connect2()، از سوکت‌های فضای نام انتزاعی لینوکس پشتیبانی نمی‌کند.

نام مستعار برای uv_pipe_connect2(req, handle, name, strlen(name), 0, cb).

نکته:

مسیرها در یونیکس به sizeof(sockaddr_un.sun_path) بایت کوتاه‌سازی می‌شوند که معمولاً بین ۹۲ تا ۱۰۸ بایت است.
اتصال به سوکت دامنه یونیکس یا پایپ نام‌گذاری‌شده در ویندوز.

flags باید صفر یا UV_PIPE_NO_TRUNCATE باشد. برای فلگ‌های پشتیبانی‌نشده بدون انجام عملیات اتصال مقدار UV_EINVAL برمی‌گرداند.

از سوکت‌های فضای نام انتزاعی لینوکس پشتیبانی می‌کند. namelen باید بایت nul ابتدایی را شامل شود، اما بایت nul انتهایی را خیر.

افزوده شده در نسخه 1.46.0.

نکته:

مسیرها در یونیکس به sizeof(sockaddr_un.sun_path) بایت کوتاه‌سازی می‌شوند که معمولاً بین ۹۲ تا ۱۰۸ بایت است، مگر اینکه فلگ UV_PIPE_NO_TRUNCATE مشخص شده باشد که در این صورت خطای UV_EINVAL برگردانده می‌شود.
دریافت نام سوکت دامنه یونیکس یا پایپ نام‌گذاری‌شده.

باید یک بافر از پیش تخصیص‌یافته فراهم شود. پارامتر size طول بافر را نگه می‌دارد و در خروجی روی تعداد بایت‌های نوشته‌شده در بافر تنظیم می‌شود. اگر بافر به اندازه کافی بزرگ نباشد UV_ENOBUFS برگردانده می‌شود و len حاوی اندازه مورد نیاز خواهد بود.

تغییریافته در نسخه 1.3.0: طول برگردانده‌شده دیگر شامل بایت تهی (null) پایانی نیست و بافر با null خاتمه نمی‌یابد.

دریافت نام سوکت دامنه یونیکس یا پایپ نام‌گذاری‌شده که هندل به آن متصل است.

باید یک بافر از پیش تخصیص‌یافته فراهم شود. پارامتر size طول بافر را نگه می‌دارد و در خروجی روی تعداد بایت‌های نوشته‌شده در بافر تنظیم می‌شود. اگر بافر به اندازه کافی بزرگ نباشد UV_ENOBUFS برگردانده می‌شود و len حاوی اندازه مورد نیاز خواهد بود.

افزوده شده در نسخه 1.3.0.

تنظیم تعداد هندل‌های وهله پایپ معلق زمانی که سرور پایپ منتظر اتصالات است.

نکته:

این تنظیم فقط در ویندوز اعمال می‌شود.
برای دریافت هندل‌ها روی پایپ‌های IPC استفاده می‌شود.

ابتدا uv_pipe_pending_count() را فراخوانی کنید؛ اگر مقدار آن بزرگتر از ۰ بود، یک هندل از نوع (type) داده‌شده که توسط uv_pipe_pending_type() برگردانده می‌شود را راه‌اندازی کرده و uv_accept(pipe, handle) را فراخوانی کنید.

همچنین ببینید (See also):

توابع API مربوط به uv_stream_t <#c.uv_stream_t> نیز اعمال می‌شوند.
مجوزهای پایپ را تغییر می‌دهد و اجازه می‌دهد پردازه‌های اجراشده توسط کاربران مختلف به آن دسترسی داشته باشند. پایپ را برای همه کاربران قابل نوشتن یا قابل خواندن می‌کند. حالت می‌تواند UV_WRITABLE، UV_READABLE یا UV_WRITABLE | UV_READABLE باشد. این تابع مسدودکننده (blocking) است.

افزوده شده در نسخه 1.16.0.

ایجاد یک جفت هندل پایپ متصل به هم. داده‌ها می‌توانند در fds[1] نوشته شده و از fds[0] خوانده شوند. هندل‌های حاصل را می‌توان به uv_pipe_open منتقل کرد، با uv_spawn استفاده نمود، یا برای هر منظور دیگری به کار برد.

مقادیر معتبر برای flags عبارتند از:

•
UV_NONBLOCK_PIPE: هندل سوکت مشخص‌شده را برای استفاده ورودی/خروجی OVERLAPPED یا FIONBIO/O_NONBLOCK باز می‌کند. این فلگ برای هندل‌هایی که قرار است توسط libuv استفاده شوند توصیه می‌شود و در غیر این صورت معمولاً توصیه نمی‌گردد.

معادل pipe(2) https://man7.org/linux/man-pages/man2/pipe.2.html با فلگ تنظیم‌شده O_CLOEXEC.

افزوده شده در نسخه 1.41.0.

هندل‌های TTY یک استریم برای کنسول فراهم می‌کنند.

uv_tty_t یک «زیرکلاس» از uv_stream_t <#c.uv_stream_t> است.

نوع هندل TTY.
افزوده شده در نسخه 1.2.0.

نوع حالت TTY:

typedef enum {
    /* حالت اولیه/عادی ترمینال */
    UV_TTY_MODE_NORMAL,
    /*
    * حالت ورودی خام (در ویندوز، ENABLE_WINDOW_INPUT نیز فعال می‌شود).
    * ممکن است در نسخه‌های آینده libuv معادل UV_TTY_MODE_RAW_VT شود.
    */
    UV_TTY_MODE_RAW,
    /* حالت ورودی/خروجی دودویی ایمن برای IPC (فقط یونیکس) */
    UV_TTY_MODE_IO,
    /* حالت ورودی خام. در ویندوز ENABLE_VIRTUAL_TERMINAL_INPUT نیز تنظیم می‌شود. */
    UV_TTY_MODE_RAW_VT
} uv_tty_mode_t;
نوع حالت ترمینال مجازی کنسول:
typedef enum {
    /*
     * کنسول از پردازش توالی‌های ترمینال مجازی پشتیبانی می‌کند
     * (کنسول جدید ویندوز ۱۰، ConEmu)
     */
    UV_TTY_SUPPORTED,
    /* کنسول نمی‌تواند توالی‌های ترمینال مجازی را پردازش کند. (کنسول قدیمی)
     */
    UV_TTY_UNSUPPORTED
} uv_tty_vtermstate_t

ندارد (N/A)

همچنین ببینید (See also):

اعضای uv_stream_t <#c.uv_stream_t> نیز اعمال می‌شوند.

راه‌اندازی یک استریم TTY جدید با توصیف‌گر فایل داده‌شده. معمولاً توصیف‌گر فایل یکی از موارد زیر خواهد بود:
  • 0 = ورودی استاندارد (stdin)
  • 1 = خروجی استاندارد (stdout)
  • 2 = خطای استاندارد (stderr)

در یونیکس این تابع مسیر fd ترمینال را با استفاده از ttyname_r(3) https://man7.org/linux/man-pages/man3/ttyname_r.3.html تعیین کرده، آن را باز می‌کند و در صورتی که توصیف‌گر فایل پاس‌داده‌شده به یک TTY اشاره کند، از آن استفاده می‌نماید. این کار به libuv اجازه می‌دهد TTY را بدون تأثیر بر سایر پردازه‌هایی که در TTY مشترک هستند در حالت غیرمسدودکننده قرار دهد.

این تابع در سیستم‌هایی که از ioctl TIOCGPTN یا TIOCPTYGNAME پشتیبانی نمی‌کنند (مانند OpenBSD و Solaris) در برابر چندنخی ایمن (thread-safe) نیست.

نکته:

اگر بازگشایی مجدد TTY با شکست مواجه شود، libuv به عملیات نوشتن مسدودکننده (blocking) بازمی‌گردد.

تغییریافته در نسخه 1.23.1: پارامتر readable اکنون استفاده‌نشده و نادیده گرفته می‌شود. مقدار صحیح اکنون به طور خودکار از هسته تشخیص داده می‌شود.

تغییریافته در نسخه 1.9.0: مسیر TTY توسط ttyname_r(3) https://man7.org/linux/man-pages/man3/ttyname_r.3.html تعیین می‌شود. در نسخه‌های قبلی، libuv به جای آن /dev/tty را باز می‌کرد.

تغییریافته در نسخه 1.5.0: تلاش برای راه‌اندازی استریم TTY با توصیف‌گر فایلی که به یک فایل معمولی اشاره دارد در یونیکس UV_EINVAL برمی‌گرداند.

تغییریافته در نسخه 1.2.0: حالت به عنوان یک مقدار uv_tty_mode_t مشخص می‌شود.

تنظیم TTY با استفاده از حالت ترمینال مشخص‌شده.

هنگام خروج برنامه فراخوانی می‌شود. تنظیمات TTY را برای پردازه بعدی که کنترل را در دست می‌گیرد، به مقادیر پیش‌فرض بازمی‌گرداند.

این تابع در پلتفرم‌های یونیکس async signal-safe است، اما اگر در حین اجرای درون uv_tty_set_mode() فراخوانی شود، می‌تواند با کد خطای UV_EBUSY با شکست مواجه شود.

دریافت اندازه فعلی پنجره. در صورت موفقیت ۰ برمی‌گرداند.

همچنین ببینید (See also):

توابع API مربوط به uv_stream_t <#c.uv_stream_t> نیز اعمال می‌شوند.
کنترل می‌کند که آیا توالی‌های ترمینال مجازی کنسول توسط libuv پردازش شوند یا کنسول. به‌ویژه برای فعال‌سازی پشتیبانی ConEmu از ANSI X3.64 و رنگ‌های ۲۵۶تایی Xterm مفید است. در غیر این صورت کنسول‌های ویندوز ۱۰ معمولاً به طور خودکار شناسایی می‌شوند.

این تابع فقط در سیستم‌های ویندوز معنادار است. در یونیکس بدون هیچ خطایی نادیده گرفته می‌شود.

افزوده شده در نسخه 1.33.0.

دریافت وضعیت فعلی پردازش توالی‌های ترمینال مجازی کنسول توسط libuv یا کنسول.

این تابع در یونیکس پیاده‌سازی نشده است و مقدار UV_ENOTSUP را برمی‌گرداند.

افزوده شده در نسخه 1.33.0.

هندل‌های UDP ارتباطات UDP را برای کلاینت‌ها و سرورها کپسوله‌سازی می‌کنند.

نوع هندل UDP.
نوع درخواست ارسال UDP.
فلگ‌های مورداستفاده در uv_udp_bind() و uv_udp_recv_cb..
enum uv_udp_flags {
    /* غیرفعال‌سازی حالت dual stack */
    UV_UDP_IPV6ONLY = 1,
    /*
     * نشان می‌دهد پیام به دلیل کوچک بودن بافر خواندن کوتاه‌سازی شده است.
     * بخش باقیمانده توسط سیستم‌عامل دور ریخته شده است. در uv_udp_recv_cb استفاده می‌شود.
     */
    UV_UDP_PARTIAL = 2,
    /*
     * نشان می‌دهد که آیا هنگام bind کردن هندل SO_REUSEADDR تنظیم شود یا خیر.
     * این فلگ در BSDها (به جز DragonFlyBSD)، OS X و پلتفرم‌های دیگری که SO_REUSEPORT
     * در آن‌ها قابلیت توزیع بار را ندارد، فلگ سوکت SO_REUSEPORT را تنظیم می‌کند
     * (برعکس عملکرد UV_UDP_REUSEPORT). در سایر پلتفرم‌های یونیکس، فلگ SO_REUSEADDR
     * را تنظیم می‌کند. بدین معنا که چندین نخ یا پردازه می‌توانند بدون خطا به یک
     * آدرس متصل شوند (به شرطی که همگی فلگ را تنظیم کنند) اما فقط آخرین متصل‌شونده
     * ترافیک را دریافت خواهد کرد و در عمل پورت را از شنونده قبلی «می‌رباید».
     */
    UV_UDP_REUSEADDR = 4,
    /*
     * نشان می‌دهد که پیام توسط recvmmsg دریافت شده است، بنابراین بافر ارائه‌شده
     * نباید توسط کالبک recv_cb آزاد شود.
     */
    UV_UDP_MMSG_CHUNK = 8,
    /*
     * نشان می‌دهد بافر ارائه‌شده به طور کامل توسط recvmmsg استفاده شده است و
     * اکنون باید توسط کالبک recv_cb آزاد شود. هنگامی که این فلگ در uv_udp_recv_cb
     * تنظیم شده باشد، nread همیشه 0 و addr همیشه NULL خواهد بود.
     */
    UV_UDP_MMSG_FREE = 16,
    /*
     * نشان می‌دهد که آیا هنگام bind کردن هندل IP_RECVERR/IPV6_RECVERR تنظیم شود یا خیر.
     * این فلگ IP_RECVERR را برای سوکت‌های IPv4 و IPV6_RECVERR را برای سوکت‌های IPv6 در
     * لینوکس تنظیم می‌کند. این کار مانع از سرکوب برخی خطاهای ICMP توسط هسته لینوکس شده
     * و گزارش کامل خطای ICMP را برای بازیابی سریع‌تر فعال می‌کند.
     * این فلگ در پلتفرم‌های غیرلینوکسی بی‌اثر (no-op) است.
     */
    UV_UDP_LINUX_RECVERR = 32,
    /*
     * نشان می‌دهد که آیا هنگام bind کردن هندل SO_REUSEPORT تنظیم شود یا خیر.
     * این گزینه سوکت SO_REUSEPORT را در پلتفرم‌های پشتیبانی‌شده تنظیم می‌کند.
     * برخلاف UV_UDP_REUSEADDR، این فلگ باعث می‌شود چندین نخ یا پردازه که به همان آدرس
     * و پورت متصل می‌شوند، پورت را «به اشتراک بگذارند»، یعنی دیتاگرام‌های ورودی بین
     * سوکت‌های گیرنده میان نخ‌ها یا پردازه‌ها توزیع شوند.
     *
     * این فلگ در حال حاضر فقط در لینوکس +3.9، دراگون‌فلای‌بی‌اس‌دی +3.6،
     * فری‌بی‌اس‌دی +12.0، سولاریس 11.4 و AIX +7.2.5 در دسترس است.
     */
    UV_UDP_REUSEPORT = 64,
    /*
     * نشان می‌دهد در صورت در دسترس بودن، باید از recvmmsg استفاده شود.
     */
    UV_UDP_RECVMMSG = 256
};
تعریف نوع برای کالبک پاس‌داده‌شده به uv_udp_send() که پس از ارسال داده فراخوانی می‌شود.
تعریف نوع برای کالبک پاس‌داده‌شده به uv_udp_recv_start() که هنگام دریافت داده توسط نقطه پایانی فراخوانی می‌شود.
  • handle: هندل UDP
  • nread: تعداد بایت‌های دریافت‌شده. اگر داده دیگری برای خواندن وجود نداشته باشد، ۰ است. توجه داشته باشید که ۰ ممکن است به این معنا نیز باشد که یک دیتاگرام خالی دریافت شده است (در این حالت addr برابر NULL نیست). اگر خطای انتقال شناسایی شده باشد، کوچکتر از ۰ است؛ در صورت استفاده از recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html قطعه داده دیگری دریافت نخواهد شد و بافر را می‌توان با اطمینان آزاد کرد.
  • buf: ساختار uv_buf_t <#c.uv_buf_t> حاوی داده‌های دریافت‌شده.
  • addr: ساختار struct sockaddr* حاوی آدرس فرستنده. می‌تواند NULL باشد. فقط در طول مدت فراخوانی کالبک معتبر است.
  • flags: یک یا چند ثابت UV_UDP_* که با عملگر OR ترکیب شده‌اند.

کالبک‌گیرنده مسئول آزادسازی بافر است، libuv از آن مجدداً استفاده نمی‌کند. در صورت بروز خطا، بافر ممکن است یک بافر تهی باشد (جایی که buf->base == NULL و buf->len == 0 است).

هنگام استفاده از recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html، قطعات داده دارای فلگ UV_UDP_MMSG_CHUNK خواهند بود که نباید آزاد شوند. در صورت عدم بروز خطا، یک فراخوانی نهایی کالبک با nread برابر با ۰، addr برابر با NULL و بافر اشاره‌کننده به داده‌های اولیه با فلگ UV_UDP_MMSG_CHUNK پاک‌شده و فلگ UV_UDP_MMSG_FREE تنظیم‌شده وجود خواهد داشت. در صورت بروز خطای سوکت UDP، مقدار nread کمتر از ۰ خواهد بود. در هر دو حالت، کالبک‌گیرنده اکنون می‌تواند با خیال راحت بافر ارائه‌شده را آزاد کند.

تغییریافته در نسخه 1.40.0: فلگ UV_UDP_MMSG_FREE اضافه شد.

نکته:

کالبک دریافت هنگامی که چیزی برای خواندن وجود ندارد با nread == 0 و addr == NULL، و هنگامی که یک بسته UDP خالی دریافت می‌شود با nread == 0 و addr != NULL فراخوانی خواهد شد.
نوع عضویت برای یک آدرس چندپخشی (multicast).
typedef enum {
    UV_LEAVE_GROUP = 0,
    UV_JOIN_GROUP
} uv_membership;

تعداد بایت‌های در صف برای ارسال. این فیلد مشخصاً نشان می‌دهد چه مقدار اطلاعات در حال حاضر در صف قرار دارد.
تعداد درخواست‌های ارسال فعلی در صف که در انتظار پردازش هستند.
هندل UDP که این درخواست ارسال روی آن در حال انجام است.

همچنین ببینید (See also):

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

راه‌اندازی یک هندل UDP جدید. سوکت واقعی به صورت تنبلانه (lazily) ایجاد می‌شود. در صورت موفقیت ۰ برمی‌گرداند.
راه‌اندازی هندل با فلگ‌های مشخص‌شده. ۸ بیت پایینی پارامتر flags به عنوان دامنه (domain) سوکت استفاده می‌شوند. یک سوکت برای دامنه داده‌شده ایجاد خواهد شد. اگر دامنه مشخص‌شده AF_UNSPEC باشد، همانند uv_udp_init() هیچ سوکتی ایجاد نمی‌شود.

بیت‌های باقیمانده می‌توانند برای تنظیم یکی از این فلگ‌ها استفاده شوند:

•
UV_UDP_RECVMMSG: در صورت تنظیم و پشتیبانی پلتفرم، از recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html استفاده خواهد شد.

افزوده شده در نسخه 1.7.0.

تغییریافته در نسخه 1.37.0: فلگ UV_UDP_RECVMMSG اضافه شد.

باز کردن یک توصیف‌گر فایل یا SOCKET ویندوز موجود به عنوان یک هندل UDP.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
  • sock -- یک سوکت موجود برای اتصال به هندل.
  • flags -- فلگ‌هایی که رفتار سوکت را کنترل می‌کنند؛ UV_UDP_REUSEADDR و UV_UDP_REUSEPORT پشتیبانی می‌شوند.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.

افزوده شده در نسخه 1.52.0.

باز کردن یک توصیف‌گر فایل یا SOCKET ویندوز موجود به عنوان یک هندل UDP.

فقط در یونیکس: تنها الزام آرگومان sock این است که از قرارداد دیتاگرام پیروی کند (در حالت بدون اتصال کار کند، از sendmsg()/recvmsg() پشتیبانی نماید و غیره). به عبارت دیگر، سایر سوکت‌های نوع دیتاگرام مانند raw sockets یا netlink sockets نیز می‌توانند به این تابع پاس داده شوند.

تغییریافته در نسخه 1.2.1: توصیف‌گر فایل روی حالت غیرمسدودکننده تنظیم می‌شود.

نکته:

نوع توصیف‌گر فایل یا SOCKET پاس‌داده‌شده بررسی نمی‌شود، اما لازم است که نمایانگر یک سوکت دیتاگرام معتبر باشد.

درون تابع، گزینه سوکت SO_REUSEADDR بدون قید و شرط تنظیم می‌شود. این بدان معناست که فلگ بازاستفاده بدون در نظر گرفتن قصد کاربر همیشه فعال است. برای کنترل بیشتر از uv_udp_open_ex() استفاده کنید.

متصل کردن هندل UDP به یک آدرس IP و پورت.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
  • addr -- ساختار struct sockaddr_in یا struct sockaddr_in6 حاوی آدرس و پورت برای متصل شدن.
  • flags -- نشان می‌دهد سوکت چگونه bind خواهد شد؛ UV_UDP_IPV6ONLY، UV_UDP_REUSEADDR، UV_UDP_REUSEPORT و UV_UDP_RECVERR پشتیبانی می‌شوند.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.

تغییریافته در نسخه 1.49.0: فلگ UV_UDP_REUSEPORT اضافه شد.

نکته:

فلگ UV_UDP_REUSEPORT در حال حاضر فقط در لینوکس +3.9، دراگون‌فلای‌بی‌اس‌دی +3.6، فری‌بی‌اس‌دی +12.0، سولاریس 11.4 و AIX +7.2.5 در دسترس است. در پلتفرم‌های دیگر این تابع خطای UV_ENOTSUP برمی‌گرداند. برای پلتفرم‌هایی که در آن‌ها SO_REUSEPORT دارای قابلیت توزیع بار است، مشخص کردن همزمان UV_UDP_REUSEADDR و UV_UDP_REUSEPORT در flags مجاز بوده و SO_REUSEPORT همیشه رفتار SO_REUSEADDR را بازنویسی (override) می‌کند. برای پلتفرم‌هایی که در آن‌ها SO_REUSEPORT قابلیت توزیع بار را ندارد، مشخص کردن همزمان UV_UDP_REUSEADDR و UV_UDP_REUSEPORT با شکست مواجه شده و خطای UV_ENOTSUP برمی‌گرداند.
مرتبط کردن هندل UDP با یک آدرس و پورت دوردست، به طوری که هر پیامی که توسط این هندل ارسال می‌شود به طور خودکار به آن مقصد فرستاده شود. فراخوانی این تابع با addr برابر NULL اتصال هندل را قطع می‌کند. تلاش برای فراخوانی uv_udp_connect() روی یک هندل از پیش متصل‌شده منجر به خطای UV_EISCONN می‌شود. تلاش برای قطع اتصال هندلی که متصل نیست خطای UV_ENOTCONN را برمی‌گرداند.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
  • addr -- ساختار struct sockaddr_in یا struct sockaddr_in6 حاوی آدرس و پورت مقصد.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.

افزوده شده در نسخه 1.27.0.

دریافت IP و پورت دوردست در هندل‌های متصل UDP. در هندل‌های غیرمتصل، مقدار UV_ENOTCONN را برمی‌گرداند.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی و bind شده باشد.
  • name -- اشاره‌گر به ساختاری که با داده‌های آدرس پر می‌شود. برای پشتیبانی از IPv4 و IPv6 باید از struct sockaddr_storage استفاده شود.
  • namelen -- در ورودی نشان‌دهنده اندازه بافر name است. در خروجی نشان می‌دهد چه مقدار از آن پر شده است.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.

افزوده شده در نسخه 1.27.0.

دریافت IP و پورت محلی هندل UDP.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی و bind شده باشد.
  • name -- اشاره‌گر به ساختاری که با داده‌های آدرس پر می‌شود. برای پشتیبانی از IPv4 و IPv6 باید از struct sockaddr_storage استفاده شود.
  • namelen -- در ورودی نشان‌دهنده اندازه بافر name است. در خروجی نشان می‌دهد چه مقدار از آن پر شده است.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
تنظیم وضعیت عضویت برای یک آدرس چندپخشی (multicast).
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
  • multicast_addr -- آدرس چندپخشی برای تنظیم عضویت.
  • interface_addr -- آدرس رابط شبکه (interface).
  • membership -- باید UV_JOIN_GROUP یا UV_LEAVE_GROUP باشد.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
تنظیم عضویت برای یک گروه چندپخشی مبتنی بر منبع خاص (source-specific multicast).
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
  • multicast_addr -- آدرس چندپخشی برای تنظیم عضویت.
  • interface_addr -- آدرس رابط شبکه.
  • source_addr -- آدرس منبع.
  • membership -- باید UV_JOIN_GROUP یا UV_LEAVE_GROUP باشد.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.

افزوده شده در نسخه 1.32.0.

تنظیم فلگ حلقه چندپخشی IP (multicast loop). باعث بازگشت بسته‌های چندپخشی به سوکت‌های محلی می‌شود.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راه‌اندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
  • on -- مقدار ۱ برای روشن، ۰ برای خاموش.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
تنظیم TTL چندپخشی.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راه‌اندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
  • ttl -- مقدار بین ۱ تا ۲۵۵.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
تنظیم رابط چندپخشی برای ارسال یا دریافت داده.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راه‌اندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
  • interface_addr -- آدرس رابط شبکه.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
روشن یا خاموش کردن پخش همگانی (broadcast).
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راه‌اندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
  • on -- مقدار ۱ برای روشن، ۰ برای خاموش.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
تنظیم طول عمر بسته (Time To Live / TTL).
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راه‌اندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
  • ttl -- مقدار بین ۱ تا ۲۵۵.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
ارسال داده روی سوکت UDP. اگر سوکت قبلاً با uv_udp_bind() متصل نشده باشد، به آدرس 0.0.0.0 (آدرس IPv4 «تمامی رابط‌ها») و یک شماره پورت تصادفی متصل خواهد شد.

در ویندوز اگر addr برای اشاره به یک آدرس نامشخص (0.0.0.0 یا ::) مقداردهی شده باشد، برای تطابق با رفتار سیستم‌های لینوکس به localhost تغییر داده می‌شود.

برای هندل‌های متصل UDP، مقدار addr باید برابر با NULL باشد، در غیر این صورت خطای UV_EISCONN برگردانده خواهد شد.

برای هندل‌های UDP بدون اتصال، مقدار addr نمی‌تواند NULL باشد، در غیر این صورت خطای UV_EDESTADDRREQ برگردانده خواهد شد.

پارامترها (Parameters)
  • req -- هندل درخواست UDP. نیازی به راه‌اندازی اولیه ندارد.
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
  • bufs -- لیستی از بافرها برای ارسال.
  • nbufs -- تعداد بافرها در bufs.
  • addr -- ساختار struct sockaddr_in یا struct sockaddr_in6 حاوی آدرس و پورت مقصد دوردست.
  • send_cb -- کالبکی که پس از ارسال داده فراخوانی می‌شود.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.

تغییریافته در نسخه 1.19.0: نگاشت 0.0.0.0 و :: به localhost اضافه شد.

تغییریافته در نسخه 1.27.0: پشتیبانی از سوکت‌های متصل اضافه شد.

همانند uv_udp_send() عمل می‌کند، اما اگر ارسال نتواند بلافاصله تکمیل شود، درخواست ارسالی را در صف قرار نمی‌دهد.

برای هندل‌های متصل UDP، مقدار addr باید برابر با NULL باشد، در غیر این صورت خطای UV_EISCONN برمی‌گرداند.

برای هندل‌های UDP بدون اتصال، مقدار addr نمی‌تواند NULL باشد، در غیر این صورت خطای UV_EDESTADDRREQ برمی‌گرداند.

بازگشتی (Returns)
>= 0: تعداد بایت‌های ارسال‌شده (با اندازه بافر داده‌شده مطابقت دارد). < 0: کد خطای منفی (هنگامی که پیام نتواند بلافاصله ارسال شود، UV_EAGAIN برگردانده می‌شود).

تغییریافته در نسخه 1.27.0: پشتیبانی از سوکت‌های متصل اضافه شد.

مشابه uv_udp_try_send()، اما می‌تواند چندین دیتاگرام ارسال کند. یک انتزاع سبک پیرامون sendmmsg(2) https://man7.org/linux/man-pages/man2/sendmmsg.2.html، همراه با یک حلقه بازگشتی به sendmsg(2) https://man7.org/linux/man-pages/man2/sendmsg.2.html برای پلتفرم‌هایی که از حالت اول پشتیبانی نمی‌کنند. هندل باید به طور کامل راه‌اندازی شده باشد؛ ابتدا uv_udp_bind را فراخوانی کنید.
بازگشتی (Returns)
>= 0: تعداد دیتاگرام‌های ارسال‌شده. تنها در صورتی ۰ است که count برابر صفر باشد. < 0: کد خطای منفی. تنها در صورتی که ارسال اولین دیتاگرام با شکست مواجه شود، در غیر این صورت تعداد مثبت ارسال‌ها را برمی‌گرداند. مقدار UV_EAGAIN زمانی بازگردانده می‌شود که دیتاگرام‌ها هم‌اکنون قابل ارسال نباشند؛ در این حالت به uv_udp_send() رجوع کنید.

افزوده شده در نسخه 1.50.0.

آماده‌سازی برای دریافت داده. اگر سوکت قبلاً با uv_udp_bind() متصل نشده باشد، به 0.0.0.0 (آدرس IPv4 «تمامی رابط‌ها») و یک پورت تصادفی متصل می‌شود.
پارامترها (Parameters)
  • handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
  • alloc_cb -- کالبکی که هنگام نیاز به فضای ذخیره‌سازی موقت فراخوانی می‌شود.
  • recv_cb -- کالبکی که با داده‌های دریافتی فراخوانی می‌شود.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.

نکته:

هنگام استفاده از recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html، تعداد پیام‌های دریافتی در هر نوبت به حداکثر تعداد دیتاگرام‌هایی با بیشینه اندازه که در بافر تخصیص‌یافته در alloc_cb جای می‌گیرند محدود می‌شود، و suggested_size در alloc_cb برای udp_recv همیشه برابر با اندازه ۱ دیتاگرام با بیشینه اندازه تنظیم می‌شود.

تغییریافته در نسخه 1.35.0: پشتیبانی از recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html در پلتفرم‌های پشتیبانی‌شده اضافه شد. استفاده از این ویژگی نیازمند ارسال بافری بزرگتر از 2 * 64KB به alloc_cb است.

تغییریافته در نسخه 1.37.0: پشتیبانی از recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html دیگر به صورت ضمنی فعال نیست و باید به صراحت با ارسال فلگ UV_UDP_RECVMMSG به uv_udp_init_ex() درخواست شود.

تغییریافته در نسخه 1.39.0: تابع uv_udp_using_recvmmsg() را می‌توان در alloc_cb استفاده کرد تا تعیین شود آیا باید بافری با اندازه متناسب برای استفاده با recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html برای هندل/پلتفرم جاری تخصیص یابد یا خیر.

اگر هندل UDP با فلگ UV_UDP_RECVMMSG ایجاد شده باشد و پلتفرم از recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html پشتیبانی کند ۱، و در غیر این صورت ۰ برمی‌گرداند.

افزوده شده در نسخه 1.39.0.

توقف شنود برای دیتاگرام‌های ورودی.
پارامترها (Parameters)
•
handle -- هندل UDP. باید با uv_udp_init() راه‌اندازی شده باشد.
بازگشتی (Returns)
۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
مقدار handle->send_queue_size را برمی‌گرداند.

افزوده شده در نسخه 1.19.0.

مقدار handle->send_queue_count را برمی‌گرداند.

افزوده شده در نسخه 1.19.0.

همچنین ببینید (See also):

توابع API مربوط به uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

هندل‌های FS Event به کاربر اجازه می‌دهند یک مسیر مشخص را برای تغییرات زیر نظر بگیرد؛ به عنوان مثال، اگر نام فایل تغییر کند یا تغییری عمومی در آن رخ دهد. این هندل از بهترین بک‌اند موجود برای این کار در هر پلتفرم استفاده می‌کند.

نکته:

برای AIX، بسته غیرپیش‌فرض IBM bos.ahafs باید نصب شده باشد. سیستم‌فایل زیرساخت رویداد AIX (ahafs) دارای محدودیت‌هایی است:
  • سیستم ahafs مانیتورینگ را به ازای هر پردازه پیگیری می‌کند و در برابر چندنخی ایمن (thread-safe) نیست. برای هر مانیتور روی رویدادی یکسان، باید یک پردازه جداگانه اجرا شود.
  • اگر تنها پوشه والد تحت نظر باشد، رویدادهای مربوط به تغییر فایل (نوشتن درون فایل) دریافت نمی‌شوند.

برای جزئیات بیشتر به مستندات https://developer.ibm.com/articles/au-aix_event_infrastructure مراجعه کنید.

زیرساخت نظارت بر رویدادهای سیستم‌فایل z/OS در صورت ایجاد یا حذف فایل درون پوشه‌ای که تحت نظر است، اعلانی ارسال نمی‌کند. برای جزئیات بیشتر به مرکز دانش آی‌بی‌ام https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.2.0/com.ibm.zos.v2r1.bpxb100/ioc.htm مراجعه کنید.

نوع هندل FS Event.
کالبک پاس‌داده‌شده به uv_fs_event_start() که پس از شروع هندل، به صورت مکرر فراخوانی خواهد شد.

اگر هندل با یک پوشه راه‌اندازی شده باشد، پارامتر filename یک مسیر نسبی به یک فایل درون آن پوشه خواهد بود، یا در صورتی که نام فایل قابل تشخیص نباشد، مقدار NULL خواهد بود.

پارامتر events یک ماسک ترکیب‌شده با عملگر OR از عناصر uv_fs_event است.

نکته:

در FreeBSD ممکن است به دلیل یک باگ در هسته، مسیر گاهی اوقات NULL باشد.

انواع رویدادهایی که هندل‌های uv_fs_event_t نظارت می‌کنند.
enum uv_fs_event {
    UV_RENAME = 1,
    UV_CHANGE = 2
};
فلگ‌هایی که می‌توانند به uv_fs_event_start() برای کنترل رفتار آن ارسال شوند.
enum uv_fs_event_flags {
    /*
    * به طور پیش‌فرض، اگر به ناظر رویداد سیستم‌فایل یک نام پوشه داده شود، تمام
    * رویدادهای درون آن پوشه را زیر نظر می‌گیرد. این فلگ رفتار مذکور را بازنویسی
    * کرده و باعث می‌شود fs_event تنها تغییرات خود مدخل پوشه را گزارش کند. این
    * فلگ بر فایل‌های منفردِ تحت نظارت تأثیری ندارد.
    * این فلگ در حال حاضر هنوز روی هیچ بک‌اندی پیاده‌سازی نشده است.
    */
    UV_FS_EVENT_WATCH_ENTRY = 1,
    /*
    * به طور پیش‌فرض uv_fs_event تلاش می‌کند از یک رابط هسته مانند inotify یا
    * kqueue برای تشخیص رویدادها استفاده کند. این قابلیت ممکن است در سیستم‌فایل‌های
    * راه دور مانند مانت‌های NFS کار نکند. این فلگ باعث می‌شود fs_event به فراخوانی
    * stat() در فواصل زمانی منظم بازگردد.
    * این فلگ در حال حاضر هنوز روی هیچ بک‌اندی پیاده‌سازی نشده است.
    */
    UV_FS_EVENT_STAT = 2,
    /*
    * به طور پیش‌فرض، ناظر رویداد هنگام نظارت بر یک پوشه، تغییرات در زیرپوشه‌های
    * آن را ثبت نکرده (نادیده می‌گیرد).
    * این فلگ این رفتار را در پلتفرم‌های پشتیبانی‌شده بازنویسی می‌کند.
    */
    UV_FS_EVENT_RECURSIVE = 4
};

ندارد (N/A)

همچنین ببینید (See also):

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

شروع هندل با کالبک داده‌شده، که path مشخص‌شده را برای تغییرات زیر نظر خواهد گرفت. flags می‌تواند یک ماسک OR شده از uv_fs_event_flags باشد.

نکته:

در حال حاضر تنها فلگ پشتیبانی‌شده UV_FS_EVENT_RECURSIVE و تنها در macOS و ویندوز است.

نکته:

در macOS، رویدادهایی که بلافاصله قبل از فراخوانی uv_fs_event_start توسط سیستم‌عامل جمع‌آوری شده‌اند ممکن است به کالبک uv_fs_event_cb گزارش شوند.
متوقف کردن هندل؛ پس از این کالبک دیگر فراخوانی نخواهد شد.
دریافت مسیری که توسط هندل تحت نظر قرار دارد. بافر باید توسط کاربر از پیش تخصیص یافته باشد. در صورت موفقیت ۰ یا در صورت شکست کد خطای کوچکتر از ۰ برمی‌گرداند. در صورت موفقیت، buffer حاوی مسیر و size حاوی طول آن خواهد بود. اگر بافر به اندازه کافی بزرگ نباشد، UV_ENOBUFS برگردانده شده و size روی اندازه مورد نیاز شامل خاتمه‌دهنده null تنظیم خواهد شد.

تغییریافته در نسخه 1.3.0: طول برگردانده‌شده دیگر شامل بایت تهی (null) پایانی نیست و بافر با null خاتمه نمی‌یابد.

تغییریافته در نسخه 1.9.0: طول برگردانده‌شده در صورت بروز UV_ENOBUFS شامل بایت تهی پایانی است، و در صورت موفقیت بافر با null خاتمه می‌یابد.

همچنین ببینید (See also):

توابع API مربوط به uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

هندل‌های FS Poll به کاربر اجازه می‌دهند یک مسیر مشخص را برای تغییرات زیر نظر بگیرد. بر خلاف uv_fs_event_t <#c.uv_fs_event_t>، هندل‌های fs poll از stat برای تشخیص تغییر یک فایل استفاده می‌کنند، بنابراین می‌توانند در سیستم‌فایل‌هایی که هندل‌های fs event کارایی ندارند کار کنند.

نوع هندل FS Poll.
کالبک پاس‌داده‌شده به uv_fs_poll_start() که پس از شروع به کار هندل، هنگام وقوع هر تغییری در مسیر تحت نظارت، به طور مکرر فراخوانی خواهد شد.

اگر path وجود نداشته باشد یا غیرقابل دسترس باشد، کالبک با status < 0 فراخوانی می‌شود. ناظر متوقف نمی‌شود، اما کالبک شما تا زمانی که تغییری رخ ندهد (مثلاً هنگامی که فایل ایجاد شود یا دلیل خطا تغییر کند) مجدداً فراخوانی نخواهد شد.

هنگامی که status == 0 باشد، کالبک اشاره‌گرهایی به ساختارهای قدیمی و جدید uv_stat_t <#c.uv_stat_t> دریافت می‌کند. این ساختارها فقط در طول مدت فراخوانی کالبک معتبر هستند.

ندارد (N/A)

همچنین ببینید (See also):

اعضای uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

بررسی فایل در مسیر path برای تغییرات در هر بازه زمانی interval میلی‌ثانیه.

نکته:

برای بیشترین قابلیت حمل‌پذیری، از بازه‌های چند ثانیه‌ای استفاده کنید. بازه‌های زمانی زیر یک ثانیه در بسیاری از سیستم‌فایل‌ها همه تغییرات را تشخیص نخواهند داد.
متوقف کردن هندل؛ کالبک دیگر فراخوانی نخواهد شد.
دریافت مسیری که توسط هندل تحت نظر است. بافر باید توسط کاربر از پیش تخصیص یافته باشد. در صورت موفقیت ۰ یا در صورت بروز شکست کد خطای کوچکتر از ۰ برمی‌گرداند. در صورت موفقیت، buffer حاوی مسیر و size حاوی طول آن خواهد بود. اگر بافر به اندازه کافی بزرگ نباشد، UV_ENOBUFS برگردانده شده و size روی اندازه مورد نیاز تنظیم خواهد شد.

تغییریافته در نسخه 1.3.0: طول برگردانده‌شده دیگر شامل بایت تهی (null) پایانی نیست و بافر با null خاتمه نمی‌یابد.

تغییریافته در نسخه 1.9.0: طول برگردانده‌شده در صورت بروز UV_ENOBUFS شامل بایت تهی پایانی است، و در صورت موفقیت بافر با null خاتمه می‌یابد.

همچنین ببینید (See also):

توابع API مربوط به uv_handle_t <#c.uv_handle_t> نیز اعمال می‌شوند.

کتابخانه libuv طیف گسترده‌ای از عملیات همگام (sync) و ناهمگام (async) سیستم فایل چندسکویی را فراهم می‌کند. تمامی توابع تعریف‌شده در این سند یک کالبک (callback) دریافت می‌کنند که می‌تواند NULL باشد. اگر کالبک NULL باشد درخواست به‌صورت همگام کامل می‌شود، در غیر این صورت به‌صورت ناهمگام انجام خواهد شد.

تمامی عملیات فایل روی استخر ریسه‌ها اجرا می‌شوند. برای اطلاعات در مورد اندازه استخر ریسه‌ها، بخش زمان‌بندی کار استخر ریسه <#threadpool> را ببینید.

از نسخه libuv v1.45.0 به بعد، برخی از عملیات فایل در لینوکس در صورت امکان به io_uring https://en.wikipedia.org/wiki/Io_uring واگذار می‌شوند. به جز افزایش (گاهی چشمگیر) نرخ ارسال/دریافت داده (throughput)، هیچ تغییری در رفتار قابل مشاهده نباید وجود داشته باشد. در صورتی که قابلیت‌های لازم هسته در دسترس یا مناسب نباشند، Libuv به استفاده از استخر ریسه خود بازمی‌گردد. از نسخه libuv v1.49.0 این رفتار بازگردانده شد و Libuv در لینوکس به‌طور پیش‌فرض مجدداً از استخر ریسه استفاده می‌کند. برای فعال‌سازی io_uring، نمونه uv_loop_t <#c.uv_loop_t> باید با گزینه UV_LOOP_USE_IO_URING_SQPOLL پیکربندی شود.

نکته:

در ویندوز، توابع uv_fs_* از کدگذاری utf-8 استفاده می‌کنند.

نوع درخواست سیستم فایل.
نوع داده ناامن در برابر مسئله سال ۲۰۳۸ (Y2K38-unsafe) برای ذخیره زمان با دقت نانوثانیه. در libuv v2.0 با uv_timespec64_t <#c.uv_timespec64_t> جایگزین خواهد شد.
typedef struct {
    long tv_sec;
    long tv_nsec;
} uv_timespec_t;
معادل چندسکویی و قابل حمل struct stat.
typedef struct {
    uint64_t st_dev;
    uint64_t st_mode;
    uint64_t st_nlink;
    uint64_t st_uid;
    uint64_t st_gid;
    uint64_t st_rdev;
    uint64_t st_ino;
    uint64_t st_size;
    uint64_t st_blksize;
    uint64_t st_blocks;
    uint64_t st_flags;
    uint64_t st_gen;
    uv_timespec_t st_atim;
    uv_timespec_t st_mtim;
    uv_timespec_t st_ctim;
    uv_timespec_t st_birthtim;
} uv_stat_t;
نوع درخواست سیستم فایل.
typedef enum {
    UV_FS_UNKNOWN = -1,
    UV_FS_CUSTOM,
    UV_FS_OPEN,
    UV_FS_CLOSE,
    UV_FS_READ,
    UV_FS_WRITE,
    UV_FS_SENDFILE,
    UV_FS_STAT,
    UV_FS_LSTAT,
    UV_FS_FSTAT,
    UV_FS_FTRUNCATE,
    UV_FS_UTIME,
    UV_FS_FUTIME,
    UV_FS_ACCESS,
    UV_FS_CHMOD,
    UV_FS_FCHMOD,
    UV_FS_FSYNC,
    UV_FS_FDATASYNC,
    UV_FS_UNLINK,
    UV_FS_RMDIR,
    UV_FS_MKDIR,
    UV_FS_MKDTEMP,
    UV_FS_RENAME,
    UV_FS_SCANDIR,
    UV_FS_LINK,
    UV_FS_SYMLINK,
    UV_FS_READLINK,
    UV_FS_CHOWN,
    UV_FS_FCHOWN,
    UV_FS_REALPATH,
    UV_FS_COPYFILE,
    UV_FS_LCHOWN,
    UV_FS_OPENDIR,
    UV_FS_READDIR,
    UV_FS_CLOSEDIR,
    UV_FS_MKSTEMP,
    UV_FS_LUTIME
} uv_fs_type;
معادل خلاصه‌شده و چندسکویی struct statfs. در uv_fs_statfs() استفاده می‌شود.
typedef struct uv_statfs_s {
    uint64_t f_type;
    uint64_t f_bsize;
    uint64_t f_blocks;
    uint64_t f_bfree;
    uint64_t f_bavail;
    uint64_t f_files;
    uint64_t f_ffree;
    uint64_t f_frsize;
    uint64_t f_spare[3];
} uv_statfs_t;
نوع مدخل دایرکتوری (dirent).
typedef enum {
    UV_DIRENT_UNKNOWN,
    UV_DIRENT_FILE,
    UV_DIRENT_DIR,
    UV_DIRENT_LINK,
    UV_DIRENT_FIFO,
    UV_DIRENT_SOCKET,
    UV_DIRENT_CHAR,
    UV_DIRENT_BLOCK
} uv_dirent_type_t;
معادل چندسکویی (خلاصه‌شده) struct dirent. در uv_fs_scandir_next() استفاده می‌شود.
typedef struct uv_dirent_s {
    const char* name;
    uv_dirent_type_t type;
} uv_dirent_t;
نوع داده استفاده‌شده برای پیمایش جریانی دایرکتوری. توسط uv_fs_opendir()، uv_fs_readdir() و uv_fs_closedir() استفاده می‌شود. dirents نشان‌دهنده آرایه‌ای از uv_dirent_t`s ارائه‌شده توسط کاربر است که برای نگهداری نتایج استفاده می‌شود. `nentries حداکثر اندازه آرایه dirents ارائه‌شده توسط کاربر است.
typedef struct uv_dir_s {
    uv_dirent_t* dirents;
    size_t nentries;
} uv_dir_t;
کالبکی که هنگام تکمیل ناهمگام یک درخواست فراخوانی می‌شود.

حلقه‌ای که این درخواست را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقط‌خواندنی.
مسیری که درخواست را تحت تأثیر قرار می‌دهد.
نتیجه درخواست. مقدار کوچکتر از ۰ به معنای خطا و در غیر این صورت نشان‌دهنده موفقیت است. در درخواست‌هایی مانند uv_fs_read() یا uv_fs_write() مقدار داده خوانده‌شده یا نوشته‌شده را نشان می‌دهد.
نتیجه uv_fs_stat() و سایر درخواست‌های stat را ذخیره می‌کند.
نتیجه uv_fs_readlink() و uv_fs_realpath() را ذخیره می‌کند و به‌عنوان نام مستعار (alias) برای statbuf عمل می‌کند.

همچنین ببینید:

اعضای uv_req_t <#c.uv_req_t> نیز اعمال می‌شوند.

پاک‌سازی درخواست. باید پس از پایان درخواست برای آزادسازی هرگونه حافظه‌ای که ممکن است libuv تخصیص داده باشد فراخوانی شود.
معادل open(2) https://man7.org/linux/man-pages/man2/open.2.html.

نکته:

در ویندوز libuv از CreateFileW استفاده می‌کند و بنابراین فایل همیشه در حالت دودویی (binary) باز می‌شود. به همین دلیل فلگ‌های O_BINARY و O_TEXT پشتیبانی نمی‌شوند.
معادل preadv(2) https://man7.org/linux/man-pages/man2/preadv.2.html. اگر شناسه offset برابر با -1 باشد، مکان آفست فعلی فایل استفاده و به‌روزرسانی می‌شود.

هشدار:

در ویندوز، تحت محیط‌های غیر MSVC (مثلاً هنگامی که از GCC یا Clang برای ساخت libuv استفاده می‌شود)، فایل‌های بازشده با UV_FS_O_FILEMAP در صورت شکست عملیات خواندن با نگاشت حافظه ممکن است موجب کرش مهلک شوند.
معادل pwritev(2) https://man7.org/linux/man-pages/man2/pwritev.2.html. اگر شناسه offset برابر با -1 باشد، مکان آفست فعلی فایل استفاده و به‌روزرسانی می‌شود.

هشدار:

در ویندوز، تحت محیط‌های غیر MSVC (مثلاً هنگامی که از GCC یا Clang برای ساخت libuv استفاده می‌شود)، فایل‌های بازشده با UV_FS_O_FILEMAP در صورت شکست عملیات نوشتن با نگاشت حافظه ممکن است موجب کرش مهلک شوند.
معادل mkdtemp(3) https://man7.org/linux/man-pages/man3/mkdtemp.3.html. نتیجه را می‌توان به‌صورت یک رشته خاتمه‌یافته با null در req->path یافت.
معادل mkstemp(3) https://man7.org/linux/man-pages/man3/mkstemp.3.html. مسیر فایل ایجادشده را می‌توان به‌صورت یک رشته خاتمه‌یافته با null در req->path یافت. توصیف‌کننده فایل (file descriptor) را می‌توان به‌صورت یک عدد صحیح در req->result یافت.

افزوده‌شده در نسخه 1.34.0.

مسیر path را به‌عنوان یک جریان دایرکتوری باز می‌کند. در صورت موفقیت، یک uv_dir_t تخصیص یافته و از طریق req->ptr برگردانده می‌شود. این حافظه توسط uv_fs_req_cleanup() آزاد نمی‌شود، هرچند req->ptr روی NULL تنظیم می‌گردد. حافظه تخصیص‌یافته باید با فراخوانی uv_fs_closedir() آزاد شود. در صورت شکست، هیچ حافظه‌ای تخصیص نمی‌یابد.

می‌توان با ارسال uv_dir_t حاصل به uv_fs_readdir() روی محتویات دایرکتوری پیمایش کرد.

افزوده‌شده در نسخه 1.28.0.

جریان دایرکتوری مشخص‌شده با dir را می‌بندد و حافظه تخصیص‌یافته توسط uv_fs_opendir() را آزاد می‌کند.

افزوده‌شده در نسخه 1.28.0.

روی جریان دایرکتوری dir که از یک فراخوانی موفق uv_fs_opendir() بازگردانده شده، پیمایش می‌کند. پیش از فراخوانی uv_fs_readdir()، فراخواننده باید dir->dirents و dir->nentries را تنظیم کند که نشان‌دهنده آرایه‌ای از عناصر uv_dirent_t مورد استفاده برای نگهداری مدخل‌های خوانده‌شده دایرکتوری و اندازه آن است.

در صورت موفقیت، نتیجه یک عدد صحیح >= 0 است که تعداد مدخل‌های خوانده‌شده از جریان را نشان می‌دهد.

افزوده‌شده در نسخه 1.28.0.

هشدار:

تابع uv_fs_readdir() از نظر ریسه ایمن نیست (thread safe نمی‌باشد).

نکته:

این تابع مدخل‌های "." و ".." را برنمی‌گرداند.

نکته:

در صورت موفقیت، این تابع حافظه‌ای تخصیص می‌دهد که باید با استفاده از uv_fs_req_cleanup() آزاد شود. uv_fs_req_cleanup() باید قبل از بستن دایرکتوری با uv_fs_closedir() فراخوانی شود.
معادل scandir(3) https://man7.org/linux/man-pages/man3/scandir.3.html، با یک API کمی متفاوت. پس از فراخوانی کالبک برای درخواست، کاربر می‌تواند از uv_fs_scandir_next() برای پر کردن ent با داده‌های مدخل بعدی دایرکتوری استفاده کند. هنگامی که مدخل دیگری وجود نداشته باشد، UV_EOF بازگردانده خواهد شد.

نکته:

برخلاف scandir(3)، این تابع مدخل‌های "." و ".." را برنمی‌گرداند.

نکته:

در لینوکس، دریافت نوع یک مدخل تنها توسط برخی از سیستم‌های فایل پشتیبانی می‌شود (btrfs، ext2، ext3 و ext4 در زمان نگارش این متن)؛ صفحه راهنمای getdents(2) https://man7.org/linux/man-pages/man2/getdents.2.html را بررسی کنید.
معادل statfs(2) https://man7.org/linux/man-pages/man2/statfs.2.html. در صورت موفقیت، یک uv_statfs_t تخصیص یافته و از طریق req->ptr برگردانده می‌شود. این حافظه توسط uv_fs_req_cleanup() آزاد می‌شود.

نکته:

هر فیلدی در uv_statfs_t حاصل که توسط سیستم‌عامل زیربنایی پشتیبانی نشود، روی صفر تنظیم می‌گردد.

افزوده‌شده در نسخه 1.31.0.

معادل fsync(2) https://man7.org/linux/man-pages/man2/fsync.2.html.

نکته:

برای AIX، تابع uv_fs_fsync روی توصیف‌کننده‌های فایلی که به فایل‌های عادی اشاره نمی‌کنند، UV_EBADF را برمی‌گرداند.
فایلی را از path به new_path کپی می‌کند. flags پشتیبانی‌شده در زیر شرح داده شده‌اند.
  • UV_FS_COPYFILE_EXCL: در صورت وجود، اگر مسیر مقصد از قبل وجود داشته باشد، uv_fs_copyfile() با خطای UV_EEXIST شکست می‌خورد. رفتار پیش‌فرض بازنویسی روی مقصد در صورت وجود آن است.
  • UV_FS_COPYFILE_FICLONE: در صورت وجود، uv_fs_copyfile() تلاش می‌کند یک پیوند مجدد کپی هنگام نوشتن (copy-on-write reflink) ایجاد کند. اگر پلتفرم زیربنایی از copy-on-write پشتیبانی نکند یا هنگام تلاش برای استفاده از آن خطایی رخ دهد، یک مکانیزم کپی جایگزین بر پایه uv_fs_sendfile() استفاده می‌شود.
  • UV_FS_COPYFILE_FICLONE_FORCE: در صورت وجود، uv_fs_copyfile() تلاش می‌کند یک پیوند مجدد کپی هنگام نوشتن (copy-on-write reflink) ایجاد کند. اگر پلتفرم زیربنایی از copy-on-write پشتیبانی نکند یا هنگام تلاش برای استفاده از آن خطایی رخ دهد، یک خطا بازگردانده می‌شود.

هشدار:

اگر مسیر مقصد ایجاد شود اما هنگام کپی کردن داده‌ها خطایی رخ دهد، مسیر مقصد حذف می‌گردد. بازه زمانی کوتاهی میان بستن و حذف فایل وجود دارد که در آن فرآیند دیگری می‌تواند به فایل دسترسی پیدا کند.

افزوده‌شده در نسخه 1.14.0.

تغییریافته در نسخه 1.20.0: UV_FS_COPYFILE_FICLONE و UV_FS_COPYFILE_FICLONE_FORCE پشتیبانی می‌شوند.

تغییریافته در نسخه 1.33.0: اگر هنگام استفاده از UV_FS_COPYFILE_FICLONE_FORCE خطایی رخ دهد، همان خطا بازگردانده می‌شود. پیش از این، تمام خطاها به UV_ENOTSUP نگاشت می‌شدند.

به‌ترتیب معادل utime(2) https://man7.org/linux/man-pages/man2/utime.2.html، futimes(3) https://man7.org/linux/man-pages/man3/futimes.3.html و lutimes(3) https://man7.org/linux/man-pages/man3/lutimes.3.html.

ارسال UV_FS_UTIME_NOW به‌عنوان atime یا mtime، برچسب زمانی را روی زمان فعلی تنظیم می‌کند.

ارسال UV_FS_UTIME_OMIT به‌عنوان atime یا mtime، برچسب زمانی را دست‌نخورده باقی می‌گذارد.

نکته:

سیستم‌عامل z/OS: تابع uv_fs_lutime() برای z/OS پیاده‌سازی نشده است. همچنان می‌توان آن را فراخوانی کرد اما UV_ENOSYS را برمی‌گرداند.

نکته:

سیستم‌عامل AIX: توابع uv_fs_futime() و uv_fs_lutime() تنها برای AIX 7.1 و جدیدتر کار می‌کنند. در نسخه‌های قدیمی‌تر همچنان می‌توان آنها را فراخوانی کرد اما UV_ENOSYS را برمی‌گردانند.

تغییریافته در نسخه 1.10.0: دقت زیر یک ثانیه (sub-second precision) در ویندوز پشتیبانی می‌شود.

معادل symlink(2) https://man7.org/linux/man-pages/man2/symlink.2.html.

نکته:

در ویندوز می‌توان پارامتر flags را برای کنترل نحوه ایجاد پیوند نمادین (symlink) مشخص کرد:
  • UV_FS_SYMLINK_DIR: نشان می‌دهد که path به یک دایرکتوری اشاره می‌کند.
  • UV_FS_SYMLINK_JUNCTION: درخواست می‌کند که پیوند نمادین با استفاده از نقاط اتصال (junction points) ایجاد شود.
معادل realpath(3) https://man7.org/linux/man-pages/man3/realpath.3.html در یونیکس. ویندوز از GetFinalPathNameByHandleW https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew استفاده می‌کند. رشته حاصل در req->ptr ذخیره می‌شود.

هشدار:

این تابع دارای هشدارهای خاص پلتفرم است که هنگام استفاده در Node کشف شدند.
  • سیستم‌های macOS و سایر BSDها: اگر هنگام حل مسیر داده‌شده بیش از ۳۲ پیوند نمادین پیدا شود، این تابع با خطای UV_ELOOP شکست می‌خورد. این محدودیت به‌صورت سراسری و ثابت است و نمی‌توان آن را دور زد.
  • ویندوز: در حالی که این تابع در حالت‌های معمول کار می‌کند، تعدادی حالت مرزی (corner case) وجود دارد که در آنها کار نمی‌کند:
  • مسیرهای درون حجم‌های رم‌دیسک (ramdisk) ایجادشده توسط ابزارهایی که مدیر حجم (Volume Manager) را دور می‌زنند (مانند ImDisk) قابل حل نیستند.
  • بزرگ و کوچک بودن ناهمخوان حروف در هنگام استفاده از حروف درایو (drive letters).
  • مسیر حل‌شده درایوهای subst شده را دور می‌زند.

اگرچه همچنان می‌توان از این تابع استفاده کرد، اما در صورتی که نیاز به پشتیبانی از سناریوهایی مانند موارد بالا باشد، استفاده از آن توصیه نمی‌شود.

داستان پیش‌زمینه و جزئیات بیشتر در مورد این مشکلات را می‌توان در اینجا https://github.com/nodejs/node/issues/7726 بررسی کرد.

افزوده‌شده در نسخه 1.8.0.

به‌ترتیب معادل chown(2) https://man7.org/linux/man-pages/man2/chown.2.html، fchown(2) https://man7.org/linux/man-pages/man2/fchown.2.html و lchown(2) https://man7.org/linux/man-pages/man2/lchown.2.html.

نکته:

این توابع در ویندوز پیاده‌سازی نشده‌اند.

تغییریافته در نسخه 1.21.0: تابع uv_fs_lchown پیاده‌سازی شد.

مقدار req->fs_type را برمی‌گرداند.

افزوده‌شده در نسخه 1.19.0.

مقدار req->result را برمی‌گرداند.

افزوده‌شده در نسخه 1.19.0.

کد خطای مخصوص پلتفرم را برمی‌گرداند - مقدار GetLastError() در ویندوز و -(req->result) در سایر پلتفرم‌ها.

افزوده‌شده در نسخه 1.38.0.

مقدار req->ptr را برمی‌گرداند.

افزوده‌شده در نسخه 1.19.0.

مقدار req->path را برمی‌گرداند.

افزوده‌شده در نسخه 1.19.0.

مقدار &req->statbuf را برمی‌گرداند.

افزوده‌شده در نسخه 1.19.0.

همچنین ببینید:

توابع API uv_req_t <#c.uv_req_t> نیز اعمال می‌شوند.

برای یک توصیف‌کننده فایل در زمان اجرای C، هندل وابسته به سیستم‌عامل را دریافت می‌کند. در یونیکس، fd را دست‌نخورده برمی‌گرداند. در ویندوز، تابع _get_osfhandle https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2019 را فراخوانی می‌کند. توجه داشته باشید که مقدار بازگردانده‌شده همچنان متعلق به زمان اجرای C است؛ هرگونه تلاش برای بستن آن یا استفاده از آن پس از بستن fd ممکن است منجر به عملکرد نادرست شود.
افزوده‌شده در نسخه 1.12.0.
برای یک هندل وابسته به سیستم‌عامل، توصیف‌کننده فایل را در زمان اجرای C دریافت می‌کند. در یونیکس، os_fd را دست‌نخورده برمی‌گرداند. در ویندوز، تابع _open_osfhandle https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open-osfhandle?view=vs-2019 را فراخوانی می‌کند. توجه داشته باشید که این تابع آرگومان را مصرف می‌کند؛ هرگونه تلاش برای بستن آن یا استفاده از آن پس از بستن مقدار بازگردانده‌شده ممکن است منجر به عملکرد نادرست شود.
افزوده‌شده در نسخه 1.23.0.

فایل در حالت الحاق (append) باز می‌شود. قبل از هر نوشتن، آفست فایل در انتهای فایل قرار می‌گیرد.
اگر فایل از قبل وجود نداشته باشد، ایجاد می‌شود.
ورودی/خروجی (I/O) فایل به‌طور مستقیم از و به بافرهای فضای کاربر (user-space) انجام می‌شود که باید تراز (aligned) شده باشند. اندازه و آدرس بافر باید مضربی از اندازه بخش فیزیکی دستگاه بلوکی باشد.

نکته:

ثابت UV_FS_O_DIRECT در لینوکس، و در ویندوز از طریق FILE_FLAG_NO_BUFFERING https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering پشتیبانی می‌شود. ثابت UV_FS_O_DIRECT در macOS پشتیبانی نمی‌شود.
اگر مسیر یک دایرکتوری نباشد، باز کردن فایل شکست می‌خورد.

نکته:

ثابت UV_FS_O_DIRECTORY در ویندوز پشتیبانی نمی‌شود.
فایل برای I/O همگام باز می‌شود. عملیات نوشتن پس از انتقال تمام داده‌ها و حداقل فراداده‌ها (metadata) به دیسک کامل خواهد شد.

نکته:

ثابت UV_FS_O_DSYNC در ویندوز از طریق FILE_FLAG_WRITE_THROUGH https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering پشتیبانی می‌شود.
اگر فلگ O_CREAT تنظیم شده باشد و فایل از قبل وجود داشته باشد، باز کردن فایل شکست می‌خورد.

نکته:

به‌طور کلی، رفتار O_EXCL در صورت استفاده بدون O_CREAT تعریف‌نشده است. یک استثنا وجود دارد: در لینوکس 2.6 و جدیدتر، در صورتی که نام مسیر به یک دستگاه بلوکی اشاره کند، O_EXCL می‌تواند بدون O_CREAT استفاده شود. اگر دستگاه بلوکی توسط سیستم در حال استفاده باشد (مثلاً سوار/mount شده باشد)، باز کردن با خطای EBUSY شکست خواهد خورد.
به‌صورت اتمیک یک قفل انحصاری (exclusive lock) دریافت می‌کند.

نکته:

ثابت UV_FS_O_EXLOCK تنها در macOS و ویندوز پشتیبانی می‌شود.

تغییریافته در نسخه 1.17.0: پشتیبانی برای ویندوز اضافه شد.

از یک نگاشت حافظه فایل برای دسترسی به فایل استفاده می‌کند. هنگام استفاده از این فلگ، فایل نمی‌تواند چندین بار به‌طور همزمان باز شود.

نکته:

ثابت UV_FS_O_FILEMAP تنها در ویندوز پشتیبانی می‌شود.
هنگام خواندن فایل، زمان دسترسی به فایل را به‌روزرسانی نمی‌کند.

نکته:

ثابت UV_FS_O_NOATIME در ویندوز پشتیبانی نمی‌شود.
اگر مسیر یک دستگاه ترمینال را مشخص کند، باز کردن مسیر باعث نمی‌شود آن ترمینال به ترمینال کنترلی فرآیند تبدیل شود (در صورتی که فرآیند قبلاً ترمینال کنترلی نداشته باشد).

نکته:

ثابت UV_FS_O_NOCTTY در ویندوز پشتیبانی نمی‌شود.
اگر مسیر یک پیوند نمادین باشد، باز کردن فایل شکست می‌خورد.

نکته:

ثابت UV_FS_O_NOFOLLOW در ویندوز پشتیبانی نمی‌شود.
در صورت امکان، فایل را در حالت غیرمسدودکننده (nonblocking) باز می‌کند.

نکته:

ثابت UV_FS_O_NONBLOCK در ویندوز پشتیبانی نمی‌شود.
دسترسی به‌صورت تصادفی در نظر گرفته شده است. سیستم می‌تواند از این به‌عنوان یک راهنما (hint) برای بهینه‌سازی کش فایل استفاده کند.

نکته:

ثابت UV_FS_O_RANDOM تنها در ویندوز از طریق FILE_FLAG_RANDOM_ACCESS https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew پشتیبانی می‌شود.
فایل را برای دسترسی فقط‌خواندنی باز می‌کند.
فایل را برای دسترسی خواندن و نوشتن باز می‌کند.
دسترسی به‌صورت ترتیبی از ابتدا تا انتها در نظر گرفته شده است. سیستم می‌تواند از این به‌عنوان یک راهنما (hint) برای بهینه‌سازی کش فایل استفاده کند.

نکته:

ثابت UV_FS_O_SEQUENTIAL تنها در ویندوز از طریق FILE_FLAG_SEQUENTIAL_SCAN https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew پشتیبانی می‌شود.
فایل موقت است و در صورت امکان نباید روی دیسک فلاش شود.

نکته:

ثابت UV_FS_O_SHORT_LIVED تنها در ویندوز از طریق FILE_ATTRIBUTE_TEMPORARY https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew پشتیبانی می‌شود.
به‌جای منبعی که پیوند نمادین به آن اشاره می‌کند، خود پیوند نمادین را باز می‌کند.
فایل برای I/O همگام باز می‌شود. عملیات نوشتن پس از انتقال تمام داده‌ها و تمام فراداده‌ها (metadata) به دیسک کامل خواهد شد.

نکته:

ثابت UV_FS_O_SYNC در ویندوز از طریق FILE_FLAG_WRITE_THROUGH https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering پشتیبانی می‌شود.
فایل موقت است و در صورت امکان نباید روی دیسک فلاش شود.

نکته:

ثابت UV_FS_O_TEMPORARY تنها در ویندوز از طریق FILE_ATTRIBUTE_TEMPORARY https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew پشتیبانی می‌شود.
اگر فایل وجود داشته باشد و یک فایل عادی باشد، و فایل با موفقیت برای دسترسی نوشتن باز شود، طول آن به صفر کاهش می‌یابد (truncate می‌شود).
فایل را برای دسترسی فقط‌نوشتنی باز می‌کند.

کتابخانه libuv یک استخر ریسه (threadpool) فراهم می‌کند که می‌تواند برای اجرای کد کاربر و مطلع شدن در ریسه حلقه (loop thread) استفاده شود. این استخر ریسه در داخل برای اجرای تمامی عملیات سیستم فایل، و همچنین درخواست‌های getaddrinfo و getnameinfo استفاده می‌شود.

اندازه پیش‌فرض آن ۴ است، اما می‌توان آن را در زمان راه‌اندازی با تنظیم متغیر محیطی UV_THREADPOOL_SIZE به هر مقداری تغییر داد (حداکثر مطلق ۱۰۲۴ است).

تغییریافته در نسخه 1.30.0: حداکثر UV_THREADPOOL_SIZE مجاز از ۱۲۸ به ۱۰۲۴ افزایش یافت.

تغییریافته در نسخه 1.45.0: ریسه‌ها اکنون به جای پیش‌فرض پلتفرم (که گاهی بسیار کم است)، دارای ۸ مگابایت پشته (stack) هستند.

تغییریافته در نسخه 1.50.0: ریسه‌ها اکنون نام پیش‌فرض libuv-worker دارند.

استخر ریسه سراسری است و بین تمامی حلقه‌های رویداد به اشتراک گذاشته می‌شود. هنگامی که یک تابع خاص از استخر ریسه استفاده می‌کند (مثلاً هنگام استفاده از uv_queue_work())، کتابخانه libuv حداکثر تعداد ریسه‌های مجاز توسط UV_THREADPOOL_SIZE را پیش‌تخصیص داده و مقداردهی اولیه می‌کند. ریسه‌های بیشتر معمولاً به معنای بازده کاری (throughput) بیشتر اما ردپای حافظه (memory footprint) بالاتر است. البته در بیشتر پلتفرم‌ها پشته‌های ریسه به‌صورت تنبل (lazily) رشد می‌کنند.

نکته:

توجه داشته باشید که اگرچه از یک استخر ریسه سراسری مشترک بین تمامی حلقه‌های رویداد استفاده می‌شود، توابع از نظر ریسه ایمن نیستند (thread safe نمی‌باشند).

نوع درخواست کار (work request).
کالبک ارسال‌شده به uv_queue_work() که روی استخر ریسه اجرا خواهد شد.
کالبک ارسال‌شده به uv_queue_work() که پس از اتمام کار روی استخر ریسه، روی ریسه حلقه فراخوانی خواهد شد. اگر کار با استفاده از uv_cancel() <#c.uv_cancel> لغو شده باشد، status برابر با UV_ECANCELED خواهد بود.

حلقه‌ای که این درخواست را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقط‌خواندنی.

همچنین ببینید:

اعضای uv_req_t <#c.uv_req_t> نیز اعمال می‌شوند.

یک درخواست کار را مقداردهی اولیه می‌کند که work_cb داده‌شده را در یک ریسه از استخر ریسه اجرا خواهد کرد. پس از تکمیل work_cb، تابع after_work_cb روی ریسه حلقه فراخوانی خواهد شد.

این درخواست را می‌توان با uv_cancel() <#c.uv_cancel> لغو کرد.

همچنین ببینید:

توابع API uv_req_t <#c.uv_req_t> نیز اعمال می‌شوند.

کتابخانه libuv گونه‌های ناهمگام getaddrinfo و getnameinfo را فراهم می‌کند.

نوع درخواست getaddrinfo.
کالبکی که پس از تکمیل، با نتیجه درخواست getaddrinfo فراخوانی خواهد شد. در صورتی که لغو شده باشد، status مقداری برابر با UV_ECANCELED خواهد داشت.
نوع درخواست getnameinfo.
کالبکی که پس از تکمیل، با نتیجه درخواست getnameinfo فراخوانی خواهد شد. در صورتی که لغو شده باشد، status مقداری برابر با UV_ECANCELED خواهد داشت.

حلقه‌ای که این درخواست getaddrinfo را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقط‌خواندنی.
اشاره‌گر به یک struct addrinfo حاوی نتیجه. باید توسط کاربر با uv_freeaddrinfo() آزاد شود.

تغییریافته در نسخه 1.3.0: این فیلد به‌صورت عمومی (public) اعلان شده است.

حلقه‌ای که این درخواست getnameinfo را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقط‌خواندنی.
آرایه کاراکتری حاوی میزبان حاصل. با null خاتمه یافته است.

تغییریافته در نسخه 1.3.0: این فیلد به‌صورت عمومی (public) اعلان شده است.

آرایه کاراکتری حاوی سرویس حاصل. با null خاتمه یافته است.

تغییریافته در نسخه 1.3.0: این فیلد به‌صورت عمومی (public) اعلان شده است.

همچنین ببینید:

اعضای uv_req_t <#c.uv_req_t> نیز اعمال می‌شوند.

معادل ناهمگام getaddrinfo(3) https://man7.org/linux/man-pages/man3/getaddrinfo.3.html.

هر یک از node یا service می‌توانند NULL باشند اما نه هر دو به‌طور همزمان.

شناسه hints اشاره‌گری به یک struct addrinfo با محدودیت‌های نوع آدرس اضافی، یا NULL است. برای جزئیات بیشتر به man -s 3 getaddrinfo مراجعه کنید.

در صورت موفقیت ۰ یا در صورت شکست یک کد خطای کوچکتر از ۰ برمی‌گرداند. در صورت موفقیت، کالبک در زمانی در آینده با نتیجه جستجو فراخوانی می‌شود که یکی از موارد زیر است:

  • status == 0، شناسه res به یک struct addrinfo معتبر اشاره می‌کند، یا
  • status < 0، شناسه res برابر با NULL است. ثابت‌های UV_EAI_* را ببینید.

برای آزادسازی ساختار addrinfo، تابع uv_freeaddrinfo() را فراخوانی کنید.

تغییریافته در نسخه 1.3.0: پارامتر کالبک اکنون مجاز است NULL باشد، که در این حالت درخواست به‌صورت همگام اجرا خواهد شد.

ساختار struct addrinfo را آزاد می‌کند. ارسال NULL مجاز است و عملیاتی انجام نمی‌دهد (no-op).
معادل ناهمگام getnameinfo(3) https://man7.org/linux/man-pages/man3/getnameinfo.3.html.

در صورت موفقیت ۰ یا در صورت شکست یک کد خطای کوچکتر از ۰ برمی‌گرداند. در صورت موفقیت، کالبک در زمانی در آینده با نتیجه جستجو فراخوانی خواهد شد. برای جزئیات بیشتر به man -s 3 getnameinfo مراجعه کنید.

تغییریافته در نسخه 1.3.0: پارامتر کالبک اکنون مجاز است NULL باشد، که در این حالت درخواست به‌صورت همگام اجرا خواهد شد.

همچنین ببینید:

توابع API uv_req_t <#c.uv_req_t> نیز اعمال می‌شوند.

کتابخانه libuv ابزارهایی مستقل از بستر برای بارگذاری کتابخانه‌های اشتراکی و استخراج نمادها از آن‌ها با استفاده از رابط برنامه‌نویسی زیر فراهم می‌کند.

نوع داده کتابخانه اشتراکی.

موردی وجود ندارد (N/A)

یک کتابخانه اشتراکی را باز می‌کند. نام فایل در قالب utf-8 است. در صورت موفقیت مقدار 0 و در صورت خطا مقدار -1 را برمی‌گرداند. برای دریافت پیام خطا تابع uv_dlerror() را فراخوانی کنید.
کتابخانه اشتراکی را می‌بندد.
اشاره‌گر داده را از یک کتابخانه پویا بازیابی می‌کند. نگاشت یک نماد به NULL مجاز و معتبر است. در صورت موفقیت 0 و در صورتی که نماد یافت نشود -1 برمی‌گرداند.
آخرین پیام خطای uv_dlopen() یا uv_dlsym() را برمی‌گرداند.

کتابخانه libuv پیاده‌سازی‌های چندسکویی برای چندین ساختار بنیادین ریسه‌بندی و همگام‌سازی ارائه می‌دهد. این رابط برنامه‌نویسی عمدتاً از API پتردز (pthreads) پیروی می‌کند.

نوع داده ریسه (Thread).
تابع بازخوانی (Callback) که برای مقداردهی اولیه اجرای ریسه فراخوانی می‌شود. arg همان مقداری است که به uv_thread_create() ارسال شده بود.
نوع داده کلید محلی ریسه (Thread-local key).
نوع داده مقداردهی اولیه‌ساز تک‌باره (Once-only initializer).
نوع داده قفل انحصار متقابل (Mutex).
نوع داده قفل خواندن-نوشتن (Read-write lock).
نوع داده سمافور (Semaphore).
نوع داده شرط (Condition).
نوع داده مانع (Barrier).

گزینه‌های ایجاد ریسه جدید (ارسال‌شده به uv_thread_create_ex()).
typedef struct uv_thread_options_s {
  enum {
    UV_THREAD_NO_FLAGS = 0x00,
    UV_THREAD_HAS_STACK_SIZE = 0x01
  } flags;
  size_t stack_size;
} uv_thread_options_t;

ممکن است فیلدهای بیشتری در هر زمان به این ساختار اضافه شود، بنابراین نباید به چینش و اندازه دقیق آن اتکا کرد.

افزوده شده در نسخه 1.26.0.

تغییر یافته در نسخه 1.4.1: در صورت شکست، کد خطای UV_E* را برمی‌گرداند.
ریسه را جدا (Detach) می‌کند. ریسه‌های جداشده پس از خاتمه به طور خودکار منابع خود را آزاد می‌کنند و نیاز برنامه به فراخوانی uv_thread_join را از بین می‌برند.

افزوده شده در نسخه 1.50.0.

مشابه uv_thread_create()، اما علاوه بر آن گزینه‌هایی را برای ایجاد یک ریسه جدید مشخص می‌کند.

اگر UV_THREAD_HAS_STACK_SIZE تنظیم شده باشد، stack_size اندازه پشته را برای ریسه جدید مشخص می‌کند. مقدار 0 نشان می‌دهد که باید از مقدار پیش‌فرض استفاده شود (رفتاری مشابه زمانی که پرچم تنظیم نشده باشد). سایر مقادیر به نزدیک‌ترین مرز صفحه حافظه گرد می‌شوند.

افزوده شده در نسخه 1.26.0.

وابستگی پردازنده (Affinity) ریسه مشخص‌شده را به cpumask که بر حسب بایت مشخص شده است تنظیم می‌کند. در صورت تمایل، تنظیمات قبلی وابستگی را در oldmask برمی‌گرداند. در یونیکس، از pthread_getaffinity_np(3) https://man7.org/linux/man-pages/man3/pthread_getaffinity_np.3.html برای دریافت تنظیمات وابستگی استفاده کرده و cpu_set_t را به بایت‌ها در oldmask نگاشت می‌کند. سپس بایت‌های cpumask را به یک cpu_set_t نگاشت کرده و از pthread_setaffinity_np(3) https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html استفاده می‌کند. در ویندوز، بایت‌های cpumask را به یک ماسک بیتی نگاشت کرده و از SetThreadAffinityMask() استفاده می‌کند که تنظیمات وابستگی پیشین را برمی‌گرداند.

پارامتر mask_size تعداد درایه‌ها (بایت‌ها) را در cpumask / oldmask مشخص می‌کند، و باید بزرگ‌تر یا مساوی با uv_cpumask_size() <#c.uv_cpumask_size> باشد.

نکته:

تنظیم وابستگی ریسه در ویندوز به صورت اتمیک نیست. در macOS پشتیبانی نمی‌شود.

افزوده شده در نسخه 1.45.0.

تنظیمات وابستگی پردازنده ریسه مشخص‌شده را دریافت می‌کند. در یونیکس، این تابع cpu_set_t بازگردانده شده توسط pthread_getaffinity_np(3) https://man7.org/linux/man-pages/man3/pthread_getaffinity_np.3.html را به بایت‌ها در cpumask نگاشت می‌کند.

پارامتر mask_size تعداد درایه‌ها (بایت‌ها) را در cpumask مشخص می‌کند، و باید بزرگ‌تر یا مساوی با uv_cpumask_size() <#c.uv_cpumask_size> باشد.

نکته:

دریافت وابستگی ریسه در ویندوز به صورت اتمیک نیست. در macOS پشتیبانی نمی‌شود.

افزوده شده در نسخه 1.45.0.

شماره پردازنده‌ای (CPU) را که ریسه فراخوان روی آن در حال اجراست دریافت می‌کند.

نکته:

در حال حاضر فقط روی Windows، Linux و FreeBSD پیاده‌سازی شده است.

افزوده شده در نسخه 1.45.0.

نام ریسه جاری را تنظیم می‌کند. بسترهای مختلف محدودیت‌های متفاوتی روی حداکثر تعداد کاراکترهای نام ریسه تعریف می‌کنند: لینوکس و IBM i (۱۶ کاراکتر)، macOS (۶۴ کاراکتر)، ویندوز (۳۲۷۶۷ کاراکتر)، و NetBSD (۳۲ کاراکتر) و غیره. در صورتی که name بزرگ‌تر از محدودیت بستر باشد، uv_thread_setname() آن را کوتاه (truncate) می‌کند.

در Windows Server 2016 پشتیبانی نمی‌شود و UV_ENOSYS را برمی‌گرداند.

افزوده شده در نسخه 1.50.0.

نام ریسه مشخص‌شده با tid را دریافت می‌کند. نام ریسه به همراه بایت NUL انتهایی در بافر اشاره‌شده با name کپی می‌شود. پارامتر size اندازه بافر اشاره‌شده با name را مشخص می‌کند. بافر باید به اندازه کافی بزرگ باشد تا نام ریسه به اضافه NUL انتهایی را در خود جای دهد، در غیر این صورت برای جا گرفتن همراه با NUL انتهایی کوتاه خواهد شد.

در Windows Server 2016 پشتیبانی نمی‌شود و UV_ENOSYS را برمی‌گرداند.

افزوده شده در نسخه 1.50.0.

اگر تابع با موفقیت اجرا شود، مقدار بازگشتی 0 است. در صورت شکست تابع، مقدار بازگشتی کمتر از صفر است. اولویت زمان‌بندی ریسه مشخص‌شده با tid را تنظیم می‌کند. در برخی بسترها تنظیم اولویت‌های خاص نیازمند دسترسی‌های ارشد (Elevated) است. اولویت را می‌توان روی ثابت‌های زیر تنظیم کرد: UV_THREAD_PRIORITY_HIGHEST، UV_THREAD_PRIORITY_ABOVE_NORMAL، UV_THREAD_PRIORITY_NORMAL، UV_THREAD_PRIORITY_BELOW_NORMAL، UV_THREAD_PRIORITY_LOWEST.
اگر تابع با موفقیت اجرا شود، مقدار بازگشتی 0 است. در صورت شکست تابع، مقدار بازگشتی کمتر از صفر است. اولویت زمان‌بندی ریسه مشخص‌شده با tid را بازیابی می‌کند. مقدار موجود در پارامتر خروجی priority وابسته به بستر است. در لینوکس، زمانی که سیاست زمان‌بندی SCHED_OTHER (پیش‌فرض) باشد، اولویت 0 است.

نکته:

کل اندازه حافظه محلی ریسه ممکن است محدود باشد. به این معنی که ممکن است ایجاد کلیدهای TLS متعدد امکان‌پذیر نباشد.

یک تابع را یک‌بار و فقط یک‌بار اجرا می‌کند. فراخوانی‌های همزمان uv_once() با یک گارد یکسان، تمام فراخوان‌ها به جز یکی را مسدود می‌کند (مشخص نیست کدام یک). گارد باید به صورت ایستا با ماکروی UV_ONCE_INIT مقداردهی اولیه شود.

توابع در صورت موفقیت مقدار 0 یا کد خطای کوچک‌تر از 0 را برمی‌گردانند (البته مگر اینکه نوع بازگشتی void باشد).

توابع در صورت موفقیت مقدار 0 یا کد خطای کوچک‌تر از 0 را برمی‌گردانند (البته مگر اینکه نوع بازگشتی void باشد).

توابع در صورت موفقیت مقدار 0 یا کد خطای کوچک‌تر از 0 را برمی‌گردانند (البته مگر اینکه نوع بازگشتی void باشد).

توابع در صورت موفقیت مقدار 0 یا کد خطای کوچک‌تر از 0 را برمی‌گردانند (البته مگر اینکه نوع بازگشتی void باشد).

نکته:

1.
فراخوان‌ها باید برای مدیریت بیدارباش‌های کاذب (Spurious wakeups) در uv_cond_wait() و uv_cond_timedwait() آماده باشند.
2.
پارامتر timeout در uv_cond_timedwait() نسبت به زمانی است که تابع فراخوانی می‌شود.
3.
در z/OS، پارامتر timeout برای uv_cond_timedwait() به یک زمان مطلق سیستم تبدیل می‌شود که در آن انقضای انتظار رخ می‌دهد. اگر ساعت فعلی سیستم از زمان مطلق محاسبه‌شده پیش از سیگنال‌دهی شرط بگذرد، خطای ETIMEDOUT رخ می‌دهد. پس از شروع انتظار، زمان انتظار تحت تأثیر تغییرات ساعت سیستم قرار نمی‌گیرد.

توابع در صورت موفقیت مقدار 0 یا کد خطای کوچک‌تر از 0 را برمی‌گردانند (البته مگر اینکه نوع بازگشتی void باشد).

نکته:

تابع uv_barrier_wait() مقداری بزرگ‌تر از صفر را به یک ریسه به‌دلخواه انتخاب‌شده به عنوان «سریال‌ساز» (Serializer) بازمی‌گرداند تا پاک‌سازی را تسهیل کند، یعنی:
if (uv_barrier_wait(&barrier) > 0)
    uv_barrier_destroy(&barrier);

این بخش شامل توابع متفرقه‌ای است که به بخش‌های دیگر تعلق ندارند.

نوع داده بافر (Buffer).
اشاره‌گر به پایه بافر.
مجموع بایت‌ها در بافر.

نکته:

در ویندوز این فیلد از نوع ULONG است.
تابع جایگزین برای malloc(3) https://man7.org/linux/man-pages/man3/malloc.3.html. به uv_replace_allocator() مراجعه کنید.
تابع جایگزین برای realloc(3) https://man7.org/linux/man-pages/man3/realloc.3.html. به uv_replace_allocator() مراجعه کنید.
تابع جایگزین برای calloc(3) https://man7.org/linux/man-pages/man3/calloc.3.html. به uv_replace_allocator() مراجعه کنید.
تابع جایگزین برای free(3) https://man7.org/linux/man-pages/man3/free.3.html. به uv_replace_allocator() مراجعه کنید.
تابع بازخوانی ارسال‌شده به uv_random(). پارامتر status در صورت بروز خطا غیرصفر است. اشاره‌گر buf همان اشاره‌گری است که به uv_random() ارسال شده بود.
نمایش چندسکویی از دستگیره فایل (File handle).
نمایش چندسکویی از دستگیره سوکت (Socket handle).
نمایش انتزاعی از توصیف‌کننده فایل (File descriptor). در سیستم‌های یونیکس این مورد یک typedef از نوع int و در ویندوز یک HANDLE است.
نمایش چندسکویی از نوع pid_t.

افزوده شده در نسخه 1.16.0.

نوع داده ناامن در برابر مسئله سال ۲۰۳۸ (Y2K38-unsafe) برای ذخیره زمان با دقت میکروثانیه. در نسخه libuv v2.0 با uv_timeval64_t جایگزین خواهد شد.
typedef struct {
    long tv_sec;
    long tv_usec;
} uv_timeval_t;
نوع داده امن در برابر مسئله سال ۲۰۳۸ (Y2K38-safe) برای ذخیره زمان با دقت میکروثانیه.
typedef struct {
    int64_t tv_sec;
    int32_t tv_usec;
} uv_timeval64_t;
نوع داده امن در برابر مسئله سال ۲۰۳۸ (Y2K38-safe) برای ذخیره زمان با دقت نانوثانیه.
typedef struct {
    int64_t tv_sec;
    int32_t tv_nsec;
} uv_timespec64_t;
منبع ساعت برای uv_clock_gettime().
typedef enum {
  UV_CLOCK_MONOTONIC,
  UV_CLOCK_REALTIME
} uv_clock_id;
نوع داده برای نتایج مصرف منابع (Resource usage).
typedef struct {
    uv_timeval_t ru_utime; /* user CPU time used */
    uv_timeval_t ru_stime; /* system CPU time used */
    uint64_t ru_maxrss; /* maximum resident set size */
    uint64_t ru_ixrss; /* integral shared memory size (X) */
    uint64_t ru_idrss; /* integral unshared data size (X) */
    uint64_t ru_isrss; /* integral unshared stack size (X) */
    uint64_t ru_minflt; /* page reclaims (soft page faults) (X) */
    uint64_t ru_majflt; /* page faults (hard page faults) */
    uint64_t ru_nswap; /* swaps (X) */
    uint64_t ru_inblock; /* block input operations */
    uint64_t ru_oublock; /* block output operations */
    uint64_t ru_msgsnd; /* IPC messages sent (X) */
    uint64_t ru_msgrcv; /* IPC messages received (X) */
    uint64_t ru_nsignals; /* signals received (X) */
    uint64_t ru_nvcsw; /* voluntary context switches (X) */
    uint64_t ru_nivcsw; /* involuntary context switches (X) */
} uv_rusage_t;

اعضای علامت‌گذاری شده با (X) در ویندوز پشتیبانی نمی‌شوند. برای فیلدهای پشتیبانی‌شده در بسترهای شبه‌یونیکس به getrusage(2) https://man7.org/linux/man-pages/man2/getrusage.2.html مراجعه کنید.

حداکثر اندازه مقیم در حافظه (RSS) بر حسب کیلوبایت گزارش می‌شود که واحد بومی اکثر پلتفرم‌ها است.

نوع داده برای اطلاعات پردازنده (CPU).
typedef struct uv_cpu_info_s {
    char* model;
    int speed;
    struct uv_cpu_times_s {
        uint64_t user; /* milliseconds */
        uint64_t nice; /* milliseconds */
        uint64_t sys; /* milliseconds */
        uint64_t idle; /* milliseconds */
        uint64_t irq; /* milliseconds */
    } cpu_times;
} uv_cpu_info_t;
نوع داده برای آدرس‌های رابط شبکه.
typedef struct uv_interface_address_s {
    char* name;
    char phys_addr[6];
    int is_internal;
    union {
        struct sockaddr_in address4;
        struct sockaddr_in6 address6;
    } address;
    union {
        struct sockaddr_in netmask4;
        struct sockaddr_in6 netmask6;
    } netmask;
} uv_interface_address_t;
نوع داده برای اطلاعات فایل گذرواژه (Passwd).
typedef struct uv_passwd_s {
    char* username;
    long uid;
    long gid;
    char* shell;
    char* homedir;
} uv_passwd_t;
نوع داده برای اطلاعات فایل گروه (Group).
typedef struct uv_group_s {
  char* groupname;
  unsigned long gid;
  char** members;
} uv_group_t;
نوع داده برای نام و نسخه سیستم‌عامل.
typedef struct uv_utsname_s {
    char sysname[256];
    char release[256];
    char version[256];
    char machine[256];
} uv_utsname_t;
نوع داده برای ذخیره متغیرهای محیطی.
typedef struct uv_env_item_s {
    char* name;
    char* value;
} uv_env_item_t;
نوع درخواست داده‌های تصادفی.

برای تشخیص اینکه چه نوع جریانی باید با توصیف‌کننده فایل داده‌شده استفاده شود به کار می‌رود. معمولاً در هنگام مقداردهی اولیه برای حدس زدن نوع جریان‌های stdio استفاده می‌شود.

برای قابلیتی معادل isatty(3) https://man7.org/linux/man-pages/man3/isatty.3.html از این تابع استفاده کرده و برای UV_TTY بررسی کنید.

افزوده شده در نسخه 1.6.0.

جایگزین کردن توابع تخصیص حافظه کتابخانه استاندارد malloc(3) https://man7.org/linux/man-pages/man3/malloc.3.html، calloc(3) https://man7.org/linux/man-pages/man3/calloc.3.html، realloc(3) https://man7.org/linux/man-pages/man3/realloc.3.html، free(3) https://man7.org/linux/man-pages/man3/free.3.html.

این تابع باید قبل از فراخوانی هر تابع دیگر libuv یا پس از آزاد شدن تمام منابع و در نتیجه زمانی که libuv به هیچ بخش حافظه تخصیص‌یافته‌ای ارجاع ندارد، فراخوانی شود.

در صورت موفقیت مقدار 0 و اگر هر یک از اشاره‌گرهای تابع NULL باشد مقدار UV_EINVAL را برمی‌گرداند.

هشدار:

هیچ محافظتی در برابر تغییر چندباره تخصیص‌دهنده حافظه وجود ندارد. در صورت تغییر، کاربر مسئول اطمینان از این است که تغییر زمانی صورت گیرد که هیچ حافظه‌ای با تخصیص‌دهنده قبلی تخصیص نیافته باشد یا اینکه این دو تخصیص‌دهنده با هم سازگار باشند.

هشدار:

تخصیص‌دهنده باید ایمن در برابر ریسه (Thread-safe) باشد.
افزوده شده در نسخه 1.38.0.

آزاد کردن هرگونه وضعیت سراسری که libuv در دست دارد. کتابخانه libuv معمولاً هنگام تخلیه از حافظه به طور خودکار این کار را انجام می‌دهد، اما می‌توان به آن دستور داد تا پاک‌سازی را به صورت دستی انجام دهد.

هشدار:

تابع uv_library_shutdown() را فقط یک بار فراخوانی کنید.

هشدار:

زمانی که هنوز حلقه‌های رویداد یا درخواست‌های I/O فعال هستند، uv_library_shutdown() را فراخوانی نکنید.

هشدار:

پس از فراخوانی uv_library_shutdown()، توابع libuv را فراخوانی نکنید.
سازنده برای ساختار uv_buf_t.

به دلیل تفاوت‌های بسترها، کاربر نمی‌تواند به ترتیب اعضای base و len در ساختار uv_buf_t اتکا کند. مسئولیت آزادسازی base پس از اتمام کار با uv_buf_t بر عهده کاربر است. ساختار بازگشتی با مقدار (by value) ارسال می‌شود.

ذخیره آرگومان‌های برنامه. برای دریافت / تنظیم عنوان فرایند یا مسیر فایل اجرایی مورد نیاز است. ممکن است libuv مالکیت حافظه‌ای را که argv به آن اشاره می‌کند در دست بگیرد. این تابع باید دقیقاً یک‌بار و در زمان شروع برنامه فراخوانی شود.

مثال:

argv = uv_setup_args(argc, argv);  /* May return a copy of argv. */
عنوان فرایند جاری را دریافت می‌کند. در سیستم‌های Unix و AIX باید قبل از فراخوانی این تابع، uv_setup_args را فراخوانی کنید. اگر در سیستم‌هایی که به آن نیاز دارند uv_setup_args فراخوانی نشده باشد، UV_ENOBUFS برگردانده می‌شود. اگر buffer برابر با NULL یا size صفر باشد، UV_EINVAL برگردانده می‌شود. اگر size گنجایش عنوان فرایند و کاراکتر nul پایانی را نداشته باشد، تابع UV_ENOBUFS را برمی‌گرداند.

نکته:

در سیستم‌های BSD، فراخوانی uv_setup_args برای دریافت عنوان اولیه فرایند لازم است. عنوان فرایند بازگردانده شده تا زمانی که uv_setup_args یا uv_set_process_title فراخوانی نشود، یک رشته خالی خواهد بود.

تغییر یافته در نسخه 1.18.1: اکنون در تمام پلتفرم‌های پشتیبانی‌شده ایمن در برابر ریسه (Thread-safe) است.

تغییر یافته در نسخه 1.39.0: اکنون اگر uv_setup_args مورد نیاز باشد اما فراخوانی نشده باشد، یک خطا برمی‌گرداند.

عنوان فرایند جاری را تنظیم می‌کند. در سیستم‌های Unix و AIX باید قبل از فراخوانی این تابع، uv_setup_args را فراخوانی کنید. اگر در سیستم‌هایی که به آن نیاز دارند uv_setup_args فراخوانی نشده باشد، UV_ENOBUFS برگردانده می‌شود. در بسترهایی با بافر با اندازه ثابت برای عنوان فرایند، محتویات title در بافر کپی شده و در صورت بزرگ‌تر بودن از فضای موجود کوتاه (truncate) می‌شود. سایر بسترها در صورت عدم امکان تخصیص فضای کافی برای تکثیر محتوای title، مقدار UV_ENOMEM را برمی‌گردانند.

تغییر یافته در نسخه 1.18.1: اکنون در تمام پلتفرم‌های پشتیبانی‌شده ایمن در برابر ریسه (Thread-safe) است.

تغییر یافته در نسخه 1.39.0: اکنون اگر uv_setup_args مورد نیاز باشد اما فراخوانی نشده باشد، یک خطا برمی‌گرداند.

اندازه مقیم در حافظه (RSS) را برای فرایند جاری دریافت می‌کند.
زمان بالا بودن (Uptime) سیستم را دریافت می‌کند. بسته به سیستم، ثانیه‌ها به صورت کامل یا کسری برگردانده می‌شوند.
معیارهای مصرف منابع را برای فرایند جاری دریافت می‌کند.

نکته:

در ویندوز همه فیلدها مقداردهی نمی‌شوند و فیلدهای پشتیبانی‌نشده با صفر پر می‌شوند. برای جزئیات بیشتر به uv_rusage_t مراجعه کنید.
معیارهای مصرف منابع را برای ریسه فراخوان دریافت می‌کند.

افزوده شده در نسخه 1.50.0.

نکته:

در همه بسترها پشتیبانی نمی‌شود و ممکن است UV_ENOTSUP را برگرداند. در macOS و Windows همه فیلدها مقداردهی نمی‌شوند و فیلدهای پشتیبانی‌نشده با صفر پر می‌شوند. برای جزئیات بیشتر به uv_rusage_t مراجعه کنید.
شناسه فرایند جاری (Process ID) را برمی‌گرداند.

افزوده شده در نسخه 1.18.0.

شناسه فرایند والد (Parent Process ID) را برمی‌گرداند.

افزوده شده در نسخه 1.16.0.

تخمین مقدار موازی‌سازی پیش‌فرضی که یک برنامه باید استفاده کند را بازمی‌گرداند. همواره مقداری غیرصفر برمی‌گرداند.

در لینوکس، ماسک وابستگی CPU ریسه فراخوان را بررسی می‌کند تا مشخص شود آیا به CPUهای خاصی متصل (pin) شده است یا خیر.

در ویندوز، در سیستم‌هایی با بیش از ۶۴ پردازنده منطقی، موازی‌سازی در دسترس ممکن است کمتر از واقعیت گزارش شود.

در سایر بسترها، تعداد پردازنده‌هایی را که سیستم‌عامل فعال (Online) در نظر می‌گیرد گزارش می‌کند.

افزوده شده در نسخه 1.44.0.

اطلاعات مربوط به پردازنده‌های موجود در سیستم را دریافت می‌کند. آرایه cpu_infos شامل count عنصر خواهد بود و باید با uv_free_cpu_info() آزاد شود.

اگر نیاز دارید بدانید چند پردازنده برای ریسه‌ها یا فرایندهای فرزند در دسترس است، از uv_available_parallelism() استفاده کنید.

آرایه cpu_infos را که قبلاً با uv_cpu_info() تخصیص یافته بود آزاد می‌کند.
حداکثر اندازه ماسک استفاده‌شده برای وابستگی فرایند/ریسه را برمی‌گرداند، یا اگر وابستگی در بستر جاری پشتیبانی نشود، UV_ENOTSUP را برمی‌گرداند.

افزوده شده در نسخه 1.45.0.

اطلاعات آدرس مربوط به رابط‌های شبکه موجود در سیستم را دریافت می‌کند. آرایه‌ای از count عنصر تخصیص یافته و در addresses بازگردانده می‌شود. این آرایه باید توسط کاربر و با فراخوانی uv_free_interface_addresses() آزاد شود.
آرایه‌ای از ساختارهای uv_interface_address_t را که توسط uv_interface_addresses() بازگردانده شده بود آزاد می‌کند.
میانگین بار سیستم (Load average) را دریافت می‌کند. ببینید: <https://en.wikipedia.org/wiki/Load_(computing)>

نکته:

در ویندوز [0,0,0] برمی‌گرداند (یعنی پیاده‌سازی نشده است).
یک رشته حاوی آدرس IPv4 را به یک ساختار باینری تبدیل می‌کند.
یک رشته حاوی آدرس IPv6 را به یک ساختار باینری تبدیل می‌کند.
یک ساختار باینری حاوی آدرس IPv4 را به یک رشته تبدیل می‌کند.
یک ساختار باینری حاوی آدرس IPv6 را به یک رشته تبدیل می‌کند.
یک ساختار باینری حاوی آدرس IPv4 یا IPv6 را به یک رشته تبدیل می‌کند.
پیاده‌سازی چندسکویی و سازگار با IPv6 از inet_ntop(3) https://man7.org/linux/man-pages/man3/inet_ntop.3.html و inet_pton(3) https://man7.org/linux/man-pages/man3/inet_pton.3.html. در صورت موفقیت 0 را برمی‌گردانند. در صورت بروز خطا، اشاره‌گر مقصد dst دست‌نخورده باقی می‌ماند.
حداکثر طول نام شناسه رابط IPv6. در یونیکس به صورت IFNAMSIZ و در لینوکس و ویندوز به صورت IF_NAMESIZE تعریف شده است.

افزوده شده در نسخه 1.16.0.

پیاده‌سازی سازگار با IPv6 از if_indextoname(3) https://man7.org/linux/man-pages/man3/if_indextoname.3.html. هنگام فراخوانی، *size طول buffer را نشان می‌دهد که برای ذخیره نتیجه استفاده می‌شود. در صورت موفقیت، صفر برگردانده می‌شود، buffer حاوی نام رابط خواهد بود و *size طول رشته buffer را بدون احتساب بایت پایان‌بخش NUL نشان می‌دهد. در صورت خطا، یک نتیجه منفی برگردانده می‌شود. اگر buffer برای نگهداری نتیجه به اندازه کافی بزرگ نباشد، UV_ENOBUFS برگردانده شده و *size اندازه مورد نیاز بر حسب بایت را شامل بایت پایان‌بخش NUL نشان می‌دهد.

در یونیکس، نام رابط بازگردانده شده می‌تواند مستقیماً به عنوان شناسه رابط در آدرس‌های دامنه‌دار IPv6 استفاده شود، مانند fe80::abc:def1:2345%en0.

در ویندوز، رابط بازگردانده شده را نمی‌توان به عنوان شناسه رابط استفاده کرد، زیرا ویندوز از شناسه‌های عددی رابط استفاده می‌کند، مانند fe80::abc:def1:2345%5.

برای دریافت شناسه رابط به شیوه‌ای سازگار با بسترهای مختلف، از uv_if_indextoiid() استفاده کنید.

مثال:

char ifname[UV_IF_NAMESIZE];
size_t size = sizeof(ifname);
uv_if_indextoname(sin6->sin6_scope_id, ifname, &size);

افزوده شده در نسخه 1.16.0.

یک شناسه رابط شبکه مناسب برای استفاده در آدرس‌های دامنه‌دار IPv6 را بازیابی می‌کند. در ویندوز، مقدار عددی ifindex را به صورت رشته برمی‌گرداند. در سایر بسترها، uv_if_indextoname() فراخوانی می‌شود. نتیجه در buffer نوشته شده و *size طول buffer را نشان می‌دهد. اگر buffer به اندازه کافی بزرگ نباشد، مقدار UV_ENOBUFS برگردانده شده و *size اندازه مورد نیاز شامل بایت NUL برای نگهداری نتیجه را نشان می‌دهد.

برای جزئیات بیشتر به uv_if_indextoname مراجعه کنید.

افزوده شده در نسخه 1.16.0.

مسیر فایل اجرایی را دریافت می‌کند. قبل از فراخوانی این تابع باید uv_setup_args را فراخوانی کنید.

در فایل‌های اجرایی setuid احتیاط کنید. در برخی پلتفرم‌ها مسیر فایل اجرایی یک رشته دلخواه است که توسط کاربر کنترل می‌شود. در سایر پلتفرم‌ها متغیرهای محیطی بررسی می‌شوند که ممکن است تحت کنترل کاربر باشند.

دایرکتوری کاری فعلی را دریافت کرده و آن را در buffer ذخیره می‌کند. اگر دایرکتوری کاری فعلی برای جا شدن در buffer بیش از حد بزرگ باشد، این تابع مقدار UV_ENOBUFS را برمی‌گرداند و size را روی طول مورد نیاز شامل نویسه پایان‌بخش null تنظیم می‌کند.

تغییر یافته در نسخه 1.1.0: در یونیکس مسیر دیگر به اسلش ختم نمی‌شود.

تغییر یافته در نسخه 1.9.0: در صورت خطای UV_ENOBUFS طول بازگردانده شده شامل بایت پایان‌بخش null است، و در صورت موفقیت، بافر با null پایان می‌یابد.

دایرکتوری کاری جاری را تغییر می‌دهد.
پوشه خانگی کاربر جاری را دریافت می‌کند. در ویندوز، uv_os_homedir() ابتدا متغیر محیطی USERPROFILE را با استفاده از GetEnvironmentVariableW() بررسی می‌کند. اگر USERPROFILE تنظیم نشده باشد، GetUserProfileDirectoryW() فراخوانی می‌شود. در سایر سیستم‌عامل‌ها، uv_os_homedir() ابتدا متغیر محیطی HOME را با getenv(3) https://man7.org/linux/man-pages/man3/getenv.3.html بررسی می‌کند. اگر HOME تنظیم نشده باشد، getpwuid_r(3) https://man7.org/linux/man-pages/man3/getpwuid_r.3.html فراخوانی می‌شود. دایرکتوری خانگی کاربر در buffer ذخیره می‌شود. هنگام فراخوانی uv_os_homedir()، پارامتر size حداکثر اندازه buffer را نشان می‌دهد. در صورت موفقیت، size روی طول رشته buffer تنظیم می‌شود. در صورت شکست UV_ENOBUFS، پارامتر size روی طول مورد نیاز برای buffer شامل بایت null تنظیم می‌گردد.

هشدار:

تابع uv_os_homedir() ایمن در برابر ریسه (Thread-safe) نیست.

افزوده شده در نسخه 1.6.0.

دایرکتوری موقت را دریافت می‌کند. در ویندوز، uv_os_tmpdir() از GetTempPathW() استفاده می‌کند. در سایر سیستم‌عامل‌ها، uv_os_tmpdir() از اولین متغیر محیطی یافت‌شده در فهرست مرتب‌شده TMPDIR، TMP، TEMP و TEMPDIR استفاده می‌کند. اگر هیچ‌یک از این‌ها یافت نشود، مسیر "/tmp" یا در اندروید مسیر "/data/local/tmp" استفاده می‌شود. دایرکتوری موقت در buffer ذخیره می‌شود. هنگام فراخوانی uv_os_tmpdir()، مقدار size حداکثر اندازه buffer را نشان می‌دهد. در صورت موفقیت، size روی طول رشته buffer (که شامل null پایان‌بخش نیست) تنظیم می‌شود. در صورت شکست UV_ENOBUFS، مقدار size روی طول مورد نیاز برای buffer شامل بایت null تنظیم می‌گردد.

هشدار:

تابع uv_os_tmpdir() ایمن در برابر ریسه (Thread-safe) نیست.

افزوده شده در نسخه 1.9.0.

زیرمجموعه‌ای از درایه فایل گذرواژه را برای uid مؤثر فعلی (نه uid واقعی) دریافت می‌کند. داده‌های پرشده شامل نام کاربری، euid، gid، شل و دایرکتوری خانگی است. در سیستم‌های غیرویندوزی، تمام داده‌ها از getpwuid_r(3) https://man7.org/linux/man-pages/man3/getpwuid_r.3.html به دست می‌آید. در ویندوز، uid و gid روی -1 تنظیم شده و معنایی ندارند و shell برابر با NULL است. پس از فراخوانی موفق این تابع، حافظه تخصیص‌یافته به pwd باید با uv_os_free_passwd() آزاد شود.

افزوده شده در نسخه 1.9.0.

زیرمجموعه‌ای از درایه فایل گذرواژه را برای uid ارائه‌شده دریافت می‌کند. داده‌های پرشده شامل نام کاربری، euid، gid، شل و دایرکتوری خانگی است. در سیستم‌های غیرویندوزی، تمام داده‌ها از getpwuid_r(3) https://man7.org/linux/man-pages/man3/getpwuid_r.3.html به دست می‌آید. در ویندوز، uid و gid روی -1 تنظیم شده و معنایی ندارند و shell برابر با NULL است. پس از فراخوانی موفق این تابع، حافظه تخصیص‌یافته به pwd باید با uv_os_free_passwd() آزاد شود.

افزوده شده در نسخه 1.45.0.

زیرمجموعه‌ای از درایه فایل گروه را برای uid ارائه‌شده دریافت می‌کند. داده‌های پرشده شامل نام گروه، gid و اعضا است. در سیستم‌های غیرویندوزی، تمام داده‌ها از getgrgid_r(3) https://man7.org/linux/man-pages/man3/getgrgid_r.3.html به دست می‌آید. در ویندوز، uid و gid روی -1 تنظیم شده و معنایی ندارند. پس از فراخوانی موفق این تابع، حافظه تخصیص‌یافته به group باید با uv_os_free_group() آزاد شود.

افزوده شده در نسخه 1.45.0.

حافظه‌ای را که قبلاً با uv_os_get_group() تخصیص یافته بود آزاد می‌کند.

افزوده شده در نسخه 1.45.0.

حافظه pwd را که قبلاً با uv_os_get_passwd() تخصیص یافته بود آزاد می‌کند.

افزوده شده در نسخه 1.9.0.

میزان حافظه آزاد موجود در سیستم را طبق گزارش هسته (بر حسب بایت) دریافت می‌کند. در صورت نامشخص بودن 0 برمی‌گرداند.
میزان کل حافظه فیزیکی سیستم را (بر حسب بایت) دریافت می‌کند. در صورت نامشخص بودن 0 برمی‌گرداند.
میزان کل حافظه در دسترس فرایند را (بر حسب بایت) بر اساس محدودیت‌های اعمال‌شده توسط سیستم‌عامل دریافت می‌کند. اگر چنین محدودیتی وجود نداشته باشد، یا محدودیت ناشناخته باشد، 0 برگردانده می‌شود. اگر سازوکار محدودکننده وجود داشته باشد اما محدودیتی تعیین نشده باشد، UINT64_MAX برگردانده می‌شود. توجه داشته باشید که کمتر یا بیشتر بودن این مقدار از uv_get_total_memory() غیرعادی نیست.

نکته:

این تابع در حال حاضر فقط روی لینوکس (بر اساس cgroups در صورت وجود) و در z/OS (بر اساس RLIMIT_MEMLIMIT) مقداری غیرصفر برمی‌گرداند.

افزوده شده در نسخه 1.29.0.

میزان حافظه آزادی را که هنوز در دسترس فرایند است (بر حسب بایت) دریافت می‌کند. تفاوت این تابع با uv_get_free_memory() در این است که محدودیت‌های اعمال‌شده توسط سیستم‌عامل را در نظر می‌گیرد. اگر چنین محدودیتی وجود نداشته باشد یا محدودیت ناشناخته باشد، مقدار بازگردانده شده با uv_get_free_memory() یکسان خواهد بود.

نکته:

این تابع در حال حاضر تنها در لینوکس مقداری متفاوت از گزارش uv_get_free_memory() برمی‌گرداند (بر اساس cgroups در صورت وجود).

افزوده شده در نسخه 1.45.0.

برچسب زمانی با دقت بالا (High-resolution timestamp) را برمی‌گرداند. این مقدار بر حسب نانوثانیه بیان می‌شود. نسبت به یک زمان اختیاری در گذشته سنجیده می‌شود و ارتباطی با ساعت شبانه‌روز ندارد و بنابراین در معرض انحراف ساعت (Clock drift) نیست. کاربرد اصلی آن برای اندازه‌گیری کارایی بین بازه‌ها است.

نکته:

همه بسترها از دقت نانوثانیه پشتیبانی نمی‌کنند؛ با این حال، این مقدار همیشه بر حسب نانوثانیه خواهد بود.
زمان فعلی سیستم را از یک منبع ساعت بیدرنگ (Real-time) یا یکنواخت (Monotonic) با دقت بالا به دست می‌آورد.

ساعت بیدرنگ از مبدأ یونیکس (1970-01-01) شمارش می‌کند و مشمول تنظیمات زمانی است؛ ممکن است در زمان به عقب بپرد.

ساعت یکنواخت از یک نقطه دلخواه در گذشته شمارش می‌کند و هرگز در زمان به عقب نمی‌پرد.

افزوده شده در نسخه 1.45.0.

تمام دستگیره‌های مرتبط با loop داده‌شده را در stream مشخص‌شده چاپ می‌کند.

مثال:

uv_print_all_handles(uv_default_loop(), stderr);
/*
[--I] signal   0x1a25ea8
[-AI] async    0x1a25cf0
[R--] idle     0x1a7a8c8
*/

قالب به صورت [flags] handle-type handle-address است. برای flags:

  • R برای دستگیره‌ای که به آن ارجاع شده است چاپ می‌شود
  • A برای دستگیره فعال چاپ می‌شود
  • I برای دستگیره داخلی چاپ می‌شود

هشدار:

این تابع برای اشکال‌زدایی موردی (Ad hoc) در نظر گرفته شده است و هیچ تضمینی برای پایداری API/ABI وجود ندارد.

افزوده شده در نسخه 1.8.0.

این تابع مشابه uv_print_all_handles() است با این تفاوت که فقط دستگیره‌های فعال چاپ می‌شوند.

هشدار:

این تابع برای اشکال‌زدایی موردی (Ad hoc) در نظر گرفته شده است و هیچ تضمینی برای پایداری API/ABI وجود ندارد.

افزوده شده در نسخه 1.8.0.

تمام متغیرهای محیطی را بازیابی می‌کند. این تابع حافظه‌ای تخصیص می‌دهد که باید با فراخوانی uv_os_free_environ() آزاد شود.

هشدار:

این تابع ایمن در برابر ریسه (Thread-safe) نیست.

افزوده شده در نسخه 1.31.0.

حافظه تخصیص‌یافته برای متغیرهای محیطی توسط uv_os_environ() را آزاد می‌کند.

افزوده شده در نسخه 1.31.0.

متغیر محیطی مشخص‌شده توسط name را بازیابی کرده، مقدار آن را در buffer کپی می‌کند و size را روی طول رشته مقدار تنظیم می‌کند. هنگام فراخوانی این تابع، size باید روی مقدار حافظه در دسترس در buffer شامل نویسه پایان‌بخش null تنظیم شده باشد. اگر متغیر محیطی از فضای موجود در buffer فراتر رود، UV_ENOBUFS برگردانده می‌شود و size روی مقدار فضای مورد نیاز برای نگهداری مقدار تنظیم می‌گردد. اگر متغیر محیطی منطبقی وجود نداشته باشد، UV_ENOENT برگردانده می‌شود.

هشدار:

این تابع ایمن در برابر ریسه (Thread-safe) نیست.

افزوده شده در نسخه 1.12.0.

متغیر محیطی مشخص‌شده با name را ایجاد کرده یا با value به‌روزرسانی می‌کند.

هشدار:

این تابع ایمن در برابر ریسه (Thread-safe) نیست.

افزوده شده در نسخه 1.12.0.

متغیر محیطی مشخص‌شده با name را حذف می‌کند. اگر چنین متغیر محیطی وجود نداشته باشد، این تابع با موفقیت بازمی‌گردد.

هشدار:

این تابع ایمن در برابر ریسه (Thread-safe) نیست.

افزوده شده در نسخه 1.12.0.

نام میزبان (Hostname) را به عنوان یک رشته پایان‌یافته با null در buffer برمی‌گرداند، و size را روی طول رشته نام میزبان تنظیم می‌کند. هنگام فراخوانی این تابع، size باید روی مقدار فضای در دسترس در buffer شامل نویسه پایان‌بخش null تنظیم شود. اگر نام میزبان از فضای موجود در buffer فراتر رود، UV_ENOBUFS برگردانده می‌شود و size روی مقدار فضای مورد نیاز برای نگهداری مقدار تنظیم می‌گردد.

افزوده شده در نسخه 1.12.0.

تغییر یافته در نسخه 1.26.0: ثابت UV_MAXHOSTNAMESIZE در دسترس است و بیانگر حداکثر اندازه buffer مورد نیاز برای ذخیره نام میزبان و کاراکتر nul انتهایی است.

اولویت زمان‌بندی فرایند مشخص‌شده با pid را بازیابی می‌کند. مقدار بازگردانده شده priority بین -20 (اولویت بالا) و 19 (اولویت پایین) است.

نکته:

در ویندوز، اولویت بازگردانده شده برابر با یکی از ثابت‌های UV_PRIORITY خواهد بود.

افزوده شده در نسخه 1.23.0.

اولویت زمان‌بندی فرایند مشخص‌شده با pid را تنظیم می‌کند. دامنه مقدار priority بین -20 (اولویت بالا) و 19 (اولویت پایین) است. ثابت‌های UV_PRIORITY_LOW، UV_PRIORITY_BELOW_NORMAL، UV_PRIORITY_NORMAL، UV_PRIORITY_ABOVE_NORMAL، UV_PRIORITY_HIGH و UV_PRIORITY_HIGHEST نیز برای سهولت ارائه شده‌اند.

نکته:

در ویندوز، این تابع از SetPriorityClass() استفاده می‌کند. آرگومان priority به یک رده اولویت ویندوز نگاشت می‌شود. هنگام بازیابی اولویت فرایند، نتیجه برابر با یکی از ثابت‌های UV_PRIORITY خواهد بود، و لزوماً مقدار دقیق priority نیست.

نکته:

در ویندوز، تنظیم PRIORITY_HIGHEST تنها برای کاربران ارشد (Elevated) کار خواهد کرد، و برای سایرین به طور خودکار به PRIORITY_HIGH کاهش می‌یابد.

نکته:

در IBM i PASE، بالاترین اولویت فرایند -10 است. ثابت UV_PRIORITY_HIGHEST برابر -10، UV_PRIORITY_HIGH برابر -7، UV_PRIORITY_ABOVE_NORMAL برابر -4، UV_PRIORITY_NORMAL برابر 0، UV_PRIORITY_BELOW_NORMAL برابر 15 و UV_PRIORITY_LOW برابر 39 است.

نکته:

در IBM i PASE، مجاز به تغییر اولویت خود نیستید مگر اینکه دارای مجوز ویژه *JOBCTL باشید (حتی برای کاهش اولویت).

افزوده شده در نسخه 1.23.0.

اطلاعات سیستم را در buffer بازیابی می‌کند. داده‌های پرشده شامل نام سیستم‌عامل، انتشار (Release)، نسخه و معماری ماشین است. در سیستم‌های غیرویندوزی، uv_os_uname() یک پوشش نازک روی uname(2) https://man7.org/linux/man-pages/man2/uname.2.html است. در صورت موفقیت صفر و در غیر این صورت یک مقدار خطای غیرصفر برمی‌گرداند.

افزوده شده در نسخه 1.25.0.

پیاده‌سازی چندسکویی از gettimeofday(2) https://man7.org/linux/man-pages/man2/gettimeofday.2.html. آرگومان timezone در gettimeofday() پشتیبانی نمی‌شود، زیرا منسوخ تلقی می‌گردد.

افزوده شده در نسخه 1.28.0.

بافر buf را دقیقاً با buflen بایت تصادفی از نظر رمزنگاری قوی که از CSPRNG سیستم به دست آمده است پر می‌کند. پارامتر flags برای توسعه‌های آتی رزرو شده و در حال حاضر باید 0 باشد.

خواندن ناقص (Short read) امکان‌پذیر نیست. هنگامی که کمتر از buflen بایت تصادفی در دسترس باشد، مقدار خطای غیرصفر بازگردانده شده یا به تابع بازخوانی ارسال می‌شود.

نسخه همگام ممکن است در صورت عدم وجود آنتروپی کافی به طور نامحدود مسدود شود. نسخه ناهمگام ممکن است در صورت کمبود آنتروپی سیستم هرگز پایان نیابد.

منابع آنتروپی:

مقادیر بازگشتی (Returns)
در صورت موفقیت 0، یا یک کد خطای کمتر از 0 در صورت شکست. محتوای buf پس از بروز خطا تعریف‌نشده است.

نکته:

هنگام استفاده از نسخه همگام، هر دو پارامتر loop و req استفاده نمی‌شوند و می‌توان آن‌ها را روی NULL تنظیم کرد.

افزوده شده در نسخه 1.33.0.

باعث به خواب رفتن ریسه فراخوان به مدت msec میلی‌ثانیه می‌شود.

افزوده شده در نسخه 1.34.0.

این ابزارهای کار با رشته در داخل کتابخانه برای تعامل با ویندوز مورد نیاز هستند و صادر (export) شده‌اند تا به کلاینت‌ها امکان دهند در صورت کامل نبودن API libuv به صورت یکپارچه با این داده‌ها کار کنند.

طول یک مقدار UTF-16 (یا UCS-2) به نام utf16 را پس از تبدیل به WTF-8 دریافت می‌کند. اگر utf16 با NUL خاتمه یافته باشد، utf16_len را می‌توان روی -1 تنظیم کرد، در غیر این صورت باید به طور صریح مشخص شود.

افزوده شده در نسخه 1.47.0.

داده‌های UTF-16 (یا UCS-2) موجود در utf16 را به داده‌های WTF-8 در *wtf8_ptr تبدیل می‌کند. شمارش utf16_len (بر حسب کاراکتر) طول utf16 را مشخص می‌کند. اگر utf16 با NUL پایان یافته باشد، utf16_len می‌تواند روی -1 تنظیم شود، در غیر این صورت باید مقداردهی شود. اگر wtf8_ptr برابر NULL باشد، هیچ نتیجه‌ای محاسبه نمی‌شود، اما طول (برابر با uv_utf16_length_as_wtf8) در wtf8_len_ptr ذخیره خواهد شد. اگر *wtf8_ptr برابر NULL باشد، فضا برای تبدیل تخصیص داده شده و در wtf8_ptr برگردانده می‌شود و طول در wtf8_len_ptr برگردانده خواهد شد. در غیر این صورت، طول *wtf8_ptr باید در wtf8_len_ptr ارسال شود. متغیر wtf8_ptr باید شامل فضای اضافی برای یک NUL اضافی بعد از نتیجه باشد. اگر نتیجه کوتاه (truncated) شود، مقدار UV_ENOBUFS برگردانده می‌شود و wtf8_len_ptr طول مورد نیاز برای wtf8_ptr به منظور نگهداری کل نتیجه خواهد بود.

افزوده شده در نسخه 1.47.0.

طول یک مقدار WTF-8 خاتمه‌یافته با NUL به نام wtf8 را پس از تبدیل به UTF-16 (یا UCS-2)، شامل نویسه پایان‌بخش NUL، بر حسب کاراکتر دریافت می‌کند.

افزوده شده در نسخه 1.47.0.

داده‌های WTF-8 پایان‌یافته با NUL در wtf8 را به داده‌های UTF-16 (یا UCS-2) در utf16 تبدیل می‌کند. شمارش utf16_len (بر حسب کاراکتر) باید فضا برای بایت پایان‌بخش NUL را شامل شود.

افزوده شده در نسخه 1.47.0.

کتابخانه libuv یک رابط کاربری معیارسنجی (Metrics API) برای ردیابی عملیات داخلی مختلف حلقه رویداد فراهم می‌کند.

ساختاری که معیارهای حلقه رویداد را در خود نگه می‌دارد. توصیه می‌شود این معیارها در یک uv_prepare_cb <#c.uv_prepare_cb> بازیابی شوند تا اطمینان حاصل شود که هیچ‌گونه ناهمخوانی با شمارنده‌های معیارها رخ نمی‌دهد.
typedef struct {
    uint64_t loop_count;
    uint64_t events;
    uint64_t events_waiting;
    /* private */
    uint64_t* reserved[13];
} uv_metrics_t;

تعداد تکرارهای حلقه رویداد.
تعداد رویدادهایی که توسط مدیریت‌کننده رویداد (Event handler) پردازش شده‌اند.
تعداد رویدادهایی که هنگام فراخوانی ارائه‌دهنده رویداد در انتظار پردازش بودند.

مدت زمانی را که حلقه رویداد در ارائه‌دهنده رویداد هسته (مانند epoll_wait) بی‌کار (Idle) بوده است، بازیابی می‌کند. این فراخوانی ایمن در برابر ریسه (Thread-safe) است.

مقدار بازگشتی، زمان انباشته سپری‌شده در حالت بی‌کاری در ارائه‌دهنده رویداد هسته از زمانی است که uv_loop_t <#c.uv_loop_t> برای جمع‌آوری زمان بی‌کاری پیکربندی شده بود.

نکته:

حلقه رویداد تا قبل از فراخوانی uv_loop_configure <#c.uv_loop_configure> با UV_METRICS_IDLE_TIME شروع به انباشت زمان بی‌کاری ارائه‌دهنده رویداد نخواهد کرد.

افزوده شده در نسخه 1.39.0.

مجموعه فعلی معیارهای حلقه رویداد را در اشاره‌گر metrics کپی می‌کند.

افزوده شده در نسخه 1.45.0.

هشدار:

محتوای این راهنما به تازگی به مستندات libuv اضافه شده است و هنوز مورد بازبینی دقیق قرار نگرفته است. اگر متوجه خطایی شدید لطفاً یک گزارش مشکل (issue) ثبت کنید، یا حتی بهتر، یک پول ریکوئست (pull request) باز کنید!

این «کتاب» مجموعه‌ای کوچک از آموزش‌ها درباره استفاده از libuv https://github.com/libuv/libuv به عنوان یک کتابخانه ورودی/خروجی رویدادمحور (evented I/O) با کارایی بالا است که رابط برنامه‌نویسی (API) یکسانی را در ویندوز و یونیکس ارائه می‌دهد.

هدف این کتاب پوشش بخش‌های اصلی libuv است، اما مرجع جامعی برای بررسی تک‌تک توابع و ساختارهای داده نیست. برای مشاهده جزئیات کامل می‌توانید به مستندات رسمی libuv https://docs.libuv.org/en/v1.x مراجعه کنید.

این کتاب همچنان در حال تکمیل است، بنابراین ممکن است برخی بخش‌ها ناقص باشند، اما امیدوارم با گسترش آن از مطالعه‌اش لذت ببرید.

اگر در حال خواندن این کتاب هستید، یکی از این دو دسته هستید:

1.
یک برنامه‌نویس سیستم هستید که برنامه‌های سطح پایین مانند دیمن‌ها (daemons) یا سرویس‌ها و کلاینت‌های شبکه را ایجاد می‌کند. شما متوجه شده‌اید که رویکرد حلقه رویداد (event loop) برای برنامه شما بسیار مناسب است و تصمیم به استفاده از libuv گرفته‌اید.
2.
یک نویسنده ماژول node.js هستید که می‌خواهد رابط‌های برنامه‌نویسی پلتفرم را که با C یا ++C نوشته شده‌اند، با مجموعه‌ای از APIهای همگام یا ناهمگام که در اختیار JavaScript قرار می‌گیرند بسته‌بندی کند. شما از libuv صرفاً در زمینه node.js استفاده خواهید کرد. برای این کار به منابع دیگری نیاز خواهید داشت زیرا این کتاب بخش‌های اختصاصی v8/node.js را پوشش نمی‌دهد.

این کتاب فرض می‌کند که شما با زبان برنامه‌نویسی C آشنایی کامل دارید.

پروژه node.js https://www.nodejs.org در سال ۲۰۰۹ به عنوان یک محیط اجرای جاوااسکریپت مستقل از مرورگر آغاز شد. با استفاده از موتور V8 گوگل https://v8.dev و کتابخانه libev اثر مارک لمان http://software.schmorp.de/pkg/libev.html، پروژه node.js یک مدل I/O رویدادمحور را با زبانی که به دلیل شکل‌گیری در بستر مرورگرها با این سبک برنامه‌نویسی سازگار بود ترکیب کرد. با افزایش محبوبیت node.js، اجرای آن در ویندوز اهمیت یافت، اما libev فقط در یونیکس اجرا می‌شد. معادل ویندوزی مکانیزم‌های آگاه‌سازی رویداد هسته مانند kqueue یا epoll/poll، سازوکار IOCP است. کتابخانه libuv یک لایه انتزاعی روی libev یا IOCP (بسته به پلتفرم) بود که یک API بر پایه libev در اختیار کاربران قرار می‌داد. در نسخه node-v0.9.0 از libuv، وابستگی به libev حذف شد https://github.com/joyent/libuv/issues/485.

از آن زمان، libuv به بلوغ خود ادامه داده و به یک کتابخانه مستقل و باکیفیت برای برنامه‌نویسی سیستم تبدیل شده است. کاربران خارج از node.js شامل زبان برنامه‌نویسی Rust بنیاد موزیلا https://www.rust-lang.org و انواع اتصالات زبانی (language bindings) مختلف https://github.com/libuv/libuv/blob/v1.x/LINKS.md هستند.

این کتاب و کدهای آن بر اساس نسخه v1.42.0 کتابخانه libuv https://github.com/libuv/libuv/releases/tag/v1.42.0 نگارش شده است.

تمامی کدهای نمونه و منبع کتاب به عنوان بخشی از پروژه libuv https://github.com/libuv/libuv در گیت‌هاب قرار دارند. مخزن libuv https://github.com/libuv/libuv را کلون یا دانلود کنید، سپس آن را بسازید:

sh autogen.sh
./configure
make

نیازی به اجرای make install نیست. برای ساخت نمونه‌ها، دستور make را در دایرکتوری docs/code/ اجرا کنید.

کتابخانه libuv یک سبک برنامه‌نویسی ناهمگام (asynchronous) و رویدادمحور (event-driven) را اعمال می‌کند. وظیفه اصلی آن فراهم کردن یک حلقه رویداد و اعلان‌های مبتنی بر کال‌بک (callback) برای عملیات ورودی/خروجی و سایر فعالیت‌ها است. کتابخانه libuv ابزارهای پایه‌ای مانند تایمرها، پشتیبانی از شبکه غیرمسدودکننده، دسترسی ناهمگام به فایل‌سیستم، فرآیندهای فرزند و موارد دیگر را ارائه می‌دهد.

در برنامه‌نویسی رویدادمحور، یک برنامه تمایل خود را برای رویدادهای خاصی اعلام می‌کند و هنگام وقوع آن‌ها پاسخ می‌دهد. مسئولیت گردآوری رویدادها از سیستم‌عامل یا پایش سایر منابع رویداد بر عهده libuv است و کاربر می‌تواند کال‌بک‌هایی (callbacks) را ثبت کند تا هنگام رخ دادن یک رویداد فراخوانی شوند. حلقه رویداد معمولاً به صورت نامحدود (forever) اجرا می‌شود. در قالب شبه‌کد:

while there are still events to process:
    e = get the next event
    if there is a callback associated with e:
        call the callback

برخی نمونه‌های رویداد عبارتند از:

  • فایل برای نوشتن آماده است
  • یک سوکت داده‌ای آماده خواندن دارد
  • یک تایمر منقضی شده است

این حلقه رویداد درون تابع uv_run() کپسوله‌سازی شده است -- تابع نهایی و همه‌کاره هنگام استفاده از libuv.

رایج‌ترین فعالیت برنامه‌های سیستمی رسیدگی به ورودی و خروجی است، نه پردازش‌های سنگین محاسباتی. مشکل استفاده از توابع متداول ورودی/خروجی (read، fprintf و غیره) این است که آن‌ها مسدودکننده (blocking) هستند. نوشتن واقعی روی دیسک سخت یا خواندن از شبکه در مقایسه با سرعت پردازنده زمان نامتناسب و طولانی می‌برد. این توابع تا زمان اتمام کار بازنمی‌گردند، بنابراین برنامه شما عملاً هیچ کاری انجام نمی‌دهد. برای برنامه‌هایی که به کارایی بالا نیاز دارند، این یک مانع بزرگ است زیرا سایر فعالیت‌ها و عملیات ورودی/خروجی دیگر معطل می‌مانند.

یکی از راه‌حل‌های استاندارد استفاده از نخ‌ها (threads) است. هر عملیات I/O مسدودکننده در یک نخ جداگانه (یا در یک استخر نخ) آغاز می‌شود. هنگامی که تابع مسدودکننده در نخ فراخوانی می‌شود، سیستم‌عامل می‌تواند نخ دیگری را برای اجرا زمان‌بندی کند که واقعاً به CPU نیاز دارد.

رویکردی که libuv دنبال می‌کند از سبک دیگری استفاده می‌کند که سبک ناهمگام و غیرمسدودکننده (asynchronous, non-blocking) است. اکثر سیستم‌عامل‌های مدرن زیرسیستم‌های اعلان رویداد را ارائه می‌دهند. به عنوان مثال، یک فراخوانی عادی read روی سوکت مسدود می‌شود تا زمانی که فرستنده واقعاً چیزی ارسال کند. در عوض، برنامه می‌تواند از سیستم‌عامل بخواهد سوکت را پایش کند و یک اعلان رویداد در صف قرار دهد. برنامه می‌تواند به دلخواه خود رویدادها را بررسی کند (شاید پیش از آن محاسباتی انجام دهد تا حداکثر استفاده را از پردازنده ببرد) و داده‌ها را بردارد. این رویکرد ناهمگام (asynchronous) است زیرا برنامه در یک نقطه اعلام علاقه کرده و سپس در نقطه‌ای دیگر (در زمان و فضا) از داده‌ها استفاده کرده است. همچنین غیرمسدودکننده (non-blocking) است زیرا فرآیند برنامه برای انجام سایر وظایف آزاد بوده است. این سبک به خوبی با رویکرد حلقه رویداد libuv هماهنگ است، زیرا رویدادهای سیستم‌عامل می‌توانند به سادگی به عنوان یک رویداد دیگر libuv در نظر گرفته شوند. ماهیت غیرمسدودکننده تضمین می‌کند که به سایر رویدادها به محض رسیدن، با حداکثر سرعت رسیدگی شود [1].

نکته:

نحوه اجرای I/O در پس‌زمینه اهمیتی برای ما ندارد، اما به دلیل سازوکار سخت‌افزار رایانه‌ها که نخ واحد پایه‌ای پردازنده است، libuv و سیستم‌عامل‌ها معمولاً نخ‌های پس‌زمینه/کارگر و/یا نمونه‌برداری (polling) را برای انجام وظایف به صورت غیرمسدودکننده اجرا می‌کنند.

برت بلدر (Bert Belder)، یکی از توسعه‌دهندگان اصلی libuv، ویدیوی کوتاهی دارد که معماری libuv و پیش‌زمینه آن را توضیح می‌دهد. اگر تجربه قبلی کار با libuv یا libev را ندارید، تماشای آن کوتاه و مفید خواهد بود.

حلقه رویداد libuv با جزئیات بیشتر در مستندات https://docs.libuv.org/en/v1.x/design.html#the-i-o-loop توضیح داده شده است.

با گذر از مبانی اولیه، بیایید اولین برنامه libuv خود را بنویسیم. این برنامه کاری انجام نمی‌دهد جز شروع یک حلقه که بلافاصله خارج می‌شود.

helloworld/main.c

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
int main() {
    uv_loop_t *loop = malloc(sizeof(uv_loop_t));
    uv_loop_init(loop);
    printf("Now quitting.\n");
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    free(loop);
    return 0;
}

این برنامه بلافاصله خارج می‌شود زیرا هیچ رویدادی برای پردازش ندارد. یک حلقه رویداد libuv باید با استفاده از توابع مختلف API برای پایش رویدادها تنظیم شود.

از نسخه libuv v1.0 به بعد، کاربران باید پیش از مقداردهی اولیه حلقه با uv_loop_init(uv_loop_t *) حافظه مورد نیاز آن را تخصیص دهند. این کار به شما امکان می‌دهد از مدیریت حافظه سفارشی استفاده کنید. به یاد داشته باشید که حلقه را با uv_loop_close(uv_loop_t *) آزاد و غیرفعال کنید و سپس حافظه را پاک نمایید. برنامه‌های نمونه هرگز حلقه‌ها را نمی‌بندند زیرا برنامه پس از پایان حلقه به پایان می‌رسد و سیستم حافظه را پس می‌گیرد. اما در پروژه‌های سطح عملیاتی، به‌ویژه برنامه‌های سیستمی با اجرای طولانی‌مدت، باید دقت کرد که آزادسازی منابع به درستی انجام شود.

یک حلقه پیش‌فرض توسط libuv ارائه می‌شود که با uv_default_loop() قابل دسترسی است. اگر فقط به یک حلقه واحد نیاز دارید، باید از این حلقه استفاده کنید.

default-loop/main.c

#include <stdio.h>
#include <uv.h>
int main() {
    uv_loop_t *loop = uv_default_loop();
    printf("Default loop.\n");
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    return 0;
}

نکته:

محیط node.js از حلقه پیش‌فرض به عنوان حلقه اصلی خود استفاده می‌کند. اگر در حال نوشتن اتصالات زبانی (bindings) هستید، باید از این نکته آگاه باشید.

توابع مقداردهی اولیه یا توابع همگام که ممکن است با شکست مواجه شوند، در صورت خطا یک عدد منفی برمی‌گردانند. توابع ناهمگام که ممکن است با شکست مواجه شوند، یک پارامتر وضعیت (status) به کال‌بک‌های خود ارسال می‌کنند. پیام‌های خطا به صورت ثابت‌های UV_E* https://docs.libuv.org/en/v1.x/errors.html#error-constants تعریف شده‌اند.

می‌توانید از توابع uv_strerror(int) و uv_err_name(int) استفاده کنید تا به ترتیب یک رشته const char * توصیف‌کننده خطا یا نام خطا را دریافت کنید.

به کال‌بک‌های خواندن I/O (مانند فایل‌ها و سوکت‌ها) پارامتری به نام nread ارسال می‌شود. اگر nread کمتر از ۰ باشد، خطایی رخ داده است (UV_EOF خطای پایان فایل است که ممکن است بخواهید آن را به شکل متفاوتی مدیریت کنید).

کتابخانه libuv بر این اساس کار می‌کند که کاربر تمایل خود را برای رویدادهای خاص اعلام کند. این کار معمولاً با ایجاد یک هندل (handle) برای یک دستگاه I/O، تایمر یا فرآیند انجام می‌شود. هندل‌ها ساختارهای مات (opaque structs) با نام uv_TYPE_t هستند که در آن type کاربرد هندل را مشخص می‌کند.

پایشگرهای libuv

/* Handle types. */
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_dir_s uv_dir_t;
typedef struct uv_stream_s uv_stream_t;
typedef struct uv_tcp_s uv_tcp_t;
typedef struct uv_udp_s uv_udp_t;
typedef struct uv_pipe_s uv_pipe_t;
typedef struct uv_tty_s uv_tty_t;
typedef struct uv_poll_s uv_poll_t;
typedef struct uv_timer_s uv_timer_t;
typedef struct uv_prepare_s uv_prepare_t;
typedef struct uv_check_s uv_check_t;
typedef struct uv_idle_s uv_idle_t;
typedef struct uv_async_s uv_async_t;
typedef struct uv_process_s uv_process_t;
typedef struct uv_fs_event_s uv_fs_event_t;
typedef struct uv_fs_poll_s uv_fs_poll_t;
typedef struct uv_signal_s uv_signal_t;
/* Request types. */
typedef struct uv_req_s uv_req_t;
typedef struct uv_getaddrinfo_s uv_getaddrinfo_t;
typedef struct uv_getnameinfo_s uv_getnameinfo_t;
typedef struct uv_shutdown_s uv_shutdown_t;
typedef struct uv_write_s uv_write_t;
typedef struct uv_connect_s uv_connect_t;
typedef struct uv_udp_send_s uv_udp_send_t;
typedef struct uv_fs_s uv_fs_t;
typedef struct uv_work_s uv_work_t;
typedef struct uv_random_s uv_random_t;
/* None of the above. */
typedef struct uv_env_item_s uv_env_item_t;
typedef struct uv_cpu_info_s uv_cpu_info_t;
typedef struct uv_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t;
typedef struct uv_passwd_s uv_passwd_t;
typedef struct uv_utsname_s uv_utsname_t;
typedef struct uv_statfs_s uv_statfs_t;

هندل‌ها نشان‌دهنده اشیاء با طول عمر طولانی هستند. عملیات‌های ناهمگام روی این هندل‌ها با استفاده از درخواست‌ها (requests) شناسایی می‌شوند. یک درخواست عمر کوتاهی دارد (معمولاً فقط در طول یک کال‌بک استفاده می‌شود) و معمولاً نشان‌دهنده یک عملیات ورودی/خروجی روی یک هندل است. درخواست‌ها برای حفظ زمینه (context) بین شروع عملیات و کال‌بک مربوط به هر اقدام استفاده می‌شوند. برای مثال، یک سوکت UDP توسط یک uv_udp_t نشان داده می‌شود، در حالی که عملیات نوشتن روی سوکت از یک ساختار uv_udp_send_t استفاده می‌کند که پس از اتمام نوشتن به کال‌بک ارسال می‌شود.

هندل‌ها توسط تابع مربوطه مقداردهی اولیه می‌شوند:

uv_TYPE_init(uv_loop_t *, uv_TYPE_t *)

کال‌بک‌ها توابعی هستند که هر زمان رویداد مورد علاقه پایشگر رخ دهد، توسط libuv فراخوانی می‌شوند. منطق اختصاصی برنامه معمولاً در کال‌بک پیاده‌سازی می‌شود. به عنوان مثال، کال‌بک یک پایشگر IO داده‌های خوانده‌شده از فایل را دریافت می‌کند، کال‌بک تایمر هنگام انقضای زمان فعال می‌شود و غیره.

در اینجا نمونه‌ای از استفاده از یک هندل بیکاری (idle handle) آورده شده است. کال‌بک در هر دور از حلقه رویداد یک بار فراخوانی می‌شود. یک مورد کاربرد برای هندل‌های بیکار در بخش ابزارها <> بررسی شده است. بیایید از یک پایشگر بیکار برای مشاهده چرخه حیات پایشگر استفاده کنیم و ببینیم چگونه uv_run() به دلیل وجود یک پایشگر مسدود خواهد ماند. پایشگر بیکار با رسیدن شمارنده به مقدار تعیین‌شده متوقف می‌شود و uv_run() به دلیل نبود پایشگرهای فعال خارج می‌گردد.

idle-basic/main.c

#include <stdio.h>
#include <uv.h>
int64_t counter = 0;
void wait_for_a_while(uv_idle_t* handle) {
    counter++;
    if (counter >= 10e6)
        uv_idle_stop(handle);
}
int main() {
    uv_idle_t idler;
    uv_idle_init(uv_default_loop(), &idler);
    uv_idle_start(&idler, wait_for_a_while);
    printf("Idling...\n");
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    uv_loop_close(uv_default_loop());
    return 0;
}

در سبک برنامه‌نویسی مبتنی بر کال‌بک، اغلب می‌خواهید مقداری «زمینه (context)» -- اطلاعات اختصاصی برنامه -- را بین محل فراخوانی و کال‌بک منتقل کنید. تمامی هندل‌ها و درخواست‌ها دارای یک عضو void* data هستند که می‌توانید آن را روی اطلاعات زمینه تنظیم کرده و در کال‌بک مجدداً کست کنید. این یک الگوی رایج در سراسر اکوسیستم کتابخانه‌های C است. علاوه بر این، ساختار uv_loop_t نیز عضو داده مشابهی دارد.


----



[1]
البته بسته به ظرفیت سخت‌افزار.

خواندن و نوشتن ساده در سیستم فایل با استفاده از توابع uv_fs_* و ساختار uv_fs_t انجام می‌شود.

نکته:

عملیات سیستم فایل در libuv با عملیات سوکت متفاوت است <>. عملیات سوکت از عملیات غیرمسدودکننده ارائه شده توسط سیستم‌عامل استفاده می‌کنند. اما عملیات سیستم فایل در داخل از توابع مسدودکننده استفاده می‌کنند ولی این توابع را در یک استخر نخ (thread pool) https://docs.libuv.org/en/v1.x/threadpool.html#thread-pool-work-scheduling اجرا کرده و در صورت نیاز به تعامل با برنامه، پایشگرهای ثبت‌شده در حلقه رویداد را مطلع می‌سازند.

تمامی توابع سیستم فایل دارای دو شکل هستند - همگام (synchronous) و ناهمگام (asynchronous).

شکل‌های همگام در صورتی که کال‌بک null باشد به صورت خودکار فراخوانی شده (و مسدود می‌شوند). مقدار بازگشتی توابع یک کد خطای libuv است <#libuv-error-handling>. این معمولاً فقط برای فراخوانی‌های همگام کاربرد دارد. شکل ناهمگام زمانی فراخوانی می‌شود که یک کال‌بک ارسال شده باشد و مقدار بازگشتی 0 است.

توصیف‌کننده فایل با استفاده از تابع زیر به دست می‌آید:

int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb)

پارامترهای flags و mode پرچم‌های استاندارد یونیکس هستند https://man7.org/linux/man-pages/man2/open.2.html. کتابخانه libuv وظیفه تبدیل آن‌ها به پرچم‌های مناسب ویندوز را بر عهده دارد.

توصیف‌کننده‌های فایل با استفاده از تابع زیر بسته می‌شوند:

int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb)

کال‌بک‌های عملیات سیستم فایل دارای امضای زیر هستند:

void callback(uv_fs_t* req);

بیایید یک پیاده‌سازی ساده از دستور cat را ببینیم. کار را با ثبت یک کال‌بک برای زمان باز شدن فایل شروع می‌کنیم:

uvcat/main.c - باز کردن فایل

    // The request passed to the callback is the same as the one the call setup
    // function was passed.
    assert(req == &open_req);
    if (req->result >= 0) {
        iov = uv_buf_init(buffer, sizeof(buffer));
        uv_fs_read(uv_default_loop(), &read_req, req->result,
                   &iov, 1, -1, on_read);
    }
    else {
        fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));
    }
}

فیلد result در ساختار uv_fs_t در کال‌بک uv_fs_open همان توصیف‌کننده فایل است. اگر فایل با موفقیت باز شود، شروع به خواندن آن می‌کنیم.

uvcat/main.c - کال‌بک خواندن

    if (req->result < 0) {
        fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));
    }
    else if (req->result == 0) {
        uv_fs_t close_req;
        // synchronous
        uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
    }
    else if (req->result > 0) {
        iov.len = req->result;
        uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write);
    }
}

در مورد فراخوانی خواندن، باید یک بافر مقداردهی اولیه شده را ارسال کنید که پیش از فعال شدن کال‌بک خواندن با داده‌ها پر می‌شود. عملیات‌های uv_fs_* تقریباً مستقیماً به توابع خاص POSIX نگاشت می‌شوند، بنابراین پایان فایل (EOF) در این حالت با مقدار 0 در result مشخص می‌شود. در مورد استریم‌ها یا پایپ‌ها، ثابت UV_EOF به عنوان وضعیت ارسال می‌شد.

در اینجا یک الگوی رایج هنگام نوشتن برنامه‌های ناهمگام را مشاهده می‌کنید. فراخوانی uv_fs_close() به صورت همگام انجام می‌شود. معمولاً کارهایی که یک‌باره هستند یا به عنوان بخشی از مرحله راه‌اندازی یا خاموش‌سازی انجام می‌شوند، به صورت همگام اجرا می‌گردند، زیرا ما زمانی به I/O سریع علاقه‌مندیم که برنامه در حال انجام وظیفه اصلی خود و رسیدگی به چندین منبع ورودی/خروجی است. برای وظایف تک‌باره تفاوت عملکرد معمولاً ناچیز است و می‌تواند به کدهای ساده‌تر منجر شود.

نوشتن در سیستم فایل با استفاده از uv_fs_write() به همین سادگی است. کال‌بک شما پس از اتمام نوشتن فعال خواهد شد. در مورد ما، کال‌بک به سادگی خواندن بعدی را هدایت می‌کند. بنابراین خواندن و نوشتن به صورت گام‌به‌گام از طریق کال‌بک‌ها پیش می‌روند.

uvcat/main.c - کال‌بک نوشتن

    if (req->result < 0) {
        fprintf(stderr, "Write error: %s\n", uv_strerror((int)req->result));
    }
    else {
        uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read);
    }
}

هشدار:

به دلیل نحوه پیکربندی سیستم‌های فایل و دیسک‌درایوها برای کارایی، عملیات نوشتنی که «موفقیت‌آمیز» گزارش می‌شود ممکن است هنوز روی دیسک ثبت نشده باشد.

حرکت دومینوها را در تابع main() آغاز می‌کنیم:

uvcat/main.c

    uv_fs_open(uv_default_loop(), &open_req, argv[1], O_RDONLY, 0, on_open);
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    uv_fs_req_cleanup(&open_req);
    uv_fs_req_cleanup(&read_req);
    uv_fs_req_cleanup(&write_req);
    return 0;
}

هشدار:

تابع uv_fs_req_cleanup() باید همیشه روی درخواست‌های سیستم فایل فراخوانی شود تا تخصیص‌های حافظه داخلی در libuv آزاد شوند.

تمامی عملیات استاندارد سیستم فایل مانند unlink، rmdir، stat به صورت ناهمگام پشتیبانی می‌شوند و ترتیب آرگومان‌های شهودی دارند. آن‌ها از همان الگوهای فراخوانی read/write/open پیروی می‌کنند و نتیجه را در فیلد uv_fs_t.result برمی‌گردانند. فهرست کامل:

عملیات‌های سیستم فایل (Filesystem operations)

int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb);
int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb);
int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb);
int uv_fs_copyfile(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb);
int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb);
int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb);
int uv_fs_mkstemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb);
int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb);
int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent);
int uv_fs_opendir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb);
int uv_fs_closedir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb);
int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb);
int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, uv_fs_cb cb);
int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb);
int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb);
int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb);
int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb);
int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb);
int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb);
int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb);
int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb);
int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb);
int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb);
int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb);
int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb);
int uv_fs_statfs(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);

هندل پایه I/O در libuv جریان یا استریم (uv_stream_t) است. سوکت‌های TCP، سوکت‌های UDP و پایپ‌ها (pipes) برای I/O فایل و IPC همگی به عنوان زیرکلاس‌های استریم در نظر گرفته می‌شوند.

جریان‌ها با استفاده از توابع سفارشی برای هر زیرکلاس مقداردهی اولیه می‌شوند و سپس با توابع زیر مورد استفاده قرار می‌گیرند:

int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb);
int uv_read_stop(uv_stream_t*);
int uv_write(uv_write_t* req, uv_stream_t* handle,
             const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);

استفاده از توابع مبتنی بر استریم ساده‌تر از توابع سیستم فایل است و libuv با یک بار فراخوانی uv_read_start() به طور خودکار به خواندن از استریم ادامه می‌دهد تا زمانی که uv_read_stop() فراخوانی شود.

واحد گسسته داده، بافر است -- uv_buf_t. این ساختار صرفاً مجموعه‌ای از یک اشاره‌گر به بایت‌ها (uv_buf_t.base) و طول آن‌ها (uv_buf_t.len) است. ساختار uv_buf_t سبک بوده و به صورت مقداری (by value) منتقل می‌شود. آنچه به مدیریت نیاز دارد، بایت‌های واقعی هستند که باید توسط برنامه تخصیص داده شده و آزاد شوند.

خطا:

این برنامه همیشه کار نمی‌کند، به چیزی بهتر نیاز است

برای نمایش استریم‌ها باید از uv_pipe_t استفاده کنیم. این امکان استریم کردن فایل‌های محلی را فراهم می‌کند [2]. در اینجا یک ابزار ساده مانند tee با استفاده از libuv آورده شده است. انجام تمامی عملیات به صورت ناهمگام قدرت I/O رویدادمحور را نشان می‌دهد. دو عملیات نوشتن یکدیگر را مسدود نمی‌کنند، اما باید دقت کنیم که داده‌های بافر را کپی کنیم تا مطمئن شویم بافر پیش از نوشته شدن آزاد نمی‌شود.

برنامه باید به این صورت اجرا شود:

./uvtee <output_file>

کار را با باز کردن پایپ‌ها روی فایل‌های مورد نیاز شروع می‌کنیم. پایپ‌های libuv به یک فایل به طور پیش‌فرض به صورت دوطرفه (bidirectional) باز می‌شوند.

uvtee/main.c - خواندن روی پایپ‌ها

    loop = uv_default_loop();
    uv_pipe_init(loop, &stdin_pipe, 0);
    uv_pipe_open(&stdin_pipe, 0);
    uv_pipe_init(loop, &stdout_pipe, 0);
    uv_pipe_open(&stdout_pipe, 1);
    uv_fs_t file_req;
    int fd = uv_fs_open(loop, &file_req, argv[1], O_CREAT | O_RDWR, 0644, NULL);
    uv_pipe_init(loop, &file_pipe, 0);
    uv_pipe_open(&file_pipe, fd);
    uv_read_start((uv_stream_t*)&stdin_pipe, alloc_buffer, read_stdin);
    uv_run(loop, UV_RUN_DEFAULT);
    return 0;
}

آرگومان سوم uv_pipe_init() برای IPC با استفاده از پایپ‌های نام‌گذاری‌شده باید روی 1 تنظیم شود. این موضوع در بخش فرآیندها <> پوشش داده شده است. فراخوانی uv_pipe_open() پایپ را با توصیف‌کننده فایل مرتبط می‌کند، که در این مورد 0 (ورودی استاندارد) است.

ما شروع به پایش stdin می‌کنیم. کال‌بک alloc_buffer هنگام نیاز به بافرهای جدید برای نگهداری داده‌های ورودی فراخوانی می‌شود. تابع read_stdin با این بافرها فراخوانی خواهد شد.

uvtee/main.c - خواندن بافرها

    *buf = uv_buf_init((char*) malloc(suggested_size), suggested_size);
}
void free_write_req(uv_write_t *req) {
    if (nread < 0){
        if (nread == UV_EOF){
            // end of file
            uv_close((uv_handle_t *)&stdin_pipe, NULL);
            uv_close((uv_handle_t *)&stdout_pipe, NULL);
            uv_close((uv_handle_t *)&file_pipe, NULL);
        }
    } else if (nread > 0) {
        write_data((uv_stream_t *)&stdout_pipe, nread, *buf, on_stdout_write);
        write_data((uv_stream_t *)&file_pipe, nread, *buf, on_file_write);
    }
    // OK to free buffer as write_data copies it.
    if (buf->base)
        free(buf->base);
}

تابع استاندارد malloc در اینجا کافی است، اما می‌توانید از هر روش تخصیص حافظه دیگری استفاده کنید. به عنوان مثال، node.js از تخصیص‌دهنده اسلب (slab allocator) اختصاصی خود استفاده می‌کند که بافرها را با اشیاء V8 مرتبط می‌سازد.

پارامتر nread در کال‌بک خواندن در صورت بروز هرگونه خطا کمتر از 0 است. این خطا ممکن است EOF باشد، که در این حالت تمامی استریم‌ها را با استفاده از تابع عمومی uv_close() که بر اساس نوع داخلی هندل با آن رفتار می‌کند، می‌بندیم. در غیر این صورت nread یک عدد غیرمنفی است و می‌توانیم برای نوشتن آن تعداد بایت در استریم‌های خروجی تلاش کنیم. در نهایت به یاد داشته باشید که تخصیص و آزادسازی بافر بر عهده برنامه است، بنابراین ما داده‌ها را آزاد می‌کنیم.

کال‌بک تخصیص حافظه ممکن است در صورت عدم موفقیت در تخصیص حافظه، بافری با طول صفر برگرداند. در این حالت، کال‌بک خواندن با خطای UV_ENOBUFS فراخوانی می‌شود. با این حال، libuv به تلاش برای خواندن استریم ادامه می‌دهد، بنابراین اگر می‌خواهید با شکست تخصیص متوقف شوید، باید صراحتاً uv_close() را فراخوانی کنید.

کال‌بک خواندن ممکن است با nread = 0 فراخوانی شود که نشان می‌دهد در این لحظه داده‌ای برای خواندن وجود ندارد. اکثر برنامه‌ها به سادگی این وضعیت را نادیده می‌گیرند.

uvtee/main.c - نوشتن روی پایپ

    uv_write_t req;
    uv_buf_t buf;
} write_req_t;
uv_loop_t *loop;
    write_req_t *wr = (write_req_t*) req;
    free(wr->buf.base);
    free(wr);
}
void on_stdout_write(uv_write_t *req, int status) {
    free_write_req(req);
}
void on_file_write(uv_write_t *req, int status) {
    free_write_req(req);
}
void write_data(uv_stream_t *dest, size_t size, uv_buf_t buf, uv_write_cb cb) {
    write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t));
    req->buf = uv_buf_init((char*) malloc(size), size);
    memcpy(req->buf.base, buf.base, size);
    uv_write((uv_write_t*) req, (uv_stream_t*)dest, &req->buf, 1, cb);
}

تابع write_data() یک نسخه کپی از بافر به دست آمده از خواندن تهیه می‌کند. این بافر به کال‌بک نوشتن که پس از اتمام نوشتن فعال می‌شود، منتقل نمی‌شود. برای رفع این مشکل، یک درخواست نوشتن و یک بافر را در write_req_t بسته‌بندی کرده و در کال‌بک‌ها آن را باز می‌کنیم. ما یک کپی تهیه می‌کنیم تا بتوانیم دو بافر حاصل از دو فراخوانی write_data را مستقل از یکدیگر آزاد کنیم. اگرچه این کار برای یک برنامه نمایشی مانند این قابل قبول است، اما در هر برنامه بزرگ احتمالاً به مدیریت حافظه هوشمندانه‌تری مانند بافرهای با شمارش ارجاع یا استخر بافر نیاز خواهید داشت.

هشدار:

اگر برنامه شما برای استفاده در کنار برنامه‌های دیگر طراحی شده است، ممکن است دانسته یا ندانسته در حال نوشتن در یک پایپ باشد. این امر آن را در معرض توقف ناگهانی در صورت دریافت سیگنال SIGPIPE قرار می‌دهد http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_SIGPIPE. ایده خوبی است که دستور زیر را:
signal(SIGPIPE, SIG_IGN)

در مراحل مقداردهی اولیه برنامه خود قرار دهید.

تمامی سیستم‌عامل‌های مدرن APIهایی برای قرار دادن پایشگر روی فایل‌ها یا دایرکتوری‌های مجزا و اطلاع از تغییر فایل‌ها ارائه می‌دهند. کتابخانه libuv کتابخانه‌های رایج اعلان تغییر فایل را بسته‌بندی می‌کند [1]. این یکی از ناهمگون‌ترین بخش‌های libuv است. سیستم‌های اعلان تغییر فایل خود در پلتفرم‌های مختلف بسیار متفاوت هستند، بنابراین اجرای روان همه‌چیز در همه جا دشوار است. برای نمایش، یک ابزار ساده می‌سازیم که هر زمان یکی از فایل‌های پایش‌شده تغییر کرد، یک دستور را اجرا کند:

./onchange <command> <file1> [file2] ...

نکته:

در حال حاضر این مثال فقط در OSX و Windows کار می‌کند. به یادداشت‌های تابع uv_fs_event_start https://docs.libuv.org/en/v1.x/fs_event.html#c.uv_fs_event_start مراجعه کنید.

اعلان تغییر فایل با استفاده از uv_fs_event_init() آغاز می‌شود:

onchange/main.c - آماده‌سازی

int main(int argc, char **argv) {
    if (argc <= 2) {
        fprintf(stderr, "Usage: %s <command> <file1> [file2 ...]\n", argv[0]);
        return 1;
    }
    loop = uv_default_loop();
    command = argv[1];
    while (argc-- > 2) {
        fprintf(stderr, "Adding watch on %s\n", argv[argc]);
        uv_fs_event_t *fs_event_req = malloc(sizeof(uv_fs_event_t));
        uv_fs_event_init(loop, fs_event_req);
        // The recursive flag watches subdirectories too.
        uv_fs_event_start(fs_event_req, run_command, argv[argc], UV_FS_EVENT_RECURSIVE);
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

آرگومان سوم، فایل یا دایرکتوری واقعی برای پایش است. آخرین آرگومان، یعنی flags، می‌تواند مقادیر زیر باشد:

/*
 * Flags to be passed to uv_fs_event_start().
 */
enum uv_fs_event_flags {
    UV_FS_EVENT_WATCH_ENTRY = 1,
    UV_FS_EVENT_STAT = 2,
    UV_FS_EVENT_RECURSIVE = 4
};

پرچم‌های UV_FS_EVENT_WATCH_ENTRY و UV_FS_EVENT_STAT (هنوز) کاری انجام نمی‌دهند. پرچم UV_FS_EVENT_RECURSIVE در پلتفرم‌های پشتیبانی‌شده پایش زیردایرکتوری‌ها را نیز آغاز می‌کند.

کال‌بک آرگومان‌های زیر را دریافت خواهد کرد:

1.
uv_fs_event_t *handle - هندل مربوطه. فیلد path در هندل، همان فایلی است که پایش روی آن تنظیم شده است.
2.
const char *filename - اگر یک دایرکتوری در حال پایش باشد، این همان فایلی است که تغییر کرده است. فقط در لینوکس و ویندوز مقداری غیر از null دارد. حتی در آن پلتفرم‌ها نیز ممکن است null باشد.
3.
int events - یکی از مقادیر UV_RENAME یا UV_CHANGE، یا عملگر بیتی OR از هر دو.
4.
int status - اگر status < 0 باشد، یک خطای libuv رخ داده است <#libuv-error-handling>.

در مثال خود به سادگی آرگومان‌ها را چاپ کرده و با استفاده از system() دستور را اجرا می‌کنیم.

onchange/main.c - کال‌بک اعلان تغییر فایل

void run_command(uv_fs_event_t *handle, const char *filename, int events, int status) {
    char path[1024];
    size_t size = 1023;
    // Does not handle error if path is longer than 1023.
    uv_fs_event_getpath(handle, path, &size);
    path[size] = '\0';
    fprintf(stderr, "Change detected in %s: ", path);
    if (events & UV_RENAME)
        fprintf(stderr, "renamed");
    if (events & UV_CHANGE)
        fprintf(stderr, "changed");
    fprintf(stderr, " %s\n", filename ? filename : "");
    system(command);
}


----



[1]
سیستم inotify در لینوکس، FSEvents در داروین (Darwin)، سازوکار kqueue در سیستم‌های BSD، تابع ReadDirectoryChangesW در ویندوز، درگاه‌های رویداد (event ports) در سولاریس، و بدون پشتیبانی در Cygwin
[2]
به بخش ارتباط والد و فرزند <#pipes> مراجعه کنید

شبکه‌سازی در libuv تفاوت چندانی با استفاده مستقیم از رابط سوکت BSD ندارد؛ برخی موارد ساده‌تر شده‌اند، همه‌چیز غیرمسدودکننده است، اما مفاهیم یکسان باقی می‌مانند. علاوه بر این، libuv توابع کمکی را برای انتزاع وظایف خسته‌کننده، تکراری و سطح پایین مانند راه‌اندازی سوکت‌ها با استفاده از ساختارهای سوکت BSD، جستجوی DNS و تنظیم پارامترهای مختلف سوکت ارائه می‌دهد.

ساختارهای uv_tcp_t و uv_udp_t برای ورودی/خروجی شبکه استفاده می‌شوند.

نکته:

نمونه کدهای این فصل برای نمایش APIهای خاصی از libuv ارائه شده‌اند. آن‌ها نمونه‌هایی از کدهای باکیفیت نیستند. آن‌ها دچار نشت حافظه هستند و اتصالات را همیشه به درستی نمی‌بندند.

پروتکل TCP یک پروتکل مبتنی بر اتصال و جریانی (stream) است و بنابراین بر پایه زیرساخت استریم‌های libuv بنا شده است.

سوکت‌های سرور مراحل زیر را طی می‌کنند:

1.
هندل TCP را با uv_tcp_init مقداردهی اولیه کنید.
2.
آن را با uv_tcp_bind متصل (bind) کنید.
3.
تابع uv_listen را روی هندل فراخوانی کنید تا هر زمان اتصال جدیدی توسط کلاینت برقرار شد، یک کال‌بک فراخوانی شود.
4.
از uv_accept برای پذیرش اتصال استفاده کنید.
5.
از عملیات‌های استریم <#buffers-and-streams> برای برقراری ارتباط با کلاینت استفاده کنید.

در اینجا یک سرور انعکاس (echo server) ساده آورده شده است:

tcp-echo-server/main.c - سوکت شنود

        uv_close((uv_handle_t*) client, on_close);
    }
}
int main() {
    loop = uv_default_loop();
    uv_tcp_t server;
    uv_tcp_init(loop, &server);
    uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);
    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
    int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection);
    if (r) {
        fprintf(stderr, "Listen error %s\n", uv_strerror(r));
        return 1;
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

می‌توانید مشاهده کنید که تابع کمکی uv_ip4_addr برای تبدیل یک جفت آدرس IP و پورت خوانا به ساختار sockaddr_in مورد نیاز APIهای سوکت BSD استفاده شده است. عملیات معکوس را می‌توان با استفاده از uv_ip4_name انجام داد.

نکته:

توابع معادل uv_ip6_* برای توابع ip4 نیز وجود دارند.

اکثر توابع راه‌اندازی همگام هستند زیرا وابسته به پردازنده (CPU-bound) می‌باشند. در تابع uv_listen است که به سبک مبتنی بر کال‌بک libuv بازمی‌گردیم. آرگومان دوم صف backlog است -- حداکثر طول اتصالات در صف انتظار.

هنگامی که اتصالی توسط کلاینت‌ها آغاز می‌شود، کال‌بک موظف است یک هندل برای سوکت کلاینت تنظیم کند و هندل را با استفاده از uv_accept مرتبط سازد. در این حالت ما همچنین تمایل خود را برای خواندن از این استریم اعلام می‌کنیم.

tcp-echo-server/main.c - پذیرش کلاینت

    free(buf->base);
}
void on_new_connection(uv_stream_t *server, int status) {
    if (status < 0) {
        fprintf(stderr, "New connection error %s\n", uv_strerror(status));
        // error!
        return;
    }
    uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, client);
    if (uv_accept(server, (uv_stream_t*) client) == 0) {
        uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
    }

مجموعه توابع باقیمانده بسیار شبیه به مثال استریم‌ها است و می‌توان آن را در کد پیدا کرد. فقط به یاد داشته باشید هنگامی که سوکت دیگر مورد نیاز نیست، uv_close را فراخوانی کنید. این کار را حتی می‌توان در کال‌بک uv_listen انجام داد اگر تمایلی به پذیرش اتصال ندارید.

در حالی که در سمت سرور عملیات bind/listen/accept را انجام می‌دهید، در سمت کلاینت کافی است تابع uv_tcp_connect را فراخوانی کنید. همان سبک کال‌بک uv_connect_cb در uv_listen توسط uv_tcp_connect نیز استفاده می‌شود. کد زیر را امتحان کنید:

uv_tcp_t* socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
uv_tcp_init(loop, socket);
uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));
struct sockaddr_in dest;
uv_ip4_addr("127.0.0.1", 80, &dest);
uv_tcp_connect(connect, socket, (const struct sockaddr*)&dest, on_connect);

که در آن on_connect پس از برقراری اتصال فراخوانی خواهد شد. کال‌بک ساختار uv_connect_t را دریافت می‌کند که دارای عضوی به نام .handle است که به سوکت اشاره دارد.

پروتکل دیتاگرام کاربر (User Datagram Protocol) https://en.wikipedia.org/wiki/User_Datagram_Protocol ارتباط شبکه‌ای بدون اتصال و غیرقابل اعتمادی را ارائه می‌دهد. بنابراین libuv برای آن یک استریم ارائه نمی‌دهد. در عوض libuv پشتیبانی غیرمسدودکننده UDP را از طریق هندل uv_udp_t (برای دریافت) و درخواست uv_udp_send_t (برای ارسال) و توابع مربوطه فراهم می‌کند. با این حال، رابط واقعی برای خواندن/نوشتن بسیار شبیه به خواندن عادی از استریم است. برای بررسی نحوه استفاده از UDP، مثال زیر اولین مرحله دریافت آدرس IP از سرور DHCP https://tools.ietf.org/html/rfc2131 را نشان می‌دهد -- یعنی مرحله DHCP Discover.

نکته:

شما باید برنامه udp-dhcp را به عنوان root اجرا کنید زیرا از شماره پورت‌های شناخته‌شده زیر 1024 استفاده می‌کند.

udp-dhcp/main.c - آماده‌سازی و ارسال بسته‌های UDP

uv_loop_t *loop;
uv_udp_t send_socket;
uv_udp_t recv_socket;
int main() {
    loop = uv_default_loop();
    uv_udp_init(loop, &recv_socket);
    struct sockaddr_in recv_addr;
    uv_ip4_addr("0.0.0.0", 68, &recv_addr);
    uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
    uv_udp_recv_start(&recv_socket, alloc_buffer, on_read);
    uv_udp_init(loop, &send_socket);
    struct sockaddr_in broadcast_addr;
    uv_ip4_addr("0.0.0.0", 0, &broadcast_addr);
    uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0);
    uv_udp_set_broadcast(&send_socket, 1);
    uv_udp_send_t send_req;
    uv_buf_t discover_msg = make_discover_msg();
    struct sockaddr_in send_addr;
    uv_ip4_addr("255.255.255.255", 67, &send_addr);
    uv_udp_send(&send_req, &send_socket, &discover_msg, 1, (const struct sockaddr *)&send_addr, on_send);
    return uv_run(loop, UV_RUN_DEFAULT);
}

نکته:

آدرس IP با مقدار 0.0.0.0 برای اتصال به تمام رابط‌ها استفاده می‌شود. آدرس IP با مقدار 255.255.255.255 یک آدرس برودکست (پخش عمومی) است، به این معنی که بسته‌ها به تمام رابط‌های موجود در زیرشبکه ارسال خواهند شد. پورت 0 به این معنی است که سیستم‌عامل یک پورت را به طور تصادفی اختصاص می‌دهد.

ابتدا سوکت دریافت‌کننده را تنظیم می‌کنیم تا به تمام رابط‌ها روی پورت 68 (کلاینت DHCP) متصل شود و خواندن روی آن را آغاز می‌کنیم. این کار پاسخ‌های دریافتی از هر سرور DHCP که پاسخ دهد را می‌خواند. ما از پرچم UV_UDP_REUSEADDR استفاده می‌کنیم تا با سایر کلاینت‌های DHCP سیستم که روی همین پورت در رایانه اجرا می‌شوند تداخلی نداشته باشد. سپس یک سوکت ارسال مشابه را تنظیم کرده و از uv_udp_send برای ارسال یک پیام برودکست روی پورت 67 (سرور DHCP) استفاده می‌کنیم.

تنظیم پرچم برودکست الزامی است، در غیر این صورت با خطای EACCES مواجه خواهید شد [1]. پیام دقیقی که ارسال می‌شود ارتباطی به این کتاب ندارد و در صورت تمایل می‌توانید کد را مطالعه کنید. طبق معمول، کال‌بک‌های خواندن و نوشتن در صورت بروز مشکل، یک کد وضعیت کمتر از 0 دریافت خواهند کرد.

از آنجا که سوکت‌های UDP به یک طرف ارتباطی (peer) خاص متصل نیستند، کال‌بک خواندن یک پارامتر اضافی درباره فرستنده بسته دریافت می‌کند.

پارامتر nread در صورتی که داده بیشتری برای خواندن وجود نداشته باشد ممکن است صفر باشد. اگر addr برابر با NULL باشد، نشان می‌دهد چیزی برای خواندن وجود ندارد (کال‌بک نباید کاری انجام دهد)، و اگر NULL نباشد، نشان می‌دهد که یک دیتاگرام خالی از میزبان در addr دریافت شده است. پارامتر flags در صورتی که بافر ارائه‌شده توسط تخصیص‌دهنده شما به اندازه کافی بزرگ نباشد تا داده‌ها را در خود جای دهد، ممکن است UV_UDP_PARTIAL باشد. در این حالت، سیستم‌عامل داده‌هایی را که در بافر جا نشده‌اند دور می‌ریزد (این خاصیت UDP است!).

udp-dhcp/main.c - خواندن بسته‌ها

void on_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
    if (nread < 0) {
        fprintf(stderr, "Read error %s\n", uv_err_name(nread));
        uv_close((uv_handle_t*) req, NULL);
        free(buf->base);
        return;
    }
    char sender[17] = { 0 };
    uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
    fprintf(stderr, "Recv from %s\n", sender);
    // ... DHCP specific code
    unsigned int *as_integer = (unsigned int*)buf->base;
    unsigned int ipbin = ntohl(as_integer[4]);
    unsigned char ip[4] = {0};
    int i;
    for (i = 0; i < 4; i++)
        ip[i] = (ipbin >> i*8) & 0xff;
    fprintf(stderr, "Offered IP %d.%d.%d.%d\n", ip[3], ip[2], ip[1], ip[0]);
    free(buf->base);
    uv_udp_recv_stop(req);
}

مقدار TTL بسته‌های ارسال‌شده روی سوکت را می‌توان با استفاده از uv_udp_set_ttl تغییر داد.

سوکت‌های IPv6 را می‌توان هم برای ارتباط IPv4 و هم IPv6 استفاده کرد. اگر می‌خواهید سوکت را فقط به IPv6 محدود کنید، پرچم UV_UDP_IPV6ONLY را به uv_udp_bind ارسال کنید.

یک سوکت می‌تواند با استفاده از دستور زیر در یک گروه چندپخشی (multicast) عضو شده یا از آن خارج شود:

که در آن membership برابر با UV_JOIN_GROUP یا UV_LEAVE_GROUP است.

مفاهیم چندپخشی به خوبی در این راهنما https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html توضیح داده شده است.

لوپ‌بک محلی بسته‌های چندپخشی به طور پیش‌فرض فعال است [2]، برای غیرفعال کردن آن از uv_udp_set_multicast_loop استفاده کنید.

طول عمر بسته برای بسته‌های چندپخشی را می‌توان با استفاده از uv_udp_set_multicast_ttl تغییر داد.

کتابخانه libuv امکان تحلیل و تفکیک نام ناهمگام DNS را فراهم می‌کند. برای این منظور، جایگزین اختصاصی getaddrinfo را ارائه می‌دهد [3]. در کال‌بک می‌توانید عملیات معمول سوکت را روی آدرس‌های دریافت شده انجام دهید. بیایید برای مشاهده نمونه‌ای از تحلیل DNS به Libera.chat متصل شویم.

dns/main.c

int main() {
    loop = uv_default_loop();
    struct addrinfo hints;
    hints.ai_family = PF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = 0;
    uv_getaddrinfo_t resolver;
    fprintf(stderr, "irc.libera.chat is... ");
    int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.libera.chat", "6667", &hints);
    if (r) {
        fprintf(stderr, "getaddrinfo call error %s\n", uv_err_name(r));
        return 1;
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

اگر uv_getaddrinfo مقداری غیر از صفر برگرداند، مشکلی در راه‌اندازی رخ داده و کال‌بک شما اصلاً فراخوانی نخواهد شد. تمامی آرگومان‌ها را می‌توان بلافاصله پس از بازگشت uv_getaddrinfo آزاد کرد. ساختارهای hostname، servname و hints در صفحه راهنمای getaddrinfo https://man7.org/linux/man-pages/man3/getaddrinfo.3.html مستند شده‌اند. کال‌بک می‌تواند NULL باشد که در این صورت تابع به صورت همگام اجرا خواهد شد.

در کال‌بک تحلیل‌گر، می‌توانید هر IP را از فهرست پیوندی ساختارهای struct addrinfo(s) انتخاب کنید. این مثال همچنین نحوه استفاده از uv_tcp_connect را نشان می‌دهد. فراخوانی uv_freeaddrinfo در کال‌بک الزامی است.

dns/main.c

void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) {
    if (status < 0) {
        fprintf(stderr, "getaddrinfo callback error %s\n", uv_err_name(status));
        return;
    }
    char addr[17] = {'\0'};
    uv_ip4_name((struct sockaddr_in*) res->ai_addr, addr, 16);
    fprintf(stderr, "%s\n", addr);
    uv_connect_t *connect_req = (uv_connect_t*) malloc(sizeof(uv_connect_t));
    uv_tcp_t *socket = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, socket);
    uv_tcp_connect(connect_req, socket, (const struct sockaddr*) res->ai_addr, on_connect);
    uv_freeaddrinfo(res);
}

کتابخانه libuv همچنین تابع معکوس uv_getnameinfo http://docs.libuv.org/en/v1.x/dns.html#c.uv_getnameinfo را ارائه می‌دهد.

اطلاعات مربوط به رابط‌های شبکه سیستم را می‌توان از طریق libuv با استفاده از uv_interface_addresses به دست آورد. این برنامه ساده فقط تمام جزئیات رابط‌ها را چاپ می‌کند تا با فیلدهای موجود آشنا شوید. این ویژگی برای اینکه به سرویس خود اجازه دهید هنگام شروع به آدرس‌های IP متصل شود مفید است.

interfaces/main.c

#include <stdio.h>
#include <uv.h>
int main() {
    char buf[512];
    uv_interface_address_t *info;
    int count, i;
    uv_interface_addresses(&info, &count);
    i = count;
    printf("Number of interfaces: %d\n", count);
    while (i--) {
        uv_interface_address_t interface_a = info[i];
        printf("Name: %s\n", interface_a.name);
        printf("Internal? %s\n", interface_a.is_internal ? "Yes" : "No");
        if (interface_a.address.address4.sin_family == AF_INET) {
            uv_ip4_name(&interface_a.address.address4, buf, sizeof(buf));
            printf("IPv4 address: %s\n", buf);
        }
        else if (interface_a.address.address4.sin_family == AF_INET6) {
            uv_ip6_name(&interface_a.address.address6, buf, sizeof(buf));
            printf("IPv6 address: %s\n", buf);
        }
        printf("\n");
    }
    uv_free_interface_addresses(info, count);
    return 0;
}

مقدار is_internal برای رابط‌های لوپ‌بک (loopback) برابر با true است. توجه داشته باشید که اگر یک رابط فیزیکی دارای چندین آدرس IPv4/IPv6 باشد، نام آن چندین بار گزارش می‌شود و هر بار یکی از آدرس‌ها گزارش خواهد شد.


----



[1]
https://beej.us/guide/bgnet/html/#broadcast-packetshello-world
[2]
https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html#ss6.1
[3]
کتابخانه libuv از تابع سیستم getaddrinfo در استخر نخ libuv استفاده می‌کند. نسخه v0.8.0 و نسخه‌های قدیمی‌تر libuv همچنین شامل c-ares https://c-ares.haxx.se به عنوان یک جایگزین بودند، اما این مورد در v0.9.0 حذف شده است.

یک لحظه صبر کنید! چرا سراغ رشته‌ها رفتیم؟ مگر قرار نبود حلقه‌های رویداد روش اصلی برای برنامه‌نویسی مقیاس وب (web-scale programming) باشند؟ خوب... خیر. رشته‌ها همچنان بستری هستند که پردازنده‌ها کارهای خود را در آن انجام می‌دهند. بنابراین رشته‌ها گاهی بسیار مفید هستند، حتی اگر مجبور باشید با سازوکارهای گوناگون همگام‌سازی دست‌وپنجه نرم کنید.

رشته‌ها در داخل سیستم برای شبیه‌سازی ماهیت غیرهمگام تمام فراخوان‌های سیستمی استفاده می‌شوند. libuv همچنین با اجرای یک رشته و جمع‌آوری نتیجه پس از پایان کار، به شما (برنامه کاربردی) اجازه می‌دهد وظایفی را که در واقع مسدودکننده (blocking) هستند، به صورت غیرهمگام انجام دهید.

امروزه دو کتابخانه اصلی رشته وجود دارد: پیاده‌سازی رشته‌های ویندوز و pthreads(7) در POSIX https://man7.org/linux/man-pages/man7/pthreads.7.html. رابط برنامه‌نویسی رشته در libuv مشابه API مربوط به pthreads است و اغلب معناشناسی یکسانی دارد.

یک جنبه قابل توجه از امکانات رشته libuv این است که بخشی مستقل در داخل libuv محسوب می‌شود. در حالی که سایر قابلیت‌ها وابستگی نزدیکی به حلقه رویداد و اصول بازخوانی (callback) دارند، رشته‌ها کاملاً مستقل عمل می‌کنند؛ در صورت نیاز مسدود می‌شوند، خطاها را مستقیماً از طریق مقادیر بازگشتی اعلام می‌کنند و همان‌طور که در اولین مثال نشان داده شده است، حتی نیازی به یک حلقه رویداد در حال اجرا ندارند.

رابط برنامه‌نویسی رشته libuv همچنین بسیار محدود است، زیرا معناشناسی و نحو رشته‌ها در پلتفرم‌های مختلف متفاوت بوده و سطوح متفاوتی از کامل بودن را دارند.

این بخش فرض زیر را در نظر می‌گیرد: تنها یک حلقه رویداد وجود دارد که در یک رشته (رشته اصلی) اجرا می‌شود. هیچ رشته دیگری با حلقه رویداد تعامل ندارد (مگر با استفاده از uv_async_send).

در اینجا پیچیدگی خاصی وجود ندارد، فقط یک رشته را با استفاده از uv_thread_create() شروع می‌کنید و با uv_thread_join() منتظر بسته‌شدن آن می‌مانید.

thread-create/main.c

    int tracklen = 10;
    uv_thread_t hare_id;
    uv_thread_t tortoise_id;
    uv_thread_create(&hare_id, hare, &tracklen);
    uv_thread_create(&tortoise_id, tortoise, &tracklen);
    uv_thread_join(&hare_id);
    uv_thread_join(&tortoise_id);
    return 0;
}

نکته:

شناسه uv_thread_t در یونیکس صرفاً یک نام مستعار برای pthread_t است، اما این یک جزئیات پیاده‌سازی است و نباید همیشه به آن وابسته باشید.

پارامتر دوم تابعی است که به عنوان نقطه ورود رشته عمل می‌کند و پارامتر آخر یک آرگومان void * است که می‌توان از آن برای ارسال پارامترهای سفارشی به رشته استفاده کرد. تابع hare اکنون در یک رشته جداگانه اجرا می‌شود و زمان‌بندی آن توسط سیستم‌عامل به صورت پیش‌دستانه (pre-emptively) انجام می‌گیرد:

thread-create/main.c

    int tracklen = *((int *) arg);
    while (tracklen) {
        tracklen--;
        uv_sleep(1000);
        fprintf(stderr, "Hare ran another step\n");
    }
    fprintf(stderr, "Hare done running!\n");
}

برخلاف pthread_join() که به رشته هدف اجازه می‌دهد با استفاده از پارامتر دوم یک مقدار را به رشته فراخوان بازگرداند، uv_thread_join() این کار را انجام نمی‌دهد. برای ارسال مقادیر از بخش ارتباط بین‌رشته‌ای استفاده کنید.

این بخش عمداً مختصر نوشته شده است. این مستند درباره رشته‌ها نیست، بنابراین فقط موارد غیرمنتظره در رابط‌های libuv فهرست شده‌اند. برای سایر موارد می‌توانید به صفحات راهنمای pthreads(7) https://man7.org/linux/man-pages/man7/pthreads.7.html مراجعه کنید.

توابع موتکس نگاشت مستقیمی به معادل‌های pthread خود دارند.

توابع موتکس libuv

int uv_mutex_init(uv_mutex_t* handle);
int uv_mutex_init_recursive(uv_mutex_t* handle);
void uv_mutex_destroy(uv_mutex_t* handle);
void uv_mutex_lock(uv_mutex_t* handle);
int uv_mutex_trylock(uv_mutex_t* handle);
void uv_mutex_unlock(uv_mutex_t* handle);

توابع uv_mutex_init()، uv_mutex_init_recursive() و uv_mutex_trylock() در صورت موفقیت 0 و در غیر این صورت یک کد خطا بازمی‌گردانند.

اگر libuv با فعال بودن خطایابی کامپایل شده باشد، توابع uv_mutex_destroy()، uv_mutex_lock() و uv_mutex_unlock() در صورت بروز خطا تابع abort() را فراخوانی می‌کنند. به طور مشابه، uv_mutex_trylock() در صورتی که خطا چیزی به‌جز EAGAIN یا EBUSY باشد برنامه را متوقف می‌کند.

موتکس‌های بازگشتی (Recursive mutexes) پشتیبانی می‌شوند، اما نباید به آن‌ها وابسته باشید. همچنین، نباید از آن‌ها با متغیرهای uv_cond_t استفاده کرد.

پیاده‌سازی پیش‌فرض موتکس BSD اگر رشته‌ای که یک موتکس را قفل کرده دوباره تلاش کند آن را قفل کند، خطا ایجاد می‌کند. به عنوان مثال، ساختاری مانند:

uv_mutex_init(a_mutex);
uv_mutex_lock(a_mutex);
uv_thread_create(thread_id, entry, (void *)a_mutex);
uv_mutex_lock(a_mutex);
// more things here

می‌تواند برای انتظار تا زمانی که رشته دیگر مواردی را مقداردهی کرده و سپس a_mutex را باز کند استفاده شود، اما در حالت اشکال‌زدایی (debug) باعث کرش برنامه شده یا در فراخوانی دوم uv_mutex_lock() خطا بازمی‌گرداند.

نکته:

موتکس‌ها در ویندوز همیشه بازگشتی هستند.

قفل‌های خواندن-نوشتن (Read-write locks) سازوکار دسترسی دقیق‌تری هستند. دو خواننده می‌توانند همزمان به حافظه مشترک دسترسی داشته باشند. یک نویسنده نمی‌تواند قفلی را که در اختیار یک خواننده است دریافت کند. یک خواننده یا نویسنده نمی‌تواند قفلی را که در اختیار یک نویسنده است دریافت کند. قفل‌های خواندن-نوشتن مکرراً در پایگاه‌های داده استفاده می‌شوند. در اینجا یک مثال ساده آورده شده است.

locks/main.c - قفل‌های rwlock ساده

#include <stdio.h>
#include <uv.h>
uv_barrier_t blocker;
uv_rwlock_t numlock;
int shared_num;
void reader(void *n)
{
    int num = *(int *)n;
    int i;
    for (i = 0; i < 20; i++) {
        uv_rwlock_rdlock(&numlock);
        printf("Reader %d: acquired lock\n", num);
        printf("Reader %d: shared num = %d\n", num, shared_num);
        uv_rwlock_rdunlock(&numlock);
        printf("Reader %d: released lock\n", num);
    }
    uv_barrier_wait(&blocker);
}
void writer(void *n)
{
    int num = *(int *)n;
    int i;
    for (i = 0; i < 20; i++) {
        uv_rwlock_wrlock(&numlock);
        printf("Writer %d: acquired lock\n", num);
        shared_num++;
        printf("Writer %d: incremented shared num = %d\n", num, shared_num);
        uv_rwlock_wrunlock(&numlock);
        printf("Writer %d: released lock\n", num);
    }
    uv_barrier_wait(&blocker);
}
int main()
{
    uv_barrier_init(&blocker, 4);
    shared_num = 0;
    uv_rwlock_init(&numlock);
    uv_thread_t threads[3];
    int thread_nums[] = {1, 2, 1};
    uv_thread_create(&threads[0], reader, &thread_nums[0]);
    uv_thread_create(&threads[1], reader, &thread_nums[1]);
    uv_thread_create(&threads[2], writer, &thread_nums[2]);
    uv_barrier_wait(&blocker);
    uv_barrier_destroy(&blocker);
    uv_rwlock_destroy(&numlock);
    return 0;
}

این برنامه را اجرا کنید و ببینید چگونه خواننده‌ها گاهی با یکدیگر همپوشانی دارند. در صورت وجود چندین نویسنده، زمان‌بندها معمولاً اولویت بالاتری به آن‌ها می‌دهند، بنابراین اگر دو نویسنده اضافه کنید، خواهید دید که هر دو نویسنده تمایل دارند ابتدا کار خود را به پایان برسانند قبل از اینکه دوباره به خواننده‌ها فرصتی داده شود.

ما همچنین در مثال بالا از موانع (barriers) استفاده می‌کنیم تا رشته اصلی بتواند منتظر بماند تا همه خواننده‌ها و نویسنده‌ها اتمام کار خود را اعلام کنند.

کتابخانه libuv همچنین از سمافورها (semaphores) <https://en.wikipedia.org/wiki/Semaphore_(programming)>، متغیرهای شرطی (condition variables) https://en.wikipedia.org/wiki/Monitor_(synchronization)#Condition_variables_2 و موانع (barriers) <https://en.wikipedia.org/wiki/Barrier_(computer_science)> با رابط‌هایی بسیار مشابه معادل‌های pthread پشتیبانی می‌کند.

علاوه بر این، libuv تابع کمکی uv_once() را ارائه می‌دهد. چندین رشته می‌توانند با یک گارد مشخص و یک اشاره‌گر تابع اقدام به فراخوانی uv_once() کنند؛ فقط اولین فراخوانی برنده خواهد شد و تابع تنها و تنها یک بار فراخوانی می‌شود:

/* Initialize guard */
static uv_once_t once_only = UV_ONCE_INIT;
int i = 0;
void increment() {
    i++;
}
void thread1() {
    /* ... work */
    uv_once(once_only, increment);
}
void thread2() {
    /* ... work */
    uv_once(once_only, increment);
}
int main() {
    /* ... spawn threads */
}

پس از اتمام تمام رشته‌ها، مقدار i == 1 خواهد بود.

از نسخه v0.11.11 به بعد libuv ساختار و API جدیدی به نام uv_key_t http://docs.libuv.org/en/v1.x/threading.html#thread-local-storage برای حافظه محلی رشته (thread-local storage) اضافه کرده است.

تابع uv_queue_work() یک تابع کمکی است که به برنامه اجازه می‌دهد وظیفه‌ای را در یک رشته جداگانه اجرا کند و پس از اتمام کار، بازخوانی مربوطه فراخوانی شود. یک تابع به ظاهر ساده، آنچه uv_queue_work() را جذاب می‌کند این است که اجازه می‌دهد به طور بالقوه هر کتابخانه شخص ثالثی با الگوی حلقه رویداد استفاده شود. وقتی از حلقه‌های رویداد استفاده می‌کنید، ضروری است مطمئن شوید هیچ تابعی که به صورت دوره‌ای در رشته حلقه اجرا می‌شود، هنگام انجام I/O مسدود نشده و مصرف سنگین CPU نداشته باشد، زیرا این امر باعث کند شدن حلقه شده و رویدادها با حداکثر ظرفیت پردازش نمی‌شوند.

با این حال، بسیاری از کدهای موجود دارای توابع مسدودکننده هستند (برای مثال روالی که در پس‌زمینه I/O انجام می‌دهد) که در صورت تمایل به پاسخ‌گویی سریع باید با رشته‌ها استفاده شوند (مدل سرور کلاسیک «یک رشته به ازای هر کلاینت»)، و هماهنگ کردن آن‌ها با یک کتابخانه حلقه رویداد معمولاً نیازمند پیاده‌سازی سیستم اختصاصی برای اجرای وظیفه در رشته‌ای جداگانه است. libuv یک انتزاع مناسب برای این منظور ارائه می‌دهد.

در اینجا یک مثال ساده با الهام از node.js is cancer http://widgetsandshit.com/teddziuba/2011/10/node-js-is-cancer.html آورده شده است. ما می‌خواهیم اعداد فیبوناچی را محاسبه کنیم، در این مسیر کمی توقف (sleep) داشته باشیم، اما آن را در یک رشته جداگانه اجرا کنیم تا وظیفه مسدودکننده و وابسته به پردازنده مانع انجام سایر فعالیت‌های حلقه رویداد نشود.

queue-work/main.c - فیبوناچی تنبل

void fib(uv_work_t *req) {
    int n = *(int *) req->data;
    if (random() % 2)
        sleep(1);
    else
        sleep(3);
    long fib = fib_(n);
    fprintf(stderr, "%dth fibonacci is %lu\n", n, fib);
}
void after_fib(uv_work_t *req, int status) {
    fprintf(stderr, "Done calculating %dth fibonacci\n", *(int *) req->data);
}

تابع وظیفه واقعی ساده است و هیچ نشانه‌ای از اجرای آن در یک رشته جداگانه وجود ندارد. ساختار uv_work_t کلید این کار است. می‌توانید داده‌های دلخواه را از طریق فیلد void* data به آن ارسال کنید و از آن برای ارتباط به رشته و از رشته استفاده کنید. اما مطمئن شوید در صورتی که داده‌ها هنگام اجرای همزمان هر دو رشته تغییر می‌کنند، از قفل‌های مناسب استفاده می‌کنید.

محرک اجرا uv_queue_work است:

queue-work/main.c

int main() {
    loop = uv_default_loop();
    int data[FIB_UNTIL];
    uv_work_t req[FIB_UNTIL];
    int i;
    for (i = 0; i < FIB_UNTIL; i++) {
        data[i] = i;
        req[i].data = (void *) &data[i];
        uv_queue_work(loop, &req[i], fib, after_fib);
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

تابع رشته در یک رشته جداگانه راه‌اندازی شده و ساختار uv_work_t به آن ارسال می‌شود و پس از بازگشت تابع، تابع after در رشته‌ای که حلقه رویداد در آن اجرا می‌شود فراخوانی خواهد شد. همان ساختار به آن ارسال می‌شود.

برای نوشتن رَپرها (wrappers) برای کتابخانه‌های مسدودکننده، یک الگوی متداول <#baton> استفاده از باتوم (baton) برای تبادل داده است.

از نسخه 0.9.4 کتابخانه libuv یک تابع اضافی به نام uv_cancel() در دسترس قرار گرفته است. این تابع به شما امکان می‌دهد وظایف موجود در صف کار libuv را لغو کنید. فقط وظایفی که هنوز شروع نشده‌اند قابل لغو هستند. اگر وظیفه‌ای قبلاً اجرای خود را آغاز کرده باشد یا اجرای آن پایان یافته باشد، تابع uv_cancel() شکست خواهد خورد.

تابع uv_cancel() برای پاک‌سازی وظایف در انتظار در صورت درخواست پایان برنامه توسط کاربر بسیار مفید است. به عنوان مثال، یک پخش‌کننده موسیقی ممکن است چندین پوشه را برای اسکن فایل‌های صوتی در صف قرار دهد. اگر کاربر برنامه را متوقف کند، برنامه باید فوراً خارج شود و منتظر اجرای تمام درخواست‌های در صف نماند.

بیایید مثال فیبوناچی را برای نمایش عملکرد uv_cancel() تغییر دهیم. ابتدا یک گرداننده سیگنال برای خاتمه برنامه تنظیم می‌کنیم.

queue-cancel/main.c

int main() {
    loop = uv_default_loop();
    int data[FIB_UNTIL];
    int i;
    for (i = 0; i < FIB_UNTIL; i++) {
        data[i] = i;
        fib_reqs[i].data = (void *) &data[i];
        uv_queue_work(loop, &fib_reqs[i], fib, after_fib);
    }
    uv_signal_t sig;
    uv_signal_init(loop, &sig);
    uv_signal_start(&sig, signal_handler, SIGINT);
    return uv_run(loop, UV_RUN_DEFAULT);
}

هنگامی که کاربر با فشردن کلیدهای Ctrl+C سیگنال را ارسال می‌کند، ما uv_cancel() را به تمام کارگران (workers) ارسال می‌کنیم. تابع uv_cancel() برای وظایفی که در انتظار اجرا هستند و لغو شده‌اند مقدار 0 را بازمی‌گرداند.

queue-cancel/main.c

void signal_handler(uv_signal_t *req, int signum)
{
    printf("Signal received!\n");
    int i;
    for (i = 0; i < FIB_UNTIL; i++) {
        uv_cancel((uv_req_t*) &fib_reqs[i]);
    }
    uv_signal_stop(req);
}

برای وظایفی که با موفقیت لغو می‌شوند، تابع after با مقدار status برابر با UV_ECANCELED فراخوانی می‌شود.

queue-cancel/main.c

void after_fib(uv_work_t *req, int status) {
    if (status == UV_ECANCELED)
        fprintf(stderr, "Calculation of %d cancelled.\n", *(int *) req->data);
}

تابع uv_cancel() همچنین می‌تواند با درخواست‌های uv_fs_t و uv_getaddrinfo_t استفاده شود. برای خانواده توابع سیستم فایل، مقدار uv_fs_t.errorno روی UV_ECANCELED تنظیم خواهد شد.

نکته:

یک برنامه با طراحی مناسب روشی برای پایان دادن به کارگرانی که مدت زیادی در حال اجرا بوده‌اند و اجرای آن‌ها از قبل آغاز شده است خواهد داشت. چنین کارگری می‌تواند به صورت دوره‌ای متغیری را بررسی کند که فقط فرایند اصلی برای اعلام پایان تنظیم می‌کند.

گاهی اوقات می‌خواهید رشته‌های مختلف هنگام اجرا پیام‌هایی برای یکدیگر ارسال کنند. برای مثال ممکن است در حال اجرای یک وظیفه طولانی در یک رشته جداگانه باشید (شاید با استفاده از uv_queue_work) اما بخواهید پیشرفت کار را به رشته اصلی اطلاع دهید. این یک مثال ساده از یک مدیر دانلود است که وضعیت دانلودهای در حال اجرا را به کاربر اطلاع می‌دهد.

progress/main.c

uv_loop_t *loop;
uv_async_t async;
int main() {
    loop = uv_default_loop();
    uv_work_t req;
    int size = 10240;
    req.data = (void*) &size;
    uv_async_init(loop, &async, print_progress);
    uv_queue_work(loop, &req, fake_download, after);
    return uv_run(loop, UV_RUN_DEFAULT);
}

ارتباط رشته‌ای غیرهمگام روی حلقه‌ها کار می‌کند، بنابراین با اینکه هر رشته‌ای می‌تواند فرستنده پیام باشد، فقط رشته‌هایی که دارای حلقه‌های libuv هستند می‌توانند گیرنده باشند (یا بهتر بگوییم، خود حلقه گیرنده است). libuv هر زمان پیامی دریافت کند، بازخوانی (print_progress) را با پایشگر ناهمگام فراخوانی می‌کند.

هشدار:

بسیار مهم است بدانید از آنجا که ارسال پیام غیرهمگام است، بازخوانی ممکن است بلافاصله پس از فراخوانی uv_async_send در رشته دیگر اجرا شود، یا ممکن است پس از گذشت مدتی فراخوانی گردد. همچنین ممکن است libuv چندین فراخوانی به uv_async_send را ترکیب کند و بازخوانی شما را فقط یک بار فراخوانی نماید. تنها تضمینی که libuv می‌دهد این است: تابع بازخوانی حداقل یک بار پس از فراخوانی uv_async_send فراخوانی می‌شود. اگر هیچ فراخوانی معوقی به uv_async_send نداشته باشید، بازخوانی اجرا نخواهد شد. اگر دو یا چند فراخوانی انجام دهید و libuv هنوز فرصت اجرای بازخوانی را پیدا نکرده باشد، ممکن است بازخوانی شما را برای چندین فراخوانی uv_async_send فقط یک بار اجرا کند. بازخوانی شما هرگز دو بار برای یک رویداد اجرا نخواهد شد.

progress/main.c

double percentage;
void fake_download(uv_work_t *req) {
    int size = *((int*) req->data);
    int downloaded = 0;
    while (downloaded < size) {
        percentage = downloaded*100.0/size;
        async.data = (void*) &percentage;
        uv_async_send(&async);
        sleep(1);
        downloaded += (200+random())%1000; // can only download max 1000bytes/sec,
                                           // but at least a 200;
    }
}

در تابع دانلود، ما شاخص پیشرفت را تغییر می‌دهیم و پیام را برای تحویل با uv_async_send در صف قرار می‌دهیم. به یاد داشته باشید: uv_async_send نیز غیرمسدودکننده است و فوراً بازمی‌گردد.

progress/main.c

void print_progress(uv_async_t *handle) {
    double percentage = *((double*) handle->data);
    fprintf(stderr, "Downloaded %.2f%%\n", percentage);
}

تابع بازخوانی یک الگوی استاندارد در libuv است که داده‌ها را از پایشگر استخراج می‌کند.

در نهایت، بسیار مهم است که پاک‌سازی پایشگر را فراموش نکنید.

progress/main.c

void after(uv_work_t *req, int status) {
    fprintf(stderr, "Download complete\n");
    uv_close((uv_handle_t*) &async, NULL);
}

پس از این مثال که استفاده نادرست از فیلد data را نشان داد، bnoordhuis https://github.com/bnoordhuis اشاره کرد که استفاده از فیلد data امن در برابر رشته‌ها (thread safe) نیست و uv_async_send() در واقع فقط برای بیدار کردن حلقه رویداد در نظر گرفته شده است. از یک موتکس یا rwlock استفاده کنید تا مطمئن شوید دسترسی‌ها با ترتیب صحیح انجام می‌شوند.

نکته:

موتکس‌ها و rwlockها در داخل گرداننده سیگنال کار نمی‌کنند، در حالی که uv_async_send کار می‌کند.

یک مورد استفاده که در آن uv_async_send الزامی است، زمان تعامل با کتابخانه‌هایی است که برای عملکرد خود به پیوستگی رشته‌ای (thread affinity) نیاز دارند. برای مثال در node.js، یک نمونه موتور v8، کانتکست‌ها و اشیای آن به رشته‌ای که نمونه v8 در آن راه‌اندازی شده مقید هستند. تعامل با ساختارهای داده v8 از یک رشته دیگر می‌تواند منجر به نتایج تعریف‌نشده شود. اکنون یک ماژول node.js را در نظر بگیرید که یک کتابخانه شخص ثالث را متصل می‌کند. روند ممکن است به صورت زیر باشد:

1.
در node، کتابخانه شخص ثالث با یک بازخوانی جاوااسکریپت تنظیم می‌شود تا برای دریافت اطلاعات بیشتر فراخوانی شود:
var lib = require('lib');
lib.on_progress(function() {
    console.log("Progress");
});
lib.do();
// do other stuff
2.
تابع lib.do قرار است غیرمسدودکننده باشد اما کتابخانه شخص ثالث مسدودکننده است، بنابراین بایندینگ از uv_queue_work استفاده می‌کند.
3.
کار واقعی انجام‌شده در یک رشته جداگانه می‌خواهد بازخوانی پیشرفت را اجرا کند، اما نمی‌تواند مستقیماً با v8 برای تعامل با جاوااسکریپت ارتباط برقرار کند. بنابراین از uv_async_send استفاده می‌کند.
4.
بازخوانی ناهمگام که در رشته حلقه اصلی (که همان رشته v8 است) اجرا می‌شود، سپس با v8 تعامل برقرار کرده تا بازخوانی جاوااسکریپت را فراخوانی کند.


----



کتابخانه libuv امکانات قابل توجهی برای مدیریت فرایندهای فرزند ارائه می‌دهد و تفاوت‌های پلتفرم‌ها را انتزاع کرده و ارتباط با فرایند فرزند را با استفاده از جریان‌ها (streams) یا پایپ‌های نام‌گذاری‌شده فراهم می‌کند.

یک اصطلاح رایج در یونیکس این است که هر فرایند یک کار را انجام دهد و آن را به خوبی انجام دهد. در چنین حالتی، یک فرایند اغلب از چندین فرایند فرزند برای انجام وظایف استفاده می‌کند (مشابه استفاده از پایپ‌ها در شل). یک مدل چندفرایندی همراه با پیام‌ها نیز ممکن است در مقایسه با مدلی با رشته‌ها و حافظه مشترک، تحلیل و درک آسان‌تری داشته باشد.

یک انتقاد رایج علیه برنامه‌های مبتنی بر رویداد این است که نمی‌توانند از چندین هسته در کامپیوترهای مدرن بهره ببرند. در یک برنامه چندرشته‌ای، هسته سیستم‌عامل می‌تواند زمان‌بندی را انجام داده و رشته‌های مختلف را به هسته‌های مختلف اختصاص دهد و کارایی را افزایش دهد. اما یک حلقه رویداد فقط یک رشته دارد. راه‌حل جایگزین می‌تواند راه‌اندازی چندین فرایند باشد، به‌طوری‌که هر فرایند یک حلقه رویداد را اجرا کرده و به یک هسته پردازنده جداگانه اختصاص یابد.

ساده‌ترین حالت زمانی است که صرفاً می‌خواهید یک فرایند را اجرا کرده و از زمان خروج آن مطلع شوید. این کار با استفاده از uv_spawn انجام می‌شود.

spawn/main.c

uv_loop_t *loop;
uv_process_t child_req;
uv_process_options_t options;
int main() {
    loop = uv_default_loop();
    char* args[3];
    args[0] = "mkdir";
    args[1] = "test-dir";
    args[2] = NULL;
    options.exit_cb = on_exit;
    options.file = "mkdir";
    options.args = args;
    int r;
    if ((r = uv_spawn(loop, &child_req, &options))) {
        fprintf(stderr, "%s\n", uv_strerror(r));
        return 1;
    } else {
        fprintf(stderr, "Launched process with ID %d\n", child_req.pid);
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

نکته:

متغیر options به دلیل سراسری بودن، به طور ضمنی با صفر مقداردهی اولیه می‌شود. اگر options را به یک متغیر محلی تغییر دادید، به یاد داشته باشید که تمام فیلدهای بدون استفاده آن را با صفر مقداردهی کنید:
uv_process_options_t options = {0};

ساختار uv_process_t فقط به عنوان هندل عمل می‌کند و تمام گزینه‌ها از طریق uv_process_options_t تنظیم می‌شوند. برای اجرای ساده یک فرایند، فقط باید فیلدهای file و args را تنظیم کنید. فیلد file برنامه‌ای است که باید اجرا شود. از آنجا که uv_spawn در داخل از execvp(3) https://man7.org/linux/man-pages/man3/execvp.3.html استفاده می‌کند، نیازی به ارائه مسیر کامل نیست. در نهایت طبق قراردادهای بنیادین، طول آرایه آرگومان‌ها باید یکی بیشتر از تعداد آرگومان‌ها باشد و آخرین عنصر آن برابر با NULL قرار گیرد.

پس از فراخوانی uv_spawn، فیلد uv_process_t.pid حاوی شناسه فرایند (PID) فرزند خواهد بود.

بازخوانی خروج با وضعیت خروج و نوع سیگنالی که باعث خروج شده فراخوانی خواهد شد.

توجه داشته باشید که بسیار مهم است قبل از اجرای بازخوانی خروج، تابع uv_close را فراخوانی نکنید.

spawn/main.c

void on_exit(uv_process_t *req, int64_t exit_status, int term_signal) {
    fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal);
    uv_close((uv_handle_t*) req, NULL);

بستن پایشگر فرایند پس از خروج فرایند الزامی است.

قبل از اجرای فرایند فرزند می‌توانید محیط اجرای آن را با استفاده از فیلدهای موجود در uv_process_options_t کنترل کنید.

فیلد uv_process_options_t.cwd را روی دایرکتوری مربوطه تنظیم کنید.

فیلد uv_process_options_t.env یک آرایه رشته‌ای پایان‌یافته با null است که هر عنصر آن به شکل VAR=VALUE برای تنظیم متغیرهای محیطی فرایند استفاده می‌شود. برای به ارث بردن محیط از فرایند والد (این فرایند)، آن را برابر NULL قرار دهید.

تنظیم uv_process_options_t.flags با عملگر بیتی OR از فلگ‌های زیر، رفتار فرایند فرزند را تغییر می‌دهد:

  • UV_PROCESS_SETUID - شناسه کاربر اجرای فرزند را روی uv_process_options_t.uid تنظیم می‌کند.
  • UV_PROCESS_SETGID - شناسه گروه اجرای فرزند را روی uv_process_options_t.gid تنظیم می‌کند.

تغییر UID/GID فقط در یونیکس پشتیبانی می‌شود و uv_spawn در ویندوز با خطای UV_ENOTSUP شکست خواهد خورد.

  • UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS - در ویندوز هیچ‌گونه کوتیشن یا اسکیپ کردنی روی uv_process_options_t.args اعمال نمی‌شود. در یونیکس نادیده گرفته می‌شود.
  • UV_PROCESS_DETACHED - فرایند فرزند را در یک سشن (session) جدید راه‌اندازی می‌کند که پس از خروج فرایند والد به اجرای خود ادامه خواهد داد. مثال زیر را ببینید.

ارسال فلگ UV_PROCESS_DETACHED می‌تواند برای راه‌اندازی دیمون‌ها (daemons) یا فرایندهای فرزندی که مستقل از والد هستند استفاده شود تا خروج والد تأثیری روی آن‌ها نداشته باشد.

detach/main.c

int main() {
    loop = uv_default_loop();
    char* args[3];
    args[0] = "sleep";
    args[1] = "100";
    args[2] = NULL;
    options.exit_cb = NULL;
    options.file = "sleep";
    options.args = args;
    options.flags = UV_PROCESS_DETACHED;
    int r;
    if ((r = uv_spawn(loop, &child_req, &options))) {
        fprintf(stderr, "%s\n", uv_strerror(r));
        return 1;
    }
    fprintf(stderr, "Launched sleep with PID %d\n", child_req.pid);
    uv_unref((uv_handle_t*) &child_req);
    return uv_run(loop, UV_RUN_DEFAULT);

فقط به یاد داشته باشید که هندل همچنان در حال نظارت بر فرزند است، بنابراین برنامه شما خارج نخواهد شد. اگر می‌خواهید رویکرد اجرا کن و رها کن (fire-and-forget) داشته باشید، از uv_unref() استفاده کنید.

کتابخانه libuv فراخوان سیستمی استاندارد kill(2) را در یونیکس پوشش داده و رفتاری مشابه در ویندوز پیاده‌سازی می‌کند، البته با یک نکته مهم: تمام سیگنال‌های SIGTERM، SIGINT و SIGKILL منجر به خاتمه فرایند می‌شوند. امضای تابع uv_kill به صورت زیر است:

uv_err_t uv_kill(int pid, int signum);

برای فرایندهایی که با استفاده از libuv شروع شده‌اند، می‌توانید به جای آن از uv_process_kill استفاده کنید که به جای pid، پایشگر uv_process_t را به عنوان آرگومان اول می‌پذیرد. در این حالت، به یاد داشته باشید که uv_close را روی پایشگر _پس از_ فراخوانی بازخوانی خروج صدا بزنید.

کتابخانه libuv رَپرهایی را در کنار برخی پشتیبانی‌ها از ویندوز http://docs.libuv.org/en/v1.x/signal.html#signal برای سیگنال‌های یونیکس فراهم می‌کند.

از uv_signal_init() برای مقداردهی اولیه یک هندل و مرتبط کردن آن با یک حلقه استفاده کنید. برای گوش دادن به سیگنال‌های خاص در آن هندلر، از uv_signal_start() به همراه تابع گرداننده استفاده کنید. هر گرداننده فقط می‌تواند با یک شماره سیگنال مرتبط باشد و فراخوانی‌های بعدی به uv_signal_start() ارتباط‌های قبلی را بازنویسی می‌کنند. از uv_signal_stop() برای توقف نظارت استفاده کنید. در اینجا یک مثال کوچک برای نمایش حالات مختلف آورده شده است:

signal/main.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <uv.h>
uv_loop_t* create_loop()
{
    uv_loop_t *loop = malloc(sizeof(uv_loop_t));
    if (loop) {
      uv_loop_init(loop);
    }
    return loop;
}
void signal_handler(uv_signal_t *handle, int signum)
{
    printf("Signal received: %d\n", signum);
    uv_signal_stop(handle);
}
// two signal handlers in one loop
void thread1_worker(void *userp)
{
    uv_loop_t *loop1 = create_loop();
    uv_signal_t sig1a, sig1b;
    uv_signal_init(loop1, &sig1a);
    uv_signal_start(&sig1a, signal_handler, SIGUSR1);
    uv_signal_init(loop1, &sig1b);
    uv_signal_start(&sig1b, signal_handler, SIGUSR1);
    uv_run(loop1, UV_RUN_DEFAULT);
}
// two signal handlers, each in its own loop
void thread2_worker(void *userp)
{
    uv_loop_t *loop2 = create_loop();
    uv_loop_t *loop3 = create_loop();
    uv_signal_t sig2;
    uv_signal_init(loop2, &sig2);
    uv_signal_start(&sig2, signal_handler, SIGUSR1);
    uv_signal_t sig3;
    uv_signal_init(loop3, &sig3);
    uv_signal_start(&sig3, signal_handler, SIGUSR1);
    while (uv_run(loop2, UV_RUN_NOWAIT) || uv_run(loop3, UV_RUN_NOWAIT)) {
    }
}
int main()
{
    printf("PID %d\n", getpid());
    uv_thread_t thread1, thread2;
    uv_thread_create(&thread1, thread1_worker, 0);
    uv_thread_create(&thread2, thread2_worker, 0);
    uv_thread_join(&thread1);
    uv_thread_join(&thread2);
    return 0;
}

نکته:

تابع uv_run(loop, UV_RUN_NOWAIT) شبیه به uv_run(loop, UV_RUN_ONCE) است از این جهت که فقط یک رویداد را پردازش می‌کند. حالت UV_RUN_ONCE در صورت عدم وجود رویدادهای در انتظار مسدود می‌شود، در حالی که UV_RUN_NOWAIT فوراً بازمی‌گردد. ما از NOWAIT استفاده می‌کنیم تا یکی از حلقه‌ها به دلیل عدم وجود فعالیت معوق در حلقه دیگر دچار گرسنگی (starvation) نشود.

سیگنال SIGUSR1 را به فرایند ارسال کنید، خواهید دید که هندلر ۴ بار اجرا می‌شود، یک بار برای هر uv_signal_t. هندلر به سادگی هر هندل را متوقف می‌کند تا برنامه خارج شود. این نوع توزیع پیام به همه هندلرها بسیار مفید است. سروری که از چندین حلقه رویداد استفاده می‌کند می‌تواند اطمینان حاصل کند که همه داده‌ها قبل از خاتمه با امنیت ذخیره می‌شوند، صرفاً با افزودن یک پایشگر برای SIGINT به هر حلقه.

یک فرایند معمولی و تازه ایجادشده دارای مجموعه توصیف‌کننده‌های فایل اختصاصی خود است که 0، 1 و 2 به ترتیب stdin، stdout و stderr هستند. گاهی اوقات ممکن است بخواهید توصیف‌کننده‌های فایل را با فرزند به اشتراک بگذارید. برای مثال، شاید برنامه شما یک زیردستور را اجرا می‌کند و می‌خواهید هرگونه خطا در فایل لاگ ثبت شود، اما stdout نادیده گرفته شود. برای این منظور می‌خواهید stderr فرزند همانند stderr والد باشد. در این حالت، libuv از به ارث بردن توصیف‌کننده‌های فایل پشتیبانی می‌کند. در این نمونه، برنامه آزمایشی زیر را فراخوانی می‌کنیم:

proc-streams/test.c

#include <stdio.h>
int main()
{
    fprintf(stderr, "This is stderr\n");
    printf("This is stdout\n");
    return 0;
}

برنامه واقعی proc-streams این کد را اجرا می‌کند در حالی که فقط stderr را به اشتراک می‌گذارد. توصیف‌کننده‌های فایل فرایند فرزند با استفاده از فیلد stdio در uv_process_options_t تنظیم می‌شوند. ابتدا فیلد stdio_count را روی تعداد توصیف‌کننده‌های فایل تنظیم‌شده قرار دهید. فیلد uv_process_options_t.stdio آرایه‌ای از ساختار uv_stdio_container_t است:

typedef struct uv_stdio_container_s {
    uv_stdio_flags flags;
    union {
        uv_stream_t* stream;
        int fd;
    } data;
} uv_stdio_container_t;

که در آن flags می‌تواند مقادیر متعددی داشته باشد. اگر قرار نیست استفاده شود از UV_IGNORE استفاده کنید. اگر سه فیلد اول stdio به عنوان UV_IGNORE علامت‌گذاری شوند، به /dev/null هدایت خواهند شد.

از آنجا که می‌خواهیم یک توصیف‌کننده موجود را منتقل کنیم، از UV_INHERIT_FD استفاده می‌کنیم. سپس مقدار fd را روی stderr تنظیم می‌کنیم.

proc-streams/main.c

int main() {
    loop = uv_default_loop();
    /* ... */
    options.stdio_count = 3;
    uv_stdio_container_t child_stdio[3];
    child_stdio[0].flags = UV_IGNORE;
    child_stdio[1].flags = UV_IGNORE;
    child_stdio[2].flags = UV_INHERIT_FD;
    child_stdio[2].data.fd = 2;
    options.stdio = child_stdio;
    options.exit_cb = on_exit;
    options.file = args[0];
    options.args = args;
    int r;
    if ((r = uv_spawn(loop, &child_req, &options))) {
        fprintf(stderr, "%s\n", uv_strerror(r));
        return 1;
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

اگر proc-stream را اجرا کنید، خواهید دید که فقط خط "This is stderr" نمایش داده می‌شود. علامت‌گذاری stdout به عنوان به ارث برده شده را امتحان کنید و خروجی را بررسی نمایید.

اعمال این تغییر مسیر به جریان‌ها بسیار ساده است. با تنظیم flags روی UV_INHERIT_STREAM و تنظیم data.stream به جریان موجود در فرایند والد، فرایند فرزند می‌تواند آن جریان را به عنوان ورودی/خروجی استاندارد در نظر بگیرد. این روش می‌تواند برای پیاده‌سازی چیزی شبیه به CGI https://en.wikipedia.org/wiki/Common_Gateway_Interface استفاده شود.

یک اسکریپت/برنامه اجرایی نمونه CGI به صورت زیر است:

cgi/tick.c

#include <stdio.h>
#include <unistd.h>
int main() {
    int i;
    for (i = 0; i < 10; i++) {
        printf("tick\n");
        fflush(stdout);
        sleep(1);
    }
    printf("BOOM!\n");
    return 0;
}

سرور CGI مفاهیم این بخش و بخش شبکه‌سازی را ترکیب می‌کند تا به هر کلاینت ده بار عبارت tick ارسال شود و پس از آن اتصال بسته شود.

cgi/main.c

void on_new_connection(uv_stream_t *server, int status) {
    if (status == -1) {
        // error!
        return;
    }
    uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, client);
    if (uv_accept(server, (uv_stream_t*) client) == 0) {
        invoke_cgi_script(client);
    }
    else {
        uv_close((uv_handle_t*) client, NULL);
    }

در اینجا ما به سادگی اتصال TCP را می‌پذیریم و سوکت (stream) را به invoke_cgi_script منتقل می‌کنیم.

cgi/main.c

args[1] = NULL;
/* ... finding the executable path and setting up arguments ... */
options.stdio_count = 3;
uv_stdio_container_t child_stdio[3];
child_stdio[0].flags = UV_IGNORE;
child_stdio[1].flags = UV_INHERIT_STREAM;
child_stdio[1].data.stream = (uv_stream_t*) client;
child_stdio[2].flags = UV_IGNORE;
options.stdio = child_stdio;
options.exit_cb = cleanup_handles;
options.file = args[0];
options.args = args;
// Set this so we can close the socket after the child process exits.
child_req.data = (void*) client;
int r;
if ((r = uv_spawn(loop, &child_req, &options))) {
    fprintf(stderr, "%s\n", uv_strerror(r));

خروجی استاندارد stdout اسکریپت CGI روی سوکت تنظیم می‌شود تا هر آنچه اسکریپت تیک چاپ می‌کند به کلاینت ارسال شود. با استفاده از فرایندها، می‌توانیم بافرینگ خواندن/نوشتن را به سیستم‌عامل واگذار کنیم، بنابراین از نظر راحتی این روش عالی است. فقط توجه داشته باشید که ایجاد فرایندها عملیاتی سنگین و هزینه‌بر است.

والد و فرزند می‌توانند از طریق پایپی که با تنظیم uv_stdio_container_t.flags با ترکیب بیتی UV_CREATE_PIPE و UV_READABLE_PIPE یا UV_WRITABLE_PIPE ایجاد می‌شود، ارتباط یک‌طرفه یا دوطرفه داشته باشند. فلگ خواندن/نوشتن از دیدگاه فرایند فرزند است. در این حالت، فیلد uv_stream_t* stream باید روی یک نمونه مقداردهی‌شده و بازنشده از uv_pipe_t تنظیم شود.

ساختار uv_pipe_t چیزی فراتر از صرفاً pipe(7) https://man7.org/linux/man-pages/man7/pipe.7.html (یا |) را نمایندگی می‌کند و از هر شیء استریم‌گونه شبه‌فایل پشتیبانی می‌نماید. در ویندوز، تنها شیء با این توصیف Named Pipe https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes است. در یونیکس، این می‌تواند هر یک از Unix Domain Socket https://man7.org/linux/man-pages/man7/unix.7.html، موارد مشتق‌شده از mkfifo(1) https://man7.org/linux/man-pages/man1/mkfifo.1.html یا در واقع یک pipe(7) https://man7.org/linux/man-pages/man7/pipe.7.html واقعی باشد. هنگامی که uv_spawn ساختار uv_pipe_t را به دلیل فلگ UV_CREATE_PIPE مقداردهی اولیه می‌کند، ایجاد یک socketpair(2) https://man7.org/linux/man-pages/man2/socketpair.2.html را برمی‌گزیند.

هدف از این کار فراهم کردن امکان ارتباط چند فرایند libuv با استفاده از IPC است که در ادامه به آن پرداخته می‌شود.

از آنجا که سوکت‌های دامنه [1] می‌توانند نامی مشخص و مکانی در سیستم فایل داشته باشند، می‌توان از آن‌ها برای IPC میان فرایندهای غیرمرتبط استفاده کرد. سیستم D-BUS https://www.freedesktop.org/wiki/Software/dbus که توسط محیط‌های دسکتاپ متن‌باز استفاده می‌شود از سوکت‌های دامنه برای اعلان رویدادها بهره می‌برد. برنامه‌های مختلف می‌توانند هنگام آنلاین شدن یک مخاطب یا شناسایی سخت‌افزار جدید واکنش نشان دهند. سرور MySQL نیز یک سوکت دامنه اجرا می‌کند که کلاینت‌ها می‌توانند با آن تعامل داشته باشند.

هنگام استفاده از سوکت‌های دامنه، معمولاً از الگوی کلاینت-سرور پیروی می‌شود و ایجادکننده/مالک سوکت به عنوان سرور عمل می‌کند. پس از راه‌اندازی اولیه، تبادل پیام تفاوتی با TCP ندارد، بنابراین مجدداً از مثال سرور اکو استفاده می‌کنیم.

pipe-echo-server/main.c

void remove_sock(int sig) {
    uv_fs_t req;
    uv_fs_unlink(loop, &req, PIPENAME, NULL);
    exit(0);
}
int main() {
    loop = uv_default_loop();
    uv_pipe_t server;
    uv_pipe_init(loop, &server, 0);
    signal(SIGINT, remove_sock);
    int r;
    if ((r = uv_pipe_bind(&server, PIPENAME))) {
        fprintf(stderr, "Bind error %s\n", uv_err_name(r));
        return 1;
    }
    if ((r = uv_listen((uv_stream_t*) &server, 128, on_new_connection))) {
        fprintf(stderr, "Listen error %s\n", uv_err_name(r));
        return 2;
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

ما سوکت را echo.sock نام‌گذاری می‌کنیم، به این معنی که در دایرکتوری محلی ایجاد خواهد شد. این سوکت اکنون تا جایی که به API جریان‌ها مربوط می‌شود رفتاری مشابه سوکت‌های TCP دارد. می‌توانید این سرور را با استفاده از socat http://www.dest-unreach.org/socat آزمایش کنید:

$ socat - /path/to/socket

کلاینتی که می‌خواهد به یک سوکت دامنه متصل شود از تابع زیر استفاده می‌کند:

void uv_pipe_connect(uv_connect_t *req, uv_pipe_t *handle, const char *name, uv_connect_cb cb);

که در آن name برابر echo.sock یا مشابه آن خواهد بود. در سیستم‌های یونیکس، name باید به یک فایل معتبر اشاره کند (مانند /tmp/echo.sock). در ویندوز، name از قالب \\?\pipe\echo.sock پیروی می‌کند.

ویژگی جالب سوکت‌های دامنه این است که می‌توان توصیف‌کننده‌های فایل را با ارسال از طریق یک سوکت دامنه میان فرایندها ردوبدل کرد. این ویژگی به فرایندها امکان می‌دهد ورودی/خروجی خود را به فرایندهای دیگر واگذار کنند. کاربردها شامل سرورهای توزیع بار (load-balancing)، فرایندهای کارگر و سایر روش‌های استفاده بهینه از پردازنده است. کتابخانه libuv در حال حاضر فقط از ارسال سوکت‌های TCP یا سایر پایپ‌ها از طریق پایپ‌ها پشتیبانی می‌کند.

برای نشان دادن این موضوع، به پیاده‌سازی یک سرور اکو نگاهی می‌اندازیم که کلاینت‌ها را به صورت چرخشی (round-robin) به فرایندهای کارگر واگذار می‌کند. این برنامه کمی پیچیده‌تر است و در حالی که فقط بخش‌هایی از آن در این کتاب گنجانده شده، توصیه می‌شود برای درک واقعی آن کد کامل را مطالعه کنید.

فرایند کارگر کاملاً ساده است، زیرا توصیف‌کننده فایل توسط فرایند اصلی (master) به آن تحویل داده می‌شود.

multi-echo-server/worker.c

uv_loop_t *loop;
uv_pipe_t queue;
int main() {
    loop = uv_default_loop();
    uv_pipe_init(loop, &queue, 1 /* ipc */);
    uv_pipe_open(&queue, 0);
    uv_read_start((uv_stream_t*)&queue, alloc_buffer, on_new_connection);
    return uv_run(loop, UV_RUN_DEFAULT);
}

متغیر queue پایپی است که به فرایند اصلی در طرف دیگر متصل است و توصیف‌کننده‌های فایل جدید از طریق آن ارسال می‌شوند. بسیار مهم است که آرگومان ipc در uv_pipe_init را روی 1 قرار دهید تا مشخص شود از این پایپ برای ارتباط بین‌فرایندی استفاده خواهد شد! از آنجا که فرایند اصلی هندل فایل را در ورودی استاندارد کارگر می‌نویسد، ما با استفاده از uv_pipe_open پایپ را به stdin متصل می‌کنیم.

multi-echo-server/worker.c

void on_new_connection(uv_stream_t *q, ssize_t nread, const uv_buf_t *buf) {
    if (nread < 0) {
        if (nread != UV_EOF)
            fprintf(stderr, "Read error %s\n", uv_err_name(nread));
        uv_close((uv_handle_t*) q, NULL);
        return;
    }
    uv_pipe_t *pipe = (uv_pipe_t*) q;
    if (!uv_pipe_pending_count(pipe)) {
        fprintf(stderr, "No pending count\n");
        return;
    }
    uv_handle_type pending = uv_pipe_pending_type(pipe);
    assert(pending == UV_TCP);
    uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, client);
    if (uv_accept(q, (uv_stream_t*) client) == 0) {
        uv_os_fd_t fd;
        uv_fileno((const uv_handle_t*) client, &fd);
        fprintf(stderr, "Worker %d: Accepted fd %d\n", getpid(), fd);
        uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
    }
    else {
        uv_close((uv_handle_t*) client, NULL);
    }
}

ابتدا uv_pipe_pending_count() را فراخوانی می‌کنیم تا مطمئن شویم هندلی برای خواندن در دسترس است. اگر برنامه شما می‌توانست با انواع مختلفی از هندل‌ها کار کند، می‌توانستید از uv_pipe_pending_type() برای تعیین نوع استفاده کنید. اگرچه استفاده از accept در این کد عجیب به نظر می‌رسد، اما در واقع کاملاً منطقی است. کاری که accept به طور سنتی انجام می‌دهد دریافت یک توصیف‌کننده فایل (کلاینت) از یک توصیف‌کننده فایل دیگر (سوکت شنود) است؛ که دقیقاً همان کاری است که ما در اینجا انجام می‌دهیم: دریافت توصیف‌کننده فایل (client) از queue. از این نقطه به بعد کارگر کارهای معمول سرور اکو را انجام می‌دهد.

اکنون به سراغ فرایند اصلی (master) می‌رویم و نگاهی می‌اندازیم به اینکه چگونه کارگران برای برقراری تعادل بار راه‌اندازی می‌شوند.

multi-echo-server/main.c

struct child_worker {
    uv_process_t req;
    uv_process_options_t options;
    uv_pipe_t pipe;
} *workers;

ساختار child_worker فرایند و پایپ میان فرایند اصلی و هر فرایند اختصاصی را بسته‌بندی می‌کند.

multi-echo-server/main.c

void setup_workers() {
    round_robin_counter = 0;
    // ...
    // launch same number of workers as number of CPUs
    uv_cpu_info_t *info;
    int cpu_count;
    uv_cpu_info(&info, &cpu_count);
    uv_free_cpu_info(info, cpu_count);
    child_worker_count = cpu_count;
    workers = calloc(cpu_count, sizeof(struct child_worker));
    while (cpu_count--) {
        struct child_worker *worker = &workers[cpu_count];
        uv_pipe_init(loop, &worker->pipe, 1);
        uv_stdio_container_t child_stdio[3];
        child_stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
        child_stdio[0].data.stream = (uv_stream_t*) &worker->pipe;
        child_stdio[1].flags = UV_IGNORE;
        child_stdio[2].flags = UV_INHERIT_FD;
        child_stdio[2].data.fd = 2;
        worker->options.stdio = child_stdio;
        worker->options.stdio_count = 3;
        worker->options.exit_cb = close_process_handle;
        worker->options.file = args[0];
        worker->options.args = args;
        uv_spawn(loop, &worker->req, &worker->options);
        fprintf(stderr, "Started worker %d\n", worker->req.pid);
    }
}

در راه‌اندازی کارگران، ما از تابع سودمند uv_cpu_info در libuv برای به دست آوردن تعداد پردازنده‌ها استفاده می‌کنیم تا بتوانیم به همان تعداد کارگر راه‌اندازی کنیم. باز هم تأکید می‌شود که مقداردهی اولیه پایپی که به عنوان کانال IPC عمل می‌کند با آرگومان سوم برابر با 1 الزامی است. سپس مشخص می‌کنیم که ورودی استاندارد stdin فرایند فرزند قرار است یک پایپ خواندنی باشد (از دیدگاه فرزند). تا اینجا همه چیز سرراست است. کارگران راه‌اندازی شده و منتظر نوشتن توصیف‌کننده‌های فایل در ورودی استاندارد خود هستند.

در تابع on_new_connection (زیرساخت TCP در main() مقداردهی اولیه شده است) است که ما سوکت کلاینت را می‌پذیریم و آن را به کارگر بعدی در چرخه ارسال می‌کنیم.

multi-echo-server/main.c

void on_new_connection(uv_stream_t *server, int status) {
    if (status == -1) {
        // error!
        return;
    }
    uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, client);
    if (uv_accept(server, (uv_stream_t*) client) == 0) {
        uv_write_t *write_req = (uv_write_t*) malloc(sizeof(uv_write_t));
        dummy_buf = uv_buf_init("a", 1);
        struct child_worker *worker = &workers[round_robin_counter];
        uv_write2(write_req, (uv_stream_t*) &worker->pipe, &dummy_buf, 1, (uv_stream_t*) client, NULL);
        round_robin_counter = (round_robin_counter + 1) % child_worker_count;
    }
    else {
        uv_close((uv_handle_t*) client, NULL);
    }
}

فراخوانی uv_write2 تمام انتزاع را انجام می‌دهد و صرفاً ارسال هندل (client) به عنوان آرگومان مناسب مطرح است. با این کار، سرور اکو چندفرایندی ما آماده به کار خواهد بود.

با تشکر از Kyle به خاطر اشاره به این موضوع https://github.com/nikhilm/uvbook/issues/56 که تابع uv_write2() حتی هنگام ارسال هندل‌ها نیازمند یک بافر غیرخالی است.


----



[1]
در این بخش سوکت‌های دامنه نماینده پایپ‌های نام‌گذاری‌شده در ویندوز نیز هستند.

کتابخانه libuv کنترل قابل‌توجهی بر روی حلقه‌های رویداد در اختیار کاربر قرار می‌دهد و شما می‌توانید با مدیریت همزمان چند حلقه به نتایج جالبی دست یابید. همچنین می‌توانید حلقه رویداد libuv را در کتابخانه دیگری که مبتنی بر حلقه رویداد است جاسازی کنید -- تصور کنید یک رابط کاربری مبتنی بر Qt دارید و حلقه رویداد Qt بخش بک‌اند libuv را که وظایف فشرده سیستمی را انجام می‌دهد هدایت می‌کند.

تابع uv_stop() می‌تواند برای متوقف کردن یک حلقه رویداد استفاده شود. زودترین زمانی که حلقه متوقف خواهد شد در تکرار (iteration) بعدی است، و احتمالاً دیرتر. این بدان معناست که رویدادهایی که برای پردازش در این تکرار حلقه آماده هستند همچنان پردازش خواهند شد، بنابراین uv_stop() نمی‌تواند به عنوان یک کلید قطع اضطراری (kill switch) استفاده شود. هنگامی که uv_stop() فراخوانی می‌شود، حلقه در این تکرار برای I/O مسدود (block) نخواهد شد. درک معناشناسی این موارد ممکن است کمی دشوار باشد، بنابراین بیایید به uv_run() نگاه کنیم که تمام جریان کنترل در آن رخ می‌دهد.

src/unix/core.c - uv_run

uv_signal_t* sh;
/* Note: while the handle is in the UV_HANDLE_CLOSING state now, it's still
 * possible for it to be active in the sense that uv__is_active() returns
 * true.
 *
 * A good example is when the user calls uv_shutdown(), immediately followed
 * by uv_close(). The handle is considered active at this point because the
 * completion of the shutdown req is still pending.
 */
assert(handle->flags & UV_HANDLE_CLOSING);
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
switch (handle->type) {
  case UV_PREPARE:
  case UV_CHECK:
  case UV_IDLE:
  case UV_ASYNC:
  case UV_TIMER:
  case UV_PROCESS:

پرچم stop_flag توسط uv_stop() تنظیم می‌شود. اکنون تمام فراخوان‌های (callbacks) libuv درون حلقه رویداد اجرا می‌شوند، به همین دلیل فراخوانی uv_stop() در آنها همچنان منجر به اجرای تکرار فعلی حلقه می‌شود. ابتدا libuv تایمرها را به‌روزرسانی می‌کند، سپس فراخوان‌های معلق تایمر، idle و prepare را اجرا کرده و هرگونه فراخوان معلق I/O را فراخوانی می‌کند. اگر در هر یک از آنها uv_stop() را فراخوانی کنید، stop_flag تنظیم خواهد شد. این باعث می‌شود uv_backend_timeout() مقدار 0 را برگرداند، به همین دلیل است که حلقه روی I/O مسدود نمی‌شود. از سوی دیگر، اگر uv_stop() را در یکی از گرداننده‌های check فراخوانی کنید، عملیات I/O از قبل تمام شده است و تحت تأثیر قرار نمی‌گیرد.

تابع uv_stop() برای خاموش کردن یک حلقه در زمانی که نتیجه محاسبه شده یا خطایی رخ داده است مفید است، بدون اینکه نیاز باشد تک‌تک گرداننده‌ها را یکی‌یکی متوقف کنید.

در اینجا یک مثال ساده آورده شده است که حلقه را متوقف کرده و نشان می‌دهد چگونه تکرار فعلی حلقه همچنان اجرا می‌شود.

uvstop/main.c

#include <stdio.h>
#include <uv.h>
int64_t counter = 0;
void idle_cb(uv_idle_t *handle) {
    printf("Idle callback\n");
    counter++;
    if (counter >= 5) {
        uv_stop(uv_default_loop());
        printf("uv_stop() called\n");
    }
}
void prep_cb(uv_prepare_t *handle) {
    printf("Prep callback\n");
}
int main() {
    uv_idle_t idler;
    uv_prepare_t prep;
    uv_idle_init(uv_default_loop(), &idler);
    uv_idle_start(&idler, idle_cb);
    uv_prepare_init(uv_default_loop(), &prep);
    uv_prepare_start(&prep, prep_cb);
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    return 0;
}

این فصل ابزارها و تکنیک‌هایی را فهرست می‌کند که برای کارهای متداول مفید هستند. صفحه راهنمای libev در نشانی http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_OR_BOTH قبلاً برخی الگوها را پوشش داده است که می‌توانند با تغییرات ساده در رابط برنامه‌نویسی به libuv منتقل شوند. همچنین بخش‌هایی از API کتابخانه libuv را که نیازی به اختصاص یک فصل کامل ندارند پوشش می‌دهد.

تایمرها پس از گذشت مدت زمان مشخصی از شروع به کار، تابع فراخوان (callback) را اجرا می‌کنند. تایمرهای libuv همچنین می‌توانند طوری تنظیم شوند که به جای فقط یک بار، در فواصل منظم تکرار شوند.

استفاده ساده به این صورت است که یک ناظر (watcher) را مقداردهی اولیه کرده و آن را با یک timeout و مقدار اختیاری repeat شروع کنید. تایمرها در هر زمانی می‌توانند متوقف شوند.

uv_timer_t timer_req;
uv_timer_init(loop, &timer_req);
uv_timer_start(&timer_req, callback, 5000, 2000);

یک تایمر تکرارشونده را شروع می‌کند که اولین بار ۵ ثانیه (مقدار timeout) پس از اجرای uv_timer_start شروع می‌شود، سپس هر ۲ ثانیه یک‌بار (مقدار repeat) تکرار می‌گردد. از دستور زیر استفاده کنید:

uv_timer_stop(&timer_req);

برای متوقف کردن تایمر. این کار می‌تواند با ایمنی کامل از داخل خود تابع فراخوان نیز انجام شود.

فاصله تکرار می‌تواند در هر زمانی با دستور زیر تغییر کند:

uv_timer_set_repeat(uv_timer_t *timer, int64_t repeat);

که در صورت امکان اعمال خواهد شد. اگر این تابع از داخل یک تابع فراخوان تایمر فراخوانی شود، به این معناست که:

  • اگر تایمر غیرتکرارشونده بود، تایمر قبلاً متوقف شده است. دوباره از uv_timer_start استفاده کنید.
  • اگر تایمر تکرارشونده است، مهلت بعدی از قبل زمان‌بندی شده است، بنابراین فاصله تکرار قدیمی یک بار دیگر پیش از تغییر به فاصله جدید استفاده خواهد شد.

تابع کمکی:

int uv_timer_again(uv_timer_t *)

فقط برای تایمرهای تکرارشونده کاربرد دارد و معادل متوقف کردن تایمر و سپس راه‌اندازی مجدد آن با تنظیم هر دو مقدار اولیه timeout و repeat به مقدار قبلی repeat است. اگر تایمر هنوز شروع نشده باشد با شکست مواجه می‌شود (کد خطای UV_EINVAL) و مقدار -1 را برمی‌گرداند.

یک مثال واقعی از تایمر در بخش شمارش ارجاع آورده شده است.

حلقه رویداد فقط تا زمانی اجرا می‌شود که هندل‌های فعالی وجود داشته باشند. این سیستم به این صورت کار می‌کند که هر هندل هنگام شروع، شمارنده ارجاع (reference count) حلقه رویداد را افزایش داده و هنگام توقف، شمارنده ارجاع را کاهش می‌دهد. همچنین می‌توان شمارنده ارجاع هندل‌ها را به صورت دستی با استفاده از توابع زیر تغییر داد:

void uv_ref(uv_handle_t*);
void uv_unref(uv_handle_t*);

این توابع می‌توانند برای اجازه دادن به حلقه برای خروج حتی در زمان فعال بودن یک ناظر استفاده شوند، یا برای استفاده از شیءهای سفارشی جهت زنده نگه داشتن حلقه به کار روند.

حالت دوم می‌تواند با تایمرهای دوره‌ای استفاده شود. ممکن است یک زباله‌روب (garbage collector) داشته باشید که هر X ثانیه اجرا می‌شود، یا سرویس شبکه شما ممکن است به صورت دوره‌ای سیگنال ضربان قلب (heartbeat) به دیگران ارسال کند، اما شما نمی‌خواهید مجبور باشید آنها را در تمام مسیرهای خروج پاک یا سناریوهای خطا متوقف کنید. یا ممکن است بخواهید برنامه زمانی که تمام ناظران دیگر کارشان تمام شد خارج شود. در این صورت بلافاصله پس از ایجاد، تایمر را unref کنید تا اگر تنها ناظر در حال اجرا باشد، uv_run همچنان خارج شود.

این موضوع همچنین در node.js استفاده می‌شود که در آن برخی از متدهای libuv به سطح JavaScript API منتقل می‌شوند. برای هر شیء JS یک uv_handle_t (کلاس پایه تمام ناظران) ایجاد می‌شود و می‌تواند ref یا unref شود.

ref-timer/main.c

uv_loop_t *loop;
uv_timer_t gc_req;
uv_timer_t fake_job_req;
int main() {
    loop = uv_default_loop();
    uv_timer_init(loop, &gc_req);
    uv_unref((uv_handle_t*) &gc_req);
    uv_timer_start(&gc_req, gc, 0, 2000);
    // could actually be a TCP download or something
    uv_timer_init(loop, &fake_job_req);
    uv_timer_start(&fake_job_req, fake_job, 9000, 0);
    return uv_run(loop, UV_RUN_DEFAULT);
}

ما تایمر زباله‌روب را مقداردهی اولیه می‌کنیم، سپس بلافاصله آن را unref می‌کنیم. مشاهده کنید که چگونه پس از ۹ ثانیه، وقتی کار ساختگی تمام می‌شود، برنامه به‌طور خودکار خارج می‌شود، حتی با اینکه زباله‌روب همچنان در حال اجراست.

فراخوان‌های هندل‌های idle یک‌بار در هر حلقه رویداد فراخوانی می‌شوند. فراخوان idle می‌تواند برای انجام برخی فعالیت‌های با اولویت بسیار پایین استفاده شود. برای مثال، می‌توانید در دوره‌های بیکاری، خلاصه‌ای از عملکرد روزانه برنامه را برای تحلیل به توسعه‌دهندگان ارسال کنید، یا از زمان پردازنده برنامه برای انجام محاسبات SETI استفاده کنید :) یک ناظر idle در یک برنامه گرافیکی (GUI) نیز مفید است. فرض کنید از یک حلقه رویداد برای دانلود فایل استفاده می‌کنید. اگر سوکت TCP هنوز در حال برقراری ارتباط باشد و رویداد دیگری وجود نداشته باشد، حلقه رویداد شما متوقف (block) می‌شود، به این معنی که نوار پیشرفت شما قفل شده و کاربر با یک برنامه پاسخ‌نداده مواجه خواهد شد. در چنین حالتی یک ناظر idle را در صف قرار دهید تا رابط کاربری فعال و پاسخگو باقی بماند.

idle-compute/main.c

uv_loop_t *loop;
uv_fs_t stdin_watcher;
uv_idle_t idler;
char buffer[1024];
int main() {
    loop = uv_default_loop();
    uv_idle_init(loop, &idler);
    uv_buf_t buf = uv_buf_init(buffer, 1024);
    uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type);
    uv_idle_start(&idler, crunch_away);
    return uv_run(loop, UV_RUN_DEFAULT);
}

در اینجا ما ناظر idle را مقداردهی اولیه کرده و آن را همراه با رویدادهای واقعی که مد نظرمان است در صف قرار می‌دهیم. تابع crunch_away اکنون به طور مکرر فراخوانی می‌شود تا زمانی که کاربر چیزی تایپ کند و Return را فشار دهد. سپس برای مدت کوتاهی متوقف می‌شود زیرا حلقه داده‌های ورودی را پردازش می‌کند، و پس از آن دوباره فراخوانی فراخوان idle ادامه می‌یابد.

idle-compute/main.c

void crunch_away(uv_idle_t* handle) {
    // Compute extra-terrestrial life
    // fold proteins
    // computer another digit of PI
    // or similar
    fprintf(stderr, "Computing PI...\n");
    // just to avoid overwhelming your terminal emulator
    uv_idle_stop(handle);
}

هنگام استفاده از uv_queue_work معمولاً باید داده‌های پیچیده‌ای را به ریسه کارگر ارسال کنید. راه‌حل استفاده از یک struct و تنظیم uv_work_t.data برای اشاره به آن است. یک تغییر کوچک این است که خود uv_work_t به عنوان اولین عضو این ساختار قرار گیرد (که baton [1] نامیده می‌شود). این کار امکان پاکسازی درخواست کار و تمام داده‌ها را در یک فراخوانی free فراهم می‌کند.

struct ftp_baton {
    uv_work_t req;
    char *host;
    int port;
    char *username;
    char *password;
}
ftp_baton *baton = (ftp_baton*) malloc(sizeof(ftp_baton));
baton->req.data = (void*) baton;
baton->host = strdup("my.webhost.com");
baton->port = 21;
// ...
uv_queue_work(loop, &baton->req, ftp_session, ftp_cleanup);

در اینجا baton را ایجاد کرده و تسک را در صف قرار می‌دهیم.

اکنون تابع تسک می‌تواند داده‌های مورد نیاز خود را استخراج کند:

void ftp_session(uv_work_t *req) {
    ftp_baton *baton = (ftp_baton*) req->data;
    fprintf(stderr, "Connecting to %s\n", baton->host);
}
void ftp_cleanup(uv_work_t *req) {
    ftp_baton *baton = (ftp_baton*) req->data;
    free(baton->host);
    // ...
    free(baton);
}

سپس baton را آزاد می‌کنیم که ناظر را نیز آزاد می‌کند.

معمولاً کتابخانه‌های شخص ثالث I/O خود را مدیریت کرده و سوکت‌ها و سایر فایل‌های خود را به صورت داخلی ردیابی می‌کنند. در این حالت امکان استفاده از عملیات استاندارد جریان I/O وجود ندارد، اما کتابخانه همچنان می‌تواند در حلقه رویداد libuv ادغام شود. تنها چیزی که نیاز است این است که کتابخانه به شما اجازه دهد به توصیف‌گرهای فایل زیرین دسترسی داشته باشید و توابعی را ارائه دهد که وظایف را در مراحل کوچک بر اساس تصمیم برنامه شما پردازش کنند. با این حال برخی کتابخانه‌ها چنین دسترسی را مجاز نمی‌دانند و فقط یک تابع مسدودکننده استاندارد ارائه می‌دهند که کل تراکنش I/O را انجام می‌دهد و سپس بازمی‌گردد. استفاده از این موارد در ریسه حلقه رویداد عاقلانه نیست، در عوض از زمان‌بندی کار مخزن ریسه در <#threadpool> استفاده کنید. البته این کار به معنای از دست دادن کنترل دقیق بر کتابخانه خواهد بود.

بخش uv_poll در libuv صرفاً توصیف‌گرهای فایل را با استفاده از مکانیزم اطلاع‌رسانی سیستم‌عامل نظارت می‌کند. به نوعی، تمام عملیات I/O که خود libuv پیاده‌سازی می‌کند نیز مبتنی بر کدهایی شبیه به uv_poll هستند. هر زمان که سیستم‌عامل متوجه تغییر وضعیت در توصیف‌گرهای فایل تحت نظارت شود، libuv تابع فراخوان مربوطه را اجرا خواهد کرد.

در اینجا یک برنامه مدیریت دانلود ساده را بررسی می‌کنیم که از libcurl در نشانی https://curl.se/libcurl برای دانلود فایل‌ها استفاده می‌کند. به جای دادن تمام کنترل به libcurl، از حلقه رویداد libuv استفاده خواهیم کرد و از رابط غیرمسدودکننده و ناهمگام multi در نشانی https://curl.se/libcurl/c/libcurl-multi.html برای پیشبرد دانلود در هر زمان که libuv آمادگی I/O را اطلاع می‌دهد بهره می‌بریم.

uvwget/main.c - پیکربندی و راه‌اندازی

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#include <curl/curl.h>
uv_loop_t *loop;
CURLM *curl_handle;
uv_timer_t timeout;
int main(int argc, char **argv) {
    loop = uv_default_loop();
    if (argc <= 1)
        return 0;
    if (curl_global_init(CURL_GLOBAL_ALL)) {
        fprintf(stderr, "Could not init cURL\n");
        return 1;
    }
    uv_timer_init(loop, &timeout);
    curl_handle = curl_multi_init();
    curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
    curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
    while (argc-- > 1) {
        add_download(argv[argc], argc);
    }
    uv_run(loop, UV_RUN_DEFAULT);
    curl_multi_cleanup(curl_handle);
    return 0;
}

نحوه ادغام هر کتابخانه با libuv متفاوت خواهد بود. در مورد libcurl، می‌توانیم دو تابع فراخوان ثبت کنیم. فراخوان سوکت handle_socket هر زمان که وضعیت یک سوکت تغییر کند و مجبور باشیم نظارت بر آن را آغاز کنیم، فراخوانی می‌شود. تابع start_timeout توسط libcurl فراخوانی می‌شود تا فاصله مهلت زمانی (timeout) بعدی را به ما اطلاع دهد، که پس از آن باید صرف‌نظر از وضعیت I/O کار libcurl را پیش ببریم. این برای آن است که libcurl بتواند خطاها را مدیریت کند یا هر کار دیگری که برای ادامه دانلود لازم است انجام دهد.

دانلودر ما به این صورت اجرا می‌شود:

$ ./uvwget [url1] [url2] ...

بنابراین هر آرگومان را به عنوان یک نشانی اینترنتی (URL) اضافه می‌کنیم:

uvwget/main.c - افزودن نشانی‌ها (URLs)

void add_download(const char *url, int num) {
    char filename[50];
    sprintf(filename, "%d.download", num);
    FILE *file;
    file = fopen(filename, "w");
    if (file == NULL) {
        fprintf(stderr, "Error opening %s\n", filename);
        return;
    }
    CURL *handle = curl_easy_init();
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);
    curl_easy_setopt(handle, CURLOPT_URL, url);
    curl_multi_add_handle(curl_handle, handle);
    fprintf(stderr, "Added download %s -> %s\n", url, filename);
}

ما اجازه می‌دهیم libcurl داده‌ها را مستقیماً در یک فایل بنویسد، اما در صورت تمایل کارهای بسیار بیشتری امکان‌پذیر است.

تابع start_timeout بلافاصله برای اولین بار توسط libcurl فراخوانی می‌شود، بنابراین فرآیندها به حرکت درمی‌آیند. این تابع به سادگی یک تایمر libuv در <#timers> را شروع می‌کند که هر زمان منقضی شود، curl_multi_socket_action را با CURL_SOCKET_TIMEOUT هدایت می‌کند. تابع curl_multi_socket_action چیزی است که libcurl را به پیش می‌برد و هر زمان که سوکت‌ها تغییر وضعیت دهند آن را فراخوانی می‌کنیم. اما قبل از پرداختن به آن، باید هر زمان که handle_socket فراخوانی شد، روی سوکت‌ها نظارت (poll) کنیم.

uvwget/main.c - راه‌اندازی نظارت (Polling)

void start_timeout(CURLM *multi, long timeout_ms, void *userp) {
    if (timeout_ms <= 0)
        timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */
    uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
}
int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) {
    curl_context_t *curl_context;
    if (action == CURL_POLL_IN || action == CURL_POLL_OUT) {
        if (socketp) {
            curl_context = (curl_context_t*) socketp;
        }
        else {
            curl_context = create_curl_context(s);
            curl_multi_assign(curl_handle, s, (void *) curl_context);
        }
    }
    switch (action) {
        case CURL_POLL_IN:
            uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform);
            break;
        case CURL_POLL_OUT:
            uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform);
            break;
        case CURL_POLL_REMOVE:
            if (socketp) {
                uv_poll_stop(&((curl_context_t*)socketp)->poll_handle);
                destroy_curl_context((curl_context_t*) socketp);
                curl_multi_assign(curl_handle, s, NULL);
            }
            break;
        default:
            abort();
    }
    return 0;
}

ما به توصیف‌گر فایل سوکت s و عملیات action علاقه‌مندیم. برای هر سوکت در صورتی که وجود نداشته باشد یک هندل uv_poll_t ایجاد می‌کنیم و با استفاده از curl_multi_assign آن را به سوکت متصل می‌سازیم. به این ترتیب هر زمان که تابع فراخوان اجرا شود، socketp به آن اشاره می‌کند.

در صورتی که دانلود کامل شده یا با شکست مواجه شود، libcurl درخواست حذف نظارت را می‌دهد. بنابراین ما هندل poll را متوقف کرده و آزاد می‌کنیم.

بسته به اینکه libcurl می‌خواهد چه رویدادهایی را نظارت کند، نظارت را با UV_READABLE یا UV_WRITABLE شروع می‌کنیم. اکنون هر زمان که سوکت برای خواندن یا نوشتن آماده باشد، libuv فراخوان poll را اجرا خواهد کرد. فراخوانی چندباره uv_poll_start روی یک هندل یکسان مجاز است، زیرا فقط ماسک رویدادها را با مقدار جدید به‌روزرسانی می‌کند. تابع curl_perform بخش اصلی این برنامه است.

uvwget/main.c - هدایت libcurl

void curl_perform(uv_poll_t *req, int status, int events) {
    uv_timer_stop(&timeout);
    int running_handles;
    int flags = 0;
    if (status < 0)                      flags = CURL_CSELECT_ERR;
    if (!status && events & UV_READABLE) flags |= CURL_CSELECT_IN;
    if (!status && events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
    curl_context_t *context;
    context = (curl_context_t*)req;
    curl_multi_socket_action(curl_handle, context->sockfd, flags, &running_handles);
    check_multi_info();
}

اولین کاری که انجام می‌دهیم متوقف کردن تایمر است، زیرا در این بازه زمانی مقداری پیشرفت حاصل شده است. سپس بسته به اینکه چه رویدادی باعث فراخوانی شده، پرچم‌های مناسب را تنظیم می‌کنیم. سپس curl_multi_socket_action را با سوکتی که پیشرفت داشته و پرچم‌هایی که رویدادهای رخ‌داده را اعلام می‌کنند فراخوانی می‌نماییم. در این مرحله libcurl تمام وظایف داخلی خود را در بخش‌های کوچک انجام داده و تلاش می‌کند در سریع‌ترین زمان ممکن برگردد، که دقیقاً همان چیزی است که یک برنامه رویدادمحور در ریسه اصلی خود نیاز دارد. کتابخانه libcurl پیام‌های پیشرفت انتقال را در صف مخصوص خود قرار می‌دهد. در مورد ما، ما فقط به انتقال‌هایی علاقه‌مندیم که تکمیل شده باشند. بنابراین این پیام‌ها را استخراج کرده و هندل‌هایی را که انتقال آنها انجام شده است پاکسازی می‌کنیم.

uvwget/main.c - خواندن وضعیت انتقال

void check_multi_info(void) {
    char *done_url;
    CURLMsg *message;
    int pending;
    while ((message = curl_multi_info_read(curl_handle, &pending))) {
        switch (message->msg) {
        case CURLMSG_DONE:
            curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
                            &done_url);
            printf("%s DONE\n", done_url);
            curl_multi_remove_handle(curl_handle, message->easy_handle);
            curl_easy_cleanup(message->easy_handle);
            break;
        default:
            fprintf(stderr, "CURLMSG default\n");
            abort();
        }
    }
}

در دست تکمیل (TODO)

کتابخانه libuv یک رابط چندسکویی برای بارگذاری پویای کتابخانه‌های اشتراکی در نشانی https://en.wikipedia.org/wiki/Shared_library فراهم می‌کند. این قابلیت می‌تواند برای پیاده‌سازی سیستم افزونه/پلاگین/ماژول اختصاصی شما استفاده شود و توسط node.js برای پیاده‌سازی پشتیبانی از require() در بایندینگ‌ها به کار می‌رود. استفاده از آن تا زمانی که کتابخانه شما نمادهای (symbols) مناسب را صادر کند بسیار ساده است. هنگام بارگذاری کدهای شخص ثالث مراقب بررسی‌های امنیتی و صحت‌سنجی باشید، در غیر این صورت برنامه شما رفتاری غیرقابل پیش‌بینی خواهد داشت. این مثال یک سیستم افزونه بسیار ساده را پیاده‌سازی می‌کند که کاری جز چاپ نام افزونه انجام نمی‌دهد.

بیایید ابتدا به رابط ارائه شده به توسعه‌دهندگان افزونه نگاه کنیم.

plugin/plugin.h

#ifndef UVBOOK_PLUGIN_SYSTEM
#define UVBOOK_PLUGIN_SYSTEM
// Plugin authors should use this to register their plugins with mfp.
void mfp_register(const char *name);
#endif

شما می‌توانید به طور مشابه توابع بیشتری اضافه کنید که توسعه‌دهندگان افزونه بتوانند کارهای مفیدی در برنامه شما انجام دهند [2]. یک نمونه افزونه با استفاده از این رابط:

plugin/hello.c

#include "plugin.h"
void initialize() {
    mfp_register("Hello World!");
}

رابط ما تعریف می‌کند که همه افزونه‌ها باید یک تابع initialize داشته باشند که توسط برنامه فراخوانی خواهد شد. این افزونه به عنوان یک کتابخانه اشتراکی کامپایل شده و می‌تواند با اجرای برنامه ما بارگذاری شود:

$ ./plugin libhello.dylib
Loading libhello.dylib
Registered plugin "Hello World!"

نکته:

نام فایل کتابخانه اشتراکی بسته به پلتفرم متفاوت خواهد بود. در لینوکس libhello.so است.

این کار با استفاده از uv_dlopen برای بارگذاری اولیه کتابخانه اشتراکی libhello.dylib انجام می‌شود. سپس با استفاده از uv_dlsym به تابع initialize دسترسی پیدا کرده و آن را فراخوانی می‌کنیم.

plugin/main.c

#include "plugin.h"
typedef void (*init_plugin_function)();
void mfp_register(const char *name) {
    fprintf(stderr, "Registered plugin \"%s\"\n", name);
}
int main(int argc, char **argv) {
    if (argc == 1) {
        fprintf(stderr, "Usage: %s [plugin1] [plugin2] ...\n", argv[0]);
        return 0;
    }
    uv_lib_t lib;
    while (--argc) {
        fprintf(stderr, "Loading %s\n", argv[argc]);
        if (uv_dlopen(argv[argc], &lib)) {
            fprintf(stderr, "Error: %s\n", uv_dlerror(&lib));
            continue;
        }
        init_plugin_function init_plugin;
        if (uv_dlsym(&lib, "initialize", (void **) &init_plugin)) {
            fprintf(stderr, "dlsym error: %s\n", uv_dlerror(&lib));
            continue;
        }
        init_plugin();
    }
    return 0;
}

تابع uv_dlopen مسیری به کتابخانه اشتراکی را دریافت کرده و اشاره‌گر مات uv_lib_t را مقداردهی می‌کند. در صورت موفقیت 0 و در صورت بروز خطا -1 را برمی‌گرداند. از uv_dlerror برای دریافت پیام خطا استفاده کنید.

تابع uv_dlsym اشاره‌گری به نماد موجود در آرگومان دوم را در آرگومان سوم ذخیره می‌کند. نوع init_plugin_function اشاره‌گری تابعی به نوع تابعی است که ما در افزونه‌های برنامه به دنبال آن هستیم.

ترمینال‌های متنی برای مدت طولانی از قالب‌بندی اولیه با یک مجموعه دستور نسبتاً استاندارد در نشانی https://en.wikipedia.org/wiki/ANSI_escape_sequences پشتیبانی کرده‌اند. این قالب‌بندی اغلب توسط برنامه‌ها برای بهبود خوانایی خروجی ترمینال استفاده می‌شود. به عنوان مثال grep --colour. کتابخانه libuv انتزاع uv_tty_t (یک stream) و توابع مربوطه را برای پیاده‌سازی کدهای گریز ANSI در تمام پلتفرم‌ها فراهم می‌کند. منظور از این کار این است که libuv کدهای ANSI را به معادل آنها در ویندوز تبدیل کرده و توابعی را برای دریافت اطلاعات ترمینال ارائه می‌دهد.

اولین کاری که باید انجام شود مقداردهی اولیه یک uv_tty_t با توصیف‌گر فایلی است که از آن می‌خواند یا در آن می‌نویسد. این کار با دستور زیر انجام می‌شود:

int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int unused)

پارامتر unused اکنون به طور خودکار تشخیص داده شده و نادیده گرفته می‌شود. پیش از این برای استفاده از uv_read_start() روی stream لازم بود مقداردهی شود.

سپس بهتر است از uv_tty_set_mode برای تنظیم حالت روی normal استفاده کنید که بیشتر قابلیت‌های قالب‌بندی، کنترل جریان و سایر تنظیمات TTY را فعال می‌کند. حالت‌های دیگر در نشانی http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t نیز در دسترس هستند.

به یاد داشته باشید که هنگام خروج برنامه، uv_tty_reset_mode را فراخوانی کنید تا وضعیت ترمینال بازیابی شود؛ این یک رفتار استاندارد و حرفه‌ای است. یک نکته حرفه‌ای دیگر این است که به هدایت مجدد (redirection) خروجی توجه داشته باشید. اگر کاربر خروجی دستور شما را به یک فایل هدایت کند، دنباله‌های کنترلی نباید نوشته شوند زیرا خوانایی و کار با grep را مختل می‌کنند. برای بررسی اینکه آیا توصیف‌گر فایل واقعاً یک TTY است، uv_guess_handle را با توصیف‌گر فایل فراخوانی کرده و مقدار بازگشتی را با UV_TTY مقایسه کنید.

در اینجا یک مثال ساده آورده شده است که متن سفید را روی پس‌زمینه قرمز چاپ می‌کند:

tty/main.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <uv.h>
uv_loop_t *loop;
uv_tty_t tty;
int main() {
    uv_write_t req;
    uv_buf_t buf;
    uv_write_t req1;
    uv_buf_t buf1;
    loop = uv_default_loop();
    uv_tty_init(loop, &tty, STDOUT_FILENO, 0);
    uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);
    if (uv_guess_handle(1) == UV_TTY) {
        buf1.base = "\033[41;37m";
        buf1.len = strlen(buf1.base);
        uv_write(&req1, (uv_stream_t*) &tty, &buf1, 1, NULL);
    }
    buf.base = "Hello TTY\n";
    buf.len = strlen(buf.base);
    uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
    uv_tty_reset_mode();
    return uv_run(loop, UV_RUN_DEFAULT);
}

آخرین تابع کمکی TTY تابع uv_tty_get_winsize() است که برای دریافت عرض و ارتفاع ترمینال استفاده می‌شود و در صورت موفقیت مقدار 0 را برمی‌گرداند. در اینجا یک برنامه کوچک وجود دارد که با استفاده از این تابع و کدهای گریز موقعیت کاراکتر، مقداری انیمیشن انجام می‌دهد.

tty-gravity/main.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <uv.h>
uv_loop_t *loop;
uv_tty_t tty;
uv_timer_t tick;
uv_write_t write_req;
int width, height;
int pos = 0;
char *message = "  Hello TTY  ";
void update(uv_timer_t *req) {
    char data[500];
    uv_buf_t buf;
    buf.base = data;
    buf.len = sprintf(data, "\033[2J\033[H\033[%dB\033[%luC\033[42;37m%s",
                            pos,
                            (unsigned long) (width-strlen(message))/2,
                            message);
    uv_write(&write_req, (uv_stream_t*) &tty, &buf, 1, NULL);
    pos++;
    if (pos > height) {
        uv_tty_reset_mode();
        uv_timer_stop(&tick);
    }
}
int main() {
    loop = uv_default_loop();
    uv_tty_init(loop, &tty, STDOUT_FILENO, 0);
    uv_tty_set_mode(&tty, 0);
    if (uv_tty_get_winsize(&tty, &width, &height)) {
        fprintf(stderr, "Could not get TTY information\n");
        uv_tty_reset_mode();
        return 1;
    }
    fprintf(stderr, "Width %d, height %d\n", width, height);
    uv_timer_init(loop, &tick);
    uv_timer_start(&tick, update, 200, 200);
    return uv_run(loop, UV_RUN_DEFAULT);
}

کدهای گریز عبارتند از:

کد معنی
2 J پاکسازی بخشی از صفحه، مقدار 2 کل صفحه است
H انتقال مکان‌نما به یک موقعیت خاص، پیش‌فرض بالا-چپ
n B انتقال مکان‌نما به پایین به اندازه n خط
n C انتقال مکان‌نما به راست به اندازه n ستون
m پیروی از رشته تنظیمات نمایشگر، در این حالت پس‌زمینه سبز (40+2)، متن سفید (30+7)

همانطور که می‌بینید این کار برای تولید خروجی با قالب‌بندی زیبا بسیار مفید است، یا حتی بازی‌های آرکید مبتنی بر کنسول اگر علاقه‌مند باشید. برای کنترل‌های پیشرفته‌تر می‌توانید ncurses را در نشانی https://invisible-island.net/ncurses/announce.html امتحان کنید.

تغییر یافته در نسخه 1.23.1:: پارامتر readable اکنون استفاده نشده و نادیده گرفته می‌شود. مقدار مناسب اکنون به طور خودکار از هسته سیستم‌عامل تشخیص داده خواهد شد.


----



[1]
من اولین بار با اصطلاح baton در این زمینه، در اسلایدهای عالی Konstantin Käfer درباره نوشتن بایندینگ‌های node.js آشنا شدم -- https://kkaefer.com/node-cpp-modules/#baton
[2]
عبارت mfp مخفف My Fancy Plugin است.

آقای Nikhil Marathe در نشانی https://nikhilism.com یک بعدازظهر (۱۶ ژوئن ۲۰۱۲) زمانی که تمایلی به برنامه‌نویسی نداشت شروع به نوشتن این کتاب کرد. او اخیراً هنگام کار روی node-taglib در نشانی https://github.com/nikhilm/node-taglib از کمبود مستندات خوب برای libuv آسیب دیده بود. اگرچه مستندات مرجع وجود داشت، اما هیچ آموزش جامعی موجود نبود. این کتاب حاصل آن نیاز است و تلاش می‌کند دقیق باشد. با این حال، کتاب ممکن است اشتباهاتی داشته باشد. از ارسال Pull Request استقبال می‌شود.

نیخیل مدیون صفحه راهنمای جامع Marc Lehmann در نشانی http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod درباره libev است که بخش زیادی از معناشناسی این دو کتابخانه را شرح می‌دهد.

این کتاب با استفاده از Sphinx در نشانی https://www.sphinx-doc.org و vim در نشانی https://www.vim.org ساخته شده است.

نکته:

در سال ۲۰۱۷ پروژه libuv کارهای Nikhil را در مستندات رسمی گنجاند و از آن زمان به بعد در آنجا نگهداری می‌شود.

راهنماهای مهاجرت برای نسخه‌های مختلف libuv، با شروع از نسخه 1.0.

برخی از APIها در طول فرآیند توسعه 1.0.0 تغییرات زیادی کردند. در اینجا یک راهنمای مهاجرت برای مهم‌ترین تغییراتی که پس از انتشار 0.10 رخ داده آورده شده است.

در libuv 0.10 (و نسخه‌های قبلی)، حلقه‌ها با uv_loop_new ایجاد می‌شدند که حافظه را برای یک حلقه جدید تخصیص می‌داد و آن را مقداردهی می‌کرد؛ و با uv_loop_delete تخریب می‌شدند که حلقه را از بین برده و حافظه را آزاد می‌کرد. با شروع از نسخه 1.0، این توابع منسوخ شده‌اند و کاربر مسئول تخصیص حافظه و سپس مقداردهی اولیه حلقه است.

کتابخانه libuv 0.10

uv_loop_t* loop = uv_loop_new();
...
uv_loop_delete(loop);

کتابخانه libuv 1.0

uv_loop_t* loop = malloc(sizeof *loop);
uv_loop_init(loop);
...
uv_loop_close(loop);
free(loop);

نکته:

مدیریت خطا برای رعایت اختصار حذف شد. مستندات مربوط به uv_loop_init() در <#c.uv_loop_init> و uv_loop_close() در <#c.uv_loop_close> را بررسی کنید.

مدیریت خطا در libuv 1.0 دستخوش بازنگری اساسی شد. به طور کلی، در libuv 0.10 توابع و پارامترهای وضعیت در صورت موفقیت مقدار 0 و در صورت شکست مقدار -1 دریافت می‌کردند و کاربر باید از uv_last_error برای واکشی کد خطا که یک عدد مثبت بود استفاده می‌کرد.

در نسخه 1.0، توابع و پارامترهای وضعیت حاوی کد خطای واقعی هستند که برای موفقیت برابر با 0 و در صورت بروز خطا یک عدد منفی است.

کتابخانه libuv 0.10

... assume 'server' is a TCP server which is already listening
r = uv_listen((uv_stream_t*) server, 511, NULL);
if (r == -1) {
  uv_err_t err = uv_last_error(uv_default_loop());
  /* err.code contains UV_EADDRINUSE */
}

کتابخانه libuv 1.0

... assume 'server' is a TCP server which is already listening
r = uv_listen((uv_stream_t*) server, 511, NULL);
if (r < 0) {
  /* r contains UV_EADDRINUSE */
}

در libuv 0.10 سیستم‌های یونیکس از یک threadpool استفاده می‌کردند که به طور پیش‌فرض دارای ۴ ریسه بود، در حالی که ویندوز از API مربوط به QueueUserWorkItem استفاده می‌کرد که از threadpool داخلی ویندوز با پیش‌فرض ۵۱۲ ریسه برای هر فرآیند بهره می‌برد.

در نسخه 1.0، هر دو پیاده‌سازی را یکپارچه کردیم، بنابراین ویندوز اکنون از همان پیاده‌سازی یونیکس استفاده می‌کند. اندازه threadpool را می‌توان با اکسپورت کردن متغیر محیطی UV_THREADPOOL_SIZE تنظیم کرد. بخش زمان‌بندی کار مخزن ریسه در <#threadpool> را ببینید.

در libuv 0.10 تابع فراخوان باید یک ساختار uv_buf_t در <#c.uv_buf_t> پرشده را بر اساس مقدار (by value) برمی‌گرداند:

uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) {
    return uv_buf_init(malloc(size), size);
}

در libuv 1.0 یک اشاره‌گر به بافر به تابع فراخوان ارسال می‌شود که کاربر باید آن را مقداردهی و پر کند:

void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
    buf->base = malloc(size);
    buf->len = size;
}

نسخه 1.0 کتابخانه libuv رابط‌های IPv4 و IPv6 را یکپارچه کرد. دیگر دوگانگی بین uv_tcp_bind و uv_tcp_bind6 وجود ندارد و اکنون فقط uv_tcp_bind() در <#c.uv_tcp_bind> در دسترس است.

توابع IPv4 ساختارهای struct sockaddr_in را بر اساس مقدار دریافت می‌کردند و توابع IPv6 ساختار struct sockaddr_in6 را می‌گرفتند. اکنون توابع یک struct sockaddr* (توجه داشته باشید که یک اشاره‌گر است) دریافت می‌کنند. این ساختار می‌تواند روی پشته (stack) تخصیص یابد.

کتابخانه libuv 0.10

struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", 1234);
...
uv_tcp_bind(&server, addr)

کتابخانه libuv 1.0

struct sockaddr_in addr;
uv_ip4_addr("0.0.0.0", 1234, &addr)
...
uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0);

توابع ایجاد ساختارهای IPv4 و IPv6 (توابع uv_ip4_addr() در <#c.uv_ip4_addr> و uv_ip6_addr() در <#c.uv_ip6_addr>) نیز تغییر کرده‌اند، حتماً مستندات آنها را بررسی کنید.

..note::
این تغییر برای تمامی توابعی که بین آدرس‌های IPv4 و IPv6 تمایز قائل می‌شدند اعمال می‌شود.

فراخوان‌های دریافت داده در streams و UDP اکنون اشاره‌گری به یک بافر uv_buf_t در <#c.uv_buf_t> دریافت می‌کنند، نه یک ساختار بر اساس مقدار.

کتابخانه libuv 0.10

void on_read(uv_stream_t* handle,
             ssize_t nread,
             uv_buf_t buf) {
    ...
}
void recv_cb(uv_udp_t* handle,
             ssize_t nread,
             uv_buf_t buf,
             struct sockaddr* addr,
             unsigned flags) {
    ...
}

کتابخانه libuv 1.0

void on_read(uv_stream_t* handle,
             ssize_t nread,
             const uv_buf_t* buf) {
    ...
}
void recv_cb(uv_udp_t* handle,
             ssize_t nread,
             const uv_buf_t* buf,
             const struct sockaddr* addr,
             unsigned flags) {
    ...
}

در libuv 0.10 (و نسخه‌های پیشین) تابع uv_read2_start برای شروع خواندن داده‌ها روی یک pipe استفاده می‌شد که می‌توانست منجر به دریافت هندل‌ها از طریق آن نیز شود. تابع فراخوان برای چنین تابعی به شکل زیر بود:

void on_read(uv_pipe_t* pipe,
             ssize_t nread,
             uv_buf_t buf,
             uv_handle_type pending) {
    ...
}

در libuv 1.0 تابع uv_read2_start حذف شد و کاربر باید در حین اجرای فراخوان خواندن، با استفاده از uv_pipe_pending_count() در <#c.uv_pipe_pending_count> و uv_pipe_pending_type() در <#c.uv_pipe_pending_type> بررسی کند که آیا هندل‌های معلقی وجود دارند یا خیر:

void on_read(uv_stream_t* handle,
             ssize_t nread,
             const uv_buf_t* buf) {
    ...
    while (uv_pipe_pending_count((uv_pipe_t*) handle) != 0) {
        pending = uv_pipe_pending_type((uv_pipe_t*) handle);
        ...
    }
    ...
}

اگرچه این کار توسط API پشتیبانی نمی‌شد، اما کاربران اغلب به بخش‌های داخلی libuv دسترسی پیدا می‌کردند تا مثلاً به توصیف‌گر فایل یک هندل TCP دسترسی یابند.

fd = handle->io_watcher.fd;

این قابلیت اکنون به درستی از طریق تابع uv_fileno() در <#c.uv_fileno> در دسترس قرار گرفته است.

تابع uv_fs_readdir در libuv 0.10 پس از اتمام لیستی از رشته‌ها را در فیلد req->ptr برمی‌گرداند. در نسخه 1.0، نام این تابع به uv_fs_scandir() در <#c.uv_fs_scandir> تغییر یافت، زیرا در واقع با استفاده از scandir(3) پیاده‌سازی شده است.

علاوه بر این، به جای تخصیص یک لیست کامل از رشته‌ها، کاربر می‌تواند با استفاده از تابع uv_fs_scandir_next() در <#c.uv_fs_scandir_next> هر بار یک نتیجه را دریافت کند. این تابع نیازی به رفت و برگشت به threadpool ندارد، زیرا libuv لیست dents بازگردانده شده توسط scandir(3) را نگه می‌دارد.

کتابخانه libuv می‌تواند از اینجا دانلود شود https://dist.libuv.org/dist.

دستورالعمل‌های نصب را می‌توان در فایل README در نشانی https://github.com/libuv/libuv/blob/master/README.md یافت.

مشارکت‌کنندگان libuv

2014-present, مشارکت‌کنندگان libuv

August 26, 2026 1.52.1