| LIBUV(1) | libuv API documentation | LIBUV(1) |
نام (NAME)
libuv - کتابخانه پشتیبانی از ورودی/خروجی ناهمگام و رویدادمحور چندسکویی
نمای کلی (OVERVIEW)
کتابخانه 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 نیز استفاده میشود.
نکته:
ویژگیها (FEATURES)
- حلقه رویداد کامل با پشتیبانی از epoll، kqueue، IOCP و event ports.
- سوکتهای ناهمگام TCP و UDP
- تفکیک نام دامنه (DNS) بهصورت ناهمگام
- عملیات ناهمگام فایل و سیستم فایل
- رویدادهای سیستم فایل
- پایانه TTY کنترلشده با کدهای گریز ANSI
- ارتباط بینپردازشی (IPC) همراه با اشتراکگذاری سوکت، با استفاده از سوکتهای دامنه یونیکس یا لولههای نامگذاریشده (ویندوز)
- فرآیندهای فرزند (Child processes)
- استخر ریسهها (thread pool)
- مدیریت سیگنالها
- ساعت با دقت بالا (High resolution clock)
- مفاهیم پایهای ریسهبندی و همگامسازی
مستندات (DOCUMENTATION)
نمای کلی طراحی (Design overview)
کتابخانه libuv یک کتابخانه پشتیبانی چندسکویی است که در ابتدا برای Node.js https://nodejs.org نوشته شد. این کتابخانه بر اساس مدل ورودی/خروجی ناهمگام و رویدادمحور طراحی شده است.
این کتابخانه امکاناتی بسیار فراتر از یک لایه انتزاعی ساده بر روی سازوکارهای مختلف پایش (polling) ورودی/خروجی ارائه میدهد: «هندلها» (handles) و «جریانها» (streams) یک انتزاع سطح بالا برای سوکتها و دیگر موجودیتها فراهم میکنند؛ همچنین قابلیتهای چندسکویی ورودی/خروجی فایل و کار با ریسهها نیز در میان سایر موارد ارائه شده است.
نمودار زیر بخشهای گوناگون تشکیلدهنده libuv و زیرسیستم مربوط به هر یک را نشان میدهد: [image]
هندلها و درخواستها (Handles and requests)
کتابخانه 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.
- حافظه درخواست فقط از آن نقطه به بعد قابل آزادسازی یا استفاده مجدد است.
حلقه ورودی/خروجی (The I/O loop)
حلقه ورودی/خروجی (یا حلقه رویداد) بخش مرکزی 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 اجرا شده باشد و هنوز زنده باشد از ابتدا ادامه مییابد، در غیر این صورت آن نیز پایان مییابد.
مهم:
نکته:
ورودی/خروجی فایل (File I/O)
برخلاف ورودی/خروجی شبکه، هیچ سازوکار پایه ورودی/خروجی فایل وابسته به سکویی وجود ندارد که libuv بتواند به آن متکی باشد، بنابراین رویکرد کنونی اجرای عملیات مسدودکننده ورودی/خروجی فایل در استخر ریسهها (thread pool) است.
برای توضیحات جامع درباره وضعیت ورودی/خروجی فایل در سکوهای گوناگون، این مطلب را بررسی کنید: https://blog.libtorrent.org/2012/10/asynchronous-disk-io.
در حال حاضر libuv از یک استخر ریسه سراسری استفاده میکند که همه حلقهها میتوانند کارهای خود را در آن صفبندی کنند. در حال حاضر ۳ نوع عملیات در این استخر اجرا میشوند:
- عملیات سیستم فایل
- توابع DNS (مانند getaddrinfo و getnameinfo)
- کدهای مشخصشده توسط کاربر از طریق uv_queue_work() <#c.uv_queue_work>
هشدار:
مستندات رابط برنامهنویسی (API documentation)
مدیریت خطاها (Error handling)
در libuv، خطاها ثابتهایی با اعداد منفی هستند. به عنوان یک قاعده کلی، هر جا یک پارامتر وضعیت وجود دارد، یا یک تابع API یک عدد صحیح برمیگرداند، عدد منفی به معنای بروز خطا خواهد بود.
هنگامی که تابعی با دریافت کالبک دچار خطا میشود، کالبک هرگز فراخوانی نخواهد شد.
نکته:
ثابتهای خطا (Error constants)
- UV_E2BIG
- لیست آرگومانها بیش از حد طولانی است
- UV_EACCES
- دسترسی غیرمجاز است
- UV_EADDRINUSE
- آدرس قبلاً استفاده شده است
- UV_EADDRNOTAVAIL
- آدرس در دسترس نیست
- UV_EAFNOSUPPORT
- خانواده آدرس پشتیبانی نمیشود
- UV_EAGAIN
- منبع موقتاً در دسترس نیست
- UV_EAI_ADDRFAMILY
- خانواده آدرس پشتیبانی نمیشود
- UV_EAI_AGAIN
- شکست موقت
- UV_EAI_BADFLAGS
- مقدار ai_flags نامعتبر است
- UV_EAI_BADHINTS
- مقدار نامعتبر برای hints
- UV_EAI_CANCELED
- درخواست لغو شد
- UV_EAI_FAIL
- شکست دائمی
- UV_EAI_FAMILY
- مقدار ai_family پشتیبانی نمیشود
- UV_EAI_MEMORY
- کمبود حافظه
- UV_EAI_NODATA
- بدون آدرس
- UV_EAI_NONAME
- گره یا سرویس ناشناخته است
- UV_EAI_OVERFLOW
- سرریز بافر آرگومان
- UV_EAI_PROTOCOL
- پروتکل تفکیکشده ناشناخته است
- UV_EAI_SERVICE
- سرویس برای نوع سوکت در دسترس نیست
- UV_EAI_SOCKTYPE
- نوع سوکت پشتیبانی نمیشود
- UV_EALREADY
- اتصال از قبل در حال انجام است
- UV_EBADF
- توصیفکننده فایل نامعتبر است
- UV_EBUSY
- منبع مشغول یا قفل است
- UV_ECANCELED
- عملیات لغو شد
- UV_ECHARSET
- نویسه یونیکد نامعتبر است
- UV_ECONNABORTED
- قطع اتصال ناشی از نرمافزار
- UV_ECONNREFUSED
- اتصال رد شد
- UV_ECONNRESET
- اتصال توسط طرف مقابل بازنشانی شد
- UV_EDESTADDRREQ
- آدرس مقصد الزامی است
- UV_EEXIST
- فایل از قبل وجود دارد
- UV_EFAULT
- آدرس نامعتبر در آرگومان فراخوانی سیستمی
- UV_EFBIG
- فایل بیش از حد بزرگ است
- UV_EHOSTUNREACH
- میزبان در دسترس نیست
- UV_EINTR
- وقفه در فراخوانی سیستمی
- UV_EINVAL
- آرگومان نامعتبر است
- UV_EIO
- خطای ورودی/خروجی
- UV_EISCONN
- سوکت از قبل متصل است
- UV_EISDIR
- عملیات غیرمجاز روی دایرکتوری
- UV_ELOOP
- برخورد با پیوندهای نمادین بیش از حد
- UV_EMFILE
- تعداد فایلهای باز بیش از حد است
- UV_EMSGSIZE
- پیام بیش از حد طولانی است
- UV_ENAMETOOLONG
- نام بیش از حد طولانی است
- UV_ENETDOWN
- شبکه قطع است
- UV_ENETUNREACH
- شبکه در دسترس نیست
- UV_ENFILE
- سرریز جدول فایلها
- UV_ENOBUFS
- فضای بافر در دسترس نیست
- UV_ENODEV
- چنین دستگاهی وجود ندارد
- UV_ENOENT
- چنین فایل یا دایرکتوری وجود ندارد
- UV_ENOMEM
- حافظه کافی نیست
- UV_ENONET
- رایانه در شبکه نیست
- UV_ENOPROTOOPT
- پروتکل در دسترس نیست
- UV_ENOSPC
- فضایی روی دستگاه باقی نمانده است
- UV_ENOSYS
- تابع پیادهسازی نشده است
- UV_ENOTCONN
- سوکت متصل نیست
- UV_ENOTDIR
- دایرکتوری نیست
- UV_ENOTEMPTY
- دایرکتوری خالی نیست
- UV_ENOTSOCK
- عملیات سوکت روی غیرسوکت
- UV_ENOTSUP
- عملیات روی سوکت پشتیبانی نمیشود
- UV_EOVERFLOW
- مقدار برای نوع داده تعریفشده بیش از حد بزرگ است
- UV_EPERM
- عملیات مجاز نیست
- UV_EPIPE
- لوله شکسته است
- UV_EPROTO
- خطای پروتکل
- UV_EPROTONOSUPPORT
- پروتکل پشتیبانی نمیشود
- UV_EPROTOTYPE
- نوع پروتکل برای سوکت نادرست است
- UV_ERANGE
- نتیجه بیش از حد بزرگ است
- UV_EROFS
- سیستم فایل فقطخواندنی است
- UV_ESHUTDOWN
- ارسال پس از خاموش شدن نقطه پایانی انتقال امکانپذیر نیست
- UV_ESPIPE
- جستجوی موقعیت نامعتبر است
- UV_ESRCH
- چنین فرآیندی وجود ندارد
- UV_ETIMEDOUT
- مهلت اتصال به پایان رسید
- UV_ETXTBSY
- فایل متنی مشغول است
- UV_EXDEV
- پیوند بیندستگاهی مجاز نیست
- UV_UNKNOWN
- خطای ناشناخته
- UV_EOF
- پایان فایل
- UV_ENXIO
- چنین دستگاه یا آدرسی وجود ندارد
- UV_EMLINK
- تعداد پیوندها بیش از حد است
- UV_ENOTTY
- دستور ioctl برای دستگاه نامناسب است
- UV_EFTYPE
- نوع فایل یا قالب آن نامناسب است
- UV_EILSEQ
- توالی بایت غیرمجاز
- UV_ESOCKTNOSUPPORT
- نوع سوکت پشتیبانی نمیشود
- UV_EUNATCH
- درایور پروتکل متصل نیست
رابط برنامهنویسی (API)
- UV_ERRNO_MAP(iter_macro)
- ماکرویی که به مجموعهای از فراخوانیهای iter_macro برای هر یک از ثابتهای خطای فوق بسط مییابد. ماکروی iter_macro با دو آرگومان فراخوانی میشود: نام ثابت خطا بدون پیشوند UV_، و رشته متنی پیام خطا.
- const char *uv_strerror(int err)
- پیام خطای مربوط به کد خطای دادهشده را برمیگرداند. در صورت فراخوانی با یک کد خطای ناشناخته، چند بایت حافظه نشت میکند.
- char *uv_strerror_r(int err, char *buf, size_t buflen)
- پیام خطای
مربوط به کد
خطای
دادهشده
را
برمیگرداند.
پیام
خاتمهیافته
با صفر در
بافر buf
ارائهشده
توسط کاربر
با حداکثر
طول buflen بایت
ذخیره
میشود.
در نسخه 1.22.0 اضافه شد.
- const char *uv_err_name(int err)
- نام خطای مربوط به کد خطای دادهشده را برمیگرداند. در صورت فراخوانی با یک کد خطای ناشناخته، چند بایت حافظه نشت میکند.
- char *uv_err_name_r(int err, char *buf, size_t buflen)
- نام خطای
مربوط به کد
خطای
دادهشده
را
برمیگرداند.
نام
خاتمهیافته
با صفر در
بافر buf
ارائهشده
توسط کاربر
با حداکثر
طول buflen بایت
ذخیره
میشود.
در نسخه 1.22.0 اضافه شد.
- int uv_translate_sys_error(int sys_errno)
- کد خطای
معادل در libuv
را برای کد
خطای
وابسته به
سکوی
دادهشده
برمیگرداند:
کدهای خطای
POSIX در یونیکس
(مواردی که
در errno ذخیره
میشوند)، و
کدهای خطای
Win32 در ویندوز
(مواردی که
توسط GetLastError() یا
WSAGetLastError()
برگردانده
میشوند).
اگر sys_errno از قبل یک خطای libuv باشد، صرفاً همان برگردانده میشود.
در نسخه 1.10.0 تغییر کرد: تابع بهصورت عمومی اعلام شد.
ماکروها و توابع بررسی نسخه (Version-checking macros and functions)
از نسخه 1.0.0 به بعد، libuv از طرح نسخهبندی معنایی https://semver.org پیروی میکند. این بدان معناست که در طول چرخه عمر یک انتشار اصلی میتوان رابطهای برنامهنویسی جدیدی اضافه کرد. در این بخش تمامی ماکروها و توابعی را خواهید یافت که امکان نوشتن یا کامپایل شرطی کد را برای کار با چندین نسخه مختلف libuv فراهم میکنند.
ماکروها (Macros)
- UV_VERSION_MAJOR
- شماره اصلی نسخه libuv.
- UV_VERSION_MINOR
- شماره فرعی نسخه libuv.
- UV_VERSION_PATCH
- شماره وصله نسخه libuv.
- UV_VERSION_IS_RELEASE
- تنظیم روی 1 برای نشان دادن نسخه انتشار پایدار libuv، و 0 برای نسخه در حال توسعه.
- UV_VERSION_SUFFIX
- پسوند نسخه libuv. برخی از نسخههای در حال توسعه مانند کاندیداهای انتشار (Release Candidates) ممکن است دارای پسوندی مانند "rc" باشند.
- UV_VERSION_HEX
- نسخه libuv را در
قالب یک عدد
صحیح فشرده
برمیگرداند.
۸ بیت برای
هر مؤلفه
استفاده
میشود که
شماره وصله
در ۸ بیت
کمارزشتر
ذخیره
میشود.
برای مثال
برای libuv 1.2.3 این
مقدار 0x010203
خواهد بود.
در نسخه 1.7.0 اضافه شد.
توابع (Functions)
- unsigned int uv_version(void)
- مقدار UV_VERSION_HEX را برمیگرداند.
- const char *uv_version_string(void)
- شماره نسخه libuv را به صورت یک رشته برمیگرداند. برای نسخههای غیر انتشار، پسوند نسخه نیز گنجانده میشود.
uv_loop_t --- حلقه رویداد (Event loop)
حلقه رویداد (Event loop) بخش مرکزی و هسته اصلی قابلیتهای libuv است. این حلقه وظیفه سرکشی (polling) برای ورودی/خروجی (I/O) و زمانبندی اجرای توابع بازخوانی (callbacks) را بر اساس منابع مختلف رویداد بر عهده دارد.
انواع داده (Data types)
- type uv_loop_t
- نوع داده حلقه رویداد.
- enum uv_loop_option
- گزینههای تکمیلی حلقه رویداد. ببینید: uv_loop_configure().
typedef enum {
UV_LOOP_BLOCK_SIGNAL = 0,
UV_METRICS_IDLE_TIME,
UV_LOOP_USE_IO_URING_SQPOLL
} uv_loop_option;
- enum uv_run_mode
- حالت مورد استفاده برای اجرای حلقه با uv_run().
typedef enum {
UV_RUN_DEFAULT = 0,
UV_RUN_ONCE,
UV_RUN_NOWAIT
} uv_run_mode;
- typedef void (*uv_walk_cb)(uv_handle_t <#c.uv_handle_t> *handle, void *arg)
- تعریف نوع تابع بازخوانی ارسالشده به uv_walk().
اعضای عمومی (Public members)
- void *uv_loop_t <#c.uv_loop_t>.data
- فضایی برای نگهداری دادههای دلخواه تعریفشده توسط کاربر. libuv از این فیلد استفاده نمیکند و دست به آن نمیزند.
رابط برنامهنویسی (API)
- int uv_loop_init(uv_loop_t <#c.uv_loop_t> *loop)
- ساختار uv_loop_t دادهشده را مقداردهی اولیه میکند.
- int uv_loop_configure(uv_loop_t <#c.uv_loop_t> *loop, uv_loop_option <#c.uv_loop_option> option, ...)
- اضافهشده
در نسخه 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 اضافه شد.
- int uv_loop_close(uv_loop_t <#c.uv_loop_t> *loop)
- تمام منابع داخلی حلقه را آزاد میکند. این تابع را تنها زمانی فراخوانی کنید که اجرای حلقه به پایان رسیده باشد و تمام هندلها و درخواستهای باز بسته شده باشند، در غیر این صورت مقدار UV_EBUSY را برمیگرداند. پس از بازگشت این تابع، کاربر میتواند حافظه تخصیصیافته برای حلقه را آزاد کند.
- uv_loop_t <#c.uv_loop_t> *uv_default_loop(void)
- حلقه
پیشفرض
مقداردهیشده
را
برمیگرداند.
در صورت
شکست در
تخصیص
حافظه ممکن
است NULL
برگرداند.
این تابع صرفاً روشی مناسب برای داشتن یک حلقه سراسری در سرتاسر برنامه است، حلقه پیشفرض به هیچ وجه تفاوتی با حلقههای مقداردهیشده توسط uv_loop_init() ندارد. به همین ترتیب، حلقه پیشفرض میتواند (و باید) با uv_loop_close() بسته شود تا منابع مرتبط با آن آزاد شوند.
هشدار:
- int uv_run(uv_loop_t <#c.uv_loop_t> *loop, uv_run_mode <#c.uv_run_mode> mode)
- این تابع حلقه رویداد را اجرا میکند. رفتار آن بسته به حالت مشخصشده متفاوت خواهد بود:
- UV_RUN_DEFAULT: حلقه رویداد را تا زمانی که هیچ هندل یا درخواست فعال و ارجاعشدهای باقی نمانده باشد اجرا میکند. اگر uv_stop() فراخوانی شده باشد و همچنان هندلها یا درخواستهای فعالی وجود داشته باشند، مقدار غیرصفر برمیگرداند. در سایر موارد مقدار صفر برمیگرداند.
- UV_RUN_ONCE: یک بار برای ورودی/خروجی سرکشی (poll) میکند. توجه داشته باشید که اگر هیچ تابع بازخوانی معلقی وجود نداشته باشد، این تابع مسدود (block) میشود. در صورت اتمام (عدم وجود هندل یا درخواست فعال باقیمانده) مقدار صفر، یا اگر انتظار بازخوانیهای بیشتری میرود (به این معنی که باید حلقه رویداد را در آینده دوباره اجرا کنید) مقدار غیرصفر برمیگرداند.
- UV_RUN_NOWAIT: یک بار برای ورودی/خروجی سرکشی میکند اما اگر هیچ تابع بازخوانی معلقی وجود نداشته باشد مسدود نمیشود. در صورت اتمام (عدم وجود هندل یا درخواست فعال باقیمانده) مقدار صفر، یا اگر انتظار بازخوانیهای بیشتری میرود مقدار غیرصفر برمیگرداند.
تابع uv_run() بازگشتپذیر مجدد (reentrant) نیست. نباید آن را از درون یک تابع بازخوانی فراخوانی کرد.
- int uv_loop_alive(const uv_loop_t <#c.uv_loop_t> *loop)
- اگر هندلهای فعال دارای ارجاع، درخواستهای فعال یا هندلهای در حال بسته شدن در حلقه وجود داشته باشند، مقدار غیرصفر برمیگرداند.
- void uv_stop(uv_loop_t <#c.uv_loop_t> *loop)
- حلقه رویداد را متوقف میکند و باعث میشود uv_run() در سریعترین زمان ممکن به پایان برسد. این اتفاق زودتر از تکرار (iteration) بعدی حلقه رخ نخواهد داد. اگر این تابع قبل از مسدود شدن برای ورودی/خروجی فراخوانی شده باشد، حلقه در این تکرار برای ورودی/خروجی مسدود نخواهد شد.
- size_t uv_loop_size(void)
- اندازه ساختار uv_loop_t را برمیگرداند. برای نویسندگان بایندرهای FFI که مایل به دانستن ساختار درونی نیستند مفید است.
- int uv_backend_fd(const uv_loop_t <#c.uv_loop_t> *loop)
- توصیفکننده
فایل (file descriptor)
بکاند را
دریافت
میکند. فقط
kqueue، epoll و event ports
پشتیبانی
میشوند.
این تابع میتواند همراه با uv_run(loop, UV_RUN_NOWAIT) برای سرکشی در یک ریسه (thread) و اجرای توابع بازخوانی حلقه رویداد در ریسهای دیگر استفاده شود؛ برای مشاهده مثال به test/test-embed.c مراجعه کنید.
نکته:
- int uv_backend_timeout(const uv_loop_t <#c.uv_loop_t> *loop)
- مهلت زمانی سرکشی (poll timeout) را برمیگرداند. مقدار برگشتی به میلیثانیه است، یا -1 برای عدم وجود مهلت زمانی.
- uint64_t uv_now(const uv_loop_t <#c.uv_loop_t> *loop)
- برچسب
زمانی فعلی
را به
میلیثانیه
برمیگرداند.
این برچسب
زمانی در
ابتدای تیک
حلقه
رویداد
ذخیره (cache)
میشود؛
برای
جزئیات و
دلایل آن به
uv_update_time() مراجعه
کنید.
برچسب زمانی به صورت یکنواخت (monotonically) از یک نقطه زمانی دلخواه افزایش مییابد. هیچ فرضی درباره نقطه شروع در نظر نگیرید.
نکته:
- void uv_update_time(uv_loop_t <#c.uv_loop_t> *loop)
- مفهوم
«اکنون» (now)
را در حلقه
رویداد
بهروزرسانی
میکند. Libuv
زمان فعلی
را در
ابتدای تیک
حلقه
رویداد
ذخیره (cache)
میکند تا
تعداد
فراخوانیهای
سیستمی
مرتبط با
زمان کاهش
یابد.
معمولاً نیازی به فراخوانی این تابع نخواهید داشت مگر اینکه توابع بازخوانی داشته باشید که حلقه رویداد را برای مدت طولانی مسدود کنند، جایی که «طولانی» تا حدی نسبی است اما معمولاً در مقیاس یک میلیثانیه یا بیشتر است.
- void uv_walk(uv_loop_t <#c.uv_loop_t> *loop, uv_walk_cb <#c.uv_walk_cb> walk_cb, void *arg)
- پیمایش لیست هندلها: تابع walk_cb با آرگومان arg دادهشده اجرا خواهد شد.
- int uv_loop_fork(uv_loop_t <#c.uv_loop_t> *loop)
- اضافهشده
در نسخه 1.12.0.
مقداردهی مجدد هرگونه وضعیت کرنل لازم در فرآیند فرزند پس از یک فراخوانی سیستمی fork(2) https://man7.org/linux/man-pages/man2/fork.2.html.
پایشگرهایی (watchers) که قبلاً شروع شدهاند، در فرآیند فرزند نیز به کار خود ادامه خواهند داد.
فراخوانی صریح این تابع روی هر حلقه رویدادی که در فرآیند والد ایجاد شده و قصد ادامه استفاده از آن در فرآیند فرزند را دارید ضروری است، از جمله حلقه پیشفرض (حتی اگر دیگر در والد از آن استفاده نکنید). این تابع باید قبل از فراخوانی uv_run() یا هر تابع API دیگر که از حلقه در فرزند استفاده میکند فراخوانی شود. عدم انجام این کار منجر به رفتار تعریفنشده خواهد شد، از جمله احتمال تحویل رویدادهای تکراری به هر دو فرآیند والد و فرزند یا متوقف شدن غیرمنتظره فرآیند فرزند.
در صورت امکان، ترجیح داده میشود به جای استفاده مجدد از حلقهای که در والد ایجاد شده، یک حلقه جدید در فرآیند فرزند ایجاد شود. حلقههای جدیدی که در فرآیند فرزند پس از fork ایجاد میشوند نباید از این تابع استفاده کنند.
این تابع روی ویندوز پیادهسازی نشده است و در آنجا مقدار UV_ENOSYS را برمیگرداند.
احتیاط:
نکته:
احتیاط:
احتیاط:
- void *uv_loop_get_data(const uv_loop_t <#c.uv_loop_t> *loop)
- مقدار loop->data
را
برمیگرداند.
اضافهشده در نسخه 1.19.0.
- void uv_loop_set_data(uv_loop_t <#c.uv_loop_t> *loop, void *data)
- مقدار loop->data
را روی data
تنظیم
میکند.
اضافهشده در نسخه 1.19.0.
uv_handle_t --- هندل پایه (Base handle)
ساختار uv_handle_t نوع پایه برای تمامی انواع هندلها در libuv است.
ساختارها به گونهای تراز شدهاند که هر هندل libuv را میتوان به uv_handle_t تبدیل (cast) کرد. تمامی توابع API تعریفشده در اینجا با هر نوع هندلی کار میکنند.
هندلهای Libuv غیرقابل جابهجایی (non-movable) هستند. اشارهگرهای ساختار هندل که به توابع ارسال میشوند باید در طول مدت عملیات درخواستی معتبر باقی بمانند. هنگام استفاده از هندلهای تخصیصیافته در پشته (stack) دقت کنید.
انواع داده (Data types)
- type uv_handle_t
- نوع هندل پایه در libuv.
- enum uv_handle_type
- نوع هندل 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;
- type uv_any_handle
- اجتماع (Union) تمام انواع هندلها.
- typedef void (*uv_alloc_cb)(uv_handle_t <#c.uv_handle_t> *handle, size_t suggested_size, uv_buf_t <#c.uv_buf_t> *buf)
- تعریف نوع
تابع
بازخوانی
ارسالشده
به 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;
}
- typedef void (*uv_close_cb)(uv_handle_t <#c.uv_handle_t> *handle)
- تعریف نوع تابع بازخوانی ارسالشده به uv_close().
اعضای عمومی (Public members)
- uv_loop_t <#c.uv_loop_t> *uv_handle_t <#c.uv_handle_t>.loop
- اشارهگر به uv_loop_t <#c.uv_loop_t> که هندل روی آن در حال اجرا است. فقطخواندنی.
- uv_handle_type <#c.uv_handle_type> uv_handle_t <#c.uv_handle_t>.type
- مقدار uv_handle_type که نوع هندل زیرین را مشخص میکند. فقطخواندنی.
- void *uv_handle_t <#c.uv_handle_t>.data
- فضایی برای نگهداری دادههای دلخواه تعریفشده توسط کاربر. libuv از این فیلد استفاده نمیکند.
رابط برنامهنویسی (API)
- UV_HANDLE_TYPE_MAP(iter_macro)
- ماکرویی که به مجموعهای از فراخوانیهای iter_macro برای هر یک از انواع هندل باز میشود. ماکروی iter_macro با دو آرگومان فراخوانی میشود: نام عنصر uv_handle_type بدون پیشوند UV_، و نام نوع ساختار متناظر بدون پیشوند uv_ و پسوند _t.
- int uv_is_active(const uv_handle_t <#c.uv_handle_t> *handle)
- در صورتی که هندل فعال باشد مقدار غیرصفر و اگر غیرفعال باشد مقدار صفر را برمیگرداند. معنای «فعال» به نوع هندل بستگی دارد:
- یک هندل 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() مجدداً هندل را غیرفعال میکند.
- int uv_is_closing(const uv_handle_t <#c.uv_handle_t> *handle)
- در صورتی که
هندل در حال
بسته شدن یا
بسته شده
باشد مقدار
غیرصفر و در
غیر این
صورت صفر
برمیگرداند.
نکته:
- void uv_close(uv_handle_t <#c.uv_handle_t> *handle, uv_close_cb <#c.uv_close_cb> close_cb)
- درخواست
بسته شدن
هندل. تابع
close_cb پس از این
فراخوانی
به صورت
ناهمگام
صدا زده
خواهد شد.
این تابع
الزماً
باید قبل از
آزادسازی
حافظه روی
هر هندل
فراخوانی
شود. علاوه
بر این،
حافظه فقط
در close_cb یا پس
از بازگشت
آن قابل
آزادسازی
است.
هندلهایی که توصیفکنندههای فایل را در بر میگیرند بلافاصله بسته میشوند اما close_cb همچنان به تکرار بعدی حلقه رویداد موکول میشود. این به شما فرصت میدهد تا هرگونه منبع مرتبط با هندل را آزاد کنید.
درخواستهای در حال انجام، مانند uv_connect_t یا uv_write_t، لغو میشوند و بازخوانیهای آنها به صورت ناهمگام با وضعیت status=UV_ECANCELED فراخوانی میشوند.
در مواردی که پاکسازی یا آزادسازی حافظه لازم نیست، close_cb میتواند NULL باشد.
- void uv_ref(uv_handle_t <#c.uv_handle_t> *handle)
- ارجاع به
هندل
دادهشده
اضافه
میکند.
ارجاعها
خودتوان (idempotent)
هستند،
یعنی اگر
هندلی از
قبل ارجاع
داده شده
باشد،
فراخوانی
مجدد این
تابع اثری
نخواهد
داشت.
ببینید: شمارش ارجاع (Reference counting).
- void uv_unref(uv_handle_t <#c.uv_handle_t> *handle)
- ارجاع از
هندل
دادهشده
را حذف
میکند.
ارجاعها
خودتوان
هستند،
یعنی اگر
هندل
ارجاعی
نداشته
باشد،
فراخوانی
مجدد این
تابع اثری
نخواهد
داشت.
ببینید: شمارش ارجاع (Reference counting).
- int uv_has_ref(const uv_handle_t <#c.uv_handle_t> *handle)
- اگر هندل
دارای
ارجاع باشد
مقدار
غیرصفر و در
غیر این
صورت صفر
برمیگرداند.
ببینید: شمارش ارجاع (Reference counting).
- size_t uv_handle_size(uv_handle_type <#c.uv_handle_type> type)
- اندازه نوع هندل دادهشده را برمیگرداند. برای نویسندگان بایندرهای FFI که مایل به دانستن ساختار درونی نیستند مفید است.
توابع متفرقه رابط برنامهنویسی (Miscellaneous API functions)
توابع API زیر یک آرگومان uv_handle_t دریافت میکنند اما فقط برای برخی از انواع هندل کار میکنند.
- int uv_send_buffer_size(uv_handle_t <#c.uv_handle_t> *handle, int *value)
- اندازه
بافر
ارسالی که
سیستمعامل
برای سوکت
استفاده
میکند را
دریافت یا
تنظیم
میکند.
اگر *value == 0 باشد، مقدار *value را روی اندازه فعلی بافر ارسال تنظیم میکند. اگر *value > 0 باشد، از *value برای تنظیم اندازه جدید بافر ارسال استفاده میکند.
در صورت موفقیت صفر و در صورت بروز خطا یک مقدار منفی برمیگرداند.
این تابع برای هندلهای TCP، پایپ و UDP در یونیکس و برای هندلهای TCP و UDP در ویندوز کار میکند.
نکته:
- int uv_recv_buffer_size(uv_handle_t <#c.uv_handle_t> *handle, int *value)
- اندازه
بافر
دریافتی که
سیستمعامل
برای سوکت
استفاده
میکند را
دریافت یا
تنظیم
میکند.
اگر *value == 0 باشد، مقدار *value را روی اندازه فعلی بافر دریافت تنظیم میکند. اگر *value > 0 باشد، از *value برای تنظیم اندازه جدید بافر دریافت استفاده میکند.
در صورت موفقیت صفر و در صورت بروز خطا یک مقدار منفی برمیگرداند.
این تابع برای هندلهای TCP، پایپ و UDP در یونیکس و برای هندلهای TCP و UDP در ویندوز کار میکند.
نکته:
- int uv_fileno(const uv_handle_t <#c.uv_handle_t> *handle, uv_os_fd_t <#c.uv_os_fd_t> *fd)
- معادل
توصیفکننده
فایل
وابسته به
پلتفرم را
دریافت
میکند.
هندلهای زیر پشتیبانی میشوند: TCP، پایپها، TTY، UDP و poll. ارسال هر نوع هندل دیگر با خطای UV_EINVAL شکست خواهد خورد.
اگر هندلی هنوز توصیفکننده فایل متصل نداشته باشد یا خود هندل بسته شده باشد، این تابع UV_EBADF را برمیگرداند.
هشدار:
- uv_loop_t <#c.uv_loop_t> *uv_handle_get_loop(const uv_handle_t <#c.uv_handle_t> *handle)
- مقدار handle->loop
را
برمیگرداند.
اضافهشده در نسخه 1.19.0.
- void *uv_handle_get_data(const uv_handle_t <#c.uv_handle_t> *handle)
- مقدار handle->data
را
برمیگرداند.
اضافهشده در نسخه 1.19.0.
- void uv_handle_set_data(uv_handle_t <#c.uv_handle_t> *handle, void *data)
- مقدار handle->data
را روی data
تنظیم
میکند.
اضافهشده در نسخه 1.19.0.
- uv_handle_type <#c.uv_handle_type> uv_handle_get_type(const uv_handle_t <#c.uv_handle_t> *handle)
- مقدار handle->type
را
برمیگرداند.
اضافهشده در نسخه 1.19.0.
- const char *uv_handle_type_name(uv_handle_type <#c.uv_handle_type> type)
- نام ساختار
معادل را
برای یک نوع
هندل
دادهشده
برمیگرداند،
مثلاً "pipe"
(همانند uv_pipe_t
<#c.uv_pipe_t>) برای
UV_NAMED_PIPE.
اگر چنین نوع هندلی وجود نداشته باشد، مقدار NULL برمیگرداند.
اضافهشده در نسخه 1.19.0.
شمارش ارجاع (Reference counting)
حلقه رویداد libuv (اگر در حالت پیشفرض اجرا شود) تا زمانی اجرا میشود که هیچ هندل فعال و دارای ارجاعی باقی نمانده باشد. کاربر میتواند با حذف ارجاع از هندلهای فعال، حلقه را وادار به خروج زودهنگام کند؛ برای مثال با فراخوانی uv_unref() پس از فراخوانی uv_timer_start() <#c.uv_timer_start>.
یک هندل میتواند ارجاعدار یا بدونارجاع باشد، طرح شمارش ارجاع از یک شمارنده استفاده نمیکند، بنابراین هر دو عملیات خودتوان (idempotent) هستند.
تمام هندلها هنگام فعال بودن به طور پیشفرض ارجاع دارند؛ برای توضیح دقیقتر درباره اینکه فعال بودن شامل چه مواردی است به uv_is_active() مراجعه کنید.
uv_req_t --- درخواست پایه (Base request)
ساختار uv_req_t نوع پایه برای تمامی انواع درخواستها در libuv است.
ساختارها به گونهای تراز شدهاند که هر درخواست libuv را میتوان به uv_req_t تبدیل (cast) کرد. تمامی توابع API تعریفشده در اینجا با هر نوع درخواستی کار میکنند.
انواع داده (Data types)
- type uv_req_t
- ساختار پایه درخواست libuv.
- type uv_any_req
- اجتماع (Union) تمام انواع درخواستها.
- enum uv_req_type
- نوع درخواست 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;
اعضای عمومی (Public members)
- void *uv_req_t <#c.uv_req_t>.data
- فضایی برای نگهداری دادههای دلخواه تعریفشده توسط کاربر. libuv از این فیلد استفاده نمیکند.
- uv_req_type <#c.uv_req_type> uv_req_t <#c.uv_req_t>.type
- مقدار uv_req_type که نوع درخواست را مشخص میکند. فقطخواندنی.
رابط برنامهنویسی (API)
- UV_REQ_TYPE_MAP(iter_macro)
- ماکرویی که به مجموعهای از فراخوانیهای iter_macro برای هر یک از انواع درخواست باز میشود. ماکروی iter_macro با دو آرگومان فراخوانی میشود: نام عنصر uv_req_type بدون پیشوند UV_، و نام نوع ساختار متناظر بدون پیشوند uv_ و پسوند _t.
- int uv_cancel(uv_req_t <#c.uv_req_t> *req)
- لغو یک
درخواست
معلق. اگر
درخواست در
حال اجرا
باشد یا
اجرای آن به
پایان
رسیده باشد
با شکست
مواجه
میشود.
در صورت موفقیت مقدار 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 فراخوانی میشود.
- size_t uv_req_size(uv_req_type <#c.uv_req_type> type)
- اندازه نوع درخواست دادهشده را برمیگرداند. برای نویسندگان بایندرهای FFI که مایل به دانستن ساختار درونی نیستند مفید است.
- void *uv_req_get_data(const uv_req_t <#c.uv_req_t> *req)
- مقدار req->data
را
برمیگرداند.
اضافهشده در نسخه 1.19.0.
- void uv_req_set_data(uv_req_t <#c.uv_req_t> *req, void *data)
- مقدار req->data
را روی data
تنظیم
میکند.
اضافهشده در نسخه 1.19.0.
- uv_req_type <#c.uv_req_type> uv_req_get_type(const uv_req_t <#c.uv_req_t> *req)
- مقدار req->type
را
برمیگرداند.
اضافهشده در نسخه 1.19.0.
- const char *uv_req_type_name(uv_req_type <#c.uv_req_type> type)
- نام ساختار
معادل را
برای یک نوع
درخواست
دادهشده
برمیگرداند،
مثلاً "connect"
(همانند uv_connect_t
<#c.uv_connect_t>) برای
UV_CONNECT.
اگر چنین نوع درخواستی وجود نداشته باشد، مقدار NULL برمیگرداند.
اضافهشده در نسخه 1.19.0.
uv_timer_t --- هندل تایمر (Timer handle)
هندلهای تایمر برای زمانبندی فراخوانی توابع بازخوانی در آینده استفاده میشوند.
تایمرها یا تکباره (single-shot) هستند یا تکرارشونده. تایمرهای تکرارشونده سربار زمانی را تنظیم نمیکنند بلکه نسبت به مفهوم «اکنون» در حلقه رویداد مجدداً آمادهباش (rearm) میشوند.
Libuv مفهوم «اکنون» خود را درست پیش از اجرای بازخوانیهای تایمر و بلافاصله پس از بیدار شدن از انتظار برای ورودی/خروجی بهروزرسانی میکند. همچنین ببینید: uv_update_time() <#c.uv_update_time>.
مثال: یک تایمر تکرارشونده با بازه 50 میلیثانیهای که اجرای بازخوانی آن 17 میلیثانیه طول میکشد، 33 میلیثانیه بعد دوباره اجرا میشود. اگر کارهای دیگر بیشتر از 33 میلیثانیه طول بکشند، بازخوانی تایمر در سریعترین زمان ممکن اجرا خواهد شد.
انواع داده (Data types)
- type uv_timer_t
- نوع هندل تایمر.
- typedef void (*uv_timer_cb)(uv_timer_t <#c.uv_timer_t> *handle)
- تعریف نوع تابع بازخوانی ارسالشده به uv_timer_start().
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_timer_start(uv_timer_t <#c.uv_timer_t> *handle, uv_timer_cb <#c.uv_timer_cb> cb, uint64_t timeout, uint64_t repeat)
- راهاندازی
تایمر.
مقادیر timeout و
repeat به
میلیثانیه
هستند.
اگر timeout صفر باشد، تابع بازخوانی در تکرار بعدی حلقه رویداد اجرا میشود. اگر repeat غیرصفر باشد، بازخوانی ابتدا پس از timeout میلیثانیه و سپس به صورت مکرر پس از هر repeat میلیثانیه اجرا میشود.
نکته:
اگر تایمر از قبل فعال باشد، صرفاً بهروزرسانی میشود.
- int uv_timer_stop(uv_timer_t <#c.uv_timer_t> *handle)
- متوقف کردن تایمر، تابع بازخوانی دیگر فراخوانی نخواهد شد.
- int uv_timer_again(uv_timer_t <#c.uv_timer_t> *handle)
- تایمر را متوقف میکند، و اگر تکرارشونده باشد آن را با استفاده از مقدار repeat به عنوان مهلت زمانی (timeout) مجدداً راهاندازی میکند. اگر تایمر قبلاً هرگز شروع نشده باشد مقدار UV_EINVAL برمیگرداند.
- void uv_timer_set_repeat(uv_timer_t <#c.uv_timer_t> *handle, uint64_t repeat)
- مقدار بازه
تکرار را به
میلیثانیه
تنظیم
میکند.
تایمر برای
اجرا در
بازه زمانی
دادهشده
زمانبندی
خواهد شد،
صرفنظر از
مدت زمان
اجرای تابع
بازخوانی،
و در صورت
عبور از
برهه زمانی
(time-slice overrun) از
معناشناسی
عادی تایمر
پیروی
خواهد کرد.
نکته:
- uint64_t uv_timer_get_repeat(const uv_timer_t <#c.uv_timer_t> *handle)
- دریافت مقدار تکرار تایمر.
- uint64_t uv_timer_get_due_in(const uv_timer_t <#c.uv_timer_t> *handle)
- زمان
باقیمانده
تا سررسید
تایمر یا 0
را در صورت
منقضی شدن
برمیگرداند.
زمان نسبت
به uv_now() <#c.uv_now>
است.
اضافهشده در نسخه 1.40.0.
همچنین ببینید:
uv_prepare_t --- هندل آمادهسازی (Prepare handle)
هندلهای آمادهسازی (Prepare) تابع بازخوانی دادهشده را یک بار در هر تکرار حلقه، درست قبل از سرکشی (poll) برای ورودی/خروجی اجرا میکنند.
انواع داده (Data types)
- type uv_prepare_t
- نوع هندل آمادهسازی (Prepare).
- typedef void (*uv_prepare_cb)(uv_prepare_t <#c.uv_prepare_t> *handle)
- تعریف نوع تابع بازخوانی ارسالشده به uv_prepare_start().
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_prepare_init(uv_loop_t <#c.uv_loop_t> *loop, uv_prepare_t <#c.uv_prepare_t> *prepare)
- مقداردهی اولیه هندل. این تابع همیشه با موفقیت انجام میشود.
- بازگشتیها
- 0
- int uv_prepare_start(uv_prepare_t <#c.uv_prepare_t> *prepare, uv_prepare_cb <#c.uv_prepare_cb> cb)
- راهاندازی هندل با تابع بازخوانی دادهشده. این تابع همیشه با موفقیت انجام میشود، مگر زمانی که cb مقدار NULL باشد.
- بازگشتیها
- 0 در صورت موفقیت، یا UV_EINVAL زمانی که cb == NULL باشد.
- int uv_prepare_stop(uv_prepare_t <#c.uv_prepare_t> *prepare)
- متوقف کردن هندل، تابع بازخوانی دیگر فراخوانی نخواهد شد. این تابع همیشه با موفقیت انجام میشود.
- بازگشتیها
- 0
همچنین ببینید:
uv_check_t --- هندل بررسی (Check handle)
هندلهای بررسی (Check) تابع بازخوانی دادهشده را یک بار در هر تکرار حلقه، درست بعد از سرکشی برای ورودی/خروجی اجرا میکنند.
انواع داده (Data types)
- type uv_check_t
- نوع هندل بررسی (Check).
- typedef void (*uv_check_cb)(uv_check_t <#c.uv_check_t> *handle)
- تعریف نوع تابع بازخوانی ارسالشده به uv_check_start().
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_check_init(uv_loop_t <#c.uv_loop_t> *loop, uv_check_t <#c.uv_check_t> *check)
- مقداردهی اولیه هندل. این تابع همیشه با موفقیت انجام میشود.
- بازگشتیها
- 0
- int uv_check_start(uv_check_t <#c.uv_check_t> *check, uv_check_cb <#c.uv_check_cb> cb)
- راهاندازی هندل با تابع بازخوانی دادهشده. این تابع همیشه با موفقیت انجام میشود، مگر زمانی که cb مقدار NULL باشد.
- بازگشتیها
- 0 در صورت موفقیت، یا UV_EINVAL زمانی که cb == NULL باشد.
- int uv_check_stop(uv_check_t <#c.uv_check_t> *check)
- متوقف کردن هندل، تابع بازخوانی دیگر فراخوانی نخواهد شد. این تابع همیشه با موفقیت انجام میشود.
- بازگشتیها
- 0
همچنین ببینید:
uv_idle_t --- هندل بیکار (Idle handle)
هندلهای بیکار (Idle) تابع بازخوانی دادهشده را یک بار در هر تکرار حلقه، درست قبل از هندلهای uv_prepare_t <#c.uv_prepare_t> اجرا میکنند.
نکته:
هشدار:
انواع داده (Data types)
- type uv_idle_t
- نوع هندل بیکار (Idle).
- typedef void (*uv_idle_cb)(uv_idle_t <#c.uv_idle_t> *handle)
- تعریف نوع تابع بازخوانی ارسالشده به uv_idle_start().
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_idle_init(uv_loop_t <#c.uv_loop_t> *loop, uv_idle_t <#c.uv_idle_t> *idle)
- مقداردهی اولیه هندل. این تابع همیشه با موفقیت انجام میشود.
- بازگشتیها
- 0
- int uv_idle_start(uv_idle_t <#c.uv_idle_t> *idle, uv_idle_cb <#c.uv_idle_cb> cb)
- راهاندازی هندل با تابع بازخوانی دادهشده. این تابع همیشه با موفقیت انجام میشود، مگر زمانی که cb مقدار NULL باشد.
- بازگشتیها
- 0 در صورت موفقیت، یا UV_EINVAL زمانی که cb == NULL باشد.
- int uv_idle_stop(uv_idle_t <#c.uv_idle_t> *idle)
- متوقف کردن هندل، تابع بازخوانی دیگر فراخوانی نخواهد شد. این تابع همیشه با موفقیت انجام میشود.
- بازگشتیها
- 0
همچنین ببینید:
uv_async_t --- هندل ناهمگام (Async handle)
هندلهای ناهمگام (Async) به کاربر اجازه میدهند حلقه رویداد را «بیدار» (wakeup) کنند و بازخوانی فراخوانیشدهای از ریسه دیگر دریافت کنند.
انواع داده (Data types)
- type uv_async_t
- نوع هندل ناهمگام (Async).
- typedef void (*uv_async_cb)(uv_async_t <#c.uv_async_t> *handle)
- تعریف نوع تابع بازخوانی ارسالشده به uv_async_init().
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_async_init(uv_loop_t <#c.uv_loop_t> *loop, uv_async_t <#c.uv_async_t> *async, uv_async_cb <#c.uv_async_cb> async_cb)
- مقداردهی اولیه هندل. ارسال تابع بازخوانی NULL مجاز است.
- بازگشتیها
- 0 در صورت موفقیت، یا کد خطای < 0 در صورت شکست.
نکته:
- int uv_async_send(uv_async_t <#c.uv_async_t> *async)
- بیدار کردن حلقه رویداد و فراخوانی تابع بازخوانی هندل ناهمگام.
- بازگشتیها
- 0 در صورت موفقیت، یا کد خطای < 0 در صورت شکست.
نکته:
نکته:
هشدار:
همچنین ببینید:
uv_poll_t --- قلاب نظرسنجی (Poll handle)
قلابهای نظرسنجی (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> کاملاً ایمن است.
نکته:
نکته:
انواع داده (Data types)
- type uv_poll_t
- نوع داده قلاب نظرسنجی (Poll handle).
- typedef void (*uv_poll_cb)(uv_poll_t <#c.uv_poll_t> *handle, int status, int events)
- تعریف نوع برای تابع بازخوانی ارسالشده به uv_poll_start().
- enum uv_poll_event
- انواع رویدادهای نظرسنجی (Poll event types)
enum uv_poll_event {
UV_READABLE = 1,
UV_WRITABLE = 2,
UV_DISCONNECT = 4,
UV_PRIORITIZED = 8
};
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_poll_init(uv_loop_t <#c.uv_loop_t> *loop, uv_poll_t <#c.uv_poll_t> *handle, int fd)
- راهاندازی
اولیه قلاب
با استفاده
از یک
توصیفکننده
فایل (file descriptor).
تغییریافته در نسخه 1.2.2: توصیفکننده فایل روی حالت غیرمسدودکننده (non-blocking) تنظیم میشود.
- int uv_poll_init_socket(uv_loop_t <#c.uv_loop_t> *loop, uv_poll_t <#c.uv_poll_t> *handle, uv_os_sock_t <#c.uv_os_sock_t> socket)
- راهاندازی
اولیه قلاب
با استفاده
از یک
توصیفکننده
سوکت. در
یونیکس این
تابع
کاملاً
معادل uv_poll_init()
است. در
ویندوز یک
قلاب SOCKET
دریافت
میکند.
تغییریافته در نسخه 1.2.2: سوکت روی حالت غیرمسدودکننده (non-blocking) تنظیم میشود.
- int uv_poll_start(uv_poll_t <#c.uv_poll_t> *handle, int events, uv_poll_cb <#c.uv_poll_cb> cb)
- آغاز
نظرسنجی
توصیفکننده
فایل.
پارامتر 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_READABLE تنظیم شده است).
- تابع بازخوانی عملیات نوشتن را روی سوکت انجام داده، اما سوکت پس از آن نیز همچنان قابل نوشتن باقی مانده است (زمانی که UV_WRITABLE تنظیم شده است).
- سوکت پیش از فراخوانی uv_poll_start() روی قلاب نظرسنجی مرتبط با آن، از قبل خواندنی/نوشتنی شده و از آن زمان وضعیت سوکت تغییر نکرده است.
در تمامی سناریوهای فوق، سوکت خواندنی یا نوشتنی باقی میماند و بنابراین تابع بازخوانی مجدداً فراخوانی خواهد شد (بسته به رویدادهای تنظیمشده در ماسک بیتی). این رفتار به عنوان تحریک سطحی (level triggering) شناخته میشود.
تغییریافته در نسخه 1.9.0: رویداد UV_DISCONNECT اضافه شد.
تغییریافته در نسخه 1.14.0: رویداد UV_PRIORITIZED اضافه شد.
- int uv_poll_stop(uv_poll_t <#c.uv_poll_t> *poll)
- توقف
نظرسنجی
توصیفکننده
فایل؛ تابع
بازخوانی
دیگر
فراخوانی
نخواهد شد.
نکته:
همچنین ببینید:
uv_signal_t --- قلاب سیگنال (Signal handle)
قلابهای سیگنال، مدیریت سیگنال به سبک یونیکس را بر پایه هر حلقه رویداد پیادهسازی میکنند.
نکات ویندوز (Windows notes)
دریافت برخی سیگنالها شبیهسازی شده است:
- سیگنال 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 در ویندوز ۶۴ بیتی به پیادهسازی قدیمی بازگردانده شد.
نکات یونیکس (Unix notes)
- گرفتن سیگنالهای SIGKILL و SIGSTOP غیرممکن است.
- مدیریت SIGBUS، SIGFPE، SIGILL یا SIGSEGV از طریق libuv منجر به رفتار تعریفنشده میشود.
- سیگنال SIGABRT در صورتی که توسط abort() تولید شود (مثلاً از طریق assert())، توسط libuv گرفته نخواهد شد.
- در لینوکس، سیگنالهای SIGRT0 و SIGRT1 (سیگنالهای ۳۲ و ۳۳) توسط کتابخانه NPTL pthreads برای مدیریت نخها استفاده میشوند. نصب دیدهبان برای این سیگنالها منجر به رفتار غیرقابل پیشبینی میشود و اکیداً منع شده است. نسخههای آینده libuv ممکن است به سادگی آنها را رد کنند.
انواع داده (Data types)
- type uv_signal_t
- نوع داده قلاب سیگنال (Signal handle).
- typedef void (*uv_signal_cb)(uv_signal_t <#c.uv_signal_t> *handle, int signum)
- تعریف نوع برای تابع بازخوانی ارسالشده به uv_signal_start().
اعضای عمومی (Public members)
- int uv_signal_t <#c.uv_signal_t>.signum
- سیگنالی که توسط این قلاب پایش میشود. فقطخواندنی.
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_signal_init(uv_loop_t <#c.uv_loop_t> *loop, uv_signal_t <#c.uv_signal_t> *signal)
- راهاندازی اولیه قلاب.
- int uv_signal_start(uv_signal_t <#c.uv_signal_t> *signal, uv_signal_cb <#c.uv_signal_cb> cb, int signum)
- شروع کار قلاب با تابع بازخوانی مشخصشده، در حال پایش سیگنال دادهشده.
- int uv_signal_start_oneshot(uv_signal_t <#c.uv_signal_t> *signal, uv_signal_cb <#c.uv_signal_cb> cb, int signum)
- افزودهشده
در نسخه 1.12.0.
عملکرد مشابه با uv_signal_start() دارد اما کنترلکننده سیگنال به محض دریافت سیگنال بازنشانی میشود.
- int uv_signal_stop(uv_signal_t <#c.uv_signal_t> *signal)
- توقف قلاب؛ تابع بازخوانی دیگر فراخوانی نخواهد شد.
همچنین ببینید:
uv_process_t --- قلاب فرایند (Process handle)
قلابهای فرایند یک فرایند جدید را اجرا (spawn) میکنند و به کاربر اجازه میدهند آن را کنترل کرده و با استفاده از جریانها (streams) کانالهای ارتباطی با آن برقرار کند.
انواع داده (Data types)
- type uv_process_t
- نوع داده قلاب فرایند (Process handle).
- type uv_process_options_t
- گزینهها برای اجرای فرایند (ارسالشده به 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;
- typedef void (*uv_exit_cb)(uv_process_t <#c.uv_process_t>*, int64_t exit_status, int term_signal)
- تعریف نوع برای تابع بازخوانی ارسالشده در uv_process_options_t که وضعیت خروج و سیگنالی را که در صورت وجود باعث خاتمه فرایند شده است، نشان میدهد.
- enum uv_process_flags
- فلگهایی که روی فیلد 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)
};
- type uv_stdio_container_t
- ظرف نگهدارنده برای هر قلاب stdio یا توصیفکننده فایل ارسالی به فرایند فرزند.
typedef struct uv_stdio_container_s {
uv_stdio_flags flags;
union {
uv_stream_t* stream;
int fd;
} data;
} uv_stdio_container_t;
- enum uv_stdio_flags
- فلگهایی که نحوه انتقال 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;
اعضای عمومی (Public members)
- int uv_process_t <#c.uv_process_t>.pid
- شناسه فرایند (PID) فرایند اجراشده. پس از فراخوانی uv_spawn() تنظیم میشود.
نکته:
- uv_exit_cb <#c.uv_exit_cb> uv_process_options_t <#c.uv_process_options_t>.exit_cb
- تابع بازخوانی که پس از خروج فرایند فراخوانی میشود.
- const char *uv_process_options_t <#c.uv_process_options_t>.file
- مسیری که به برنامه اجرایی اشاره میکند.
- char **uv_process_options_t <#c.uv_process_options_t>.args
- آرگومانهای خط فرمان. آرگومان args[0] باید مسیر برنامه باشد. در ویندوز از CreateProcess استفاده میشود که آرگومانها را درون یک رشته الحاق میکند و این امر میتواند خطاهای عجیبی ایجاد کند. به فلگ UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS در uv_process_flags مراجعه کنید.
- char **uv_process_options_t <#c.uv_process_options_t>.env
- متغیرهای محیطی برای فرایند جدید. اگر NULL باشد از محیط فرایند والد استفاده میشود.
- const char *uv_process_options_t <#c.uv_process_options_t>.cwd
- دایرکتوری کاری جاری برای زیرفرایند.
- unsigned int uv_process_options_t <#c.uv_process_options_t>.flags
- فلگهای گوناگونی که نحوه رفتار uv_spawn() را کنترل میکنند. به uv_process_flags مراجعه کنید.
- uv_stdio_container_t <#c.uv_stdio_container_t> *uv_process_options_t <#c.uv_process_options_t>.stdio
- فیلد stdio به
آرایهای
از
ساختارهای
uv_stdio_container_t اشاره
میکند که
توصیفکنندههای
فایل قابل
دسترسی
برای
فرایند
فرزند را
توصیف
میکنند.
قرارداد
استاندارد
این است که
stdio[0] به ورودی
استاندارد
(stdin)،
توصیفکننده
1 به خروجی
استاندارد
(stdout) و
توصیفکننده
2 به خطای
استاندارد
(stderr) اختصاص
دارد.
نکته:
- uv_gid_t uv_process_options_t <#c.uv_process_options_t>.gid
- کتابخانه libuv
میتواند
شناسه
کاربر/گروه
فرایند
فرزند را
تغییر دهد.
این تغییر
تنها زمانی
رخ میدهد
که بیتهای
مناسب در
فیلدهای flags
تنظیم شده
باشند.
نکته:
- uv_stdio_flags <#c.uv_stdio_flags> uv_stdio_container_t <#c.uv_stdio_container_t>.flags
- فلگهایی که نحوه ارسال ظرف stdio به فرایند فرزند را مشخص میکنند.
- union [anonymous] uv_stdio_container_t <#c.uv_stdio_container_t>.data
- اجتماعی (Union) شامل stream یا fd برای ارسال به فرایند فرزند.
رابط برنامهنویسی (API)
- void uv_disable_stdio_inheritance(void)
- وراثت
توصیفکنندههای
فایل /
قلابهایی
را که این
فرایند از
والد خود به
ارث برده
است
غیرفعال
میکند. اثر
این کار این
است که
فرایندهای
فرزندی که
توسط این
فرایند
اجرا
میشوند به
طور تصادفی
این
قلابها را
به ارث
نمیبرند.
توصیه میشود این تابع را در ابتدای برنامه در سریعترین زمان ممکن فراخوانی کنید، پیش از آنکه توصیفکنندههای فایل به ارث رسیده بتوانند بسته یا تکثیر شوند.
نکته:
- int uv_spawn(uv_loop_t <#c.uv_loop_t> *loop, uv_process_t <#c.uv_process_t> *handle, const uv_process_options_t <#c.uv_process_options_t> *options)
- قلاب
فرایند را
راهاندازی
اولیه کرده
و فرایند را
اجرا
میکند. اگر
فرایند با
موفقیت
اجرا شود،
این تابع
مقدار 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 اضافه شد.
- int uv_process_kill(uv_process_t <#c.uv_process_t> *handle, int signum)
- سیگنال مشخصشده را به قلاب فرایند دادهشده ارسال میکند. برای بررسی پشتیبانی از سیگنالها، بهویژه در ویندوز، به مستندات uv_signal_t --- Signal handle <#signal> مراجعه کنید.
- int uv_kill(int pid, int signum)
- سیگنال مشخصشده را به شناسه فرایند (PID) دادهشده ارسال میکند. برای بررسی پشتیبانی از سیگنالها، بهویژه در ویندوز، به مستندات uv_signal_t --- Signal handle <#signal> مراجعه کنید.
- uv_pid_t <#c.uv_pid_t> uv_process_get_pid(const uv_process_t <#c.uv_process_t> *handle)
- مقدار handle->pid
را
برمیگرداند.
افزودهشده در نسخه 1.19.0.
همچنین ببینید:
uv_stream_t --- قلاب جریان (Stream handle)
قلابهای جریان انتزاعی از یک کانال ارتباطی دوطرفه (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> ارائه میدهد.
انواع داده (Data types)
- type uv_stream_t
- نوع داده قلاب جریان (Stream handle).
- type uv_connect_t
- نوع درخواست اتصال (Connect request).
- type uv_shutdown_t
- نوع درخواست خاموشسازی/بستن اتصال (Shutdown request).
- type uv_write_t
- نوع درخواست نوشتن (Write request). هنگام استفاده مجدد از اشیاء متعلق به این نوع، باید دقت فراوان داشت. زمانی که یک جریان در حالت غیرمسدودکننده (non-blocking) قرار دارد، درخواستهای نوشتن ارسالشده با uv_write در صف قرار میگیرند. استفاده مجدد از اشیاء در این مقطع زمانی رفتاری تعریفنشده به همراه دارد. استفاده مجدد از شیء uv_write_t تنها پس از برانگیخته شدن تابع بازخوانی ارسالشده به uv_write ایمن است.
- typedef void (*uv_read_cb)(uv_stream_t <#c.uv_stream_t> *stream, ssize_t nread, const uv_buf_t <#c.uv_buf_t> *buf)
- تابع
بازخوانی
هنگام
خوانده شدن
دادهها
روی یک
جریان.
اگر دادهای در دسترس باشد، مقدار nread بزرگتر از 0 و در صورت بروز خطا کوچکتر از 0 خواهد بود. هنگام رسیدن به پایان فایل (EOF)، مقدار nread برابر با UV_EOF خواهد بود. زمانی که nread کوچکتر از 0 باشد، پارامتر buf ممکن است به بافر معتبری اشاره نکند؛ در این حالت buf.len و buf.base هر دو برابر با 0 تنظیم میشوند.
نکته:
فراخواننده مسئول توقف/بستن جریان هنگام بروز خطا از طریق فراخوانی uv_read_stop() یا uv_close() <#c.uv_close> است. تلاش برای خواندن مجدد از جریان رفتاری تعریفنشده دارد.
فراخواننده مسئول آزادسازی بافر است، libuv از آن مجدداً استفاده نمیکند. بافر ممکن است در زمان بروز خطا یک بافر تهی (که در آن buf->base == NULL و buf->len == 0 باشد) باشد.
- typedef void (*uv_write_cb)(uv_write_t <#c.uv_write_t> *req, int status)
- تابع بازخوانی پس از نوشته شدن دادهها روی جریان. در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.
- typedef void (*uv_connect_cb)(uv_connect_t <#c.uv_connect_t> *req, int status)
- تابع بازخوانی پس از اتمام اتصالی که توسط uv_connect() آغاز شده است. در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.
- typedef void (*uv_shutdown_cb)(uv_shutdown_t <#c.uv_shutdown_t> *req, int status)
- تابع بازخوانی پس از تکمیل یک درخواست خاموشسازی (shutdown). در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.
- typedef void (*uv_connection_cb)(uv_stream_t <#c.uv_stream_t> *server, int status)
- تابع بازخوانی هنگامی که یک سرور جریان، اتصال ورودی جدیدی را دریافت میکند. کاربر میتواند با فراخوانی uv_accept() اتصال را بپذیرد. در صورت موفقیت مقدار status برابر با 0 و در غیر این صورت کوچکتر از 0 خواهد بود.
اعضای عمومی (Public members)
- size_t uv_stream_t <#c.uv_stream_t>.write_queue_size
- شامل حجم بایتهای در صف انتظار برای ارسال. فقطخواندنی.
- uv_stream_t <#c.uv_stream_t> *uv_connect_t <#c.uv_connect_t>.handle
- اشارهگر به جریانی که این درخواست اتصال روی آن در حال اجراست.
- uv_stream_t <#c.uv_stream_t> *uv_shutdown_t <#c.uv_shutdown_t>.handle
- اشارهگر به جریانی که این درخواست خاموشسازی روی آن در حال اجراست.
- uv_stream_t <#c.uv_stream_t> *uv_write_t <#c.uv_write_t>.handle
- اشارهگر به جریانی که این درخواست نوشتن روی آن در حال اجراست.
- uv_stream_t <#c.uv_stream_t> *uv_write_t <#c.uv_write_t>.send_handle
- اشارهگر به جریانی که با استفاده از این درخواست نوشتن ارسال میشود.
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_shutdown(uv_shutdown_t <#c.uv_shutdown_t> *req, uv_stream_t <#c.uv_stream_t> *handle, uv_shutdown_cb <#c.uv_shutdown_cb> cb)
- خاموشسازی سمت خروجی (نوشتن) یک جریان دوطرفه (duplex). این تابع منتظر تکمیل درخواستهای نوشتن معلق میماند. پارامتر handle باید به یک جریان راهاندازیشده اشاره کند. پارامتر req باید یک ساختار درخواست خاموشسازی راهاندازینشده باشد. پس از اتمام خاموشسازی، تابع cb فراخوانی میشود.
- int uv_listen(uv_stream_t <#c.uv_stream_t> *stream, int backlog, uv_connection_cb <#c.uv_connection_cb> cb)
- آغاز گوش دادن به اتصالات ورودی. پارامتر backlog تعداد اتصالات قابل قرارگیری در صف توسط هسته سیستمعامل را مشخص میکند، مشابه با listen(2) https://man7.org/linux/man-pages/man2/listen.2.html. هنگامی که یک اتصال ورودی جدید دریافت شود، تابع بازخوانی uv_connection_cb فراخوانی میگردد.
- int uv_accept(uv_stream_t <#c.uv_stream_t> *server, uv_stream_t <#c.uv_stream_t> *client)
- این
فراخوانی
همراه با
uv_listen() برای
پذیرش
اتصالات
ورودی
استفاده
میشود. این
تابع را پس
از دریافت
uv_connection_cb جهت
پذیرش
اتصال
فراخوانی
کنید. قبل
از
فراخوانی
این تابع،
قلاب
کلاینت
باید
راهاندازی
اولیه شده
باشد. مقدار
بازگشتی
کمتر از 0
نشاندهنده
خطاست.
هنگامی که تابع بازخوانی uv_connection_cb فراخوانی میشود، تضمین میگردد که این تابع برای بار اول با موفقیت به پایان برسد. اگر بیش از یک بار تلاش به استفاده از آن کنید، ممکن است شکست بخورد. پیشنهاد میشود این تابع را فقط یک بار به ازای هر فراخوانی uv_connection_cb اجرا کنید.
نکته:
- int uv_read_start(uv_stream_t <#c.uv_stream_t> *stream, uv_alloc_cb <#c.uv_alloc_cb> alloc_cb, uv_read_cb <#c.uv_read_cb> read_cb)
- خواندن
دادهها از
یک جریان
ورودی. تابع
بازخوانی
uv_read_cb چندین
بار
فراخوانی
خواهد شد تا
زمانی که
داده دیگری
برای
خواندن
نماند یا
uv_read_stop()
فراخوانی
شود.
تغییریافته در نسخه 1.38.0: اکنون تابع uv_read_start() در صورت فراخوانی دوباره همواره UV_EALREADY و در صورت بسته شدن جریان مقدار UV_EINVAL را برمیگرداند. در نسخههای قدیمیتر libuv، مقدار UV_EALREADY روی ویندوز برمیگشت اما روی یونیکس خیر، و UV_EINVAL روی یونیکس برمیگشت اما روی ویندوز خیر.
- int uv_read_stop(uv_stream_t <#c.uv_stream_t>*)
- توقف
خواندن
دادهها از
جریان. تابع
بازخوانی
uv_read_cb دیگر
فراخوانی
نخواهد شد.
این تابع خودتوان (idempotent) است و فراخوانی آن روی جریانی که قبلاً متوقف شده ایمن است.
این تابع همواره با موفقیت انجام میشود؛ از این رو بررسی مقدار بازگشتی آن ضرورتی ندارد. مقدار بازگشتی غیرصفر نشان میدهد که اتمام آزادسازی منابع ممکن است به رویداد ورودی بعدی روی آن TTY در ویندوز موکول شود و به معنای شکست عملیات نیست.
- int uv_write(uv_write_t <#c.uv_write_t> *req, uv_stream_t <#c.uv_stream_t> *handle, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs, uv_write_cb <#c.uv_write_cb> cb)
- نوشتن دادهها روی جریان. بافرها به ترتیب نوشته میشوند. مثال:
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);
نکته:
- int uv_write2(uv_write_t <#c.uv_write_t> *req, uv_stream_t <#c.uv_stream_t> *handle, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs, uv_stream_t <#c.uv_stream_t> *send_handle, uv_write_cb <#c.uv_write_cb> cb)
- تابع نوشتن
پیشرفته
برای ارسال
قلابها از
طریق یک
لوله (pipe). لوله
باید با ipc == 1
راهاندازی
شده باشد.
نکته:
- int uv_try_write(uv_stream_t <#c.uv_stream_t> *handle, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs)
- همانند uv_write()
عمل
میکند،
اما اگر
نوشتن
بلافاصله
تکمیل
نشود،
درخواست
نوشتن را در
صف قرار
نمیدهد.
یکی از مقادیر زیر را برمیگرداند:
- بزرگتر از 0: تعداد بایتهای نوشتهشده (میتواند کمتر از اندازه بافر ارائهشده باشد).
- کوچکتر از 0: کد خطای منفی (در صورتی که هیچ دادهای نتواند بلافاصله ارسال شود، UV_EAGAIN بازگردانده میشود).
- int uv_try_write2(uv_stream_t <#c.uv_stream_t> *handle, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs, uv_stream_t <#c.uv_stream_t> *send_handle)
- مشابه uv_try_write() و
تابع نوشتن
گسترشیافته
برای ارسال
قلابها از
طریق یک
لوله
همانند c:func:uv_write2
است.
تلاش برای ارسال یک قلاب در ویندوز پشتیبانی نمیشود و در آنجا مقدار UV_EAGAIN را برمیگرداند.
افزودهشده در نسخه 1.42.0.
- int uv_is_readable(const uv_stream_t <#c.uv_stream_t> *handle)
- اگر جریان قابل خواندن باشد مقدار 1 و در غیر این صورت 0 را برمیگرداند.
- int uv_is_writable(const uv_stream_t <#c.uv_stream_t> *handle)
- اگر جریان قابل نوشتن باشد مقدار 1 و در غیر این صورت 0 را برمیگرداند.
- int uv_stream_set_blocking(uv_stream_t <#c.uv_stream_t> *handle, int blocking)
- فعال یا
غیرفعال
کردن حالت
مسدودکننده
(blocking mode) برای یک
جریان.
هنگامی که حالت مسدودکننده فعال باشد، تمام عملیات نوشتن به صورت همگام تکمیل میشوند. با این حال رابط کاربری بدون تغییر باقی میماند، مثلاً تکمیل یا عدم موفقیت در عملیات همچنان از طریق تابع بازخوانی که به صورت ناهمگام فراخوانی میشود گزارش خواهد شد.
هشدار:
در حال حاضر در ویندوز تنها برای قلابهای uv_pipe_t <#c.uv_pipe_t> کار میکند. در پلتفرمهای یونیکس، تمام قلابهای uv_stream_t پشتیبانی میشوند.
همچنین libuv در حال حاضر هیچ تضمینی در مورد ترتیب اجرا هنگام تغییر حالت مسدودکننده پس از ارسال درخواستهای نوشتن ارائه نمیدهد. بنابراین توصیه میشود حالت مسدودکننده بلافاصله پس از باز کردن یا ایجاد جریان تنظیم شود.
تغییریافته در نسخه 1.4.0: پیادهسازی یونیکس اضافه شد.
- size_t uv_stream_get_write_queue_size(const uv_stream_t <#c.uv_stream_t> *stream)
- مقدار
stream->write_queue_size را
برمیگرداند.
افزودهشده در نسخه 1.19.0.
همچنین ببینید:
uv_tcp_t --- قلاب تیسیپی (TCP handle)
قلابهای TCP برای نمایش هر دو مورد جریانها و سرورهای TCP استفاده میشوند.
ساختار uv_tcp_t یک زیرکلاس از uv_stream_t <#c.uv_stream_t> است.
انواع داده (Data types)
- type uv_tcp_t
- نوع داده قلاب TCP (TCP handle).
- enum uv_tcp_flags
- فلگهای استفادهشده در 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,
};
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_tcp_init(uv_loop_t <#c.uv_loop_t> *loop, uv_tcp_t <#c.uv_tcp_t> *handle)
- راهاندازی اولیه قلاب. تا این لحظه هیچ سوکتی ایجاد نشده است.
- int uv_tcp_init_ex(uv_loop_t <#c.uv_loop_t> *loop, uv_tcp_t <#c.uv_tcp_t> *handle, unsigned int flags)
- راهاندازی
اولیه قلاب
با فلگهای
مشخصشده.
در حال حاضر
تنها ۸ بیت
پایینی
پارامتر flags
به عنوان
دامنه سوکت
(socket domain) استفاده
میشوند. یک
سوکت برای
دامنه
مشخصشده
ایجاد
خواهد شد.
اگر دامنه
مشخصشده
AF_UNSPEC باشد هیچ
سوکتی
ایجاد
نمیشود،
دقیقاً
مانند uv_tcp_init().
افزودهشده در نسخه 1.7.0.
- int uv_tcp_open(uv_tcp_t <#c.uv_tcp_t> *handle, uv_os_sock_t <#c.uv_os_sock_t> sock)
- باز کردن یک
توصیفکننده
فایل یا SOCKET
موجود به
عنوان یک
قلاب TCP.
تغییریافته در نسخه 1.2.1: توصیفکننده فایل روی حالت غیرمسدودکننده (non-blocking) تنظیم میشود.
نکته:
- int uv_tcp_nodelay(uv_tcp_t <#c.uv_tcp_t> *handle, int enable)
- فعال کردن TCP_NODELAY که الگوریتم Nagle را غیرفعال میکند.
- int uv_tcp_keepalive(uv_tcp_t <#c.uv_tcp_t> *handle, int enable, unsigned int delay)
- فعال /
غیرفعال
کردن
قابلیت TCP keep-alive.
پارامتر delay
تأخیر
اولیه بر
حسب ثانیه
است و زمانی
که enable برابر
صفر باشد
نادیده
گرفته
میشود.
پس از رسیدن به زمان delay، ۱۰ کاوش پیاپی با فاصله ۱ ثانیه از یکدیگر انجام خواهد شد. اگر در پایان این روند اتصال همچنان برقرار نشود، قلاب با خطای UV_ETIMEDOUT ارسالی به تابع بازخوانی مربوطه نابود میشود.
اگر delay کمتر از ۱ باشد، مقدار UV_EINVAL برگردانده میشود.
تغییریافته در نسخه 1.49.0: اگر delay کمتر از ۱ باشد، مقدار UV_EINVAL` بازگردانده میشود.
- int uv_tcp_keepalive_ex(uv_tcp_t <#c.uv_tcp_t> *handle, int on, unsigned int idle, unsigned int intvl, unsigned int cnt)
- فعال /
غیرفعال
کردن 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
- int uv_tcp_simultaneous_accepts(uv_tcp_t <#c.uv_tcp_t> *handle, int enable)
- فعال /
غیرفعال
کردن
درخواستهای
پذیرش
همگام
ناهمگام (simultaneous
accepts) که توسط
سیستمعامل
هنگام گوش
دادن به
اتصالات
جدید TCP در صف
قرار
میگیرند.
این تنظیم برای تنظیم کارایی سرور TCP به عملکرد مطلوب استفاده میشود. فعال بودن پذیرش همزمان میتواند سرعت پذیرش اتصالات را به طور قابل توجهی افزایش دهد (که به همین دلیل به طور پیشفرض فعال است)، اما ممکن است منجر به توزیع بار نامتوازن در محیطهای چندفرایندی شود.
- int uv_tcp_bind(uv_tcp_t <#c.uv_tcp_t> *handle, const struct sockaddr *addr, unsigned int flags)
- اتصال (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 اضافه شد.
نکته:
- int uv_tcp_getsockname(const uv_tcp_t <#c.uv_tcp_t> *handle, struct sockaddr *name, int *namelen)
- دریافت آدرس جاری که قلاب به آن متصل است. پارامتر name باید به یک قطعه حافظه معتبر و به اندازه کافی بزرگ اشاره کند؛ ساختار struct sockaddr_storage برای پشتیبانی از IPv4 و IPv6 توصیه میشود.
- int uv_tcp_getpeername(const uv_tcp_t <#c.uv_tcp_t> *handle, struct sockaddr *name, int *namelen)
- دریافت آدرس طرف متصل به قلاب (peer). پارامتر name باید به یک قطعه حافظه معتبر و به اندازه کافی بزرگ اشاره کند؛ ساختار struct sockaddr_storage برای پشتیبانی از IPv4 و IPv6 توصیه میشود.
- int uv_tcp_connect(uv_connect_t <#c.uv_connect_t> *req, uv_tcp_t <#c.uv_tcp_t> *handle, const struct sockaddr *addr, uv_connect_cb <#c.uv_connect_cb> cb)
- برقراری یک
اتصال 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 اضافه شد.
همچنین ببینید:
- int uv_tcp_close_reset(uv_tcp_t <#c.uv_tcp_t> *handle, uv_close_cb <#c.uv_close_cb> close_cb)
- بازنشانی
یک اتصال TCP
با ارسال یک
بسته RST. این
کار با
تنظیم
گزینه سوکت
SO_LINGER با بازه
ماندگاری (linger
interval) صفر و سپس
فراخوانی
uv_close() <#c.uv_close>
انجام
میشود. به
دلیل برخی
ناهماهنگیهای
پلتفرم،
ترکیب
فراخوانیهای
uv_shutdown() <#c.uv_shutdown> و
uv_tcp_close_reset() مجاز
نیست.
افزودهشده در نسخه 1.32.0.
- int uv_socketpair(int type, int protocol, uv_os_sock_t <#c.uv_os_sock_t> socket_vector[2], int flags0, int flags1)
- ایجاد یک
جفت سوکت
متصلبههم
با مشخصات
تعیینشده.
قلابهای
حاصل را
میتوان به
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.
uv_pipe_t --- هندل پایپ (Pipe handle)
هندلهای پایپ یک لایه انتزاعی روی فایلهای استریم در یونیکس (شامل سوکتهای دامنه محلی، پایپها و FIFOها) و پایپهای نامگذاریشده در ویندوز فراهم میکنند.
uv_pipe_t یک «زیرکلاس» از uv_stream_t <#c.uv_stream_t> است.
انواع داده (Data types)
- type uv_pipe_t
- نوع هندل پایپ.
اعضای عمومی (Public members)
- int uv_pipe_t <#c.uv_pipe_t>.ipc
- مشخص میکند که آیا این پایپ برای انتقال هندل بین پردازهها مناسب است یا خیر. تنها پایپ متصلی که قرار است هندلها را انتقال دهد باید این فلگ را تنظیم داشته باشد، نه پایپ شنوندهای که uv_accept روی آن فراخوانی میشود.
همچنین ببینید (See also):
رابط برنامهنویسی (API)
- int uv_pipe_init(uv_loop_t <#c.uv_loop_t> *loop, uv_pipe_t <#c.uv_pipe_t> *handle, int ipc)
- راهاندازی یک هندل پایپ. آرگومان ipc یک مقدار بولی است که نشان میدهد آیا این پایپ برای انتقال هندل بین پردازهها استفاده خواهد شد یا خیر (که ممکن است بایتهای ارسالی روی خط را تغییر دهد). تنها پایپ متصلی که قرار است هندلها را انتقال دهد باید این فلگ را تنظیم داشته باشد، نه پایپ شنوندهای که uv_accept روی آن فراخوانی میشود.
- int uv_pipe_open(uv_pipe_t <#c.uv_pipe_t> *handle, uv_file <#c.uv_file> file)
- باز کردن یک
توصیفگر
فایل یا HANDLE
موجود به
عنوان پایپ.
تغییریافته در نسخه 1.2.1: توصیفگر فایل روی حالت غیرمسدودکننده (non-blocking) تنظیم میشود.
نکته:
- int uv_pipe_bind(uv_pipe_t <#c.uv_pipe_t> *handle, const char *name)
- متصل کردن
پایپ به یک
مسیر فایل
(در یونیکس)
یا یک نام
(در ویندوز).
برخلاف uv_pipe_bind2()، از سوکتهای فضای نام انتزاعی (abstract namespace) لینوکس پشتیبانی نمیکند.
نام مستعار برای uv_pipe_bind2(handle, name, strlen(name), 0).
نکته:
- int uv_pipe_bind2(uv_pipe_t <#c.uv_pipe_t> *handle, const char *name, size_t namelen, unsigned int flags)
- متصل کردن
پایپ به یک
مسیر فایل
(در یونیکس)
یا یک نام
(در ویندوز).
flags باید صفر یا UV_PIPE_NO_TRUNCATE باشد. برای فلگهای پشتیبانینشده بدون انجام عملیات bind مقدار UV_EINVAL برمیگرداند.
از سوکتهای فضای نام انتزاعی لینوکس پشتیبانی میکند. namelen باید بایت nul ابتدایی را شامل شود، اما بایت nul انتهایی را خیر.
افزوده شده در نسخه 1.46.0.
نکته:
- void uv_pipe_connect(uv_connect_t <#c.uv_connect_t> *req, uv_pipe_t <#c.uv_pipe_t> *handle, const char *name, uv_connect_cb <#c.uv_connect_cb> cb)
- اتصال به
سوکت دامنه
یونیکس یا
پایپ
نامگذاریشده
در ویندوز.
برخلاف uv_pipe_connect2()، از سوکتهای فضای نام انتزاعی لینوکس پشتیبانی نمیکند.
نام مستعار برای uv_pipe_connect2(req, handle, name, strlen(name), 0, cb).
نکته:
- int uv_pipe_connect2(uv_connect_t <#c.uv_connect_t> *req, uv_pipe_t <#c.uv_pipe_t> *handle, const char *name, size_t namelen, unsigned int flags, uv_connect_cb <#c.uv_connect_cb> cb)
- اتصال به
سوکت دامنه
یونیکس یا
پایپ
نامگذاریشده
در ویندوز.
flags باید صفر یا UV_PIPE_NO_TRUNCATE باشد. برای فلگهای پشتیبانینشده بدون انجام عملیات اتصال مقدار UV_EINVAL برمیگرداند.
از سوکتهای فضای نام انتزاعی لینوکس پشتیبانی میکند. namelen باید بایت nul ابتدایی را شامل شود، اما بایت nul انتهایی را خیر.
افزوده شده در نسخه 1.46.0.
نکته:
- int uv_pipe_getsockname(const uv_pipe_t <#c.uv_pipe_t> *handle, char *buffer, size_t *size)
- دریافت نام
سوکت دامنه
یونیکس یا
پایپ
نامگذاریشده.
باید یک بافر از پیش تخصیصیافته فراهم شود. پارامتر size طول بافر را نگه میدارد و در خروجی روی تعداد بایتهای نوشتهشده در بافر تنظیم میشود. اگر بافر به اندازه کافی بزرگ نباشد UV_ENOBUFS برگردانده میشود و len حاوی اندازه مورد نیاز خواهد بود.
تغییریافته در نسخه 1.3.0: طول برگرداندهشده دیگر شامل بایت تهی (null) پایانی نیست و بافر با null خاتمه نمییابد.
- int uv_pipe_getpeername(const uv_pipe_t <#c.uv_pipe_t> *handle, char *buffer, size_t *size)
- دریافت نام
سوکت دامنه
یونیکس یا
پایپ
نامگذاریشده
که هندل به
آن متصل
است.
باید یک بافر از پیش تخصیصیافته فراهم شود. پارامتر size طول بافر را نگه میدارد و در خروجی روی تعداد بایتهای نوشتهشده در بافر تنظیم میشود. اگر بافر به اندازه کافی بزرگ نباشد UV_ENOBUFS برگردانده میشود و len حاوی اندازه مورد نیاز خواهد بود.
افزوده شده در نسخه 1.3.0.
- void uv_pipe_pending_instances(uv_pipe_t <#c.uv_pipe_t> *handle, int count)
- تنظیم
تعداد
هندلهای
وهله پایپ
معلق زمانی
که سرور
پایپ منتظر
اتصالات
است.
نکته:
- uv_handle_type <#c.uv_handle_type> uv_pipe_pending_type(uv_pipe_t <#c.uv_pipe_t> *handle)
- برای
دریافت
هندلها
روی
پایپهای IPC
استفاده
میشود.
ابتدا uv_pipe_pending_count() را فراخوانی کنید؛ اگر مقدار آن بزرگتر از ۰ بود، یک هندل از نوع (type) دادهشده که توسط uv_pipe_pending_type() برگردانده میشود را راهاندازی کرده و uv_accept(pipe, handle) را فراخوانی کنید.
همچنین ببینید (See also):
- int uv_pipe_chmod(uv_pipe_t <#c.uv_pipe_t> *handle, int flags)
- مجوزهای
پایپ را
تغییر
میدهد و
اجازه
میدهد
پردازههای
اجراشده
توسط
کاربران
مختلف به آن
دسترسی
داشته
باشند. پایپ
را برای همه
کاربران
قابل نوشتن
یا قابل
خواندن
میکند.
حالت
میتواند
UV_WRITABLE، UV_READABLE یا
UV_WRITABLE | UV_READABLE باشد.
این تابع
مسدودکننده
(blocking) است.
افزوده شده در نسخه 1.16.0.
- int uv_pipe(uv_file <#c.uv_file> fds[2], int read_flags, int write_flags)
- ایجاد یک
جفت هندل
پایپ متصل
به هم.
دادهها
میتوانند
در 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.
uv_tty_t --- هندل TTY (TTY handle)
هندلهای TTY یک استریم برای کنسول فراهم میکنند.
uv_tty_t یک «زیرکلاس» از uv_stream_t <#c.uv_stream_t> است.
انواع داده (Data types)
- type uv_tty_t
- نوع هندل TTY.
- enum uv_tty_mode_t
- افزوده شده
در نسخه 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;
- enum uv_tty_vtermstate_t
- نوع حالت ترمینال مجازی کنسول:
typedef enum {
/*
* کنسول از پردازش توالیهای ترمینال مجازی پشتیبانی میکند
* (کنسول جدید ویندوز ۱۰، ConEmu)
*/
UV_TTY_SUPPORTED,
/* کنسول نمیتواند توالیهای ترمینال مجازی را پردازش کند. (کنسول قدیمی)
*/
UV_TTY_UNSUPPORTED
} uv_tty_vtermstate_t
اعضای عمومی (Public members)
ندارد (N/A)
همچنین ببینید (See also):
رابط برنامهنویسی (API)
- int uv_tty_init(uv_loop_t <#c.uv_loop_t> *loop, uv_tty_t <#c.uv_tty_t> *handle, uv_file <#c.uv_file> fd, int unused)
- راهاندازی یک استریم 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) نیست.
نکته:
تغییریافته در نسخه 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 برمیگرداند.
- int uv_tty_set_mode(uv_tty_t <#c.uv_tty_t> *handle, uv_tty_mode_t <#c.uv_tty_mode_t> mode)
- تغییریافته
در نسخه 1.2.0:
حالت به
عنوان یک
مقدار uv_tty_mode_t
مشخص
میشود.
تنظیم TTY با استفاده از حالت ترمینال مشخصشده.
- int uv_tty_reset_mode(void)
- هنگام خروج
برنامه
فراخوانی
میشود.
تنظیمات TTY
را برای
پردازه
بعدی که
کنترل را در
دست
میگیرد،
به مقادیر
پیشفرض
بازمیگرداند.
این تابع در پلتفرمهای یونیکس async signal-safe است، اما اگر در حین اجرای درون uv_tty_set_mode() فراخوانی شود، میتواند با کد خطای UV_EBUSY با شکست مواجه شود.
- int uv_tty_get_winsize(uv_tty_t <#c.uv_tty_t> *handle, int *width, int *height)
- دریافت اندازه فعلی پنجره. در صورت موفقیت ۰ برمیگرداند.
همچنین ببینید (See also):
- void uv_tty_set_vterm_state(uv_tty_vtermstate_t <#c.uv_tty_vtermstate_t> state)
- کنترل
میکند که
آیا
توالیهای
ترمینال
مجازی
کنسول توسط
libuv پردازش
شوند یا
کنسول.
بهویژه
برای
فعالسازی
پشتیبانی ConEmu
از ANSI X3.64 و
رنگهای
۲۵۶تایی Xterm
مفید است.
در غیر این
صورت
کنسولهای
ویندوز ۱۰
معمولاً به
طور خودکار
شناسایی
میشوند.
این تابع فقط در سیستمهای ویندوز معنادار است. در یونیکس بدون هیچ خطایی نادیده گرفته میشود.
افزوده شده در نسخه 1.33.0.
- int uv_tty_get_vterm_state(uv_tty_vtermstate_t <#c.uv_tty_vtermstate_t> *state)
- دریافت
وضعیت فعلی
پردازش
توالیهای
ترمینال
مجازی
کنسول توسط
libuv یا کنسول.
این تابع در یونیکس پیادهسازی نشده است و مقدار UV_ENOTSUP را برمیگرداند.
افزوده شده در نسخه 1.33.0.
uv_udp_t --- هندل UDP (UDP handle)
هندلهای UDP ارتباطات UDP را برای کلاینتها و سرورها کپسولهسازی میکنند.
انواع داده (Data types)
- type uv_udp_t
- نوع هندل UDP.
- type uv_udp_send_t
- نوع درخواست ارسال UDP.
- enum uv_udp_flags
- فلگهای مورداستفاده در 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
};
- typedef void (*uv_udp_send_cb)(uv_udp_send_t <#c.uv_udp_send_t> *req, int status)
- تعریف نوع برای کالبک پاسدادهشده به uv_udp_send() که پس از ارسال داده فراخوانی میشود.
- typedef void (*uv_udp_recv_cb)(uv_udp_t <#c.uv_udp_t> *handle, ssize_t nread, const uv_buf_t <#c.uv_buf_t> *buf, const struct sockaddr *addr, unsigned flags)
- تعریف نوع برای کالبک پاسدادهشده به 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 اضافه شد.
نکته:
- enum uv_membership
- نوع عضویت برای یک آدرس چندپخشی (multicast).
typedef enum {
UV_LEAVE_GROUP = 0,
UV_JOIN_GROUP
} uv_membership;
اعضای عمومی (Public members)
- size_t uv_udp_t <#c.uv_udp_t>.send_queue_size
- تعداد بایتهای در صف برای ارسال. این فیلد مشخصاً نشان میدهد چه مقدار اطلاعات در حال حاضر در صف قرار دارد.
- size_t uv_udp_t <#c.uv_udp_t>.send_queue_count
- تعداد درخواستهای ارسال فعلی در صف که در انتظار پردازش هستند.
- uv_udp_t <#c.uv_udp_t> *uv_udp_send_t <#c.uv_udp_send_t>.handle
- هندل UDP که این درخواست ارسال روی آن در حال انجام است.
همچنین ببینید (See also):
رابط برنامهنویسی (API)
- int uv_udp_init(uv_loop_t <#c.uv_loop_t> *loop, uv_udp_t <#c.uv_udp_t> *handle)
- راهاندازی یک هندل UDP جدید. سوکت واقعی به صورت تنبلانه (lazily) ایجاد میشود. در صورت موفقیت ۰ برمیگرداند.
- int uv_udp_init_ex(uv_loop_t <#c.uv_loop_t> *loop, uv_udp_t <#c.uv_udp_t> *handle, unsigned int flags)
- راهاندازی
هندل با
فلگهای
مشخصشده. ۸
بیت پایینی
پارامتر 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 اضافه شد.
- int uv_udp_open_ex(uv_udp_t <#c.uv_udp_t> *handle, uv_os_sock_t <#c.uv_os_sock_t> sock, unsigned int flags)
- باز کردن یک توصیفگر فایل یا SOCKET ویندوز موجود به عنوان یک هندل UDP.
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init() راهاندازی شده باشد.
- sock -- یک سوکت موجود برای اتصال به هندل.
- flags -- فلگهایی که رفتار سوکت را کنترل میکنند؛ UV_UDP_REUSEADDR و UV_UDP_REUSEPORT پشتیبانی میشوند.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
افزوده شده در نسخه 1.52.0.
- int uv_udp_open(uv_udp_t <#c.uv_udp_t> *handle, uv_os_sock_t <#c.uv_os_sock_t> sock)
- باز کردن یک
توصیفگر
فایل یا SOCKET
ویندوز
موجود به
عنوان یک
هندل UDP.
فقط در یونیکس: تنها الزام آرگومان sock این است که از قرارداد دیتاگرام پیروی کند (در حالت بدون اتصال کار کند، از sendmsg()/recvmsg() پشتیبانی نماید و غیره). به عبارت دیگر، سایر سوکتهای نوع دیتاگرام مانند raw sockets یا netlink sockets نیز میتوانند به این تابع پاس داده شوند.
تغییریافته در نسخه 1.2.1: توصیفگر فایل روی حالت غیرمسدودکننده تنظیم میشود.
نکته:
درون تابع، گزینه سوکت SO_REUSEADDR بدون قید و شرط تنظیم میشود. این بدان معناست که فلگ بازاستفاده بدون در نظر گرفتن قصد کاربر همیشه فعال است. برای کنترل بیشتر از uv_udp_open_ex() استفاده کنید.
- int uv_udp_bind(uv_udp_t <#c.uv_udp_t> *handle, const struct sockaddr *addr, unsigned int flags)
- متصل کردن هندل 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 اضافه شد.
نکته:
- int uv_udp_connect(uv_udp_t <#c.uv_udp_t> *handle, const struct sockaddr *addr)
- مرتبط کردن هندل 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.
- int uv_udp_getpeername(const uv_udp_t <#c.uv_udp_t> *handle, struct sockaddr *name, int *namelen)
- دریافت IP و پورت دوردست در هندلهای متصل UDP. در هندلهای غیرمتصل، مقدار UV_ENOTCONN را برمیگرداند.
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init() راهاندازی و bind شده باشد.
- name -- اشارهگر به ساختاری که با دادههای آدرس پر میشود. برای پشتیبانی از IPv4 و IPv6 باید از struct sockaddr_storage استفاده شود.
- namelen -- در ورودی نشاندهنده اندازه بافر name است. در خروجی نشان میدهد چه مقدار از آن پر شده است.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
افزوده شده در نسخه 1.27.0.
- int uv_udp_getsockname(const uv_udp_t <#c.uv_udp_t> *handle, struct sockaddr *name, int *namelen)
- دریافت IP و پورت محلی هندل UDP.
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init() راهاندازی و bind شده باشد.
- name -- اشارهگر به ساختاری که با دادههای آدرس پر میشود. برای پشتیبانی از IPv4 و IPv6 باید از struct sockaddr_storage استفاده شود.
- namelen -- در ورودی نشاندهنده اندازه بافر name است. در خروجی نشان میدهد چه مقدار از آن پر شده است.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- int uv_udp_set_membership(uv_udp_t <#c.uv_udp_t> *handle, const char *multicast_addr, const char *interface_addr, uv_membership <#c.uv_membership> membership)
- تنظیم وضعیت عضویت برای یک آدرس چندپخشی (multicast).
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init() راهاندازی شده باشد.
- multicast_addr -- آدرس چندپخشی برای تنظیم عضویت.
- interface_addr -- آدرس رابط شبکه (interface).
- membership -- باید UV_JOIN_GROUP یا UV_LEAVE_GROUP باشد.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- int uv_udp_set_source_membership(uv_udp_t <#c.uv_udp_t> *handle, const char *multicast_addr, const char *interface_addr, const char *source_addr, uv_membership <#c.uv_membership> membership)
- تنظیم عضویت برای یک گروه چندپخشی مبتنی بر منبع خاص (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.
- int uv_udp_set_multicast_loop(uv_udp_t <#c.uv_udp_t> *handle, int on)
- تنظیم فلگ حلقه چندپخشی 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)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- int uv_udp_set_multicast_ttl(uv_udp_t <#c.uv_udp_t> *handle, int ttl)
- تنظیم TTL چندپخشی.
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راهاندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
- ttl -- مقدار بین ۱ تا ۲۵۵.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- int uv_udp_set_multicast_interface(uv_udp_t <#c.uv_udp_t> *handle, const char *interface_addr)
- تنظیم رابط چندپخشی برای ارسال یا دریافت داده.
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راهاندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
- interface_addr -- آدرس رابط شبکه.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- int uv_udp_set_broadcast(uv_udp_t <#c.uv_udp_t> *handle, int on)
- روشن یا خاموش کردن پخش همگانی (broadcast).
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init_ex() به صورت AF_INET یا AF_INET6 راهاندازی شده باشد، یا به صراحت با uv_udp_bind() یا به طور ضمنی با uv_udp_send() یا uv_udp_recv_start() به یک آدرس متصل شده باشد.
- on -- مقدار ۱ برای روشن، ۰ برای خاموش.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- int uv_udp_set_ttl(uv_udp_t <#c.uv_udp_t> *handle, int ttl)
- تنظیم طول عمر بسته (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)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- int uv_udp_send(uv_udp_send_t <#c.uv_udp_send_t> *req, uv_udp_t <#c.uv_udp_t> *handle, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs, const struct sockaddr *addr, uv_udp_send_cb <#c.uv_udp_send_cb> send_cb)
- ارسال داده
روی سوکت 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: پشتیبانی از سوکتهای متصل اضافه شد.
- int uv_udp_try_send(uv_udp_t <#c.uv_udp_t> *handle, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs, const struct sockaddr *addr)
- همانند uv_udp_send()
عمل
میکند،
اما اگر
ارسال
نتواند
بلافاصله
تکمیل شود،
درخواست
ارسالی را
در صف قرار
نمیدهد.
برای هندلهای متصل UDP، مقدار addr باید برابر با NULL باشد، در غیر این صورت خطای UV_EISCONN برمیگرداند.
برای هندلهای UDP بدون اتصال، مقدار addr نمیتواند NULL باشد، در غیر این صورت خطای UV_EDESTADDRREQ برمیگرداند.
- بازگشتی (Returns)
- >= 0: تعداد بایتهای ارسالشده (با اندازه بافر دادهشده مطابقت دارد). < 0: کد خطای منفی (هنگامی که پیام نتواند بلافاصله ارسال شود، UV_EAGAIN برگردانده میشود).
تغییریافته در نسخه 1.27.0: پشتیبانی از سوکتهای متصل اضافه شد.
- int uv_udp_try_send2(uv_udp_t <#c.uv_udp_t> *handle, unsigned int count, uv_buf_t <#c.uv_buf_t> *bufs[/*count*/], unsigned int nbufs[/*count*/], struct sockaddr *addrs[/*count*/], unsigned int flags)
- مشابه 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.
- int uv_udp_recv_start(uv_udp_t <#c.uv_udp_t> *handle, uv_alloc_cb <#c.uv_alloc_cb> alloc_cb, uv_udp_recv_cb <#c.uv_udp_recv_cb> recv_cb)
- آمادهسازی برای دریافت داده. اگر سوکت قبلاً با uv_udp_bind() متصل نشده باشد، به 0.0.0.0 (آدرس IPv4 «تمامی رابطها») و یک پورت تصادفی متصل میشود.
- پارامترها (Parameters)
- handle -- هندل UDP. باید با uv_udp_init() راهاندازی شده باشد.
- alloc_cb -- کالبکی که هنگام نیاز به فضای ذخیرهسازی موقت فراخوانی میشود.
- recv_cb -- کالبکی که با دادههای دریافتی فراخوانی میشود.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
نکته:
تغییریافته در نسخه 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 برای هندل/پلتفرم جاری تخصیص یابد یا خیر.
- int uv_udp_using_recvmmsg(uv_udp_t <#c.uv_udp_t> *handle)
- اگر هندل UDP
با فلگ UV_UDP_RECVMMSG
ایجاد شده
باشد و
پلتفرم از
recvmmsg(2) https://man7.org/linux/man-pages/man2/recvmmsg.2.html
پشتیبانی
کند ۱، و در
غیر این
صورت ۰
برمیگرداند.
افزوده شده در نسخه 1.39.0.
- int uv_udp_recv_stop(uv_udp_t <#c.uv_udp_t> *handle)
- توقف شنود برای دیتاگرامهای ورودی.
- پارامترها (Parameters)
- •
- handle -- هندل UDP. باید با uv_udp_init() راهاندازی شده باشد.
- بازگشتی (Returns)
- ۰ در صورت موفقیت، یا کد خطای کوچکتر از ۰ در صورت شکست.
- size_t uv_udp_get_send_queue_size(const uv_udp_t <#c.uv_udp_t> *handle)
- مقدار handle->send_queue_size
را
برمیگرداند.
افزوده شده در نسخه 1.19.0.
- size_t uv_udp_get_send_queue_count(const uv_udp_t <#c.uv_udp_t> *handle)
- مقدار
handle->send_queue_count را
برمیگرداند.
افزوده شده در نسخه 1.19.0.
همچنین ببینید (See also):
uv_fs_event_t --- هندل رویداد سیستمفایل (FS Event handle)
هندلهای FS Event به کاربر اجازه میدهند یک مسیر مشخص را برای تغییرات زیر نظر بگیرد؛ به عنوان مثال، اگر نام فایل تغییر کند یا تغییری عمومی در آن رخ دهد. این هندل از بهترین بکاند موجود برای این کار در هر پلتفرم استفاده میکند.
نکته:
- سیستم 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 مراجعه کنید.
انواع داده (Data types)
- type uv_fs_event_t
- نوع هندل FS Event.
- typedef void (*uv_fs_event_cb)(uv_fs_event_t <#c.uv_fs_event_t> *handle, const char *filename, int events, int status)
- کالبک
پاسدادهشده
به uv_fs_event_start() که
پس از شروع
هندل، به
صورت مکرر
فراخوانی
خواهد شد.
اگر هندل با یک پوشه راهاندازی شده باشد، پارامتر filename یک مسیر نسبی به یک فایل درون آن پوشه خواهد بود، یا در صورتی که نام فایل قابل تشخیص نباشد، مقدار NULL خواهد بود.
پارامتر events یک ماسک ترکیبشده با عملگر OR از عناصر uv_fs_event است.
نکته:
- enum uv_fs_event
- انواع رویدادهایی که هندلهای uv_fs_event_t نظارت میکنند.
enum uv_fs_event {
UV_RENAME = 1,
UV_CHANGE = 2
};
- enum uv_fs_event_flags
- فلگهایی که میتوانند به 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
};
اعضای عمومی (Public members)
ندارد (N/A)
همچنین ببینید (See also):
رابط برنامهنویسی (API)
- int uv_fs_event_start(uv_fs_event_t <#c.uv_fs_event_t> *handle, uv_fs_event_cb <#c.uv_fs_event_cb> cb, const char *path, unsigned int flags)
- شروع هندل
با کالبک
دادهشده،
که path
مشخصشده
را برای
تغییرات
زیر نظر
خواهد گرفت.
flags
میتواند
یک ماسک OR
شده از uv_fs_event_flags
باشد.
نکته:
نکته:
- int uv_fs_event_stop(uv_fs_event_t <#c.uv_fs_event_t> *handle)
- متوقف کردن هندل؛ پس از این کالبک دیگر فراخوانی نخواهد شد.
- int uv_fs_event_getpath(uv_fs_event_t <#c.uv_fs_event_t> *handle, char *buffer, size_t *size)
- دریافت
مسیری که
توسط هندل
تحت نظر
قرار دارد.
بافر باید
توسط کاربر
از پیش
تخصیص
یافته باشد.
در صورت
موفقیت ۰ یا
در صورت
شکست کد
خطای
کوچکتر از ۰
برمیگرداند.
در صورت
موفقیت، buffer
حاوی مسیر و
size حاوی طول
آن خواهد
بود. اگر
بافر به
اندازه
کافی بزرگ
نباشد، UV_ENOBUFS
برگردانده
شده و size روی
اندازه
مورد نیاز
شامل
خاتمهدهنده
null تنظیم
خواهد شد.
تغییریافته در نسخه 1.3.0: طول برگرداندهشده دیگر شامل بایت تهی (null) پایانی نیست و بافر با null خاتمه نمییابد.
تغییریافته در نسخه 1.9.0: طول برگرداندهشده در صورت بروز UV_ENOBUFS شامل بایت تهی پایانی است، و در صورت موفقیت بافر با null خاتمه مییابد.
همچنین ببینید (See also):
uv_fs_poll_t --- هندل پایش سیستمفایل (FS Poll handle)
هندلهای FS Poll به کاربر اجازه میدهند یک مسیر مشخص را برای تغییرات زیر نظر بگیرد. بر خلاف uv_fs_event_t <#c.uv_fs_event_t>، هندلهای fs poll از stat برای تشخیص تغییر یک فایل استفاده میکنند، بنابراین میتوانند در سیستمفایلهایی که هندلهای fs event کارایی ندارند کار کنند.
انواع داده (Data types)
- type uv_fs_poll_t
- نوع هندل FS Poll.
- typedef void (*uv_fs_poll_cb)(uv_fs_poll_t <#c.uv_fs_poll_t> *handle, int status, const uv_stat_t <#c.uv_stat_t> *prev, const uv_stat_t <#c.uv_stat_t> *curr)
- کالبک
پاسدادهشده
به uv_fs_poll_start() که
پس از شروع
به کار
هندل،
هنگام وقوع
هر تغییری
در مسیر تحت
نظارت، به
طور مکرر
فراخوانی
خواهد شد.
اگر path وجود نداشته باشد یا غیرقابل دسترس باشد، کالبک با status < 0 فراخوانی میشود. ناظر متوقف نمیشود، اما کالبک شما تا زمانی که تغییری رخ ندهد (مثلاً هنگامی که فایل ایجاد شود یا دلیل خطا تغییر کند) مجدداً فراخوانی نخواهد شد.
هنگامی که status == 0 باشد، کالبک اشارهگرهایی به ساختارهای قدیمی و جدید uv_stat_t <#c.uv_stat_t> دریافت میکند. این ساختارها فقط در طول مدت فراخوانی کالبک معتبر هستند.
اعضای عمومی (Public members)
ندارد (N/A)
همچنین ببینید (See also):
رابط برنامهنویسی (API)
- int uv_fs_poll_init(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_poll_t <#c.uv_fs_poll_t> *handle)
- راهاندازی اولیه هندل.
- int uv_fs_poll_start(uv_fs_poll_t <#c.uv_fs_poll_t> *handle, uv_fs_poll_cb <#c.uv_fs_poll_cb> poll_cb, const char *path, unsigned int interval)
- بررسی فایل
در مسیر path
برای
تغییرات در
هر بازه
زمانی interval
میلیثانیه.
نکته:
- int uv_fs_poll_stop(uv_fs_poll_t <#c.uv_fs_poll_t> *handle)
- متوقف کردن هندل؛ کالبک دیگر فراخوانی نخواهد شد.
- int uv_fs_poll_getpath(uv_fs_poll_t <#c.uv_fs_poll_t> *handle, char *buffer, size_t *size)
- دریافت
مسیری که
توسط هندل
تحت نظر
است. بافر
باید توسط
کاربر از
پیش تخصیص
یافته باشد.
در صورت
موفقیت ۰ یا
در صورت
بروز شکست
کد خطای
کوچکتر از ۰
برمیگرداند.
در صورت
موفقیت، buffer
حاوی مسیر و
size حاوی طول
آن خواهد
بود. اگر
بافر به
اندازه
کافی بزرگ
نباشد، UV_ENOBUFS
برگردانده
شده و size روی
اندازه
مورد نیاز
تنظیم
خواهد شد.
تغییریافته در نسخه 1.3.0: طول برگرداندهشده دیگر شامل بایت تهی (null) پایانی نیست و بافر با null خاتمه نمییابد.
تغییریافته در نسخه 1.9.0: طول برگرداندهشده در صورت بروز UV_ENOBUFS شامل بایت تهی پایانی است، و در صورت موفقیت بافر با null خاتمه مییابد.
همچنین ببینید (See also):
عملیات سیستم فایل (File system operations)
کتابخانه 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 پیکربندی شود.
نکته:
انواع داده (Data types)
- type uv_fs_t
- نوع درخواست سیستم فایل.
- type uv_timespec_t
- نوع داده ناامن در برابر مسئله سال ۲۰۳۸ (Y2K38-unsafe) برای ذخیره زمان با دقت نانوثانیه. در libuv v2.0 با uv_timespec64_t <#c.uv_timespec64_t> جایگزین خواهد شد.
typedef struct {
long tv_sec;
long tv_nsec;
} uv_timespec_t;
- type uv_stat_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;
- enum uv_fs_type
- نوع درخواست سیستم فایل.
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;
- type uv_statfs_t
- معادل خلاصهشده و چندسکویی 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;
- enum uv_dirent_type_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;
- type uv_dirent_t
- معادل چندسکویی (خلاصهشده) struct dirent. در uv_fs_scandir_next() استفاده میشود.
typedef struct uv_dirent_s {
const char* name;
uv_dirent_type_t type;
} uv_dirent_t;
- type uv_dir_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;
- typedef void (*uv_fs_cb)(uv_fs_t <#c.uv_fs_t> *req)
- کالبکی که هنگام تکمیل ناهمگام یک درخواست فراخوانی میشود.
اعضای عمومی (Public members)
- uv_loop_t <#c.uv_loop_t> *uv_fs_t <#c.uv_fs_t>.loop
- حلقهای که این درخواست را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقطخواندنی.
- uv_fs_type <#c.uv_fs_type> uv_fs_t <#c.uv_fs_t>.fs_type
- نوع درخواست FS.
- const char *uv_fs_t <#c.uv_fs_t>.path
- مسیری که درخواست را تحت تأثیر قرار میدهد.
- ssize_t uv_fs_t <#c.uv_fs_t>.result
- نتیجه درخواست. مقدار کوچکتر از ۰ به معنای خطا و در غیر این صورت نشاندهنده موفقیت است. در درخواستهایی مانند uv_fs_read() یا uv_fs_write() مقدار داده خواندهشده یا نوشتهشده را نشان میدهد.
- uv_stat_t <#c.uv_stat_t> uv_fs_t <#c.uv_fs_t>.statbuf
- نتیجه uv_fs_stat() و سایر درخواستهای stat را ذخیره میکند.
- void *uv_fs_t <#c.uv_fs_t>.ptr
- نتیجه uv_fs_readlink() و uv_fs_realpath() را ذخیره میکند و بهعنوان نام مستعار (alias) برای statbuf عمل میکند.
همچنین ببینید:
رابط برنامهنویسی (API)
- void uv_fs_req_cleanup(uv_fs_t <#c.uv_fs_t> *req)
- پاکسازی درخواست. باید پس از پایان درخواست برای آزادسازی هرگونه حافظهای که ممکن است libuv تخصیص داده باشد فراخوانی شود.
- int uv_fs_open(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, int flags, int mode, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل open(2)
https://man7.org/linux/man-pages/man2/open.2.html.
نکته:
- int uv_fs_read(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, uv_file <#c.uv_file> file, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل preadv(2)
https://man7.org/linux/man-pages/man2/preadv.2.html.
اگر شناسه
offset برابر با
-1 باشد،
مکان آفست
فعلی فایل
استفاده و
بهروزرسانی
میشود.
هشدار:
- int uv_fs_write(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, uv_file <#c.uv_file> file, const uv_buf_t <#c.uv_buf_t> bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل pwritev(2)
https://man7.org/linux/man-pages/man2/pwritev.2.html.
اگر شناسه
offset برابر با
-1 باشد،
مکان آفست
فعلی فایل
استفاده و
بهروزرسانی
میشود.
هشدار:
- int uv_fs_mkdir(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, int mode, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل mkdir(2)
https://man7.org/linux/man-pages/man2/mkdir.2.html.
نکته:
- int uv_fs_mkdtemp(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *tpl, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل mkdtemp(3) https://man7.org/linux/man-pages/man3/mkdtemp.3.html. نتیجه را میتوان بهصورت یک رشته خاتمهیافته با null در req->path یافت.
- int uv_fs_mkstemp(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *tpl, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل mkstemp(3)
https://man7.org/linux/man-pages/man3/mkstemp.3.html.
مسیر فایل
ایجادشده
را میتوان
بهصورت یک
رشته
خاتمهیافته
با null در req->path
یافت.
توصیفکننده
فایل (file descriptor) را
میتوان
بهصورت یک
عدد صحیح در
req->result یافت.
افزودهشده در نسخه 1.34.0.
- int uv_fs_opendir(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, uv_fs_cb <#c.uv_fs_cb> cb)
- مسیر 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.
- int uv_fs_closedir(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, uv_dir_t <#c.uv_dir_t> *dir, uv_fs_cb <#c.uv_fs_cb> cb)
- جریان
دایرکتوری
مشخصشده
با dir را
میبندد و
حافظه
تخصیصیافته
توسط uv_fs_opendir() را
آزاد
میکند.
افزودهشده در نسخه 1.28.0.
- int uv_fs_readdir(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, uv_dir_t <#c.uv_dir_t> *dir, uv_fs_cb <#c.uv_fs_cb> cb)
- روی جریان
دایرکتوری
dir که از یک
فراخوانی
موفق uv_fs_opendir()
بازگردانده
شده،
پیمایش
میکند. پیش
از
فراخوانی
uv_fs_readdir()،
فراخواننده
باید dir->dirents و
dir->nentries را
تنظیم کند
که
نشاندهنده
آرایهای
از عناصر
uv_dirent_t مورد
استفاده
برای
نگهداری
مدخلهای
خواندهشده
دایرکتوری
و اندازه آن
است.
در صورت موفقیت، نتیجه یک عدد صحیح >= 0 است که تعداد مدخلهای خواندهشده از جریان را نشان میدهد.
افزودهشده در نسخه 1.28.0.
هشدار:
نکته:
نکته:
- int uv_fs_scandir_next(uv_fs_t <#c.uv_fs_t> *req, uv_dirent_t <#c.uv_dirent_t> *ent)
- معادل scandir(3)
https://man7.org/linux/man-pages/man3/scandir.3.html،
با یک API کمی
متفاوت. پس
از
فراخوانی
کالبک برای
درخواست،
کاربر
میتواند
از uv_fs_scandir_next()
برای پر
کردن ent با
دادههای
مدخل بعدی
دایرکتوری
استفاده
کند. هنگامی
که مدخل
دیگری وجود
نداشته
باشد، UV_EOF
بازگردانده
خواهد شد.
نکته:
نکته:
- int uv_fs_lstat(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, uv_fs_cb <#c.uv_fs_cb> cb)
- بهترتیب معادل stat(2) https://man7.org/linux/man-pages/man2/stat.2.html، fstat(2) https://man7.org/linux/man-pages/man2/fstat.2.html و lstat(2) https://man7.org/linux/man-pages/man2/lstat.2.html.
- int uv_fs_statfs(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل statfs(2)
https://man7.org/linux/man-pages/man2/statfs.2.html.
در صورت
موفقیت، یک
uv_statfs_t تخصیص
یافته و از
طریق req->ptr
برگردانده
میشود. این
حافظه توسط
uv_fs_req_cleanup() آزاد
میشود.
نکته:
افزودهشده در نسخه 1.31.0.
- int uv_fs_fsync(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, uv_file <#c.uv_file> file, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل fsync(2)
https://man7.org/linux/man-pages/man2/fsync.2.html.
نکته:
- int uv_fs_copyfile(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, const char *new_path, int flags, uv_fs_cb <#c.uv_fs_cb> cb)
- فایلی را از 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 نگاشت میشدند.
- int uv_fs_access(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, int mode, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل access(2) https://man7.org/linux/man-pages/man2/access.2.html در یونیکس. ویندوز از GetFileAttributesW() استفاده میکند.
- int uv_fs_lutime(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, double atime, double mtime, uv_fs_cb <#c.uv_fs_cb> cb)
- بهترتیب
معادل 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، برچسب زمانی را دستنخورده باقی میگذارد.
نکته:
نکته:
تغییریافته در نسخه 1.10.0: دقت زیر یک ثانیه (sub-second precision) در ویندوز پشتیبانی میشود.
- int uv_fs_symlink(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, const char *new_path, int flags, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل symlink(2)
https://man7.org/linux/man-pages/man2/symlink.2.html.
نکته:
- UV_FS_SYMLINK_DIR: نشان میدهد که path به یک دایرکتوری اشاره میکند.
- UV_FS_SYMLINK_JUNCTION: درخواست میکند که پیوند نمادین با استفاده از نقاط اتصال (junction points) ایجاد شود.
- int uv_fs_readlink(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل readlink(2) https://man7.org/linux/man-pages/man2/readlink.2.html. رشته حاصل در req->ptr ذخیره میشود.
- int uv_fs_realpath(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, uv_fs_cb <#c.uv_fs_cb> cb)
- معادل 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
ذخیره
میشود.
هشدار:
- سیستمهای macOS و سایر BSDها: اگر هنگام حل مسیر دادهشده بیش از ۳۲ پیوند نمادین پیدا شود، این تابع با خطای UV_ELOOP شکست میخورد. این محدودیت بهصورت سراسری و ثابت است و نمیتوان آن را دور زد.
- ویندوز: در حالی که این تابع در حالتهای معمول کار میکند، تعدادی حالت مرزی (corner case) وجود دارد که در آنها کار نمیکند:
- مسیرهای درون حجمهای رمدیسک (ramdisk) ایجادشده توسط ابزارهایی که مدیر حجم (Volume Manager) را دور میزنند (مانند ImDisk) قابل حل نیستند.
- بزرگ و کوچک بودن ناهمخوان حروف در هنگام استفاده از حروف درایو (drive letters).
- مسیر حلشده درایوهای subst شده را دور میزند.
اگرچه همچنان میتوان از این تابع استفاده کرد، اما در صورتی که نیاز به پشتیبانی از سناریوهایی مانند موارد بالا باشد، استفاده از آن توصیه نمیشود.
داستان پیشزمینه و جزئیات بیشتر در مورد این مشکلات را میتوان در اینجا https://github.com/nodejs/node/issues/7726 بررسی کرد.
افزودهشده در نسخه 1.8.0.
- int uv_fs_lchown(uv_loop_t <#c.uv_loop_t> *loop, uv_fs_t <#c.uv_fs_t> *req, const char *path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb <#c.uv_fs_cb> cb)
- بهترتیب
معادل 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 پیادهسازی شد.
- uv_fs_type <#c.uv_fs_type> uv_fs_get_type(const uv_fs_t <#c.uv_fs_t> *req)
- مقدار req->fs_type
را
برمیگرداند.
افزودهشده در نسخه 1.19.0.
- ssize_t uv_fs_get_result(const uv_fs_t <#c.uv_fs_t> *req)
- مقدار req->result
را
برمیگرداند.
افزودهشده در نسخه 1.19.0.
- int uv_fs_get_system_error(const uv_fs_t <#c.uv_fs_t> *req)
- کد خطای
مخصوص
پلتفرم را
برمیگرداند
- مقدار GetLastError()
در ویندوز و
-(req->result) در سایر
پلتفرمها.
افزودهشده در نسخه 1.38.0.
- void *uv_fs_get_ptr(const uv_fs_t <#c.uv_fs_t> *req)
- مقدار req->ptr
را
برمیگرداند.
افزودهشده در نسخه 1.19.0.
- const char *uv_fs_get_path(const uv_fs_t <#c.uv_fs_t> *req)
- مقدار req->path
را
برمیگرداند.
افزودهشده در نسخه 1.19.0.
- uv_stat_t <#c.uv_stat_t> *uv_fs_get_statbuf(uv_fs_t <#c.uv_fs_t> *req)
- مقدار &req->statbuf
را
برمیگرداند.
افزودهشده در نسخه 1.19.0.
همچنین ببینید:
توابع کمکی (Helper functions)
- uv_os_fd_t <#c.uv_os_fd_t> uv_get_osfhandle(int fd)
- برای یک توصیفکننده فایل در زمان اجرای C، هندل وابسته به سیستمعامل را دریافت میکند. در یونیکس، fd را دستنخورده برمیگرداند. در ویندوز، تابع _get_osfhandle https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2019 را فراخوانی میکند. توجه داشته باشید که مقدار بازگرداندهشده همچنان متعلق به زمان اجرای C است؛ هرگونه تلاش برای بستن آن یا استفاده از آن پس از بستن fd ممکن است منجر به عملکرد نادرست شود.
- int uv_open_osfhandle(uv_os_fd_t <#c.uv_os_fd_t> os_fd)
- برای یک هندل وابسته به سیستمعامل، توصیفکننده فایل را در زمان اجرای C دریافت میکند. در یونیکس، os_fd را دستنخورده برمیگرداند. در ویندوز، تابع _open_osfhandle https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open-osfhandle?view=vs-2019 را فراخوانی میکند. توجه داشته باشید که این تابع آرگومان را مصرف میکند؛ هرگونه تلاش برای بستن آن یا استفاده از آن پس از بستن مقدار بازگرداندهشده ممکن است منجر به عملکرد نادرست شود.
ثابتهای باز کردن فایل (File open constants)
- UV_FS_O_APPEND
- فایل در حالت الحاق (append) باز میشود. قبل از هر نوشتن، آفست فایل در انتهای فایل قرار میگیرد.
- UV_FS_O_CREAT
- اگر فایل از قبل وجود نداشته باشد، ایجاد میشود.
- UV_FS_O_DIRECT
- ورودی/خروجی
(I/O) فایل
بهطور
مستقیم از و
به بافرهای
فضای کاربر
(user-space) انجام
میشود که
باید تراز
(aligned) شده
باشند.
اندازه و
آدرس بافر
باید مضربی
از اندازه
بخش فیزیکی
دستگاه
بلوکی باشد.
نکته:
- UV_FS_O_DIRECTORY
- اگر مسیر یک
دایرکتوری
نباشد، باز
کردن فایل
شکست
میخورد.
نکته:
- UV_FS_O_DSYNC
- فایل برای I/O
همگام باز
میشود.
عملیات
نوشتن پس از
انتقال
تمام
دادهها و
حداقل
فرادادهها
(metadata) به دیسک
کامل خواهد
شد.
نکته:
- UV_FS_O_EXCL
- اگر فلگ O_CREAT
تنظیم شده
باشد و فایل
از قبل وجود
داشته
باشد، باز
کردن فایل
شکست
میخورد.
نکته:
- UV_FS_O_EXLOCK
- بهصورت
اتمیک یک
قفل
انحصاری (exclusive lock)
دریافت
میکند.
نکته:
تغییریافته در نسخه 1.17.0: پشتیبانی برای ویندوز اضافه شد.
- UV_FS_O_FILEMAP
- از یک نگاشت
حافظه فایل
برای
دسترسی به
فایل
استفاده
میکند.
هنگام
استفاده از
این فلگ،
فایل
نمیتواند
چندین بار
بهطور
همزمان باز
شود.
نکته:
- UV_FS_O_NOATIME
- هنگام
خواندن
فایل، زمان
دسترسی به
فایل را
بهروزرسانی
نمیکند.
نکته:
- UV_FS_O_NOCTTY
- اگر مسیر یک
دستگاه
ترمینال را
مشخص کند،
باز کردن
مسیر باعث
نمیشود آن
ترمینال به
ترمینال
کنترلی
فرآیند
تبدیل شود
(در صورتی
که فرآیند
قبلاً
ترمینال
کنترلی
نداشته
باشد).
نکته:
- UV_FS_O_NOFOLLOW
- اگر مسیر یک
پیوند
نمادین
باشد، باز
کردن فایل
شکست
میخورد.
نکته:
- UV_FS_O_NONBLOCK
- در صورت
امکان،
فایل را در
حالت
غیرمسدودکننده
(nonblocking) باز
میکند.
نکته:
- UV_FS_O_RANDOM
- دسترسی
بهصورت
تصادفی در
نظر گرفته
شده است.
سیستم
میتواند
از این
بهعنوان
یک راهنما (hint)
برای
بهینهسازی
کش فایل
استفاده
کند.
نکته:
- UV_FS_O_RDONLY
- فایل را برای دسترسی فقطخواندنی باز میکند.
- UV_FS_O_RDWR
- فایل را برای دسترسی خواندن و نوشتن باز میکند.
- UV_FS_O_SEQUENTIAL
- دسترسی
بهصورت
ترتیبی از
ابتدا تا
انتها در
نظر گرفته
شده است.
سیستم
میتواند
از این
بهعنوان
یک راهنما (hint)
برای
بهینهسازی
کش فایل
استفاده
کند.
نکته:
- UV_FS_O_SHORT_LIVED
- فایل موقت
است و در
صورت امکان
نباید روی
دیسک فلاش
شود.
نکته:
- UV_FS_O_SYMLINK
- بهجای منبعی که پیوند نمادین به آن اشاره میکند، خود پیوند نمادین را باز میکند.
- UV_FS_O_SYNC
- فایل برای I/O
همگام باز
میشود.
عملیات
نوشتن پس از
انتقال
تمام
دادهها و
تمام
فرادادهها
(metadata) به دیسک
کامل خواهد
شد.
نکته:
- UV_FS_O_TEMPORARY
- فایل موقت
است و در
صورت امکان
نباید روی
دیسک فلاش
شود.
نکته:
- UV_FS_O_TRUNC
- اگر فایل وجود داشته باشد و یک فایل عادی باشد، و فایل با موفقیت برای دسترسی نوشتن باز شود، طول آن به صفر کاهش مییابد (truncate میشود).
- UV_FS_O_WRONLY
- فایل را برای دسترسی فقطنوشتنی باز میکند.
زمانبندی کار استخر ریسه (Thread pool work scheduling)
کتابخانه 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) رشد میکنند.
نکته:
انواع داده (Data types)
- type uv_work_t
- نوع درخواست کار (work request).
- typedef void (*uv_work_cb)(uv_work_t <#c.uv_work_t> *req)
- کالبک ارسالشده به uv_queue_work() که روی استخر ریسه اجرا خواهد شد.
- typedef void (*uv_after_work_cb)(uv_work_t <#c.uv_work_t> *req, int status)
- کالبک ارسالشده به uv_queue_work() که پس از اتمام کار روی استخر ریسه، روی ریسه حلقه فراخوانی خواهد شد. اگر کار با استفاده از uv_cancel() <#c.uv_cancel> لغو شده باشد، status برابر با UV_ECANCELED خواهد بود.
اعضای عمومی (Public members)
- uv_loop_t <#c.uv_loop_t> *uv_work_t <#c.uv_work_t>.loop
- حلقهای که این درخواست را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقطخواندنی.
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_queue_work(uv_loop_t <#c.uv_loop_t> *loop, uv_work_t <#c.uv_work_t> *req, uv_work_cb <#c.uv_work_cb> work_cb, uv_after_work_cb <#c.uv_after_work_cb> after_work_cb)
- یک درخواست
کار را
مقداردهی
اولیه
میکند که
work_cb
دادهشده
را در یک
ریسه از
استخر ریسه
اجرا خواهد
کرد. پس از
تکمیل work_cb،
تابع after_work_cb
روی ریسه
حلقه
فراخوانی
خواهد شد.
این درخواست را میتوان با uv_cancel() <#c.uv_cancel> لغو کرد.
همچنین ببینید:
توابع کاربردی DNS (DNS utility functions)
کتابخانه libuv گونههای ناهمگام getaddrinfo و getnameinfo را فراهم میکند.
انواع داده (Data types)
- type uv_getaddrinfo_t
- نوع درخواست getaddrinfo.
- typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t <#c.uv_getaddrinfo_t> *req, int status, struct addrinfo *res)
- کالبکی که پس از تکمیل، با نتیجه درخواست getaddrinfo فراخوانی خواهد شد. در صورتی که لغو شده باشد، status مقداری برابر با UV_ECANCELED خواهد داشت.
- type uv_getnameinfo_t
- نوع درخواست getnameinfo.
- typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t <#c.uv_getnameinfo_t> *req, int status, const char *hostname, const char *service)
- کالبکی که پس از تکمیل، با نتیجه درخواست getnameinfo فراخوانی خواهد شد. در صورتی که لغو شده باشد، status مقداری برابر با UV_ECANCELED خواهد داشت.
اعضای عمومی (Public members)
- uv_loop_t <#c.uv_loop_t> *uv_getaddrinfo_t <#c.uv_getaddrinfo_t>.loop
- حلقهای که این درخواست getaddrinfo را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقطخواندنی.
- struct addrinfo <#c.uv_getaddrinfo_t.addrinfo> *uv_getaddrinfo_t <#c.uv_getaddrinfo_t>.addrinfo
- اشارهگر
به یک struct addrinfo
حاوی نتیجه.
باید توسط
کاربر با
uv_freeaddrinfo() آزاد
شود.
تغییریافته در نسخه 1.3.0: این فیلد بهصورت عمومی (public) اعلان شده است.
- uv_loop_t <#c.uv_loop_t> *uv_getnameinfo_t <#c.uv_getnameinfo_t>.loop
- حلقهای که این درخواست getnameinfo را آغاز کرده و تکمیل درخواست در آن گزارش خواهد شد. فقطخواندنی.
- char[NI_MAXHOST] uv_getnameinfo_t.host
- آرایه
کاراکتری
حاوی
میزبان
حاصل. با null
خاتمه
یافته است.
تغییریافته در نسخه 1.3.0: این فیلد بهصورت عمومی (public) اعلان شده است.
- char[NI_MAXSERV] uv_getnameinfo_t.service
- آرایه
کاراکتری
حاوی سرویس
حاصل. با null
خاتمه
یافته است.
تغییریافته در نسخه 1.3.0: این فیلد بهصورت عمومی (public) اعلان شده است.
همچنین ببینید:
رابط برنامهنویسی (API)
- int uv_getaddrinfo(uv_loop_t <#c.uv_loop_t> *loop, uv_getaddrinfo_t <#c.uv_getaddrinfo_t> *req, uv_getaddrinfo_cb <#c.uv_getaddrinfo_cb> getaddrinfo_cb, const char *node, const char *service, const struct addrinfo *hints)
- معادل
ناهمگام 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 باشد، که در این حالت درخواست بهصورت همگام اجرا خواهد شد.
- void uv_freeaddrinfo(struct addrinfo *ai)
- ساختار struct addrinfo را آزاد میکند. ارسال NULL مجاز است و عملیاتی انجام نمیدهد (no-op).
- int uv_getnameinfo(uv_loop_t <#c.uv_loop_t> *loop, uv_getnameinfo_t <#c.uv_getnameinfo_t> *req, uv_getnameinfo_cb <#c.uv_getnameinfo_cb> getnameinfo_cb, const struct sockaddr *addr, int flags)
- معادل
ناهمگام getnameinfo(3)
https://man7.org/linux/man-pages/man3/getnameinfo.3.html.
در صورت موفقیت ۰ یا در صورت شکست یک کد خطای کوچکتر از ۰ برمیگرداند. در صورت موفقیت، کالبک در زمانی در آینده با نتیجه جستجو فراخوانی خواهد شد. برای جزئیات بیشتر به man -s 3 getnameinfo مراجعه کنید.
تغییریافته در نسخه 1.3.0: پارامتر کالبک اکنون مجاز است NULL باشد، که در این حالت درخواست بهصورت همگام اجرا خواهد شد.
همچنین ببینید:
مدیریت کتابخانههای اشتراکی (Shared library handling)
کتابخانه libuv ابزارهایی مستقل از بستر برای بارگذاری کتابخانههای اشتراکی و استخراج نمادها از آنها با استفاده از رابط برنامهنویسی زیر فراهم میکند.
انواع داده (Data types)
- type uv_lib_t
- نوع داده کتابخانه اشتراکی.
اعضای عمومی (Public members)
موردی وجود ندارد (N/A)
رابط برنامهنویسی (API)
- int uv_dlopen(const char *filename, uv_lib_t <#c.uv_lib_t> *lib)
- یک کتابخانه اشتراکی را باز میکند. نام فایل در قالب utf-8 است. در صورت موفقیت مقدار 0 و در صورت خطا مقدار -1 را برمیگرداند. برای دریافت پیام خطا تابع uv_dlerror() را فراخوانی کنید.
- void uv_dlclose(uv_lib_t <#c.uv_lib_t> *lib)
- کتابخانه اشتراکی را میبندد.
- int uv_dlsym(uv_lib_t <#c.uv_lib_t> *lib, const char *name, void **ptr)
- اشارهگر داده را از یک کتابخانه پویا بازیابی میکند. نگاشت یک نماد به NULL مجاز و معتبر است. در صورت موفقیت 0 و در صورتی که نماد یافت نشود -1 برمیگرداند.
- const char *uv_dlerror(const uv_lib_t <#c.uv_lib_t> *lib)
- آخرین پیام خطای uv_dlopen() یا uv_dlsym() را برمیگرداند.
ابزارهای ریسهبندی و همگامسازی (Threading and synchronization utilities)
کتابخانه libuv پیادهسازیهای چندسکویی برای چندین ساختار بنیادین ریسهبندی و همگامسازی ارائه میدهد. این رابط برنامهنویسی عمدتاً از API پتردز (pthreads) پیروی میکند.
انواع داده (Data types)
- type uv_thread_t
- نوع داده ریسه (Thread).
- typedef void (*uv_thread_cb)(void *arg)
- تابع بازخوانی (Callback) که برای مقداردهی اولیه اجرای ریسه فراخوانی میشود. arg همان مقداری است که به uv_thread_create() ارسال شده بود.
- type uv_key_t
- نوع داده کلید محلی ریسه (Thread-local key).
- type uv_once_t
- نوع داده مقداردهی اولیهساز تکباره (Once-only initializer).
- type uv_mutex_t
- نوع داده قفل انحصار متقابل (Mutex).
- type uv_rwlock_t
- نوع داده قفل خواندن-نوشتن (Read-write lock).
- type uv_sem_t
- نوع داده سمافور (Semaphore).
- type uv_cond_t
- نوع داده شرط (Condition).
- type uv_barrier_t
- نوع داده مانع (Barrier).
رابط برنامهنویسی (API)
ریسهها (Threads)
- type uv_thread_options_t
- گزینههای ایجاد ریسه جدید (ارسالشده به 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.
- int uv_thread_create(uv_thread_t <#c.uv_thread_t> *tid, uv_thread_cb <#c.uv_thread_cb> entry, void *arg)
- تغییر یافته در نسخه 1.4.1: در صورت شکست، کد خطای UV_E* را برمیگرداند.
- int uv_thread_detach(uv_thread_t <#c.uv_thread_t> *tid)
- ریسه را جدا
(Detach) میکند.
ریسههای
جداشده پس
از خاتمه به
طور خودکار
منابع خود
را آزاد
میکنند و
نیاز
برنامه به
فراخوانی
uv_thread_join را از
بین
میبرند.
افزوده شده در نسخه 1.50.0.
- int uv_thread_create_ex(uv_thread_t <#c.uv_thread_t> *tid, const uv_thread_options_t <#c.uv_thread_options_t> *params, uv_thread_cb <#c.uv_thread_cb> entry, void *arg)
- مشابه uv_thread_create()،
اما علاوه
بر آن
گزینههایی
را برای
ایجاد یک
ریسه جدید
مشخص
میکند.
اگر UV_THREAD_HAS_STACK_SIZE تنظیم شده باشد، stack_size اندازه پشته را برای ریسه جدید مشخص میکند. مقدار 0 نشان میدهد که باید از مقدار پیشفرض استفاده شود (رفتاری مشابه زمانی که پرچم تنظیم نشده باشد). سایر مقادیر به نزدیکترین مرز صفحه حافظه گرد میشوند.
افزوده شده در نسخه 1.26.0.
- int uv_thread_setaffinity(uv_thread_t <#c.uv_thread_t> *tid, char *cpumask, char *oldmask, size_t mask_size)
- وابستگی
پردازنده
(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> باشد.
نکته:
افزوده شده در نسخه 1.45.0.
- int uv_thread_getaffinity(uv_thread_t <#c.uv_thread_t> *tid, char *cpumask, size_t mask_size)
- تنظیمات
وابستگی
پردازنده
ریسه
مشخصشده
را دریافت
میکند. در
یونیکس،
این تابع 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> باشد.
نکته:
افزوده شده در نسخه 1.45.0.
- int uv_thread_getcpu(void)
- شماره
پردازندهای
(CPU) را که ریسه
فراخوان
روی آن در
حال اجراست
دریافت
میکند.
نکته:
افزوده شده در نسخه 1.45.0.
- int uv_thread_setname(const char *name)
- نام ریسه
جاری را
تنظیم
میکند.
بسترهای
مختلف
محدودیتهای
متفاوتی
روی حداکثر
تعداد
کاراکترهای
نام ریسه
تعریف
میکنند:
لینوکس و IBM i
(۱۶
کاراکتر)، macOS
(۶۴
کاراکتر)،
ویندوز
(۳۲۷۶۷
کاراکتر)، و
NetBSD (۳۲
کاراکتر) و
غیره. در
صورتی که name
بزرگتر از
محدودیت
بستر باشد،
uv_thread_setname() آن را
کوتاه (truncate)
میکند.
در Windows Server 2016 پشتیبانی نمیشود و UV_ENOSYS را برمیگرداند.
افزوده شده در نسخه 1.50.0.
- int uv_thread_getname(uv_thread_t <#c.uv_thread_t> *tid, char *name, size_t *size)
- نام ریسه
مشخصشده
با tid را
دریافت
میکند. نام
ریسه به
همراه بایت
NUL انتهایی
در بافر
اشارهشده
با name کپی
میشود.
پارامتر size
اندازه
بافر
اشارهشده
با name را
مشخص
میکند.
بافر باید
به اندازه
کافی بزرگ
باشد تا نام
ریسه به
اضافه NUL
انتهایی را
در خود جای
دهد، در غیر
این صورت
برای جا
گرفتن
همراه با NUL
انتهایی
کوتاه
خواهد شد.
در Windows Server 2016 پشتیبانی نمیشود و UV_ENOSYS را برمیگرداند.
افزوده شده در نسخه 1.50.0.
- int uv_thread_setpriority(uv_thread_t <#c.uv_thread_t> tid, int priority)
- اگر تابع با موفقیت اجرا شود، مقدار بازگشتی 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.
- int uv_thread_getpriority(uv_thread_t <#c.uv_thread_t> tid, int *priority)
- اگر تابع با موفقیت اجرا شود، مقدار بازگشتی 0 است. در صورت شکست تابع، مقدار بازگشتی کمتر از صفر است. اولویت زمانبندی ریسه مشخصشده با tid را بازیابی میکند. مقدار موجود در پارامتر خروجی priority وابسته به بستر است. در لینوکس، زمانی که سیاست زمانبندی SCHED_OTHER (پیشفرض) باشد، اولویت 0 است.
حافظه محلی ریسه (Thread-local storage)
نکته:
مقداردهی اولیه یکباره (Once-only initialization)
یک تابع را یکبار و فقط یکبار اجرا میکند. فراخوانیهای همزمان uv_once() با یک گارد یکسان، تمام فراخوانها به جز یکی را مسدود میکند (مشخص نیست کدام یک). گارد باید به صورت ایستا با ماکروی UV_ONCE_INIT مقداردهی اولیه شود.
قفلهای انحصار متقابل (Mutex locks)
توابع در صورت موفقیت مقدار 0 یا کد خطای کوچکتر از 0 را برمیگردانند (البته مگر اینکه نوع بازگشتی void باشد).
قفلهای خواندن-نوشتن (Read-write locks)
توابع در صورت موفقیت مقدار 0 یا کد خطای کوچکتر از 0 را برمیگردانند (البته مگر اینکه نوع بازگشتی void باشد).
سمافورها (Semaphores)
توابع در صورت موفقیت مقدار 0 یا کد خطای کوچکتر از 0 را برمیگردانند (البته مگر اینکه نوع بازگشتی void باشد).
شرایط (Conditions)
توابع در صورت موفقیت مقدار 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 رخ میدهد. پس از شروع انتظار، زمان انتظار تحت تأثیر تغییرات ساعت سیستم قرار نمیگیرد.
مانعها (Barriers)
توابع در صورت موفقیت مقدار 0 یا کد خطای کوچکتر از 0 را برمیگردانند (البته مگر اینکه نوع بازگشتی void باشد).
نکته:
if (uv_barrier_wait(&barrier) > 0)
uv_barrier_destroy(&barrier);
ابزارهای متفرقه (Miscellaneous utilities)
این بخش شامل توابع متفرقهای است که به بخشهای دیگر تعلق ندارند.
انواع داده (Data types)
- type uv_buf_t
- نوع داده بافر (Buffer).
- char *uv_buf_t.base
- اشارهگر به پایه بافر.
- size_t uv_buf_t.len
- مجموع
بایتها در
بافر.
نکته:
- typedef void *(*uv_malloc_func)(size_t size)
- تابع جایگزین برای malloc(3) https://man7.org/linux/man-pages/man3/malloc.3.html. به uv_replace_allocator() مراجعه کنید.
- typedef void *(*uv_realloc_func)(void *ptr, size_t size)
- تابع جایگزین برای realloc(3) https://man7.org/linux/man-pages/man3/realloc.3.html. به uv_replace_allocator() مراجعه کنید.
- typedef void *(*uv_calloc_func)(size_t count, size_t size)
- تابع جایگزین برای calloc(3) https://man7.org/linux/man-pages/man3/calloc.3.html. به uv_replace_allocator() مراجعه کنید.
- typedef void (*uv_free_func)(void *ptr)
- تابع جایگزین برای free(3) https://man7.org/linux/man-pages/man3/free.3.html. به uv_replace_allocator() مراجعه کنید.
- typedef void (*uv_random_cb)(uv_random_t <#c.uv_random_t> *req, int status, void *buf, size_t buflen)
- تابع بازخوانی ارسالشده به uv_random(). پارامتر status در صورت بروز خطا غیرصفر است. اشارهگر buf همان اشارهگری است که به uv_random() ارسال شده بود.
- type uv_file
- نمایش چندسکویی از دستگیره فایل (File handle).
- type uv_os_sock_t
- نمایش چندسکویی از دستگیره سوکت (Socket handle).
- type uv_os_fd_t
- نمایش انتزاعی از توصیفکننده فایل (File descriptor). در سیستمهای یونیکس این مورد یک typedef از نوع int و در ویندوز یک HANDLE است.
- type uv_pid_t
- نمایش
چندسکویی
از نوع pid_t.
افزوده شده در نسخه 1.16.0.
- type uv_timeval_t
- نوع داده ناامن در برابر مسئله سال ۲۰۳۸ (Y2K38-unsafe) برای ذخیره زمان با دقت میکروثانیه. در نسخه libuv v2.0 با uv_timeval64_t جایگزین خواهد شد.
typedef struct {
long tv_sec;
long tv_usec;
} uv_timeval_t;
- type uv_timeval64_t
- نوع داده امن در برابر مسئله سال ۲۰۳۸ (Y2K38-safe) برای ذخیره زمان با دقت میکروثانیه.
typedef struct {
int64_t tv_sec;
int32_t tv_usec;
} uv_timeval64_t;
- type uv_timespec64_t
- نوع داده امن در برابر مسئله سال ۲۰۳۸ (Y2K38-safe) برای ذخیره زمان با دقت نانوثانیه.
typedef struct {
int64_t tv_sec;
int32_t tv_nsec;
} uv_timespec64_t;
- enum uv_clock_id
- منبع ساعت برای uv_clock_gettime().
typedef enum {
UV_CLOCK_MONOTONIC,
UV_CLOCK_REALTIME
} uv_clock_id;
- type uv_rusage_t
- نوع داده برای نتایج مصرف منابع (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) بر حسب کیلوبایت گزارش میشود که واحد بومی اکثر پلتفرمها است.
- type uv_cpu_info_t
- نوع داده برای اطلاعات پردازنده (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;
- type uv_interface_address_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;
- type uv_passwd_t
- نوع داده برای اطلاعات فایل گذرواژه (Passwd).
typedef struct uv_passwd_s {
char* username;
long uid;
long gid;
char* shell;
char* homedir;
} uv_passwd_t;
- type uv_group_t
- نوع داده برای اطلاعات فایل گروه (Group).
typedef struct uv_group_s {
char* groupname;
unsigned long gid;
char** members;
} uv_group_t;
- type uv_utsname_t
- نوع داده برای نام و نسخه سیستمعامل.
typedef struct uv_utsname_s {
char sysname[256];
char release[256];
char version[256];
char machine[256];
} uv_utsname_t;
- type uv_env_item_t
- نوع داده برای ذخیره متغیرهای محیطی.
typedef struct uv_env_item_s {
char* name;
char* value;
} uv_env_item_t;
- type uv_random_t
- نوع درخواست دادههای تصادفی.
رابط برنامهنویسی (API)
- uv_handle_type <#c.uv_handle_type> uv_guess_handle(uv_file <#c.uv_file> file)
- برای تشخیص
اینکه چه
نوع جریانی
باید با
توصیفکننده
فایل
دادهشده
استفاده
شود به کار
میرود.
معمولاً در
هنگام
مقداردهی
اولیه برای
حدس زدن نوع
جریانهای
stdio استفاده
میشود.
برای قابلیتی معادل isatty(3) https://man7.org/linux/man-pages/man3/isatty.3.html از این تابع استفاده کرده و برای UV_TTY بررسی کنید.
- int uv_replace_allocator(uv_malloc_func <#c.uv_malloc_func> malloc_func, uv_realloc_func <#c.uv_realloc_func> realloc_func, uv_calloc_func <#c.uv_calloc_func> calloc_func, uv_free_func <#c.uv_free_func> free_func)
- افزوده شده
در نسخه 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 را برمیگرداند.
هشدار:
هشدار:
- void uv_library_shutdown(void);
- افزوده شده
در نسخه 1.38.0.
آزاد کردن هرگونه وضعیت سراسری که libuv در دست دارد. کتابخانه libuv معمولاً هنگام تخلیه از حافظه به طور خودکار این کار را انجام میدهد، اما میتوان به آن دستور داد تا پاکسازی را به صورت دستی انجام دهد.
هشدار:
هشدار:
هشدار:
- uv_buf_t <#c.uv_buf_t> uv_buf_init(char *base, unsigned int len)
- سازنده
برای
ساختار uv_buf_t.
به دلیل تفاوتهای بسترها، کاربر نمیتواند به ترتیب اعضای base و len در ساختار uv_buf_t اتکا کند. مسئولیت آزادسازی base پس از اتمام کار با uv_buf_t بر عهده کاربر است. ساختار بازگشتی با مقدار (by value) ارسال میشود.
- char **uv_setup_args(int argc, char **argv)
- ذخیره
آرگومانهای
برنامه.
برای
دریافت /
تنظیم
عنوان
فرایند یا
مسیر فایل
اجرایی
مورد نیاز
است. ممکن
است libuv
مالکیت
حافظهای
را که argv به
آن اشاره
میکند در
دست بگیرد.
این تابع
باید
دقیقاً
یکبار و در
زمان شروع
برنامه
فراخوانی
شود.
مثال:
argv = uv_setup_args(argc, argv); /* May return a copy of argv. */
- int uv_get_process_title(char *buffer, size_t size)
- عنوان
فرایند
جاری را
دریافت
میکند. در
سیستمهای
Unix و AIX باید
قبل از
فراخوانی
این تابع،
uv_setup_args را
فراخوانی
کنید. اگر
در
سیستمهایی
که به آن
نیاز دارند
uv_setup_args
فراخوانی
نشده باشد،
UV_ENOBUFS
برگردانده
میشود. اگر
buffer برابر با
NULL یا size صفر
باشد، UV_EINVAL
برگردانده
میشود. اگر
size گنجایش
عنوان
فرایند و
کاراکتر nul
پایانی را
نداشته
باشد، تابع
UV_ENOBUFS را
برمیگرداند.
نکته:
تغییر یافته در نسخه 1.18.1: اکنون در تمام پلتفرمهای پشتیبانیشده ایمن در برابر ریسه (Thread-safe) است.
تغییر یافته در نسخه 1.39.0: اکنون اگر uv_setup_args مورد نیاز باشد اما فراخوانی نشده باشد، یک خطا برمیگرداند.
- int uv_set_process_title(const char *title)
- عنوان
فرایند
جاری را
تنظیم
میکند. در
سیستمهای
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 مورد نیاز باشد اما فراخوانی نشده باشد، یک خطا برمیگرداند.
- int uv_resident_set_memory(size_t *rss)
- اندازه مقیم در حافظه (RSS) را برای فرایند جاری دریافت میکند.
- int uv_uptime(double *uptime)
- زمان بالا بودن (Uptime) سیستم را دریافت میکند. بسته به سیستم، ثانیهها به صورت کامل یا کسری برگردانده میشوند.
- int uv_getrusage(uv_rusage_t <#c.uv_rusage_t> *rusage)
- معیارهای
مصرف منابع
را برای
فرایند
جاری
دریافت
میکند.
نکته:
- int uv_getrusage_thread(uv_rusage_t <#c.uv_rusage_t> *rusage)
- معیارهای
مصرف منابع
را برای
ریسه
فراخوان
دریافت
میکند.
افزوده شده در نسخه 1.50.0.
نکته:
- uv_pid_t <#c.uv_pid_t> uv_os_getpid(void)
- شناسه
فرایند
جاری (Process ID) را
برمیگرداند.
افزوده شده در نسخه 1.18.0.
- uv_pid_t <#c.uv_pid_t> uv_os_getppid(void)
- شناسه
فرایند
والد (Parent Process ID) را
برمیگرداند.
افزوده شده در نسخه 1.16.0.
- unsigned int uv_available_parallelism(void)
- تخمین
مقدار
موازیسازی
پیشفرضی
که یک
برنامه
باید
استفاده
کند را
بازمیگرداند.
همواره
مقداری
غیرصفر
برمیگرداند.
در لینوکس، ماسک وابستگی CPU ریسه فراخوان را بررسی میکند تا مشخص شود آیا به CPUهای خاصی متصل (pin) شده است یا خیر.
در ویندوز، در سیستمهایی با بیش از ۶۴ پردازنده منطقی، موازیسازی در دسترس ممکن است کمتر از واقعیت گزارش شود.
در سایر بسترها، تعداد پردازندههایی را که سیستمعامل فعال (Online) در نظر میگیرد گزارش میکند.
افزوده شده در نسخه 1.44.0.
- int uv_cpu_info(uv_cpu_info_t <#c.uv_cpu_info_t> **cpu_infos, int *count)
- اطلاعات
مربوط به
پردازندههای
موجود در
سیستم را
دریافت
میکند.
آرایه cpu_infos
شامل count
عنصر خواهد
بود و باید
با uv_free_cpu_info()
آزاد شود.
اگر نیاز دارید بدانید چند پردازنده برای ریسهها یا فرایندهای فرزند در دسترس است، از uv_available_parallelism() استفاده کنید.
- void uv_free_cpu_info(uv_cpu_info_t <#c.uv_cpu_info_t> *cpu_infos, int count)
- آرایه cpu_infos را که قبلاً با uv_cpu_info() تخصیص یافته بود آزاد میکند.
- int uv_cpumask_size(void)
- حداکثر
اندازه
ماسک
استفادهشده
برای
وابستگی
فرایند/ریسه
را
برمیگرداند،
یا اگر
وابستگی در
بستر جاری
پشتیبانی
نشود، UV_ENOTSUP
را
برمیگرداند.
افزوده شده در نسخه 1.45.0.
- int uv_interface_addresses(uv_interface_address_t <#c.uv_interface_address_t> **addresses, int *count)
- اطلاعات آدرس مربوط به رابطهای شبکه موجود در سیستم را دریافت میکند. آرایهای از count عنصر تخصیص یافته و در addresses بازگردانده میشود. این آرایه باید توسط کاربر و با فراخوانی uv_free_interface_addresses() آزاد شود.
- void uv_free_interface_addresses(uv_interface_address_t <#c.uv_interface_address_t> *addresses, int count)
- آرایهای از ساختارهای uv_interface_address_t را که توسط uv_interface_addresses() بازگردانده شده بود آزاد میکند.
- void uv_loadavg(double avg[3])
- میانگین
بار سیستم (Load
average) را دریافت
میکند.
ببینید:
<https://en.wikipedia.org/wiki/Load_(computing)>
نکته:
- int uv_ip4_addr(const char *ip, int port, struct sockaddr_in *addr)
- یک رشته حاوی آدرس IPv4 را به یک ساختار باینری تبدیل میکند.
- int uv_ip6_addr(const char *ip, int port, struct sockaddr_in6 *addr)
- یک رشته حاوی آدرس IPv6 را به یک ساختار باینری تبدیل میکند.
- int uv_ip4_name(const struct sockaddr_in *src, char *dst, size_t size)
- یک ساختار باینری حاوی آدرس IPv4 را به یک رشته تبدیل میکند.
- int uv_ip6_name(const struct sockaddr_in6 *src, char *dst, size_t size)
- یک ساختار باینری حاوی آدرس IPv6 را به یک رشته تبدیل میکند.
- int uv_ip_name(const struct sockaddr *src, char *dst, size_t size)
- یک ساختار باینری حاوی آدرس IPv4 یا IPv6 را به یک رشته تبدیل میکند.
- int uv_inet_pton(int af, const char *src, void *dst)
- پیادهسازی چندسکویی و سازگار با 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 دستنخورده باقی میماند.
- UV_IF_NAMESIZE
- حداکثر طول
نام شناسه
رابط IPv6. در
یونیکس به
صورت IFNAMSIZ و در
لینوکس و
ویندوز به
صورت IF_NAMESIZE
تعریف شده
است.
افزوده شده در نسخه 1.16.0.
- int uv_if_indextoname(unsigned int ifindex, char *buffer, size_t *size)
- پیادهسازی
سازگار با 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.
- int uv_if_indextoiid(unsigned int ifindex, char *buffer, size_t *size)
- یک شناسه
رابط شبکه
مناسب برای
استفاده در
آدرسهای
دامنهدار
IPv6 را
بازیابی
میکند. در
ویندوز،
مقدار عددی
ifindex را به
صورت رشته
برمیگرداند.
در سایر
بسترها،
uv_if_indextoname()
فراخوانی
میشود.
نتیجه در buffer
نوشته شده و
*size طول buffer را
نشان
میدهد. اگر
buffer به
اندازه
کافی بزرگ
نباشد،
مقدار UV_ENOBUFS
برگردانده
شده و *size
اندازه
مورد نیاز
شامل بایت NUL
برای
نگهداری
نتیجه را
نشان
میدهد.
برای جزئیات بیشتر به uv_if_indextoname مراجعه کنید.
افزوده شده در نسخه 1.16.0.
- int uv_exepath(char *buffer, size_t *size)
- مسیر فایل
اجرایی را
دریافت
میکند. قبل
از
فراخوانی
این تابع
باید uv_setup_args
را
فراخوانی
کنید.
در فایلهای اجرایی setuid احتیاط کنید. در برخی پلتفرمها مسیر فایل اجرایی یک رشته دلخواه است که توسط کاربر کنترل میشود. در سایر پلتفرمها متغیرهای محیطی بررسی میشوند که ممکن است تحت کنترل کاربر باشند.
- int uv_cwd(char *buffer, size_t *size)
- دایرکتوری
کاری فعلی
را دریافت
کرده و آن
را در buffer
ذخیره
میکند. اگر
دایرکتوری
کاری فعلی
برای جا شدن
در buffer بیش از
حد بزرگ
باشد، این
تابع مقدار
UV_ENOBUFS را
برمیگرداند
و size را روی
طول مورد
نیاز شامل
نویسه
پایانبخش
null تنظیم
میکند.
تغییر یافته در نسخه 1.1.0: در یونیکس مسیر دیگر به اسلش ختم نمیشود.
تغییر یافته در نسخه 1.9.0: در صورت خطای UV_ENOBUFS طول بازگردانده شده شامل بایت پایانبخش null است، و در صورت موفقیت، بافر با null پایان مییابد.
- int uv_chdir(const char *dir)
- دایرکتوری کاری جاری را تغییر میدهد.
- int uv_os_homedir(char *buffer, size_t *size)
- پوشه خانگی
کاربر جاری
را دریافت
میکند. در
ویندوز،
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
تنظیم
میگردد.
هشدار:
افزوده شده در نسخه 1.6.0.
- int uv_os_tmpdir(char *buffer, size_t *size)
- دایرکتوری
موقت را
دریافت
میکند. در
ویندوز،
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
تنظیم
میگردد.
هشدار:
افزوده شده در نسخه 1.9.0.
- int uv_os_get_passwd(uv_passwd_t <#c.uv_passwd_t> *pwd)
- زیرمجموعهای
از درایه
فایل
گذرواژه را
برای 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.
- int uv_os_get_passwd2(uv_passwd_t <#c.uv_passwd_t> *pwd, uv_uid_t 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.45.0.
- int uv_os_get_group(uv_group_t <#c.uv_group_t> *group, uv_uid_t gid)
- زیرمجموعهای
از درایه
فایل گروه
را برای 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.
- void uv_os_free_group(uv_passwd_t <#c.uv_passwd_t> *pwd)
- حافظهای
را که قبلاً
با uv_os_get_group()
تخصیص
یافته بود
آزاد
میکند.
افزوده شده در نسخه 1.45.0.
- void uv_os_free_passwd(uv_passwd_t <#c.uv_passwd_t> *pwd)
- حافظه pwd را
که قبلاً با
uv_os_get_passwd() تخصیص
یافته بود
آزاد
میکند.
افزوده شده در نسخه 1.9.0.
- uint64_t uv_get_free_memory(void)
- میزان حافظه آزاد موجود در سیستم را طبق گزارش هسته (بر حسب بایت) دریافت میکند. در صورت نامشخص بودن 0 برمیگرداند.
- uint64_t uv_get_total_memory(void)
- میزان کل حافظه فیزیکی سیستم را (بر حسب بایت) دریافت میکند. در صورت نامشخص بودن 0 برمیگرداند.
- uint64_t uv_get_constrained_memory(void)
- میزان کل
حافظه در
دسترس
فرایند را
(بر حسب
بایت) بر
اساس
محدودیتهای
اعمالشده
توسط
سیستمعامل
دریافت
میکند. اگر
چنین
محدودیتی
وجود
نداشته
باشد، یا
محدودیت
ناشناخته
باشد، 0
برگردانده
میشود. اگر
سازوکار
محدودکننده
وجود داشته
باشد اما
محدودیتی
تعیین نشده
باشد، UINT64_MAX
برگردانده
میشود.
توجه داشته
باشید که
کمتر یا
بیشتر بودن
این مقدار
از uv_get_total_memory()
غیرعادی
نیست.
نکته:
افزوده شده در نسخه 1.29.0.
- uint64_t uv_get_available_memory(void)
- میزان
حافظه
آزادی را که
هنوز در
دسترس
فرایند است
(بر حسب
بایت)
دریافت
میکند.
تفاوت این
تابع با
uv_get_free_memory() در این
است که
محدودیتهای
اعمالشده
توسط
سیستمعامل
را در نظر
میگیرد.
اگر چنین
محدودیتی
وجود
نداشته
باشد یا
محدودیت
ناشناخته
باشد،
مقدار
بازگردانده
شده با uv_get_free_memory()
یکسان
خواهد بود.
نکته:
افزوده شده در نسخه 1.45.0.
- uint64_t uv_hrtime(void)
- برچسب
زمانی با
دقت بالا (High-resolution
timestamp) را
برمیگرداند.
این مقدار
بر حسب
نانوثانیه
بیان
میشود.
نسبت به یک
زمان
اختیاری در
گذشته
سنجیده
میشود و
ارتباطی با
ساعت
شبانهروز
ندارد و
بنابراین
در معرض
انحراف
ساعت (Clock drift)
نیست.
کاربرد
اصلی آن
برای
اندازهگیری
کارایی بین
بازهها
است.
نکته:
- int uv_clock_gettime(uv_clock_id <#c.uv_clock_id> clock_id, uv_timespec64_t <#c.uv_timespec64_t> *ts)
- زمان فعلی
سیستم را از
یک منبع
ساعت
بیدرنگ (Real-time)
یا یکنواخت
(Monotonic) با دقت
بالا به دست
میآورد.
ساعت بیدرنگ از مبدأ یونیکس (1970-01-01) شمارش میکند و مشمول تنظیمات زمانی است؛ ممکن است در زمان به عقب بپرد.
ساعت یکنواخت از یک نقطه دلخواه در گذشته شمارش میکند و هرگز در زمان به عقب نمیپرد.
افزوده شده در نسخه 1.45.0.
- void uv_print_all_handles(uv_loop_t <#c.uv_loop_t> *loop, FILE *stream)
- تمام
دستگیرههای
مرتبط با 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 برای دستگیره داخلی چاپ میشود
هشدار:
افزوده شده در نسخه 1.8.0.
- void uv_print_active_handles(uv_loop_t <#c.uv_loop_t> *loop, FILE *stream)
- این تابع
مشابه uv_print_all_handles()
است با این
تفاوت که
فقط
دستگیرههای
فعال چاپ
میشوند.
هشدار:
افزوده شده در نسخه 1.8.0.
- int uv_os_environ(uv_env_item_t <#c.uv_env_item_t> **envitems, int *count)
- تمام
متغیرهای
محیطی را
بازیابی
میکند. این
تابع
حافظهای
تخصیص
میدهد که
باید با
فراخوانی
uv_os_free_environ() آزاد
شود.
هشدار:
افزوده شده در نسخه 1.31.0.
- void uv_os_free_environ(uv_env_item_t <#c.uv_env_item_t> *envitems, int count);
- حافظه
تخصیصیافته
برای
متغیرهای
محیطی توسط
uv_os_environ() را آزاد
میکند.
افزوده شده در نسخه 1.31.0.
- int uv_os_getenv(const char *name, char *buffer, size_t *size)
- متغیر
محیطی
مشخصشده
توسط name را
بازیابی
کرده،
مقدار آن را
در buffer کپی
میکند و size
را روی طول
رشته مقدار
تنظیم
میکند.
هنگام
فراخوانی
این تابع،
size باید روی
مقدار
حافظه در
دسترس در buffer
شامل نویسه
پایانبخش
null تنظیم شده
باشد. اگر
متغیر
محیطی از
فضای موجود
در buffer فراتر
رود، UV_ENOBUFS
برگردانده
میشود و size
روی مقدار
فضای مورد
نیاز برای
نگهداری
مقدار
تنظیم
میگردد.
اگر متغیر
محیطی
منطبقی
وجود
نداشته
باشد، UV_ENOENT
برگردانده
میشود.
هشدار:
افزوده شده در نسخه 1.12.0.
- int uv_os_setenv(const char *name, const char *value)
- متغیر
محیطی
مشخصشده
با name را
ایجاد کرده
یا با value
بهروزرسانی
میکند.
هشدار:
افزوده شده در نسخه 1.12.0.
- int uv_os_unsetenv(const char *name)
- متغیر
محیطی
مشخصشده
با name را حذف
میکند. اگر
چنین متغیر
محیطی وجود
نداشته
باشد، این
تابع با
موفقیت
بازمیگردد.
هشدار:
افزوده شده در نسخه 1.12.0.
- int uv_os_gethostname(char *buffer, size_t *size)
- نام میزبان
(Hostname) را به
عنوان یک
رشته
پایانیافته
با null در buffer
برمیگرداند،
و size را روی
طول رشته
نام میزبان
تنظیم
میکند.
هنگام
فراخوانی
این تابع،
size باید روی
مقدار فضای
در دسترس در
buffer شامل
نویسه
پایانبخش
null تنظیم شود.
اگر نام
میزبان از
فضای موجود
در buffer فراتر
رود، UV_ENOBUFS
برگردانده
میشود و size
روی مقدار
فضای مورد
نیاز برای
نگهداری
مقدار
تنظیم
میگردد.
افزوده شده در نسخه 1.12.0.
تغییر یافته در نسخه 1.26.0: ثابت UV_MAXHOSTNAMESIZE در دسترس است و بیانگر حداکثر اندازه buffer مورد نیاز برای ذخیره نام میزبان و کاراکتر nul انتهایی است.
- int uv_os_getpriority(uv_pid_t <#c.uv_pid_t> pid, int *priority)
- اولویت
زمانبندی
فرایند
مشخصشده
با pid را
بازیابی
میکند.
مقدار
بازگردانده
شده priority بین -20
(اولویت
بالا) و 19
(اولویت
پایین) است.
نکته:
افزوده شده در نسخه 1.23.0.
- int uv_os_setpriority(uv_pid_t <#c.uv_pid_t> pid, int priority)
- اولویت
زمانبندی
فرایند
مشخصشده
با pid را
تنظیم
میکند.
دامنه
مقدار priority
بین -20
(اولویت
بالا) و 19
(اولویت
پایین) است.
ثابتهای
UV_PRIORITY_LOW، UV_PRIORITY_BELOW_NORMAL،
UV_PRIORITY_NORMAL، UV_PRIORITY_ABOVE_NORMAL،
UV_PRIORITY_HIGH و UV_PRIORITY_HIGHEST
نیز برای
سهولت
ارائه
شدهاند.
نکته:
نکته:
نکته:
نکته:
افزوده شده در نسخه 1.23.0.
- int uv_os_uname(uv_utsname_t <#c.uv_utsname_t> *buffer)
- اطلاعات
سیستم را در
buffer بازیابی
میکند.
دادههای
پرشده شامل
نام
سیستمعامل،
انتشار (Release)،
نسخه و
معماری
ماشین است.
در
سیستمهای
غیرویندوزی،
uv_os_uname() یک پوشش
نازک روی uname(2)
https://man7.org/linux/man-pages/man2/uname.2.html
است. در
صورت
موفقیت صفر
و در غیر
این صورت یک
مقدار خطای
غیرصفر
برمیگرداند.
افزوده شده در نسخه 1.25.0.
- int uv_gettimeofday(uv_timeval64_t <#c.uv_timeval64_t> *tv)
- پیادهسازی
چندسکویی
از gettimeofday(2)
https://man7.org/linux/man-pages/man2/gettimeofday.2.html.
آرگومان timezone
در gettimeofday()
پشتیبانی
نمیشود،
زیرا منسوخ
تلقی
میگردد.
افزوده شده در نسخه 1.28.0.
- int uv_random(uv_loop_t <#c.uv_loop_t> *loop, uv_random_t <#c.uv_random_t> *req, void *buf, size_t buflen, unsigned int flags, uv_random_cb <#c.uv_random_cb> cb)
- بافر buf را
دقیقاً با
buflen بایت
تصادفی از
نظر
رمزنگاری
قوی که از CSPRNG
سیستم به
دست آمده
است پر
میکند.
پارامتر flags
برای
توسعههای
آتی رزرو
شده و در
حال حاضر
باید 0 باشد.
خواندن ناقص (Short read) امکانپذیر نیست. هنگامی که کمتر از buflen بایت تصادفی در دسترس باشد، مقدار خطای غیرصفر بازگردانده شده یا به تابع بازخوانی ارسال میشود.
نسخه همگام ممکن است در صورت عدم وجود آنتروپی کافی به طور نامحدود مسدود شود. نسخه ناهمگام ممکن است در صورت کمبود آنتروپی سیستم هرگز پایان نیابد.
منابع آنتروپی:
- ویندوز: RtlGenRandom https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom_.
- لینوکس، اندروید: getrandom(2) https://man7.org/linux/man-pages/man2/getrandom.2.html در صورت وجود، یا urandom(4) https://man7.org/linux/man-pages/man4/urandom.4.html پس از یکبار خواندن از /dev/random، یا فراخوانی KERN_RANDOM از طریق sysctl(2) https://man7.org/linux/man-pages/man2/sysctl.2.html.
- فریبیاسدی: getrandom(2) <https://www.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2>_، یا /dev/urandom پس از یکبار خواندن از /dev/random.
- نتبیاسدی: KERN_ARND sysctl(7) https://man.netbsd.org/sysctl.7_
- مک او اس، اوپنبیاسدی: getentropy(2) https://man.openbsd.org/getentropy.2_ در صورت وجود، یا /dev/urandom پس از یکبار خواندن از /dev/random.
- سیستم AIX: پرونده /dev/random.
- سیستم IBM i: پرونده /dev/urandom.
- سایر یونیکسها: /dev/urandom پس از یکبار خواندن از /dev/random.
- مقادیر بازگشتی (Returns)
- در صورت موفقیت 0، یا یک کد خطای کمتر از 0 در صورت شکست. محتوای buf پس از بروز خطا تعریفنشده است.
نکته:
افزوده شده در نسخه 1.33.0.
- void uv_sleep(unsigned int msec)
- باعث به
خواب رفتن
ریسه
فراخوان به
مدت msec
میلیثانیه
میشود.
افزوده شده در نسخه 1.34.0.
توابع دستکاری رشته (String manipulation functions)
این ابزارهای کار با رشته در داخل کتابخانه برای تعامل با ویندوز مورد نیاز هستند و صادر (export) شدهاند تا به کلاینتها امکان دهند در صورت کامل نبودن API libuv به صورت یکپارچه با این دادهها کار کنند.
- size_t uv_utf16_length_as_wtf8(const uint16_t *utf16, ssize_t utf16_len)
- طول یک
مقدار UTF-16 (یا UCS-2)
به نام utf16 را
پس از تبدیل
به WTF-8 دریافت
میکند. اگر
utf16 با NUL خاتمه
یافته
باشد، utf16_len را
میتوان
روی -1 تنظیم
کرد، در غیر
این صورت
باید به طور
صریح مشخص
شود.
افزوده شده در نسخه 1.47.0.
- int uv_utf16_to_wtf8(const uint16_t *utf16, ssize_t utf16_len, char **wtf8_ptr, size_t *wtf8_len_ptr)
- دادههای 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.
- ssize_t uv_wtf8_length_as_utf16(const char *wtf8)
- طول یک
مقدار WTF-8
خاتمهیافته
با NUL به نام wtf8
را پس از
تبدیل به UTF-16
(یا UCS-2)، شامل
نویسه
پایانبخش
NUL، بر حسب
کاراکتر
دریافت
میکند.
افزوده شده در نسخه 1.47.0.
- void uv_wtf8_to_utf16(const char *utf8, uint16_t *utf16, size_t utf16_len)
- دادههای WTF-8
پایانیافته
با NUL در wtf8 را
به
دادههای UTF-16
(یا UCS-2) در utf16
تبدیل
میکند.
شمارش utf16_len
(بر حسب
کاراکتر)
باید فضا
برای بایت
پایانبخش
NUL را شامل
شود.
افزوده شده در نسخه 1.47.0.
عملیات معیارسنجی (Metrics operations)
کتابخانه libuv یک رابط کاربری معیارسنجی (Metrics API) برای ردیابی عملیات داخلی مختلف حلقه رویداد فراهم میکند.
انواع داده (Data types)
- type uv_metrics_t
- ساختاری که معیارهای حلقه رویداد را در خود نگه میدارد. توصیه میشود این معیارها در یک 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;
اعضای عمومی (Public members)
- uint64_t uv_metrics_t <#c.uv_metrics_t>.loop_count
- تعداد تکرارهای حلقه رویداد.
- uint64_t uv_metrics_t <#c.uv_metrics_t>.events
- تعداد رویدادهایی که توسط مدیریتکننده رویداد (Event handler) پردازش شدهاند.
- uint64_t uv_metrics_t <#c.uv_metrics_t>.events_waiting
- تعداد رویدادهایی که هنگام فراخوانی ارائهدهنده رویداد در انتظار پردازش بودند.
رابط برنامهنویسی (API)
- uint64_t uv_metrics_idle_time(uv_loop_t <#c.uv_loop_t> *loop)
- مدت زمانی
را که حلقه
رویداد در
ارائهدهنده
رویداد
هسته (مانند
epoll_wait) بیکار (Idle)
بوده است،
بازیابی
میکند. این
فراخوانی
ایمن در
برابر ریسه
(Thread-safe) است.
مقدار بازگشتی، زمان انباشته سپریشده در حالت بیکاری در ارائهدهنده رویداد هسته از زمانی است که uv_loop_t <#c.uv_loop_t> برای جمعآوری زمان بیکاری پیکربندی شده بود.
نکته:
افزوده شده در نسخه 1.39.0.
- int uv_metrics_info(uv_loop_t <#c.uv_loop_t> *loop, uv_metrics_t <#c.uv_metrics_t> *metrics)
- مجموعه
فعلی
معیارهای
حلقه
رویداد را
در
اشارهگر
metrics کپی
میکند.
افزوده شده در نسخه 1.45.0.
راهنمای کاربر
هشدار:
مقدمه
این «کتاب» مجموعهای کوچک از آموزشها درباره استفاده از 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
کتابخانه 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].
نکته:
برت بلدر (Bert Belder)، یکی از توسعهدهندگان اصلی libuv، ویدیوی کوتاهی دارد که معماری libuv و پیشزمینه آن را توضیح میدهد. اگر تجربه قبلی کار با libuv یا libev را ندارید، تماشای آن کوتاه و مفید خواهد بود.
حلقه رویداد libuv با جزئیات بیشتر در مستندات https://docs.libuv.org/en/v1.x/design.html#the-i-o-loop توضیح داده شده است.
برنامه آغازین (Hello World)
با گذر از مبانی اولیه، بیایید اولین برنامه 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 *) آزاد و غیرفعال کنید و سپس حافظه را پاک نمایید. برنامههای نمونه هرگز حلقهها را نمیبندند زیرا برنامه پس از پایان حلقه به پایان میرسد و سیستم حافظه را پس میگیرد. اما در پروژههای سطح عملیاتی، بهویژه برنامههای سیستمی با اجرای طولانیمدت، باید دقت کرد که آزادسازی منابع به درستی انجام شود.
حلقه پیشفرض (Default loop)
یک حلقه پیشفرض توسط 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;
}
نکته:
مدیریت خطاها (Error handling)
توابع مقداردهی اولیه یا توابع همگام که ممکن است با شکست مواجه شوند، در صورت خطا یک عدد منفی برمیگردانند. توابع ناهمگام که ممکن است با شکست مواجه شوند، یک پارامتر وضعیت (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 خطای پایان فایل است که ممکن است بخواهید آن را به شکل متفاوتی مدیریت کنید).
هندلها و درخواستها (Handles and Requests)
کتابخانه 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 دادههای خواندهشده از فایل را دریافت میکند، کالبک تایمر هنگام انقضای زمان فعال میشود و غیره.
بیکاری (Idling)
در اینجا نمونهای از استفاده از یک هندل بیکاری (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;
}
ذخیره زمینه (Storing context)
در سبک برنامهنویسی مبتنی بر کالبک، اغلب میخواهید مقداری «زمینه (context)» -- اطلاعات اختصاصی برنامه -- را بین محل فراخوانی و کالبک منتقل کنید. تمامی هندلها و درخواستها دارای یک عضو void* data هستند که میتوانید آن را روی اطلاعات زمینه تنظیم کرده و در کالبک مجدداً کست کنید. این یک الگوی رایج در سراسر اکوسیستم کتابخانههای C است. علاوه بر این، ساختار uv_loop_t نیز عضو داده مشابهی دارد.
----
- [1]
- البته بسته به ظرفیت سختافزار.
سیستم فایل (Filesystem)
خواندن و نوشتن ساده در سیستم فایل با استفاده از توابع uv_fs_* و ساختار uv_fs_t انجام میشود.
نکته:
تمامی توابع سیستم فایل دارای دو شکل هستند - همگام (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;
}
هشدار:
عملیاتهای سیستم فایل
تمامی عملیات استاندارد سیستم فایل مانند 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 را مستقل از یکدیگر آزاد کنیم. اگرچه این کار برای یک برنامه نمایشی مانند این قابل قبول است، اما در هر برنامه بزرگ احتمالاً به مدیریت حافظه هوشمندانهتری مانند بافرهای با شمارش ارجاع یا استخر بافر نیاز خواهید داشت.
هشدار:
signal(SIGPIPE, SIG_IGN)
در مراحل مقداردهی اولیه برنامه خود قرار دهید.
رویدادهای تغییر فایل
تمامی سیستمعاملهای مدرن APIهایی برای قرار دادن پایشگر روی فایلها یا دایرکتوریهای مجزا و اطلاع از تغییر فایلها ارائه میدهند. کتابخانه libuv کتابخانههای رایج اعلان تغییر فایل را بستهبندی میکند [1]. این یکی از ناهمگونترین بخشهای libuv است. سیستمهای اعلان تغییر فایل خود در پلتفرمهای مختلف بسیار متفاوت هستند، بنابراین اجرای روان همهچیز در همه جا دشوار است. برای نمایش، یک ابزار ساده میسازیم که هر زمان یکی از فایلهای پایششده تغییر کرد، یک دستور را اجرا کند:
./onchange <command> <file1> [file2] ...
نکته:
اعلان تغییر فایل با استفاده از 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 برای ورودی/خروجی شبکه استفاده میشوند.
نکته:
پروتکل TCP
پروتکل TCP یک پروتکل مبتنی بر اتصال و جریانی (stream) است و بنابراین بر پایه زیرساخت استریمهای libuv بنا شده است.
سرور (Server)
سوکتهای سرور مراحل زیر را طی میکنند:
- 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 انجام داد.
نکته:
اکثر توابع راهاندازی همگام هستند زیرا وابسته به پردازنده (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 انجام داد اگر تمایلی به پذیرش اتصال ندارید.
کلاینت (Client)
در حالی که در سمت سرور عملیات 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 است که به سوکت اشاره دارد.
پروتکل UDP
پروتکل دیتاگرام کاربر (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/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);
}
نکته:
ابتدا سوکت دریافتکننده را تنظیم میکنیم تا به تمام رابطها روی پورت 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);
}
گزینههای UDP
طول عمر بسته (Time-to-live)
مقدار TTL بستههای ارسالشده روی سوکت را میتوان با استفاده از uv_udp_set_ttl تغییر داد.
فقط پشته IPv6
سوکتهای IPv6 را میتوان هم برای ارتباط IPv4 و هم IPv6 استفاده کرد. اگر میخواهید سوکت را فقط به IPv6 محدود کنید، پرچم UV_UDP_IPV6ONLY را به uv_udp_bind ارسال کنید.
چندپخشی (Multicast)
یک سوکت میتواند با استفاده از دستور زیر در یک گروه چندپخشی (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 تغییر داد.
پرسوجوی DNS (Querying DNS)
کتابخانه 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 را ارائه میدهد.
رابطهای شبکه (Network interfaces)
اطلاعات مربوط به رابطهای شبکه سیستم را میتوان از طریق 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 حذف شده است.
رشتهها (Threads)
یک لحظه صبر کنید! چرا سراغ رشتهها رفتیم؟ مگر قرار نبود حلقههای رویداد روش اصلی برای برنامهنویسی مقیاس وب (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).
عملیات اصلی رشتهها (Core thread operations)
در اینجا پیچیدگی خاصی وجود ندارد، فقط یک رشته را با استفاده از 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;
}
نکته:
پارامتر دوم تابعی است که به عنوان نقطه ورود رشته عمل میکند و پارامتر آخر یک آرگومان 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() این کار را انجام نمیدهد. برای ارسال مقادیر از بخش ارتباط بینرشتهای استفاده کنید.
سازوکارهای همگامسازی (Synchronization Primitives)
این بخش عمداً مختصر نوشته شده است. این مستند درباره رشتهها نیست، بنابراین فقط موارد غیرمنتظره در رابطهای libuv فهرست شدهاند. برای سایر موارد میتوانید به صفحات راهنمای pthreads(7) https://man7.org/linux/man-pages/man7/pthreads.7.html مراجعه کنید.
موتکسها (Mutexes)
توابع موتکس نگاشت مستقیمی به معادلهای 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() خطا بازمیگرداند.
نکته:
قفلها (Locks)
قفلهای خواندن-نوشتن (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) استفاده میکنیم تا رشته اصلی بتواند منتظر بماند تا همه خوانندهها و نویسندهها اتمام کار خود را اعلام کنند.
سایر موارد (Others)
کتابخانه 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) اضافه کرده است.
صف کار libuv (libuv work queue)
تابع 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 تنظیم خواهد شد.
نکته:
ارتباط بینرشتهای (Inter-thread communication)
گاهی اوقات میخواهید رشتههای مختلف هنگام اجرا پیامهایی برای یکدیگر ارسال کنند. برای مثال ممکن است در حال اجرای یک وظیفه طولانی در یک رشته جداگانه باشید (شاید با استفاده از 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) را با پایشگر ناهمگام فراخوانی میکند.
هشدار:
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 استفاده کنید تا مطمئن شوید دسترسیها با ترتیب صحیح انجام میشوند.
نکته:
یک مورد استفاده که در آن 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 تعامل برقرار کرده تا بازخوانی جاوااسکریپت را فراخوانی کند.
----
فرایندها (Processes)
کتابخانه libuv امکانات قابل توجهی برای مدیریت فرایندهای فرزند ارائه میدهد و تفاوتهای پلتفرمها را انتزاع کرده و ارتباط با فرایند فرزند را با استفاده از جریانها (streams) یا پایپهای نامگذاریشده فراهم میکند.
یک اصطلاح رایج در یونیکس این است که هر فرایند یک کار را انجام دهد و آن را به خوبی انجام دهد. در چنین حالتی، یک فرایند اغلب از چندین فرایند فرزند برای انجام وظایف استفاده میکند (مشابه استفاده از پایپها در شل). یک مدل چندفرایندی همراه با پیامها نیز ممکن است در مقایسه با مدلی با رشتهها و حافظه مشترک، تحلیل و درک آسانتری داشته باشد.
یک انتقاد رایج علیه برنامههای مبتنی بر رویداد این است که نمیتوانند از چندین هسته در کامپیوترهای مدرن بهره ببرند. در یک برنامه چندرشتهای، هسته سیستمعامل میتواند زمانبندی را انجام داده و رشتههای مختلف را به هستههای مختلف اختصاص دهد و کارایی را افزایش دهد. اما یک حلقه رویداد فقط یک رشته دارد. راهحل جایگزین میتواند راهاندازی چندین فرایند باشد، بهطوریکه هر فرایند یک حلقه رویداد را اجرا کرده و به یک هسته پردازنده جداگانه اختصاص یابد.
ایجاد فرایندهای فرزند (Spawning child processes)
سادهترین حالت زمانی است که صرفاً میخواهید یک فرایند را اجرا کرده و از زمان خروج آن مطلع شوید. این کار با استفاده از 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);
}
نکته:
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);
بستن پایشگر فرایند پس از خروج فرایند الزامی است.
تغییر پارامترهای فرایند (Changing process parameters)
قبل از اجرای فرایند فرزند میتوانید محیط اجرای آن را با استفاده از فیلدهای موجود در uv_process_options_t کنترل کنید.
تغییر دایرکتوری اجرا (Change execution directory)
فیلد uv_process_options_t.cwd را روی دایرکتوری مربوطه تنظیم کنید.
تنظیم متغیرهای محیطی (Set environment variables)
فیلد uv_process_options_t.env یک آرایه رشتهای پایانیافته با null است که هر عنصر آن به شکل VAR=VALUE برای تنظیم متغیرهای محیطی فرایند استفاده میشود. برای به ارث بردن محیط از فرایند والد (این فرایند)، آن را برابر NULL قرار دهید.
فلگهای گزینهها (Option flags)
تنظیم 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) جدید راهاندازی میکند که پس از خروج فرایند والد به اجرای خود ادامه خواهد داد. مثال زیر را ببینید.
جدا کردن فرایندها (Detaching processes)
ارسال فلگ 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() استفاده کنید.
ارسال سیگنال به فرایندها (Sending signals to processes)
کتابخانه 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 را روی پایشگر _پس از_ فراخوانی بازخوانی خروج صدا بزنید.
سیگنالها (Signals)
کتابخانه 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;
}
نکته:
سیگنال SIGUSR1 را به فرایند ارسال کنید، خواهید دید که هندلر ۴ بار اجرا میشود، یک بار برای هر uv_signal_t. هندلر به سادگی هر هندل را متوقف میکند تا برنامه خارج شود. این نوع توزیع پیام به همه هندلرها بسیار مفید است. سروری که از چندین حلقه رویداد استفاده میکند میتواند اطمینان حاصل کند که همه دادهها قبل از خاتمه با امنیت ذخیره میشوند، صرفاً با افزودن یک پایشگر برای SIGINT به هر حلقه.
ورودی/خروجی فرایند فرزند (Child Process I/O)
یک فرایند معمولی و تازه ایجادشده دارای مجموعه توصیفکنندههای فایل اختصاصی خود است که 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 روی سوکت تنظیم میشود تا هر آنچه اسکریپت تیک چاپ میکند به کلاینت ارسال شود. با استفاده از فرایندها، میتوانیم بافرینگ خواندن/نوشتن را به سیستمعامل واگذار کنیم، بنابراین از نظر راحتی این روش عالی است. فقط توجه داشته باشید که ایجاد فرایندها عملیاتی سنگین و هزینهبر است.
ارتباط بینفرایندی والد و فرزند (Parent-child IPC)
والد و فرزند میتوانند از طریق پایپی که با تنظیم uv_stdio_container_t.flags با ترکیب بیتی UV_CREATE_PIPE و UV_READABLE_PIPE یا UV_WRITABLE_PIPE ایجاد میشود، ارتباط یکطرفه یا دوطرفه داشته باشند. فلگ خواندن/نوشتن از دیدگاه فرایند فرزند است. در این حالت، فیلد uv_stream_t* stream باید روی یک نمونه مقداردهیشده و بازنشده از uv_pipe_t تنظیم شود.
پایپهای جدید stdio (New stdio Pipes)
ساختار 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 است که در ادامه به آن پرداخته میشود.
ارتباط بینفرایندی میان فرایندهای دلخواه (Arbitrary process 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 پیروی میکند.
ارسال توصیفکنندههای فایل از طریق پایپها (Sending file descriptors over pipes)
ویژگی جالب سوکتهای دامنه این است که میتوان توصیفکنندههای فایل را با ارسال از طریق یک سوکت دامنه میان فرایندها ردوبدل کرد. این ویژگی به فرایندها امکان میدهد ورودی/خروجی خود را به فرایندهای دیگر واگذار کنند. کاربردها شامل سرورهای توزیع بار (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]
- در این بخش سوکتهای دامنه نماینده پایپهای نامگذاریشده در ویندوز نیز هستند.
حلقههای رویداد پیشرفته (Advanced event loops)
کتابخانه libuv کنترل قابلتوجهی بر روی حلقههای رویداد در اختیار کاربر قرار میدهد و شما میتوانید با مدیریت همزمان چند حلقه به نتایج جالبی دست یابید. همچنین میتوانید حلقه رویداد libuv را در کتابخانه دیگری که مبتنی بر حلقه رویداد است جاسازی کنید -- تصور کنید یک رابط کاربری مبتنی بر Qt دارید و حلقه رویداد Qt بخش بکاند libuv را که وظایف فشرده سیستمی را انجام میدهد هدایت میکند.
متوقف کردن یک حلقه رویداد (Stopping an event loop)
تابع 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;
}
ابزارهای کاربردی (Utilities)
این فصل ابزارها و تکنیکهایی را فهرست میکند که برای کارهای متداول مفید هستند. صفحه راهنمای libev در نشانی http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_OR_BOTH قبلاً برخی الگوها را پوشش داده است که میتوانند با تغییرات ساده در رابط برنامهنویسی به libuv منتقل شوند. همچنین بخشهایی از API کتابخانه libuv را که نیازی به اختصاص یک فصل کامل ندارند پوشش میدهد.
تایمرها (Timers)
تایمرها پس از گذشت مدت زمان مشخصی از شروع به کار، تابع فراخوان (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 را برمیگرداند.
یک مثال واقعی از تایمر در بخش شمارش ارجاع آورده شده است.
شمارش ارجاع حلقه رویداد (Event loop reference count)
حلقه رویداد فقط تا زمانی اجرا میشود که هندلهای فعالی وجود داشته باشند. این سیستم به این صورت کار میکند که هر هندل هنگام شروع، شمارنده ارجاع (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 میکنیم. مشاهده کنید که چگونه پس از ۹ ثانیه، وقتی کار ساختگی تمام میشود، برنامه بهطور خودکار خارج میشود، حتی با اینکه زبالهروب همچنان در حال اجراست.
الگوی بیکار (Idler pattern)
فراخوانهای هندلهای 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);
}
ارسال داده به ریسه کارگر (Passing data to worker thread)
هنگام استفاده از 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 را آزاد میکنیم که ناظر را نیز آزاد میکند.
ورودی/خروجی خارجی با Polling (External I/O with polling)
معمولاً کتابخانههای شخص ثالث 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();
}
}
}
ناظران Check و Prepare (Check & Prepare watchers)
در دست تکمیل (TODO)
بارگذاری کتابخانهها (Loading libraries)
کتابخانه 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!"
نکته:
این کار با استفاده از 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 اشارهگری تابعی به نوع تابعی است که ما در افزونههای برنامه به دنبال آن هستیم.
واسط TTY
ترمینالهای متنی برای مدت طولانی از قالببندی اولیه با یک مجموعه دستور نسبتاً استاندارد در نشانی 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 است.
درباره (About)
آقای 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 ساخته شده است.
نکته:
ارتقا (Upgrading)
راهنماهای مهاجرت برای نسخههای مختلف libuv، با شروع از نسخه 1.0.
راهنمای مهاجرت از libuv 0.10 به 1.0.0 (libuv 0.10 -> 1.0.0 migration guide)
برخی از APIها در طول فرآیند توسعه 1.0.0 تغییرات زیادی کردند. در اینجا یک راهنمای مهاجرت برای مهمترین تغییراتی که پس از انتشار 0.10 رخ داده آورده شده است.
مقداردهی اولیه و بستن حلقه (Loop initialization and closing)
در 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);
نکته:
مدیریت خطاها (Error handling)
مدیریت خطا در 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 */
}
تغییرات مخزن ریسهها (Threadpool changes)
در libuv 0.10 سیستمهای یونیکس از یک threadpool استفاده میکردند که به طور پیشفرض دارای ۴ ریسه بود، در حالی که ویندوز از API مربوط به QueueUserWorkItem استفاده میکرد که از threadpool داخلی ویندوز با پیشفرض ۵۱۲ ریسه برای هر فرآیند بهره میبرد.
در نسخه 1.0، هر دو پیادهسازی را یکپارچه کردیم، بنابراین ویندوز اکنون از همان پیادهسازی یونیکس استفاده میکند. اندازه threadpool را میتوان با اکسپورت کردن متغیر محیطی UV_THREADPOOL_SIZE تنظیم کرد. بخش زمانبندی کار مخزن ریسه در <#threadpool> را ببینید.
تغییر رابط برنامهنویسی فراخوان تخصیص (Allocation callback API change)
در 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;
}
یکپارچهسازی رابطهای IPv4 و IPv6 (Unification of IPv4 / IPv6 APIs)
نسخه 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
فراخوانهای دریافت داده در 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) {
...
}
تغییر رابط دریافت هندلها از طریق Pipeها
در 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);
...
}
...
}
استخراج توصیفگر فایل از یک هندل (Extracting the file descriptor out of a handle)
اگرچه این کار توسط API پشتیبانی نمیشد، اما کاربران اغلب به بخشهای داخلی libuv دسترسی پیدا میکردند تا مثلاً به توصیفگر فایل یک هندل TCP دسترسی یابند.
fd = handle->io_watcher.fd;
این قابلیت اکنون به درستی از طریق تابع uv_fileno() در <#c.uv_fileno> در دسترس قرار گرفته است.
تغییر نام و رابط تابع uv_fs_readdir
تابع 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) را نگه میدارد.
دانلودها (DOWNLOADS)
کتابخانه libuv میتواند از اینجا دانلود شود https://dist.libuv.org/dist.
نصب (INSTALLATION)
دستورالعملهای نصب را میتوان در فایل README در نشانی https://github.com/libuv/libuv/blob/master/README.md یافت.
نویسنده (AUTHOR)
مشارکتکنندگان libuv
حق نشر (COPYRIGHT)
2014-present, مشارکتکنندگان libuv
| August 26, 2026 | 1.52.1 |