خواندن و نوشتن فایل‌ها با استفاده از memory-mapped I/O

استفاده از API فایل‌سیستم برای خواندن و نوشتن فایل‌ها تنها راه دسترسی به فایل در لینوکس نیست، راه دیگری نیز وجود دارد که به آن  memory-mapped IO می‌گویند. دانستن یک راه جایگزین برای دسترسی به فایل‌ها می‌تواند جالب باشد، در این مطلب نحوه عملکرد  ورودی/خروجی حافظه‌نگاشتی (memory-mapped IO)  برای دسترسی به فایل‌ها را خواهیم آموخت.


memory-mapped I/O چیست

درک  memory-mapped IO  

در سطح بالا، memory-mapped IO (MMIO) ساده است: بخشی از یک فایل با استفاده از یک فراخوانی سیستم mmap در حافظه مجازی نگاشت می‌شود و پس از آن، می‌توانیم طبق معمول به حافظه دسترسی داشته باشیم و هر گونه موتاسیون (mutations) به فایل اصلی منتشر می‌شود. 

درک memory-mapped IO

نمونه اولیه(prototype) فراخوانی سیستم mmap به شرح زیر است:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

  • Addr - این آرگومان نشان دهنده آدرس مجازی فرآیند فراخوانی است که mapping در آن قرار خواهد گرفت. اگر از NULL عبور کنیم، کرنل یک مکان مناسب و رایگان برای ما انتخاب می‌کند.
  • Length - این آرگومان طول (بر حسب بایت) نگاشت را مشخص نموده و تعیین می‌کند که چه مقدار داده از فایل به حافظه نگاشت می‌شود.
  • Prot - این آرگومان حفاظت از حافظه (memory protection) را برای منطقه نگاشت شده تنظیم می‌کند و می‌تواند ترکیبی از فلگ‌های زیر باشد:
    • PROT_READ - صفحات موجود در mapping را می‌توان خواند.
    • PROT_WRITE - صفحات موجود در mapping را می‌توان نوشت.
    • PROT_EXEC - صفحات موجود در mapping را می‌توان به صورت کد اجرا کرد.
    • PROT_NONE - صفحات موجود در mapping غیرقابل دسترسی هستند.
  • flags - این آرگومان گزینه‌های اضافی را برای mapping را مشخص می‌کند:
    • MAP_SHARED - چندین فرآیند می‌توانند این mapping را به اشتراک بگذارند. تغییرات ایجاد شده توسط یک فرآیند برای دیگران قابل مشاهده است.
    • MAP_PRIVATE - mapping برای فرآیند فراخوانی خصوصی است. تغییرات در منطقه نگاشت شده برای سایر فرآیندها قابل مشاهده نیست و بالعکس. اگر فرآیندی حافظه را تغییر دهد، یک کپی خصوصی از صفحه ایجاد می‌شود. در مورد نگاشت فایل، تغییرات در فایل اصلی نوشته نمی‌شود. موارد استفاده زیادی برای نگاشت یک فایل به عنوان خصوصی وجود دارد.
  • Fd - این آرگومان توصیفگر فایلی است که می‌خواهیم در حافظه نگاشت کنیم.
  • offset - این آرگومان offset درون فایل (مشخص شده با fd) که mapping باید از آنجا شروع شود. برای فایل‌های معمولی، معمولا 0 است.

فراخوانی سیستم mmap در صورت موفقیت یک اشاره گر به حافظه نگاشت شده یا در صورت شکست MAP_FAILED  برمی‌گرداند.
 


چه زمانی MMIO را به API سیستم فایل ترجیح دهید

  • دسترسی به فایل با استفاده از MMIO اغلب می‌تواند منطق برنامه را در مقایسه با استفاده صریح از توابع read() و ()write ساده کند. یک مثال برنامه‌ای است که به صورت پویا درخواست‌های مشتریان را برای دسترسی به بخش‌های مختلف یک فایل بزرگ دریافت می‌کند. قبل از دسترسی به منطقه(region) فایل، به جستجوهای صریح برای جابجایی نشانگر فایل نیاز داریم. با استفاده از MMIO، می‌توانیم بخش‌هایی از فایل را در حافظه نگاشت کنیم و به آن بخش‌ها دسترسی داشته باشیم که انگار آرایه‌هایی در حافظه هستند.
  • MMIO می‌تواند بهتر از فراخوانی‌های ()raw read و () write در رابطه با تاخیر عمل کند. فراخوانی برای read() و ()write شامل دو انتقال داده است. یکی بین فایل و یک بافر در کرنل، و دومی بین بافر کرنل به user-land buffer. با استفاده از MMIO، می‌توانیم نسخه دوم را ذخیره کنیم (از بافر کرنل تا بافر user-land) استفاده از MMIO همچنین باعث صرفه جویی در حافظه می‌شود زیرا کرنل داده‌ها را در یک صفحه map شده قرار می‌دهد که کاربر به آن دسترسی دارد.
وی پی اس یک ماشین مجازی کامل است با آی پی ثابت است که می‌توان به آن دسترسی ریموت داشت.
خرید وی پی اس در پنج موقعیت جغرافیایی ایران، ترکیه، هلند، آلمان و آمریکا با قابلیت تحویل آنی در پارسدو فراهم است.

چه زمانی API ساده فایل سیستم قدیمی را ترجیح دهید

اگر به صورت متوالی یک فایل را م‌ خوانیم، MMIO ممکن است هیچ مزیتی نسبت به () read نداشته باشد زیرا هزینه IO انتقال داده‌ها از دیسک به حافظه در هر دو مورد متحمل خواهد شد.
عملیات‌های کوچک IO با استفاده از MMIO احتمالا پرهزینه‌تر از فراخوانی‌های ()read و ()write ساده‌تر هستند، زیرا هزینه راه‌اندازی صفحات حافظه در سخت‌افزار واحد مدیریت حافظه (MMU) - تنظیم حق دسترسی و غیره - هزینه دارد.

MMIO در عمل

اکنون با استفاده از MMIO فایل‌ها را می‌نویسیم و می خوانیم. کد زیر از دو تابع ()mmio_read و ()mmio_write برای خواندن و نوشتن فایل‌ها استفاده می‌کند. ما کد زیر را برای ارائه اطلاعات در متن حاشیه نویسی کرده‌ایم. بیایید کد را با دقت بخوانیم و سپس آن را اجرا کنیم تا MMIO را در عمل ببینیم.

MMIO در عمل

 

نتیجه

در سیستم عامل لینوکس، mmap یک system call قدرتمند است. در این مطلب، ما از mmap برای نوشتن و خواندن روی یک فایل بدون استفاده از فراخوانی‌های معمول سیستم فایل ()Write و ()Read استفاده کردیم.