I/O হার্ডওয়্যার ও I/O সফটওয়্যার লেয়ার
এই পাঠে যা শিখবেন
- ডিভাইস কন্ট্রোলার ও রেজিস্টার — OS হার্ডওয়্যারের সাথে ঠিক কীভাবে "কথা বলে"
- পোলিং বনাম ইন্টারাপ্ট-চালিত I/O — কখন কোনটা ব্যবহার করা হয় ও কেন
- DMA কীভাবে বড় ডেটা ট্রান্সফারে CPU-এর ওভারহেড কমায়
- I/O সফটওয়্যারের স্তরভিত্তিক কাঠামো — অ্যাপ্লিকেশন থেকে হার্ডওয়্যার পর্যন্ত
১ · ডিভাইস কন্ট্রোলার ও রেজিস্টার
প্রতিটি I/O ডিভাইসের (ডিস্ক, কীবোর্ড, নেটওয়ার্ক কার্ড) সাথে একটি
ডিভাইস কন্ট্রোলারDevice Controllerএকটি নির্দিষ্ট হার্ডওয়্যার ডিভাইসের জন্য ইলেকট্রনিক ইন্টারফেস, যা OS-কে সেই ডিভাইসের সাথে কমান্ড/স্ট্যাটাস রেজিস্টারের মাধ্যমে যোগাযোগ করতে দেয়।
যুক্ত থাকে — একটি ছোট ইলেকট্রনিক সার্কিট যা সেই নির্দিষ্ট ডিভাইসের বিস্তারিত (মোটর ঘোরানো, বিট রিড করা) সামলায়। OS এই
কন্ট্রোলারের কয়েকটি রেজিস্টার-এ লিখে/পড়ে যোগাযোগ করে — সাধারণত একটি কমান্ড রেজিস্টার (কী করতে হবে
লেখা হয়), একটি স্ট্যাটাস রেজিস্টার (ডিভাইস ব্যস্ত/প্রস্তুত/এরর কি না জানায়), এবং একটি ডেটা রেজিস্টার (আসল ডেটা
আসা-যাওয়া করে)। L01-এর অ্যাবস্ট্রাকশন থিমের সরাসরি ভিত্তি — প্রোগ্রামার শুধু read()/write()
কল করে, এই রেজিস্টার-স্তরের বিস্তারিত কখনো দেখতে হয় না।
২ · পোলিং বনাম ইন্টারাপ্ট-চালিত I/O
একবার একটি I/O কমান্ড ইস্যু করা হলে, CPU কীভাবে জানবে সেটি শেষ হয়েছে কি না? এই প্রশ্নের দুটি মৌলিক উত্তর আছে।
CPU একটি লুপে বসে বারবার স্ট্যাটাস রেজিস্টার চেক করে — সহজ, কিন্তু busy-waiting-এর সময় CPU অন্য কোনো কাজ করতে পারে না, বিশাল অপচয়।
CPU কমান্ড দিয়ে অন্য প্রসেস চালাতে থাকে; ডিভাইস শেষ হলে একটি ইন্টারাপ্টInterruptহার্ডওয়্যার থেকে CPU-কে পাঠানো একটি সিগন্যাল, যা CPU-কে বর্তমান কাজ সাময়িকভাবে থামিয়ে একটি নির্দিষ্ট হ্যান্ডলার রুটিন চালাতে বাধ্য করে। পাঠায় — অনেক বেশি কার্যকর, বেশিরভাগ আধুনিক ডিভাইসের আদর্শ পদ্ধতি।
পোলিং-এ CPU ডিভাইসের গতির সাথে "আটকে" থাকে — একটি ধীর ডিভাইসের জন্য অপেক্ষা করলে CPU-এর হাজার হাজার সাইকেল অপচয় হতে পারে। ইন্টারাপ্ট-চালিত I/O-তে CPU সেই সময়টায় অন্য কোনো প্রসেস চালাতে পারে (M3-এর শিডিউলিং সরাসরি কাজে লাগে) — ডিভাইস প্রস্তুত হলেই শুধু CPU-কে ব্যাঘাত ঘটানো হয়, অন্য সময় CPU সম্পূর্ণ মুক্ত।
৩ · DMA (Direct Memory Access)
ইন্টারাপ্ট-চালিত I/O-ও একটি সীমাবদ্ধতা বহন করে — ডিস্কের মতো উচ্চ-থ্রুপুট ডিভাইসে প্রতিটি বাইট ট্রান্সফারের জন্য CPU-কে ইন্টারাপ্ট নিতে হলে সেটাও যথেষ্ট ওভারহেড তৈরি করে। DMADirect Memory Accessএকটি হার্ডওয়্যার মেকানিজম যা ডিভাইস কন্ট্রোলারকে CPU-কে প্রতিটি বাইটে জড়িত না করেই মেমরির সাথে সরাসরি ডেটা আদান-প্রদান করতে দেয়। এই সমস্যার সমাধান — CPU শুধু DMA কন্ট্রোলারকে সোর্স, ডেস্টিনেশন ও ডেটার পরিমাণ বলে দিয়ে অন্য কাজে চলে যায়; DMA কন্ট্রোলার নিজেই মেমরির সাথে সরাসরি ডেটা আদান-প্রদান করে, এবং সম্পূর্ণ ট্রান্সফার শেষ হলে একবার মাত্র CPU-কে ইন্টারাপ্ট করে। ফলাফল — ডিস্ক থেকে কয়েক মেগাবাইট পড়তে CPU-এর হাজার হাজার ইন্টারাপ্ট সামলাতে হয় না, মাত্র একটিই।
৪ · I/O সফটওয়্যার স্তরভিত্তিক কাঠামো
হার্ডওয়্যারের বিস্তারিত থেকে অ্যাপ্লিকেশনকে আলাদা রাখতে OS-এর I/O সফটওয়্যারও M4/L04-এর মতোই স্তরে সংগঠিত — প্রতিটি স্তর শুধু নিচের স্তরের সার্ভিস ব্যবহার করে, নিজের উপরের স্তরকে একটি পরিষ্কার ইন্টারফেস দেয়।
সবচেয়ে গুরুত্বপূর্ণ ফল — "ডিভাইস-ইনডিপেন্ডেন্ট OS I/O সফটওয়্যার" স্তরের কারণে একই read()/
write() কল যেকোনো ডিভাইসের (ডিস্ক, USB, নেটওয়ার্ক) জন্য কাজ করে — শুধু সবচেয়ে নিচের ডিভাইস
ড্রাইভারটি প্রতিটি নির্দিষ্ট হার্ডওয়্যারের বিস্তারিত জানে।
# পোলিং, ইন্টারাপ্ট-চালিত I/O ও DMA -- বৈশিষ্ট্যের তুলনা (শুধুই ধারণাগত ডেটা, কোনো আসল হার্ডওয়্যার অ্যাক্সেস নেই)
io_methods = {
"Polling": {
"cpu_overhead": "উচ্চ -- CPU লুপে বসে বারবার status রেজিস্টার চেক করে",
"complexity": "সবচেয়ে সহজ -- কোনো ইন্টারাপ্ট হ্যান্ডলার লাগে না",
"best_suited_for": "খুব দ্রুত, predictable ডিভাইস (যেমন সিম্পল embedded controller)",
},
"Interrupt-driven": {
"cpu_overhead": "কম -- CPU কমান্ড দিয়ে অন্য কাজ করে, ডিভাইস শেষ হলে জানায়",
"complexity": "মাঝারি -- একটি ইন্টারাপ্ট হ্যান্ডলার লিখতে হয়",
"best_suited_for": "বেশিরভাগ সাধারণ ডিভাইস (কীবোর্ড, মাউস, নেটওয়ার্ক কার্ড)",
},
"DMA": {
"cpu_overhead": "সর্বনিম্ন -- পুরো ট্রান্সফার শেষে মাত্র একটি ইন্টারাপ্ট",
"complexity": "সবচেয়ে জটিল -- আলাদা DMA কন্ট্রোলার হার্ডওয়্যার লাগে",
"best_suited_for": "বড়, বাল্ক ডেটা ট্রান্সফার (ডিস্ক, নেটওয়ার্ক বাফার)",
},
}
for method, info in io_methods.items():
print(f"=== {method} ===")
for key, value in info.items():
print(f" {key}: {value}")
print()
I/O ম্যানেজমেন্ট আসলে দুটি স্বতন্ত্র সমস্যার সমাধান — কীভাবে CPU জানবে একটি অপারেশন শেষ হয়েছে (পোলিং বনাম ইন্টারাপ্ট), আর কতটা CPU-কে সেই ডেটা ট্রান্সফারে সরাসরি জড়াতে হবে (সরাসরি CPU-চালিত বনাম DMA)। পরের পাঠ থেকে আমরা দেখব ঠিক এই হার্ডওয়্যার ভিত্তির উপর দাঁড়িয়ে OS কীভাবে ডিস্কের মতো ধীর, মেকানিক্যাল ডিভাইসের জন্য অনুরোধ শিডিউল করে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ একটি খুব দ্রুত ডিভাইস (যেমন কয়েক ন্যানোসেকেন্ডে সাড়া দেয়) হলে পোলিং কি ইন্টারাপ্ট-চালিত I/O-এর চেয়ে ভালো হতে পারে?
হ্যাঁ, বাস্তবে এমন ক্ষেত্রে পোলিং জিততে পারে। ইন্টারাপ্ট নেওয়ার নিজস্ব একটি ফিক্সড ওভারহেড আছে (কনটেক্সট সেভ, হ্যান্ডলার চালানো, ফিরে আসা — M2/L06-এর কনটেক্সট সুইচিং-এর সাথে সাদৃশ্যপূর্ণ)। যদি ডিভাইসটি এতই দ্রুত হয় যে অপেক্ষার সময়টাই ইন্টারাপ্ট ওভারহেডের চেয়ে কম, তাহলে পোলিং প্রকৃতপক্ষে দ্রুততর — এই কারণেই কিছু অত্যন্ত উচ্চ-গতির নেটওয়ার্ক কার্ড ড্রাইভার ইচ্ছাকৃতভাবে সংক্ষিপ্ত সময়ের জন্য পোলিং ব্যবহার করে।
প্র ০২ DMA ব্যবহার করলে কি CPU সম্পূর্ণভাবে মেমরি থেকে "বিচ্ছিন্ন" থাকে ট্রান্সফারের সময়? এর কোনো সীমাবদ্ধতা আছে কি?
মূলত হ্যাঁ, CPU-এর ডেটা ট্রান্সফারে সরাসরি অংশ নিতে হয় না। তবে DMA কন্ট্রোলার ও CPU একই মেমরি বাস শেয়ার করে — DMA ট্রান্সফারের সময় CPU যদি একই মুহূর্তে মেমরি অ্যাক্সেস করতে চায়, তবে বাস কনটেনশনের কারণে সামান্য ধীরগতির (cycle stealing) সম্মুখীন হতে পারে। তাই DMA CPU-কে সম্পূর্ণ মুক্ত করে না, বরং CPU-এর সরাসরি অংশগ্রহণ কমায় — একটি গুরুত্বপূর্ণ সূক্ষ্ম পার্থক্য।
প্র ০৩ I/O সফটওয়্যারের "ডিভাইস-ইনডিপেন্ডেন্ট" স্তর না থাকলে অ্যাপ্লিকেশন ডেভেলপারদের কী সমস্যা হতো?
প্রতিটি অ্যাপ্লিকেশনকে প্রতিটি ভিন্ন ডিভাইস মডেলের জন্য আলাদা কোড লিখতে হতো — একটি ফাইল ব্রাউজারকে
Seagate ডিস্কের জন্য এক রকম, Western Digital-এর জন্য আরেক রকম কোড লিখতে হতো। ডিভাইস-ইনডিপেন্ডেন্ট স্তর
এই বৈচিত্র্য লুকিয়ে সবকিছুকে একই read()/write() ইন্টারফেসের পেছনে নিয়ে আসে —
L01-এর অ্যাবস্ট্রাকশন থিমের প্রত্যক্ষ প্রয়োগ, কাজটা নিচের ডিভাইস ড্রাইভার স্তরে ঠেলে দেওয়া হয়।
অনুশীলন
-
চিন্তা করুন: কীবোর্ডে একটি কী চাপলে সেটি সাধারণত পোলিং না ইন্টারাপ্ট-চালিত I/O দিয়ে সামলানো হয়? কেন?
ইন্টারাপ্ট-চালিত। মানুষ যেভাবে টাইপ করে তাতে দুটি কী-প্রেসের মধ্যে (মিলিসেকেন্ডের হিসেবে) বিশাল বিরতি থাকে — যদি CPU পোলিং করে বসে থাকত, প্রতিটি কী-প্রেসের মাঝে হাজার হাজার CPU সাইকেল সম্পূর্ণ অপচয় হতো। ইন্টারাপ্ট-চালিত পদ্ধতিতে CPU সেই ফাঁকা সময়টায় সম্পূর্ণ মুক্ত থাকে, কী চাপা হলেই শুধু জানানো হয়।
-
পরীক্ষা করুন: উপরের কোড সেলে
io_methods-এ একটি নতুন এন্ট্রি "Memory-mapped I/O" যোগ করে সেটিরcpu_overhead,complexity, ওbest_suited_forমান দিয়ে Run চেপে দেখুন আউটপুটে কীভাবে যুক্ত হয়।যেহেতু কোডটি
io_methods.items()নিয়ে iterate করে, নতুন যেকোনো এন্ট্রি স্বয়ংক্রিয়ভাবেই লুপে যুক্ত হয়ে যাবে এবং একই ফরম্যাটে প্রিন্ট হবে — কোনো অতিরিক্ত কোড পরিবর্তন লাগবে না। এটি দেখায় কেন dict-ভিত্তিক তুলনা টেবিল লেখা সহজে সম্প্রসারণযোগ্য একটি প্যাটার্ন।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ M10-এর বাকি পাঠগুলো — ডিস্ক শিডিউলিং, RAID, SSD বনাম HDD।
- ফাইল সিস্টেম ইমপ্লিমেন্টেশন ও জার্নালিং পূর্ববর্তী পাঠ I/O স্তরের ঠিক উপরে বসে থাকা ফাইল সিস্টেম কীভাবে ব্লক-লেভেল I/O-এর সাথে কথা বলে তা জানুন।
- ডিস্ক শিডিউলিং অ্যালগরিদম পরবর্তী পাঠ এই পাঠের হার্ডওয়্যার ভিত্তির উপর দাঁড়িয়ে ডিস্ক অনুরোধের ক্রম কীভাবে অপ্টিমাইজ করা হয় দেখুন।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks ও Operating Systems — সব এক জায়গায়।