ডিমান্ড পেজিং
এই পাঠে যা শিখবেন
- ভার্চুয়াল মেমরি ও ডিমান্ড পেজিং কীভাবে একটি প্রসেসকে ফিজিক্যাল মেমরির সীমার বাইরে "বড়" মনে করায়
- valid/invalid বিট, পেজ ফল্ট এবং OS-এর ট্র্যাপ→লোড→রিজিউম চক্র ধাপে ধাপে
- Effective Access Time (EAT) হিসাব করে দেখা কেন সামান্য পেজ-ফল্ট রেটও পারফরম্যান্সের জন্য বিশাল ব্যাপার
- Python দিয়ে একটি বাস্তব ডিমান্ড-পেজিং সিমুলেশন — হিট/ফল্ট ট্র্যাক করা
১ · ভার্চুয়াল মেমরি ও ডিমান্ড পেজিং কী
ভার্চুয়াল মেমরি (Virtual Memory)Virtual Memoryএকটি প্রসেসের লজিক্যাল অ্যাড্রেস স্পেসকে প্রকৃত ফিজিক্যাল মেমরির চেয়ে বড় হতে দেওয়ার কৌশল — বাকি অংশ ডিস্কে (swap space) থাকে। হলো এমন একটি কৌশল যা একটি প্রসেসের লজিক্যাল অ্যাড্রেস স্পেসকে কম্পিউটারে আসলে থাকা ফিজিক্যাল RAM-এর চেয়েও বড় হতে দেয়। M7-এর পেজিং (L29) আমাদের দেখিয়েছিল কীভাবে একটি প্রসেসের পেজগুলো ফিজিক্যাল ফ্রেমে ম্যাপ হয় — কিন্তু সেখানে ধরে নেওয়া হয়েছিল প্রসেসের সবগুলো পেজ একসাথে মেমরিতে থাকে। ভার্চুয়াল মেমরি এই ধারণা ভেঙে দেয়: যেকোনো মুহূর্তে শুধু প্রসেসটি এখন সক্রিয়ভাবে যা ব্যবহার করছে তা-ই মেমরিতে থাকা যথেষ্ট, বাকিটা ডিস্কের একটি সংরক্ষিত এলাকায় (swap space/page file) অপেক্ষা করে।
ডিমান্ড পেজিং (Demand Paging)Demand Pagingএকটি পেজ ডিস্ক থেকে ফিজিক্যাল মেমরিতে লোড হয় শুধু তখনই, যখন প্রসেস সেই পেজে প্রথমবার সত্যিকারের অ্যাক্সেস করে — আগে থেকে নয়। হলো ভার্চুয়াল মেমরি বাস্তবায়নের প্রধান কৌশল — "চাহিদা অনুযায়ী" পেজিং। একটি পেজ ডিস্ক থেকে মেমরিতে লোড হয় শুধু প্রথমবার সত্যিকারের অ্যাক্সেস হলে, প্রসেস শুরু হওয়ার সময় সবকিছু আগেভাগে (eagerly) লোড করে নয়। এর ফলে একটি প্রসেস দ্রুত শুরু হতে পারে (প্রথম কয়েকটি প্রয়োজনীয় পেজ ছাড়া আর কিছু লোড করার দরকার নেই) এবং যে পেজগুলো কখনোই ব্যবহৃত হয় না (যেমন একটি প্রোগ্রামের এরর-হ্যান্ডলিং কোড যা কখনো ট্রিগার হয় না) সেগুলো ডিস্ক থেকে মেমরিতে আনার খরচও লাগে না।
প্রসেস শুরু হওয়ার সময়েই তার সবগুলো পেজ ফিজিক্যাল মেমরিতে লোড করা হয় — সরল কিন্তু অপচয়ী, বিশেষত বড় প্রোগ্রামে যার বেশিরভাগ অংশ হয়তো কখনোই ব্যবহৃত হবে না।
শুধু এখন প্রয়োজন এমন পেজ লোড হয় — মেমরি ব্যবহার কার্যকর হয়, কিন্তু প্রতিটি নতুন পেজ প্রথমবার অ্যাক্সেসে একটি "পেজ ফল্ট" ও তার সাথে ডিস্ক I/O-এর খরচ আনে।
২ · Valid/Invalid বিট, পেজ ফল্ট (Page Fault) ও রিজিউম
ডিমান্ড পেজিং বাস্তবায়ন করতে প্রতিটি পেজ টেবিল এন্ট্রিতে একটি অতিরিক্ত valid/invalid বিট যোগ করা হয়। valid মানে পেজটি এখন সত্যিই কোনো ফিজিক্যাল ফ্রেমে আছে — MMU সরাসরি ফ্রেম নাম্বার পেয়ে যায় (L29/L31-এর স্বাভাবিক অনুবাদ)। invalid মানে পেজটি এখনো ডিস্কেই আছে, ফিজিক্যাল মেমরিতে আনা হয়নি।
যখন একটি প্রসেস একটি invalid পেজে অ্যাক্সেস করার চেষ্টা করে, হার্ডওয়্যার একটি পেজ ফল্ট (Page Fault)Page Faultএকটি ইনভ্যালিড (মেমরিতে অনুপস্থিত) পেজে অ্যাক্সেসের চেষ্টা — হার্ডওয়্যার OS-কে ট্র্যাপ করায়, যাতে OS পেজটি ডিস্ক থেকে লোড করে দেয়। তৈরি করে এবং OS-কে ট্র্যাপ করায়। তারপর OS নিচের ধাপগুলো অনুসরণ করে:
পেজ ফল্ট হ্যান্ডলিংয়ের সবচেয়ে গুরুত্বপূর্ণ বৈশিষ্ট্য হলো এটি প্রসেসের কাছে সম্পূর্ণ স্বচ্ছ (transparent) — প্রসেস কখনো জানতে পারে না যে একটি ফল্ট ঘটেছিল (টাইমিং ছাড়া)। OS যে ইন্সট্রাকশনটি ফল্ট ঘটিয়েছিল, ঠিক সেই একই ইন্সট্রাকশন থেকে প্রসেসকে পুনরায় শুরু করায় — নতুন করে ডেটা লোড হয়ে যাওয়ার পর এবার এটি সফলভাবে সম্পন্ন হয় (হিট)।
৩ · পেজ ফল্টের রেট ও প্রকৃত খরচ — Effective Access Time
পেজ ফল্ট কেন এত গুরুত্বপূর্ণ একটি বিষয়? কারণ একটি ফল্ট হ্যান্ডল করতে একটি ডিস্ক I/O লাগে — যা সাধারণ মেমরি অ্যাক্সেসের চেয়ে কয়েক হাজার থেকে কয়েক লক্ষ গুণ ধীর। এই বাস্তব খরচ গণনা করার একটি প্রমিত সূত্র হলো Effective Access Time (EAT) — যদি $p$ পেজ-ফল্ট রেট (০ থেকে ১-এর মধ্যে একটি ভগ্নাংশ), $ma$ সাধারণ মেমরি অ্যাক্সেস টাইম, এবং $fault\_time$ একটি ফল্ট হ্যান্ডল করতে যে সময় লাগে:
$$ EAT = (1-p) \times ma + p \times fault\_time $$
একটি বাস্তব উদাহরণ দেখা যাক: ধরা যাক সাধারণ মেমরি অ্যাক্সেস $ma = 100$ ন্যানোসেকেন্ড, একটি পেজ ফল্ট হ্যান্ডল করতে $fault\_time = 8{,}000{,}000$ ন্যানোসেকেন্ড (৮ মিলিসেকেন্ড, একটি ডিস্ক সিকের বাস্তবসম্মত মান) লাগে, এবং পেজ-ফল্ট রেট মাত্র $p = 0.001$ (অর্থাৎ প্রতি ১০০০ অ্যাক্সেসে গড়ে ১টি ফল্ট)। তাহলে —
$$ EAT = (0.999 \times 100) + (0.001 \times 8{,}000{,}000) = 99.9 + 8000 = 8099.9 \text{ ন্যানোসেকেন্ড} $$
৪ · সিমুলেশন: ডিমান্ড পেজিং বাস্তবে কীভাবে কাজ করে
নিচের কোড সেলে একটি ছোট্ট, বাস্তব ডিমান্ড-পেজিং সিমুলেশন — একটি পেজ টেবিল (valid/invalid এন্ট্রিসহ), একটি
ফিজিক্যাল মেমরি (৪টি ফ্রেম) এবং একটি access_page() ফাংশন যা প্রতিটি অ্যাক্সেসে হিট না ফল্ট তা
ঠিক করে, ফল্ট হলে প্রকৃতপক্ষে একটি ফ্রি ফ্রেমে পেজ "লোড" করে।
# ডিমান্ড পেজিং সিমুলেশন -- বাস্তব ডিস্ক/RAM নয়, শুধুই ধারণা বোঝানোর টয় ডেটা স্ট্রাকচার
NUM_FRAMES = 4
# page_table: page_number -> {"valid": bool, "frame": frame_number or None}
page_table = {}
# physical_memory: frame_number -> page_number (কোন ফ্রেমে এখন কোন পেজ আছে)
physical_memory = {}
free_frames = list(range(NUM_FRAMES))
fault_count = 0
hit_count = 0
def access_page(page_number, page_table, physical_memory, free_frames):
global fault_count, hit_count
entry = page_table.get(page_number, {"valid": False, "frame": None})
if entry["valid"]:
hit_count += 1
print(f"পেজ {page_number}: HIT (আগে থেকেই ফ্রেম {entry['frame']}-এ আছে)")
return entry["frame"]
# --- পেজ ফল্ট ---
fault_count += 1
if not free_frames:
print(f"পেজ {page_number}: FAULT -> কিন্তু কোনো ফ্রি ফ্রেম নেই! "
f"(কাকে সরানো হবে তা ঠিক করা page-replacement অ্যালগরিদমের কাজ -- দেখুন পরবর্তী পাঠ, L33)")
return None
frame = free_frames.pop(0)
physical_memory[frame] = page_number
page_table[page_number] = {"valid": True, "frame": frame}
print(f"পেজ {page_number}: FAULT -> ডিস্ক থেকে লোড, ফ্রেম {frame}-এ বসানো হলো")
return frame
# ৪টি ফ্রেম, কিন্তু মাত্র ৪টি ভিন্ন পেজ ব্যবহৃত হচ্ছে -- তাই কখনো রিপ্লেসমেন্টের দরকার হবে না
reference_sequence = [1, 2, 3, 1, 4, 2, 1, 3, 4, 2]
for pg in reference_sequence:
access_page(pg, page_table, physical_memory, free_frames)
print("\n--- ফলাফল ---")
print("মোট অ্যাক্সেস:", len(reference_sequence))
print("পেজ ফল্ট:", fault_count)
print("পেজ হিট:", hit_count)
print(f"পেজ-ফল্ট রেট: {fault_count/len(reference_sequence):.2f}")
ডিমান্ড পেজিং ভার্চুয়াল মেমরির ভিত্তি — এটি ঠিক করে কখন একটি পেজ মেমরিতে আনা হবে (শুধু প্রথম প্রকৃত অ্যাক্সেসে)। কিন্তু মেমরি পূর্ণ থাকলে কাকে সরিয়ে জায়গা করা হবে তা এই পাঠের সিমুলেশন এড়িয়ে গেছে — ঠিক এই প্রশ্নের উত্তর নিয়েই পরবর্তী পাঠ, L33 (পেজ রিপ্লেসমেন্ট অ্যালগরিদম) শুরু হবে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ ডিমান্ড পেজিং কেন সব পেজ আগে থেকে লোড করার চেয়ে ভালো, যদি শেষ পর্যন্ত প্রতিটি পেজই একবার না একবার অ্যাক্সেস হয়?
বাস্তবে অনেক প্রোগ্রামের একটি বড় অংশ (যেমন এরর-হ্যান্ডলিং কোড, খুব কম ব্যবহৃত ফিচার) আসলে কখনোই অ্যাক্সেস হয় না একটি নির্দিষ্ট রান-এ। এছাড়া ডিমান্ড পেজিং প্রসেসকে অনেক দ্রুত শুরু করতে দেয় — প্রথম কয়েকটি প্রয়োজনীয় পেজ লোড হলেই প্রসেস চালু হয়ে যেতে পারে, বাকিটা প্রয়োজন অনুযায়ী ধীরে ধীরে আসে। ফলে গড়ে মেমরি ও সময় দুটোই সাশ্রয় হয়, এমনকি যদি প্রতিটি পেজ শেষমেশ একবার অ্যাক্সেস হয়ও।
প্র ০২ একটি পেজ ফল্ট কেন "ট্র্যাপ" হিসেবে হ্যান্ডল করা হয়, সাধারণ ফাংশন কলের মতো নয়?
একটি ট্র্যাপ হলো এমন একটি হার্ডওয়্যার-লেভেল মেকানিজম যা প্রসেসকে জানার সুযোগ না দিয়েই CPU-কে user mode থেকে kernel mode-এ পাঠিয়ে দেয় (সরাসরি L03-এর সিস্টেম-কল আলোচনার প্যাটার্ন) — কারণ শুধুমাত্র kernel mode-এ OS-এর প্রকৃত ফ্রেম বরাদ্দ ও ডিস্ক I/O-এর মতো প্রিভিলেজড কাজ করার অনুমতি আছে। প্রসেসের নিজের কোডে একটি সাধারণ ফাংশন কল দিয়ে এটি করা সম্ভব নয়, কারণ প্রসেসের কাছে সেই প্রিভিলেজই নেই।
প্র ০৩ উপরের EAT হিসাবে পেজ-ফল্ট রেট ০.০০১ থেকে ০.০১ (১%) করলে EAT-এর ওপর কী প্রভাব পড়বে, মোটামুটি অনুমান করুন।
$p=0.01$ হলে $EAT = 0.99 \times 100 + 0.01 \times 8{,}000{,}000 = 99 + 80{,}000 = 80{,}099$ ন্যানোসেকেন্ড — অর্থাৎ ফল্ট রেট ১০ গুণ বাড়ানোয় EAT-ও প্রায় ১০ গুণ বেড়ে যায় (যেহেতু $fault\_time$ পদটিই EAT-তে প্রায় সম্পূর্ণ প্রভাব ফেলে, $ma$ পদ তুলনায় নগণ্য)। এটি দেখায় কেন পেজ-ফল্ট রেট নিয়ন্ত্রণে রাখা এত গুরুত্বপূর্ণ — এটি প্রায় সরাসরি রৈখিকভাবে (linearly) সামগ্রিক পারফরম্যান্সকে প্রভাবিত করে।
অনুশীলন
-
পরীক্ষা করুন: উপরের কোড সেলে
reference_sequence-এর শেষে5যোগ করুন (যেমন[1, 2, 3, 1, 4, 2, 1, 3, 4, 2, 5]) এবং Run চেপে দেখুন কী প্রিন্ট হয়।পেজ 5-এর জন্য কোনো ফ্রি ফ্রেম বাকি নেই (৪টি ফ্রেমই 1, 2, 3, 4-এ ভরা), তাই কোড "কোনো ফ্রি ফ্রেম নেই! ... page-replacement অ্যালগরিদমের কাজ" বার্তাটি প্রিন্ট করবে এবং
Noneরিটার্ন করবে। এটিই ঠিক সেই মুহূর্ত যেখানে একটি বাস্তব OS-কে সিদ্ধান্ত নিতে হয় কোন পেজ সরানো হবে — যা পরের পাঠ L33-এর বিষয়। -
চিন্তা করুন: EAT সূত্রে
fault_timeযদি SSD-ভিত্তিক সিস্টেমে HDD-এর চেয়ে ১০ গুণ কম হয় (ধরুন ৮০০,০০০ ন্যানোসেকেন্ড, ৮ মিলিসেকেন্ডের বদলে), তাহলে একই $p=0.001$-এ EAT কেমন হবে?$EAT = 0.999 \times 100 + 0.001 \times 800{,}000 = 99.9 + 800 = 899.9$ ন্যানোসেকেন্ড — এখনও সাধারণ অ্যাক্সেসের (১০০ ন্যানোসেকেন্ড) চেয়ে প্রায় ৯ গুণ ধীর, কিন্তু HDD-ভিত্তিক সিস্টেমের ৮১ গুণের তুলনায় অনেক কম খারাপ। এটি দেখায় কেন দ্রুত স্টোরেজ (M10-এর SSD বনাম HDD পাঠ, L45) পেজ-ফল্টের বাস্তব খরচ কমাতে সরাসরি সাহায্য করে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পেজিং (L29) আবার দেখুন রিক্যাপ ডিমান্ড পেজিং বোঝার আগে পেজ টেবিল ও অ্যাড্রেস ট্রান্সলেশনের মূল ধারণাটা ঝালিয়ে নিন।
- পরের পাঠ: পেজ রিপ্লেসমেন্ট — FIFO ও Optimal L33 মেমরি পূর্ণ থাকলে OS কীভাবে ঠিক করে কোন পেজ সরানো হবে — এই পাঠের অসমাপ্ত প্রশ্নের উত্তর।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ M8-এর বাকি পাঠগুলো (পেজ রিপ্লেসমেন্ট, থ্র্যাশিং, ফ্রেম অ্যালোকেশন) এবং পুরো কোর্স।