পাঠ ২৯ · ৫৬-এর মধ্যে · মডিউল ৭
Home / Courses / Operating Systems (OS) / মেমরি ম্যানেজমেন্ট

পেজিং

Paging
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • পেজিং কীভাবে পেজ ও ফ্রেমের মাধ্যমে L28-এর এক্সটার্নাল ফ্র্যাগমেন্টেশন সমস্যা সমাধান করে
  • পেজ টেবিলের ভূমিকা এবং MMU কীভাবে প্রতিটি অ্যাক্সেসে এটি ব্যবহার করে
  • লজিক্যাল অ্যাড্রেসকে page_number ও offset-এ ভাঙার সঠিক গণিত, এবং ফিজিক্যাল অ্যাড্রেস হিসাব করার সূত্র
  • Python দিয়ে বাস্তব অ্যাড্রেস-ট্রান্সলেশন ফাংশন — হাতে-যাচাই-করা উদাহরণসহ

১ · পেজিং কীভাবে এক্সটার্নাল ফ্র্যাগমেন্টেশন দূর করে

পেজিংPagingফিজিক্যাল মেমরিকে ফিক্সড-সাইজ ফ্রেমে এবং প্রসেসের লজিক্যাল স্পেসকে একই আকারের পেজে ভাগ করার মেমরি-ম্যানেজমেন্ট কৌশল — কোনো পেজ যেকোনো ফাঁকা ফ্রেমে বসতে পারে। L28-এর মূল সমস্যাটি সমাধান করে একটি সহজ কিন্তু কার্যকরী কৌশলে — ফিজিক্যাল মেমরিকে সমান, ফিক্সড-সাইজ ফ্রেমFrameফিজিক্যাল মেমরির একটি ফিক্সড-সাইজ অংশ, একটি পেজের সমান আকারের — একটি পেজ ধারণ করতে পারে।-এ ভাগ করা হয়, এবং প্রসেসের লজিক্যাল স্পেসকে ঠিক একই আকারের পেজPageপ্রসেসের লজিক্যাল অ্যাড্রেস স্পেসের একটি ফিক্সড-সাইজ অংশ — ফ্রেমের সমান আকারের।-এ ভাগ করা হয়। যেহেতু প্রতিটি পেজ যেকোনো ফাঁকা ফ্রেমে বসতে পারে — একটানা ফ্রেমের প্রয়োজন নেই — তাই L28-এর এক্সটার্নাল ফ্র্যাগমেন্টেশন সমস্যাটিই আর তৈরি হয় না।

মূল্য একটাই — একটি প্রসেসের শেষ পেজ প্রায়ই পুরোপুরি ভরা থাকে না (প্রসেসের আকার পেজ সাইজের ঠিক গুণিতক না হলে), তাই সামান্য ইন্টারনাল ফ্র্যাগমেন্টেশন থেকে যায় — কিন্তু এটি একটি একক পেজের মধ্যে সীমাবদ্ধ, সর্বোচ্চ (page_size - 1) বাইট, যা এক্সটার্নাল ফ্র্যাগমেন্টেশনের তুলনায় নগণ্য।

পেজ ০ পেজ ১ পেজ ২ প্রসেসের লজিক্যাল স্পেস ফ্রেম ৩ ফ্রেম ১ ফ্রেম ৭ ফিজিক্যাল মেমরি (ফ্রেমসমূহ) — অ-একটানা
পেজ ০, ১, ২ যথাক্রমে ফ্রেম ৩, ১, ৭-এ — সম্পূর্ণ বিক্ষিপ্ত, তবু কোনো এক্সটার্নাল ফ্র্যাগমেন্টেশন নেই।

২ · পেজ টেবিল ও অ্যাড্রেস ট্রান্সলেশন

প্রতিটি প্রসেসের একটি নিজস্ব পেজ টেবিল থাকে — প্রতিটি পেজ নাম্বারকে কোন ফ্রেমে সেটি বর্তমানে আছে তার সাথে ম্যাপ করে। MMU (L27) প্রতিটি মেমরি অ্যাক্সেসে এই টেবিল দেখে অনুবাদ করে। একটি লজিক্যাল অ্যাড্রেসকে দুই ভাগে ভাঙা হয় —

  • page_number (উঁচু বিটগুলো) — logical_address // page_size
  • offset (নিচু বিটগুলো, পেজের ভেতরে অবস্থান) — logical_address % page_size

page_number দিয়ে পেজ টেবিলে ফ্রেম নাম্বার খুঁজে বের করা হয়, তারপর —

$$\text{physical\_address} = \text{frame\_number} \times \text{page\_size} + \text{offset}$$

লক্ষণীয়: offset কখনো বদলায় না — শুধু page_number অংশটিই ফ্রেম নাম্বারে অনুবাদ হয়।

page_number
লজিক্যাল অ্যাড্রেসের উঁচু বিটগুলো — কোন পেজ, তা নির্দেশ করে। গণনা: logical_address // page_size।
offset
লজিক্যাল অ্যাড্রেসের নিচু বিটগুলো — পেজের ভেতরে ঠিক কোথায়, তা নির্দেশ করে। গণনা: logical_address % page_size। ট্রান্সলেশনে অপরিবর্তিত থাকে।
frame_number
পেজ টেবিলে page_number দিয়ে লুকআপ করে পাওয়া যায় — ফিজিক্যাল মেমরিতে ঠিক কোন ফ্রেমে এই পেজটি বর্তমানে আছে।
একটি হাতে-যাচাই করা উদাহরণ

ধরা যাক page_size = 4096 (= $2^{12}$, একটি বাস্তবসম্মত পাওয়ার-অফ-টু মান), এবং পেজ টেবিলে {0: 3, 1: 7, 2: 1, 3: 9} — অর্থাৎ পেজ ০ আছে ফ্রেম ৩-এ, পেজ ১ আছে ফ্রেম ৭-এ, ইত্যাদি। লজিক্যাল অ্যাড্রেস 4100 নিন — page_number = 4100 // 4096 = 1, offset = 4100 % 4096 = 4। পেজ টেবিলে পেজ ১ → ফ্রেম ৭। তাই physical_address = 7 × 4096 + 4 = 28672 + 4 = 28676।

৩ · অ্যাড্রেস ট্রান্সলেশনের বাস্তব বাস্তবায়ন

নিচের কোড সেলে ঠিক উপরের উদাহরণসহ একাধিক লজিক্যাল অ্যাড্রেসের প্রকৃত ট্রান্সলেশন চালানো হয়েছে — page_number, offset, frame_number এবং চূড়ান্ত physical_address সবকিছু প্রিন্ট হবে, যাতে গণিতটি ধাপে ধাপে যাচাই করা যায়।

Python
# পেজিং অ্যাড্রেস ট্রান্সলেশন
def translate(logical_address, page_size, page_table):
    page_number = logical_address // page_size
    offset = logical_address % page_size
    if page_number not in page_table:
        return None  # পেজ ফল্ট -- এই পেজ এই প্রসেসের অংশ নয় (বা L32-এ মেমরিতে নেই)
    frame_number = page_table[page_number]
    physical_address = frame_number * page_size + offset
    return {
        "page_number": page_number,
        "offset": offset,
        "frame_number": frame_number,
        "physical_address": physical_address,
    }

page_size = 4096  # 2^12 -- একটি বাস্তবসম্মত পাওয়ার-অফ-টু পেজ সাইজ
page_table = {0: 3, 1: 7, 2: 1, 3: 9}  # page_number -> frame_number

test_addresses = [0, 4096, 4100, 8192, 8300, 13000]

for logical in test_addresses:
    result = translate(logical, page_size, page_table)
    if result is None:
        print(f"লজিক্যাল {logical}: পেজ টেবিলে নেই -> পেজ ফল্ট")
    else:
        print(
            f"লজিক্যাল {logical:>6} -> page_number={result['page_number']}, "
            f"offset={result['offset']:>4}, frame={result['frame_number']} "
            f"=> ফিজিক্যাল {result['physical_address']}"
        )

    
হাতে যাচাই: লজিক্যাল 8300 — page_number = 8300 // 4096 = 2 (যেহেতু 8192 ≤ 8300 < 12288), offset = 8300 - 8192 = 108। পেজ ২ → ফ্রেম ১। physical = 1 × 4096 + 108 = 4204। কোড সেলটি ঠিক এই মানই গণনা করবে — উপরের সূত্র হাতে করা ও কোডে করা দুই জায়গাতেই মেলে।
মূল কথা · Key takeaway

পেজিং-এর পুরো কৌশলটা দাঁড়িয়ে আছে একটি সহজ পাটিগণিতের ওপর — ভাগশেষ ও ভাগফল দিয়ে একটি লজিক্যাল অ্যাড্রেসকে পেজ নাম্বার ও অফসেটে ভাঙা। পেজ সাইজ যদি সবসময় ২-এর ঘাত (power of 2) হয়, এই ভাগ/মডুলো অপারেশনগুলো হার্ডওয়্যারে বিট-শিফটিং দিয়ে অত্যন্ত দ্রুত করা যায় — বাস্তব CPU-তে এটিই আসলে ঘটে। কিন্তু বড় অ্যাড্রেস স্পেসের জন্য একটি ফ্ল্যাট পেজ টেবিল নিজেই বিশাল হয়ে যায় — এই সমস্যাটিই L31-এ মাল্টিলেভেল পেজ টেবিল ও TLB দিয়ে সমাধান করা হবে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ পেজিং কেন এক্সটার্নাল ফ্র্যাগমেন্টেশন সম্পূর্ণভাবে দূর করে, কিন্তু ইন্টারনাল ফ্র্যাগমেন্টেশন সম্পূর্ণ দূর করে না?

এক্সটার্নাল ফ্র্যাগমেন্টেশন হয় যখন ফাঁকা জায়গা বিক্ষিপ্তভাবে ছোট ছোট টুকরায় ভাগ হয়ে যায় এবং কোনো একটি টুকরো একটি প্রসেসের জন্য যথেষ্ট বড় হয় না — পেজিং-এ যেহেতু যেকোনো পেজ যেকোনো একক ফ্রেমে বসতে পারে (একাধিক ফ্রেমের একটানা থাকার দরকার নেই), তাই এই সমস্যাই অস্তিত্বহীন হয়ে যায়। কিন্তু একটি প্রসেসের শেষ পেজ যদি পুরোপুরি ভরা না থাকে (প্রসেসের আকার page_size-এর ঠিক গুণিতক না হলে), সেই একটি পেজের ভেতরের অবশিষ্ট জায়গা অন্য কোনো প্রসেস ব্যবহার করতে পারে না — এটিই ইন্টারনাল ফ্র্যাগমেন্টেশন, যা পেজিং-এর কাঠামোগত একটি ছোট, অনিবার্য মূল্য।

প্র ০২ উপরের কোড সেলে লজিক্যাল অ্যাড্রেস 13000-এর জন্য page_number ও offset কীভাবে বের হলো?

page_number = 13000 // 4096 = 3 (যেহেতু 3 × 4096 = 12288 ≤ 13000 < 4 × 4096 = 16384)। offset = 13000 - 12288 = 712। পেজ টেবিলে পেজ ৩ → ফ্রেম ৯, তাই physical_address = 9 × 4096 + 712 = 36864 + 712 = 37576। এই একই গণনা কোড সেলের translate() ফাংশন সরাসরি চালিয়ে করে — হাতের হিসাব ও কোডের ফলাফল মিলে যাওয়াই নিশ্চিত করে সূত্রটি সঠিকভাবে বাস্তবায়িত হয়েছে।

প্র ০৩ কেন offset অংশটি কখনোই ফ্রেম-ট্রান্সলেশনের সময় বদলায় না — এর পেছনের যুক্তি কী?

একটি পেজের ভেতরে কোনো বাইটের আপেক্ষিক অবস্থান (offset) সেই পেজটি ফিজিক্যাল মেমরির কোন ফ্রেমে বসছে তার ওপর নির্ভর করে না — পেজটি ফ্রেম ৩-এ থাকুক বা ফ্রেম ৯-এ, পেজের ভেতরের চতুর্থ বাইট সবসময় সেই ফ্রেমের চতুর্থ বাইটেই থাকবে। শুধু কোন ফ্রেমে এই প্রশ্নের উত্তরটাই বদলায় (page_number → frame_number অনুবাদের মাধ্যমে) — এই কারণেই ট্রান্সলেশন সূত্রে offset অপরিবর্তিত রেখেই শুধু frame_number × page_size যোগ করা হয়।

অনুশীলন

  1. চিন্তা করুন: যদি page_size = 4096 হয় এবং একটি প্রসেসের আকার হয় 10000 বাইট, তাহলে প্রসেসটির কতগুলো পেজ লাগবে, এবং শেষ পেজে কত বাইট ইন্টারনাল ফ্র্যাগমেন্টেশন হবে?

    10000 / 4096 ≈ 2.44, তাই 3টি পেজ লাগবে (পেজ ০, ১, ২ — আংশিক পেজও পূর্ণ পেজ হিসেবে বরাদ্দ পেতে হয়)। 3 পেজ = 3 × 4096 = 12288 বাইট বরাদ্দ হবে, কিন্তু প্রসেস আসলে ব্যবহার করবে 10000 বাইট — তাই ইন্টারনাল ফ্র্যাগমেন্টেশন = 12288 - 10000 = 2288 বাইট, যা সম্পূর্ণভাবে শেষ (তৃতীয়) পেজেই ঘটে।

  2. পরীক্ষা করুন: উপরের কোড সেলে page_table-এ একটি নতুন এন্ট্রি 4: 12 যোগ করে test_addresses-এ 16400 যোগ করে Run চাপুন — ফলাফল কী দেখায়?

    16400 // 4096 = 4 (যেহেতু 4×4096=16384 ≤ 16400), offset = 16400 - 16384 = 16। পেজ ৪ → ফ্রেম ১২, তাই physical = 12 × 4096 + 16 = 49152 + 16 = 49168। এটি নিশ্চিত করে যে পেজ টেবিলে নতুন এন্ট্রি যোগ করাই যথেষ্ট নতুন পেজ রেঞ্জ সাপোর্ট করার জন্য — ট্রান্সলেশন ফাংশনে কোনো পরিবর্তন লাগে না।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
পাঠ ২৮ · কন্টিগুয়াস মেমরি অ্যালোকেশন