خب رسیدیم به یکی از مهمترین بخش ها یعنی مدیریت حافظه . حافظه نقش یه منبع خیلی حیاتی رو ایفا می کنه که اصلا نبودشو نمی شه متصور شد . برای همین در طی این سالیان در کنار اینکه تکنولوژی های جدید برای ساخته حافظه ها ابدا شده و کلی ظرفیتشون افزایش پیدا کرده ولی همیشه به یه برنامه که حافظه رو مدیریت کنه نیاز داشتیم . یکی از جالب ترین تکنیک ها Virtual Memory هست که یه لایه انتزاعی از منابع ذخیره سازی روی ماشین به ما ارائه میده . این لایه به لطف ساختار و الگوریتم هایی که توش به کار رفته فرآیند مدیریت حافظه رو راحت تر می کنه . اکثر سیستم عامل های جدید از این روش برای مدیریت حافظه استفاده می کنن و همینطور لینوکس و به لطف جامعه باز لینوکس می تونیم بهتر با این روش آشنا بشیم .

ما میدونم که هر قسمتی از حافظه رممون یه آدرس فیزیکی داره که کاملا یونیک هست . سی پی یو می تونه از این آدرس ها استفاده کنه و مستقیما به قسمت خاصی از حافظه دسترسی پیدا کنه . هر کدوم از این قسمتا اندازه مشخصی دارن مثلا فرض کنید ۳۲ بیت اگه بخوایید داده ای به اندازه ۶۴ بیت ذخیره کنید به ۲ تا نیاز دارید و همینظور رو به بالا . منطقی نیست که هر نرم افزار به صورت جداگانه حافظه ای که استفاده می کنه رو مدیریت کنه برا همین سیستم عامل ها خودشون این کارو انجام میدن برنامه درخواست حافظه می کنه سیستم عامل به مقداری که درخواست داده شده یک فضای خالی تو حافظه پیدا می کنه در اینجا بهتره که از آدرس فیزیکی استفاده نشه پس مدیر حافظه باید برای خودش یه چیزی شبیه لیست داشته باشه و بگه مثلا حافظه۱ مربوط به این آدرس فیزیکی هست و همینو به برنامه برمیگردونه و میگنه بیا داده تو اینجا ذخیره شد . به این نوع آدرس دهی ، آدرس لاجیکال یا virtual address میگن .

برنامه ها درخواست حافظه میدن و بعد که درخواست آزاد کردن حافظه رو میدن مدیر حافظه قسمتی که به اون برنامه داده بود رو خالی میکنه . مشکلی از این مرحله به بعد به وجود میاد اینه که حافظه ما سوراخ سوراخ میشه ! یعنی یه تیکه از حافظه استفاده می شه و یه قسمت دیگه خالی مونده به این اتفاق میگن Fragmentation . حالا اگه برنامه درخواست حافظه ی بزرگی رو بده ما باید چیکار کینم ؟ بریم بیشتر با Virtual Memory آشنا بشیم .

Virtual Memory and Paging

خب در مورد آدرس لاجیکال یا ویرچوال صحبت کردیم همه این آدرس ها کنار هم دیگه یه مفهوم انتزاعی به نام ویرچوال مموری به وجود میاره . از اونجایی که یه پیاده سازی abstract از حافظه هست پس می تونه بزرگتر از حافظه رم باشه و مثلا قسمتی از داده ها روی دیسک ذخیره بشن . Virtual Memory mangment از واحد های کوچیکتری به اسم page ، صفحه استفاده می کنه . هر صفحه شامل بلاک هایی پیوسته از آدرس های ویرچوال هست و معمولا سایزشون ۴ کیلوبایت هست که تو سیستم ها با معماری مختلف می تونه کمتر یا بیشتر بشه .

الان هر نوع دسترسی به حافظه فقط از طریق Virtual address ها اتفاق می افته حتی سی پی یو از این آدرسا استفاده می کنه که توسط MMU تبدیل به آدرس های فیزیکی می شن . این تبدیل به کمک page table انجام میشه . که خب یه جدوله که نشون میده فلان آدرس ویرچوال به کدوم آدرس فیزیکی اشاره داره . وقتی سی پی یو یه آدرس حافظه ویرچوال رو میخواد فریم نامبر اون آدرس ویرچوال رو استخراج میکنه و این عدد توسط page table تبدیل به عدد فیزیکال اون آدرس لاجیکال میشه .

page table

مزایای ویرچوال مموری :

  • فضای آدرس بزرگتر
  • امنیت :‌ چون هر پروسس دارای فضای آدرس ویرچوال مختص به خودش هست پروسس ها رو توی یه فضای ایزوله شده ای قرار میده و هیچ پروسسی نمی تونه به فضای آدرس دیگری دسترسی پیدا کنه
  • Memory Maping : ویرچوال مموری این امکان رو داره که مستقیم یه فایل رو به اصطلاح مپ کنیم به فضای آدرس یه پروسس .
  • تخصیص حافظه عادلانه
  • Shared Virtual Memory : این ویژگی این امکان رو فراهم می کنه که پروسس ها به صورت اشتراکی از یه فضای حافظه استفاده کنن . این مورد برای shared library ها یا برنامه هایی مثل bash کاربرد داره . این برنامه یه قسمتی از حافظه رو اشغال می کنه و همه پروسس ها می تونن ازش بهره ببرن .

virtual-memory

Zone

به خاطر یک سری محدودیت های سخت افزاری لینوکس نمی تونه با همه page ها یک جور رفتار کنه . برای همین لینوکس میاد و page هایی که ویژگی های مشابه دارن رو دسته بندی می کنه که بهش Zone می گیم . این محدودیت ها در حالت کلی دو نوع هستن :

  • بعضی از سخت افزار ها فقط توانایی انجام DMA (direct memory access ) رو بعضی از آدرس ها رون دارن .
  • بعضی از معماری ها توانایی آدرس دهی فیزیکی بیشتری نسبت به آدرس دهی لاجیکال دارن . پس در نتیجه بعضی از حافظه ها به طور دائم آدرس دهی لاجیکال روشون انجام نمی گیره .

این باعث میشه که لینوکس سه تا منطقه (Zone) برای حافظه ایجاد کنه :‌

  • ZONE_DMA : این زون شامل پیج هایی هست که توانایی انجام DMA رو دارن . احتمالا به زودی شاهد حذف شدنش باشیم شایدم تا الآن شده . Is is time to remove ZONE_DMA
  • ZONE_NORMAL : خب این گروه برای پیج های عادی هست
  • ZONE_HIGHMEM : این منطقه شامل high memory هست . این قسمت از حافظه ها به صورت دائمی و اتوماتیک تو فضای آدرس کرنل آدرس دهی نمی شن . تو معماری x86 همه حافظه های بعد از 896MB ، High memroy محسوب میشن .

کاربرد zone ها مستقل از معماری هست برای مثال تو معماری که هیچ مشکلی برای انجام DMA وجود نداره ZONE_DMA خالی هست همین قضیه برای ZONE_HIGHMEM هم هست اگه تو معماری امکان مپ کردن کل حافظه وجود داره این زون خالی خواهد بود.

memory zone

تخصیص و آزاد کردن حافظه

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

Slab allocation

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

ولی مشکلی که این لیست ها و این روش داره اینکه به صورت گلوبال در دسترس نیستن و لینوکس هیچ دسترسی بهشون ندارن . وقتی با کمبود حافظه مواجه هستیم لینوکس نمی تونه از این لیست ها استفاده کنه . برای حل این مشکل لینوکس از مکانیزیمی به نام slab allocation ( یا slab layer ) استفاده می کنه . که به عنوان یه لایه برای کش کردن هست . مزایای استفاده از slab allocation :

  • ساختار داده هایی که مکررا اختصاص پیدا می کنن و بعد آزاد میشن رو کش میکنه
  • درخواست حافظه می تونه خیلی سریع تر پاسخ داده بشه
  • مموری به دلیل fragmentation هدر نمی ره و استفاده از حافظه خیلی بهتری خواهیم داشت

relationship between caches , slabs and objects

Defragmention mechanism

با اینکه مکانیزم slab allocation جلوی هدر رفتن حافظه رو تا حد زیادی میگیره ولی بازم سناریو هایی هست که مموری دچارdefragmention بشه . لینوکس روش های مختلفی برای defragment کردن مموری در طی این سال ها پیاده سازی کرده و بهبود داده . در ابتدا سعی می کردن که بزرگ ترین و کوچیکترین بلاک های حافظه رو حرکت بدن تا بلاک های آزاد بزرگتری بسازن . بعد اومدن پیج ها رو به دو سته تقسیم کردن easily-reclaimable , non-reclaimable . پیچ هایی که تو کرنل استفاده می شد باز پس گیریشون یا حرکت دادنشون سختتر بود و یه مقداری نسبت به قبل فرآیند رو سرعت می بخشید و نتیجه بهتری داشت بعد مموری رو به zone هایی تقسیم کردن و میرسیم به جایی که الگوریتمی به نام memory compaction به وجود اومد . کاری که این الگوریتم انجام میده فشرده کردن بلاک های اختصاص یافته س تا بلاک های آزاد پشت سرهم بزرگی بسازه با هم قدم به قدم میریم جلو تا بیشتر باهاش آشنا بشیم .

Memory Compatcion

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

fragmented memory

در این حالت اگه ما درخواست یه یه پیچ به اندازه ۴ بلاک بکنیم این تخصیص صورت نمی گیره چون ۴ بلاک خالی پشت سرهم نداریم . حتی درخواست ۲ تا بلاک هم میسر نخواهد بود چون بلاک ها به درستی مرتب نشدن . ایجاست که Memory Compaction وارد میشه . این روش دوتا الگوریتم مجزا ست که یکی از اونها برای مثال از سمت چپ به راست شروع می کنه و یه لیست از بلاک های اختصاص یافته می سازه این بلاک ها باید پیج های قابل حرکت (Movable ) باشن .

list of movable page

و الگوریتم دیگه ای از سمت راست شروع میکنه و لیستی از پیج های خالی رو میسازه .

list of free page

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

defragmented memory

  • این الگوریتم فقط در صورتی می تونه درست کار کنه پیج های درگیر اینکار همشون movable باشن اگه فقط یدونه پیج غیر قابل حرکت وجود داشته باشه کل کارو خراب میکنه .

  • این الگوریتم می تونه حدود ۹۰ درصد از مواقع بلاک های خالی تیکه تیکه شده مموری رو تبدیل به یه پیج خیلی بزرگ خالی کنه .

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

Proactive Compaction

تو الگوریتم قبلی کل حافظه برای ساخت پیج های آزاد بزرگ پیمایش می شد و اگه پیج غیر متحرکی این وسط بود کار خراب میشد . اما کرنل کمی تغییرش داده و این الگوریتم فقط به اندازه ای که نیاز هست اقدام به ساخت پیج های آزاد میکنه که بهش on-demand compaction میگن.

در واقع تردی به اسم kcompactd شروع به کار می کنه و فقط به اندازه ای که نیاز هست بلاک هارو فشرده میکنه ولی این روش می تونه تاخیر داشته باشه برای پیج های بزرگ و روی پرفورمنس تاثیر منفی بزاره . پس تغییری که لینوکس تو این الگوریتم داده و بهش procative compaction میگن فشرده سازی رو به صورت یه تسک تو بکگراند داره تو یه بازه زمانی خاصی شروع به کار می کنه . این پیاده سازی جدید و حدود ۳ سال پیش مطرح شده و احتمالا هنوز در حال پیاده سازی هست . ادامه تو این لینک

Swap

اگه تا حالا لینوکس نصب کرده باشید حداقل اسمشو شنیدید . swap space یه مکانی هست مثلا یه پارتیشن جدا که لینوکس در مواقع لازم برای ایجاد فضای خالی تو رم پیج هایی رو منتقل به این فضا می کنه به این عمل swapping می گن . swapping یکی از قابلیت های ویرچوال مموری هست که باعث میشه حافظه رممون بیشتر به نظر بیاد. همچنین برای قابلیت suspend to disk کاربرد داره .

هر کسی در مورد اندازه swap یه نظری داره ! به نظر من هم برای سیستم های امروزی که حافظه رم نسبتا بالایی دارن نصف یا برابر اندازه رم کافی خواهد بود ولی اگه از اون دسته افراد هستید که سیستم رو با کلی برنامه باز hibernate می کنید در اینصورت ۱٫۵ تا ۲ برابر اندازه رم .

برای دیدن میزان استفاده از رم و swap space از دو دستور زیر استفاده میشه :

# shows RAM and swap space usage 
free -h 
# shows swap space usage 
swapon --show

فضای سوآپ لازم نیست حتما یه دیوایس بلاک باشه لینوکس می تونه از یه فایل هم به عنوان سوآپ استفاده کنه . برای مثال یه فایل سوآپ ۵۱۲ مگی ایجاد می کنیم و swapping رو روش فعال می کنیم .

# create a swap file 
dd if=/dev/zero of=/swapfile bs=1M count=512 status=progress

chmod 600 /swapfile

mkswap /swapfile

swapon /swapfile 

# edit fstab and this file will be used as swap every time Linux boots up 
/swapfile none swap defaults 0 0

خب امیدوارم این پست هم براتون مفید بوده باشه . برا من که بود ;-)

نظرتون رو بنویسد
کپی بخش یا کل هر کدام از مطالب لینوکس ۹۸ تنها با کسب مجوز مکتوب امکان پذیر است.
وبلاگ لینوکس ۹۸ یک پروژه متن باز بوده و سورس آن در گیت‌هاب موجود است.