অ্যাড্রেস বাইন্ডিং ও লজিক্যাল বনাম ফিজিক্যাল অ্যাড্রেস
এই পাঠে যা শিখবেন
- অ্যাড্রেস বাইন্ডিং কী এবং এটি compile time, load time ও execution time-এ কীভাবে ভিন্নভাবে ঘটে
- লজিক্যাল (ভার্চুয়াল) অ্যাড্রেস বনাম ফিজিক্যাল অ্যাড্রেসের সঠিক পার্থক্য
- MMU (Memory Management Unit) কী করে এবং কেন এই হার্ডওয়্যার সাপোর্ট ছাড়া execution-time বাইন্ডিং অসম্ভব
- Python দিয়ে একটি রিলোকেশন-রেজিস্টার-স্টাইল MMU সিমুলেশন — একই লজিক্যাল অ্যাড্রেস কীভাবে ভিন্ন ভিন্ন ফিজিক্যাল অ্যাড্রেসে ম্যাপ হতে পারে
১ · অ্যাড্রেস বাইন্ডিং কী
একটি প্রোগ্রাম যখন লেখা হয়, তখন এর ভেতরের ভ্যারিয়েবল ও ইনস্ট্রাকশনগুলো প্রতীকী (symbolic) ঠিকানা ব্যবহার করে —
যেমন একটি ভ্যারিয়েবলের নাম x, একটি ফাংশনের নাম। কিন্তু শেষ পর্যন্ত এগুলোকে RAM-এর কোনো একটি
প্রকৃত নাম্বার-ঠিকানায় বসতে হয়।
অ্যাড্রেস বাইন্ডিংAddress Bindingপ্রোগ্রামের ইনস্ট্রাকশন ও ডেটা রেফারেন্সকে প্রকৃত মেমরি অ্যাড্রেসে ম্যাপ করার প্রক্রিয়া — এটি তিনটি ভিন্ন পর্যায়ে ঘটতে পারে।
হলো ঠিক এই ম্যাপিং প্রক্রিয়া — এবং এটি কখন ঘটে তার ওপর ভিত্তি করে তিন ধরনের বাইন্ডিং হয়।
যদি কম্পাইল করার সময়ই ঠিক জানা যায় প্রোগ্রামটি মেমরিতে কোথায় লোড হবে, কম্পাইলার সরাসরি চূড়ান্ত ফিজিক্যাল অ্যাড্রেস বসিয়ে দিতে পারে। কিন্তু এই অবস্থান যদি পরে বদলাতে হয়, পুরো কোড পুনরায় কম্পাইল করতে হবে — আজকাল প্রায় অব্যবহৃত।
কম্পাইল করার সময় চূড়ান্ত অবস্থান অজানা থাকলে, প্রোগ্রামটি রিলোকেটেবল কোড তৈরি করে — এবং প্রোগ্রামটি মেমরিতে লোড হওয়ার মুহূর্তেই ঠিকানা স্থির হয়। কিন্তু একবার লোড হয়ে গেলে, প্রসেস চলাকালীন আর সরানো যায় না।
ঠিকানা বাইন্ডিং স্থগিত থাকে যতক্ষণ না প্রোগ্রামটি আসলে চলে — এমনকি চলাকালীনও অ্যাড্রেস বদলাতে পারে। এর জন্য হার্ডওয়্যার সাপোর্ট (MMU) দরকার — এটিই আধুনিক OS-এর স্ট্যান্ডার্ড পদ্ধতি।
২ · লজিক্যাল বনাম ফিজিক্যাল অ্যাড্রেস
লজিক্যাল অ্যাড্রেসLogical Address (Virtual Address)একটি চলমান প্রসেস যে অ্যাড্রেস "দেখে" ও ব্যবহার করে — প্রসেসের নিজস্ব দৃষ্টিকোণ থেকে, যেন তার কাছে ০ থেকে শুরু হওয়া একটি সম্পূর্ণ, একচেটিয়া মেমরি স্পেস আছে। হলো একটি প্রসেস তার নিজের দৃষ্টিকোণ থেকে যে অ্যাড্রেস ব্যবহার করে — যেন তার কাছে গোটা মেমরি একাই আছে, ঠিকানা শুরু হয় ০ থেকে। কিন্তু বাস্তবে একই সময়ে বহু প্রসেস মেমরিতে থাকে, তাই প্রতিটি প্রসেসকে RAM-এর ভিন্ন ভিন্ন অংশে বসতে হয় — এই প্রকৃত অবস্থানকে বলা হয় ফিজিক্যাল অ্যাড্রেসPhysical Addressকম্পিউটারের RAM হার্ডওয়্যারে অ্যাড্রেসটি প্রকৃতপক্ষে যেখানে অবস্থিত।।
এই দুটোকে সংযুক্ত করে MMUMemory Management Unitএকটি হার্ডওয়্যার উপাদান যা প্রতিটি মেমরি অ্যাক্সেসে লজিক্যাল অ্যাড্রেসকে রান-টাইমে ফিজিক্যাল অ্যাড্রেসে অনুবাদ করে — প্রসেসের কাছে সম্পূর্ণ স্বচ্ছভাবে। — একটি হার্ডওয়্যার উপাদান যা প্রতিটি একক মেমরি অ্যাক্সেসে লজিক্যাল অ্যাড্রেসকে ফিজিক্যাল অ্যাড্রেসে রূপান্তর করে। সবচেয়ে সহজ রূপ — একটি রিলোকেশন রেজিস্টার (base register) — যা প্রসেসের বরাদ্দকৃত মেমরির শুরুর ফিজিক্যাল ঠিকানা ধরে রাখে; প্রতিটি লজিক্যাল অ্যাড্রেসের সাথে এই base যোগ করলেই ফিজিক্যাল অ্যাড্রেস পাওয়া যায়।
যেহেতু প্রসেস শুধু লজিক্যাল অ্যাড্রেস নিয়েই কাজ করে, OS চাইলে প্রসেসটিকে ফিজিক্যাল মেমরির এক জায়গা থেকে আরেক জায়গায় সরিয়ে দিতে পারে (শুধু base register-টি বদলে দিয়ে) — প্রসেসের নিজের কোডে এক লাইনও পরিবর্তন করতে হয় না, এবং প্রসেস নিজেও জানে না তাকে সরানো হয়েছে। এই ধারণাটিই L29-এর পেজিং-এর প্রকৃত, বাস্তবসম্মত বাস্তবায়নের ভিত্তি।
৩ · একটি রিলোকেশন-রেজিস্টার MMU সিমুলেশন
নিচের কোড সেলে একটি অতি-সরলীকৃত MMU সিমুলেট করা হয়েছে — translate() ফাংশনটি একটি লজিক্যাল
অ্যাড্রেসকে base register যোগ করে ফিজিক্যাল অ্যাড্রেসে রূপান্তর করে। লক্ষ্য করুন — একই
লজিক্যাল অ্যাড্রেস ভিন্ন ভিন্ন base register-এর সাথে ভিন্ন ভিন্ন ফিজিক্যাল অ্যাড্রেসে ম্যাপ হয়, যা দেখায়
কীভাবে একটি প্রসেসকে সরানো (relocate) হলেও তার নিজের লজিক্যাল দৃষ্টিভঙ্গি অপরিবর্তিত থাকে।
# একটি অতি-সরলীকৃত MMU সিমুলেশন — রিলোকেশন (base) রেজিস্টার
def translate(logical_address, base_register):
"""লজিক্যাল অ্যাড্রেসকে ফিজিক্যাল অ্যাড্রেসে অনুবাদ করে।"""
physical_address = logical_address + base_register
return physical_address
# প্রসেস P1 প্রথমে একটি অবস্থানে লোড হয়েছে -- base register = 20000
base_p1_first_load = 20000
logical_addresses = [0, 100, 4096]
print("--- প্রসেস P1, প্রথম লোড (base = 20000) ---")
for logical in logical_addresses:
physical = translate(logical, base_p1_first_load)
print(f"লজিক্যাল {logical:>6} -> ফিজিক্যাল {physical:>6}")
# OS পরে P1-কে ভিন্ন একটি মেমরি অঞ্চলে সরিয়ে দিলো -- base register = 50000
base_p1_relocated = 50000
print("\n--- একই প্রসেস P1, রিলোকেশনের পর (base = 50000) ---")
for logical in logical_addresses:
physical = translate(logical, base_p1_relocated)
print(f"লজিক্যাল {logical:>6} -> ফিজিক্যাল {physical:>6}")
print("\nলক্ষ্য করুন: logical_addresses তালিকাটি অপরিবর্তিত -- শুধু base register বদলেছে,")
print("তবু ফিজিক্যাল অ্যাড্রেস সম্পূর্ণ ভিন্ন। প্রসেস নিজে এই পরিবর্তন সম্পর্কে কিছুই জানে না।")
অ্যাড্রেস বাইন্ডিং যত পরে (execution time-এ) হয়, তত বেশি নমনীয়তা পাওয়া যায় — একটি প্রসেসকে মেমরিতে সরানো, একাধিক প্রসেসকে নিরাপদে একসাথে চালানো সম্ভব হয় শুধু এই কারণেই যে প্রতিটি প্রসেস তার নিজস্ব লজিক্যাল দৃষ্টিভঙ্গিতে কাজ করে, আর MMU-ই প্রকৃত ফিজিক্যাল বাস্তবতার সাথে সংযোগ ঘটায়। L29-L31-এ আমরা দেখব কীভাবে আধুনিক OS এই একই ধারণাকে পেজিং ও পেজ টেবিলের মাধ্যমে অনেক বড় স্কেলে বাস্তবায়ন করে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ Compile-time অ্যাড্রেস বাইন্ডিং আজকাল কেন প্রায় ব্যবহৃত হয় না?
Compile-time বাইন্ডিং কাজ করার জন্য কম্পাইল করার সময়েই ঠিক জানা লাগে প্রোগ্রামটি মেমরির ঠিক কোন ফিজিক্যাল ঠিকানায় লোড হবে — বাস্তবে এটি নিশ্চিত করা প্রায় অসম্ভব, কারণ আধুনিক OS-এ একই সময়ে বহু প্রোগ্রাম চলে এবং কোনটি কোথায় লোড হবে তা আগে থেকে জানা যায় না। এমনকি জানা গেলেও, প্রোগ্রামটিকে পরে সরাতে হলে পুরো প্রোগ্রাম পুনরায় কম্পাইল করা লাগত — অত্যন্ত অব্যবহারিক।
প্র ০২ MMU ছাড়া (অর্থাৎ শুধু compile/load-time বাইন্ডিং থাকলে) একটি চলমান প্রসেসকে ফিজিক্যাল মেমরির অন্য কোথাও সরানো কেন সম্ভব হতো না?
Load-time বাইন্ডিং-এ অ্যাড্রেস একবার লোড হওয়ার সময়েই স্থায়ীভাবে চূড়ান্ত হয়ে যায় — প্রোগ্রামের প্রতিটি ইনস্ট্রাকশন ইতিমধ্যে একটি নির্দিষ্ট ফিজিক্যাল অ্যাড্রেস ধরেই লেখা। এরপর প্রসেসটি সরালে সেই সব হার্ডকোডেড অ্যাড্রেস ভুল হয়ে যাবে। MMU-ভিত্তিক execution-time বাইন্ডিং-এ প্রসেস কখনোই ফিজিক্যাল অ্যাড্রেস সরাসরি জানে না — শুধু base register বদলে দিলেই একই লজিক্যাল অ্যাড্রেসগুলো নতুন সঠিক ফিজিক্যাল অ্যাড্রেসে অনুবাদ হতে থাকে, প্রোগ্রামের কোনো কোড না বদলেই।
প্র ০৩ উপরের কোড সেলে P1-কে "রিলোকেট" করার সময় ঠিক কোন একটি মান বদলানো হলো — এবং কোনটি অপরিবর্তিত রইল?
শুধুমাত্র base_register-এর মান বদলানো হলো (20000 থেকে 50000-এ)। logical_addresses
তালিকাটি — অর্থাৎ প্রসেসের নিজস্ব দৃষ্টিভঙ্গি — সম্পূর্ণ অপরিবর্তিত রইল। এটিই দেখায় যে লজিক্যাল অ্যাড্রেস
স্পেস প্রসেসের একটি স্থিতিশীল, নিজস্ব "জগৎ" — ফিজিক্যাল বাস্তবতার পরিবর্তন সেই জগতে কখনো প্রবেশ করে না,
কারণ MMU-ই সেই সীমানা রক্ষা করে।
অনুশীলন
-
চিন্তা করুন: যদি দুটি ভিন্ন প্রসেসের base register যথাক্রমে 20000 ও 20050 হয়, এবং উভয়েরই লজিক্যাল অ্যাড্রেস স্পেস আকার 100 হয়, তাহলে কী সমস্যা হতে পারে?
প্রসেস ১-এর ফিজিক্যাল রেঞ্জ হবে 20000-20099, আর প্রসেস ২-এর 20050-20149 — এই দুটি রেঞ্জ ওভারল্যাপ করছে (20050-20099 অংশে)! বাস্তব OS-এ base register-এর পাশাপাশি একটি limit registerও থাকে, যা নিশ্চিত করে একটি প্রসেস তার নিজের বরাদ্দের বাইরে অ্যাক্সেস করতে না পারে — এবং OS নিজে নিশ্চিত করে বরাদ্দকৃত রেঞ্জগুলো কখনো ওভারল্যাপ না করে। L28-এ কন্টিগুয়াস মেমরি অ্যালোকেশনে ঠিক এই সমস্যাটিই বিস্তারিত আলোচিত হবে।
-
পরীক্ষা করুন: উপরের কোড সেলে
base_p1_relocated-কে 50000 থেকে 0 করে Run চেপে দেখুন ফলাফল কী হয়।base = 0 হলে physical_address ঠিক logical_address-এর সমান হয়ে যাবে (যেহেতু 0 যোগ করলে কোনো পরিবর্তন হয় না) — যেন প্রসেসটি একদম মেমরির শুরুতেই বসানো হয়েছে। এটি নিশ্চিত করে যে base = 0 আসলে "কোনো রিলোকেশন নেই"-এর সমতুল্য একটি বিশেষ ক্ষেত্র, সাধারণ সূত্রেরই একটি স্বাভাবিক ফলাফল।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ পড়ুন L28 কন্টিগুয়াস মেমরি অ্যালোকেশন — base/limit register ব্যবহার করে কীভাবে OS একাধিক প্রসেসকে ফিজিক্যাল মেমরিতে বণ্টন করে।
- পেজিং L29 এখানকার সরল "base + logical" সূত্রটিই পেজিং-এ একটি পূর্ণাঙ্গ পেজ টেবিলে রূপান্তরিত হয় — MMU-এর প্রকৃত আধুনিক বাস্তবায়ন।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ সব ৫৬টি পাঠের তালিকা এবং মডিউলভিত্তিক অগ্রগতি দেখুন।