موضوع مقاله: ورودی/خروجی ناهمگام | مخاطب: عمومی | لحن: دانشنامه‌ای | ساختار: عنوان، مقدمه، 3 بخش، جمع‌بندی | مقاله: | # ورودی/خروجی ناهمگام | مقدمه: ورودی/خروجی ناهمگام یا ورودی/خروجی مسدودنشدنی یک نوع روش عمل ورودی/خروجی داده‌ها است که با جلوگیری از مسدودشدن یک پروسه، به آن اجازه می‌دهد تا در حین انجام عمل ورودی/خروجی هم بتواند مشغول باشد و کارهای مورد نظر خود را انجام دهد. | ## بخش 1: ورودی/خروجی ناهمگام | عملیات ورودی و خروجی در یک رایانه، در مقایسه با پردازش کردن داده‌ها، ممکن است تا حد بسیار زیادی آکرنل باشد. یک دستگاه ورودی/خروجی ممکن است از دستگاه‌های مکانیکی تشکیل شده باشد که نیاز به جابجایی فیزیکی داشته باشند، مثلاً در دیسک سخت هد خواندن نوشتن باید برای خواندن اطلاعات بین شیارها جابجا شود. برای مثال، در حین انجام یک عملیات در دیسک که مثلاً ممکن است ۱۰ میلی‌ثانیه طول بکشد، پردازنده‌ای که با پالس ساعت ۱ گیگاهرتز کار می‌کند، می‌تواند ده میلیون دستورالعمل را اجرا کند. یکی از روش‌های انجام عمل ورودی/خروجی داده‌ها این است که پس از شروع شدن این عمل، تا اتمام آن منتظر ماند. اگر یک برنامه تعداد زیادی عمل ورودی/خروجی انجام دهد، ممکن است پردازنده تمام‌وقت بیکار بماند و منتظر باشد تا عمل ورودی/خروجی تمام شود. یک روش دیگر این است که ابتدا عمل ورودی/خروجی آغاز شود، سپس به جای منتظر ماندن برای اتمام آن، همزمان به انجام کارهایی پرداخت که نیازی به تمام شدن عمل ورودی/خروجی ندارند. هر کاری که نیازمند اتمام عمل ورودی/خروجی مورد نظر باشد (از جمله پردازش کردن اطلاعات ورودی، و همچنین عملیات‌های حیاتی دیگری که نیازمند این هستند که یک عمل نوشتن پایان یابد) هنوز هم باید صبر کنند تا عمل ورودی/خروجی به اتمام برسد، در نتیجه باید مسدود شوند، اما کارهای دیگری که نیاز به اتمام عمل ورودی/خروجی ندارند، می‌توانند همچنان پردازش شوند. این فریم‌ورک، اینترفیسی پر امکانات و با تاخیر زمانی کم، فراهم آورد تا نیاز اپلیکیشن‌هایی که به عملکرد ورودی/خروجی ناهمگام کرنل لینوکس نیاز داشتند را برطرف کند. | ## بخش 2: ورودی/خروجی ناهمگام | فریم‌ورک قبلی AIO لینوکس، محدودیت‌هایی داشت که هدف io_uring، رفع آن محدودیت‌ها بود: این فریم‌ورک، ورودی و خروجی بافر را پشتیبانی نمی‌کرد و تنها ورودی و خروجی مستقیم، پشتیبانی می‌شد. رفتارهای این فریم‌ورک زمانی که Block، تحت موقعیت‌های مختلف قرار داشت، کاملاً قابل پیش‌بینی نبود و ممکن بود رفتار متفاوتی نشان دهد. واسط برنامه‌نویسی این فریم‌ورک نیاز به حداقل دو فراخوانی سیستمی به ازای هر ورودی/خروجی داشت. هر ارسالی درخواستی به رونوشت ۶۴ + ۸ بایت از داده‌ها نیاز داشت و هر تکمیل درخواستی هم به کپی ۳۲ بایت داده. صف‌ها هر کدام یک پاسخ‌دهنده و یک درخواست‌کننده دارند و اندازه‌شان توانی از دو است. هر برنامه، یک یا چند ورودی به صف ارسال می‌دهد و انتهای این صف را بروزرسانی می‌کند. صف تکمیل هم توسط کرنل با پاسخ‌های درخواست‌ها بروزرسانی می‌شود و برنامه از سر صف، پاسخ‌ها را دریافت می‌کند. ثبت‌کردن بافر فایل یا کاربر به کرنل اجازه می‌دهد که به مدت طولانی به ساختمان‌داده‌های داخلی کرنل که به فایل مرتبط است، اشاره داشته باشد یا باعث ایجاد نگاشت (Map) طولانی مدت حافظه برنامه که با بافرها مرتبط است می‌شود. | ## بخش 3: ورودی/خروجی ناهمگام | ۲) فراخوانی دوم، زمینه را برای آماده‌سازی و برپایی ورودی/خروجی ناهمگام آماده می‌کند. این فراخوانی سیستمی، صف ارسال و صف تکمیل را با المان‌های ورودی اول برپا می‌نماید. خروجی این فراخوانی سیستمی، یک توصیف‌کننده فایل است که در مراحل بعد قرار است روی آن به عنوان یک نمونه io-uring، عملیات انجام شود. وجود این دو صف در میان کرنل و برنامه باعث می‌شود که دیگر نیازی به کپی‌کردن داده‌ها در زمان مقداردهی اولیه و تکمیل ورودی/خروجی نباشد. ۳) از فراخوانی سوم برای مقداردهی اولیه و تکمیل ورودی/خروجی با استفاده از صفوف مشترک ارسال و تکمیلی که با تابع io_uring_setup برپا شده استفاده می‌کنیم. تنها یک فراخوانی می‌تواند ارسال ورودی/خروجی‌های جدید و تکمیل آن‌ها را با هم انجام دهد اگر که با فراخوانی سوم، الان یا قبلا فراخوانی شده باشد. Liburing API لیبرینگ، رابط برنامه‌نویسی‌ای سطح بالا برای موارد استفاده‌ی ساده است و همچنین باعث می‌شود تا برنامه با پیاده‌سازی کامل فراخوانی سیستمی درگیر نشود. | ## جمع‌بندی | این رابط همچنین از بازنویسی و تکرار کد برای عملیات‌هایی مثل برپایی نمونه‌های io_uring جلوگیری می‌کند.