حافظهی مجازی: هر برنامه نقشهی خودش رو داره (Virtual Memory)
ببین هر پردازه چطور فضای آدرس خصوصی خودش رو میگیره، جدول صفحهها چطور آدرسهاش رو به رم واقعی ترجمه میکنن، و وقتی رم تموم میشه چی میشه.
ایده
هر پردازه آدرسهای خصوصی خودش رو داره
با حافظهی مجازی، هر پردازه فضای آدرس خودش رو میبینه که از نزدیکی ۰ شروع میشه، انگار کل حافظه مال خودشه. آدرسهایی که استفاده میکنه مجازیان: شمارهی واقعی جعبههای رم نیستن. پشت صحنه، کرنل و پردازنده هر آدرس مجازی رو به یه آدرس واقعی، فیزیکی، ترجمه میکنن. دو تا برنامه میتونن هر دو از آدرس ۴۰۹۶ استفاده کنن و هیچوقت به هم نخورن، چون ۴۰۹۶ِ هر کدوم به یه جای متفاوت میرسه.
یه بازی و یه مرورگر هر دو میتونن فکر کنن دادهشون از یه آدرس یکسان شروع میشه. هیچکدوم نمیدونه، و لازم هم نیست بدونه، واقعاً کجای رمه.
خودت رو امتحان کن
چرا سیستمعاملها نمیذارن هر پردازه مستقیم از آدرسهای واقعی رم استفاده کنه؟
- چون آدرسهای واقعی اونقدر بلندن که توی یه رجیستر جا نمیشن
- چون یه برنامهی باگدار میتونست دادهی یکی دیگه رو خراب کنه، و هر برنامه باید میدونست بقیه کجان
- چون رم رو فقط کرنل میتونه بخونه
- چون آدرسهای واقعی با هر تیک ساعت عوض میشن
جواب رو ببین
چون یه برنامهی باگدار میتونست دادهی یکی دیگه رو خراب کنه، و هر برنامه باید میدونست بقیه کجان
درسته. آدرسهای واقعیِ مشترک یعنی هیچ دیواری بین برنامهها نیست و مدام باید حساب کرد کی کجاست. آدرسهای مجازیِ خصوصی هر دو رو حل میکنن.
جدول صفحه (Page Table)
حافظه بهشکل تکههایی به اسم صفحه (Page) مدیریت میشه، معمولاً هر کدوم ۴ کیلوبایت (۴۰۹۶ بایت). کرنل برای هر پردازه یه جدول صفحه نگه میداره: فهرستی که میگه هر صفحهی مجازی توی کدوم صفحهی فیزیکیِ رم نشسته، که بهش میگن قاب (Frame). بخشی از پردازنده به اسم MMU (واحد مدیریت حافظه) تکتک آدرسها رو همون لحظه ترجمه میکنه. یه کش کوچیک از ترجمههای اخیر نگه میداره، پس این ترجمه تقریباً همیشه سریعه.
آدرسهای مجازی ۴۰۹۶ تا ۸۱۹۱ صفحهی ۱ان. اگه جدول یه پردازه بگه «صفحهی ۱ ← قاب ۷»، پس آدرس ۴۰۹۶ـش در واقع یعنی ابتدای قاب ۷ توی رم.
واحد ۱ توی دو تا ساختمون متفاوت
دو تا دوست هر دو توی «واحد ۱» زندگی میکنن. مشکلی نیست، چون یکی توی ساختمون الف زندگی میکنه و اون یکی توی ساختمون ب؛ ساختمونه که «واحد ۱» رو به یه جای واقعی روی نقشه تبدیل میکنه. آدرس مجازی یه پردازه همون شمارهی واحده، جدول صفحهش ساختمونه، و قاب فیزیکی همون جای واقعی. جایی که این تشبیه میلنگه: کرنل میتونه بیسروصدا صفحهی یه پردازه رو به یه قاب دیگه، یا حتی روی دیسک، ببره و فقط جدول رو بهروز کنه؛ کاری که هیچ ساختمون واقعیای با یه واحد نمیتونه بکنه.
خودت رو امتحان کن
جدول صفحهی یه پردازه چیکار میکنه؟
- فایلهایی که پردازه باز کرده رو فهرست میکنه
- صفحههای مجازی پردازه رو به قابهای فیزیکی توی رم وصل میکنه
- تعیین میکنه برش زمانی پردازه چقدر باشه
- کد ماشین پردازه رو ذخیره میکنه
جواب رو ببین
صفحههای مجازی پردازه رو به قابهای فیزیکی توی رم وصل میکنه
درسته. فهرست ترجمهست: صفحهی مجازی میره تو، قاب فیزیکی میاد بیرون. MMU برای هر آدرسی که پردازه بهش دست میزنه ازش استفاده میکنه.
قدمبهقدم ببین

دو تا پردازه، هر دو با آدرس ۴۰۹۶ سمت چپ دو تا پردازهست، A و B، هر کدوم با سه تا صفحه که ۰، ۱ و ۲ شماره خوردن؛ صفحهی ۱ توی هر دوتاشون از آدرس ۴۰۹۶ شروع میشه. سمت راست رمه با هشت تا قاب، ۰ تا ۷. هنوز هیچی وصل نیست: هر دو برنامه آدرسهای خودشون رو دارن که از صفر شروع میشه، و هر دو از ۴۰۹۶ استفاده میکنن.

۴۰۹۶ِ A میره قاب ۷، مال B قاب ۳ حالا فلشها از صفحههای هر پردازه، از طریق MMU، به قابهای رم کشیده شدن. صفحهی ۱ِ A توی قاب ۷ میافته و صفحهی ۱ِ B توی قاب ۳؛ بقیهی صفحهها میرن توی قابهای خالی دیگه. عدد یکیه، ۴۰۹۶، ولی حافظهی واقعی فرق داره، پس هیچکدوم دادهی اون یکی رو نمیبینه.

رم پره: صفحهی ۲ِ A میره توی swap حالا قابهای ۱ و ۴ هم پر میشن، پس همهی قابهای رم پر شدن. کرنل یه صفحه رو که یه مدته استفاده نشده، یعنی صفحهی ۲ِ A، انتخاب میکنه، روی یه فضای swap توی اساسدی کپیش میکنه و قابش، یعنی ۵، رو آزاد میکنه. حالا جدول صفحهی A صفحهی ۲ رو به دیسک وصل کرده، که با خطچین نشون داده شده.

خطای صفحه: صفحهی ۲ به قاب ۵ برمیگرده پردازهی A دوباره به صفحهی ۲ دست میزنه. MMU میبینه توی رم نیست و یه خطای صفحه (Page Fault) میده؛ کرنل A رو نگه میداره، صفحه رو از اساسدی توی قاب خالیِ ۵ برمیگردونه، و فلش دوباره پررنگ میشه. A بدون اینکه بفهمه ادامه میده، فقط مجبور شد منتظر دیسک بمونه.
خودت رو امتحان کن
توی قابها، پردازهی A و پردازهی B هر دو توی آدرس ۴۰۹۶ِ خودشون نوشتن، پس یکیشون دادهی اون یکی رو خراب کرد.
جواب رو ببین
نادرست
غلطه. جدول صفحهی A، ۴۰۹۶ـش رو به قاب ۷ میفرسته و جدول B، ۴۰۹۶ـش رو به قاب ۳. عدد مجازی یکیه، حافظهی فیزیکی فرق داره، پس هیچوقت به هم نمیخورن.
محافظت
چرا کرش یه برنامه کل کامپیوتر رو از کار نمیندازه
اگه یه برنامه از آدرسی استفاده کنه که اصلاً توی جدول صفحهش نیست، MMU جلوش رو میگیره و کرنل فقط همون برنامه رو میبنده. توی لینوکس و مکاواس به این میگن segmentation fault و توی ویندوز access violation. کرش توی همون یه پردازه میمونه، بهجای اینکه نوشتنِ اشتباه توی حافظهی یکی دیگه بیفته و کل دستگاه رو از کار بندازه.
یه بازی باگدار یه اشارهگر خراب رو دنبال میکنه تا یه آدرسی که هیچوقت بهش داده نشده بود. بازی با یه خطا بسته میشه؛ مرورگر و موسیقیت به کارشون ادامه میدن.
وقتی رم تموم میشه
- بیرون فرستادن صفحه
کرنل صفحهای رو که یه مدته استفاده نشده توی یه فایل swap (ویندوز بهش میگه paging file) روی دیسک کپی میکنه و قابش رو آزاد میکنه.
- برنامه بهش دست میزنه
بعداً برنامه از یه آدرس توی همون صفحه استفاده میکنه. MMU میبینه توی رم نیست: خطای صفحه.
- برگردوندن صفحه
کرنل برنامه رو نگه میداره، صفحه رو از دیسک توی یه قاب خالی میخونه و جدول صفحه رو بهروز میکنه.
- ادامه
برنامه از همون دستور ادامه میده. هیچوقت متوجه نمیشه، جز اینکه منتظر مونده.
خودت رو امتحان کن
یه برنامه به صفحهای دست میزنه که کرنل قبلاً به swap فرستاده بود. چی میشه؟
- برنامه با segmentation fault کرش میکنه
- یه خطای صفحه: کرنل نگهش میداره، صفحه رو از دیسک به رم برمیگردونه، جدول رو بهروز میکنه و میذاره ادامه بده
- پردازنده مقدار رو مستقیم از فایل swap میخونه و همونجا نگهش میداره
- برنامه باید از اول شروع کنه
جواب رو ببین
یه خطای صفحه: کرنل نگهش میداره، صفحه رو از دیسک به رم برمیگردونه، جدول رو بهروز میکنه و میذاره ادامه بده
درسته. آدرس معتبره، فقط الان توی رم نیست. خطای صفحه به کرنل اجازه میده برش گردونه و برنامه رو از همون دستور ادامه بده.
خودت رو امتحان کن
هر اصطلاح رو به معنیش وصل کن
جواب رو ببین
- آدرس مجازی → عددی که یه پردازه استفاده میکنه و مخصوص خودشه
- صفحه → یه تکهی ۴ کیلوبایتی از فضای آدرس یه پردازه
- MMU → بخشی از پردازنده که هر آدرس رو ترجمه میکنه
- Swap → فضایی روی دیسک که وقتی رم پره صفحهها میرن اونجا
- Thrashing → رفتوبرگشت دائمی صفحهها که کامپیوتر رو بهشدت کند میکنه
مرور درس
- حافظهی مجازی به هر پردازه فضای آدرس خصوصی خودش رو میده که از نزدیکی ۰ شروع میشه.
- حافظه بهشکل صفحههای ۴ کیلوبایتی مدیریت میشه؛ جدول صفحهی هر پردازه صفحههای مجازیش رو به قابهای فیزیکی وصل میکنه و MMU هر آدرس رو ترجمه میکنه.
- دو تا پردازه میتونن از یه آدرس یکسان، مثل ۴۰۹۶، استفاده کنن و باز هم به حافظهی واقعی متفاوتی برسن.
- آدرسی بیرون از جدول یه پردازه فقط همون برنامه رو متوقف میکنه: segmentation fault یا access violation.
- وقتی رم پره، صفحههای بیکار به swap روی دیسک میرن و با یه خطای صفحه برمیگردن؛ زیادیِ این کار یعنی Thrashing.