মাল্টিলেভেল কিউ ও মাল্টিলেভেল ফিডব্যাক কিউ শিডিউলিং
এই পাঠে যা শিখবেন
- মাল্টিলেভেল কিউ শিডিউলিং কী এবং কিউগুলোর মধ্যে অগ্রাধিকার কীভাবে কাজ করে
- মাল্টিলেভেল ফিডব্যাক কিউ কীভাবে প্রসেসকে তার আচরণের ভিত্তিতে কিউর মধ্যে সরায় (ডিমোশন/প্রমোশন)
- কেন এই অভিযোজন ক্ষমতাই ফিডব্যাক কিউকে বাস্তব সিস্টেমে এত ব্যবহারযোগ্য করে তোলে
- Python দিয়ে একটি বাস্তব ২-লেভেল ফিডব্যাক কিউ সিমুলেশন — কোন প্রসেস কোন কিউতে শেষ হয় তা প্রকৃত বার্স্ট আচরণ থেকে গণনা করে দেখানো
১ · মাল্টিলেভেল কিউ শিডিউলিং কী
মাল্টিলেভেল কিউ (Multilevel Queue)Multilevel Queue Schedulingরেডি কিউকে একাধিক আলাদা কিউতে ভাগ করা, যেখানে প্রতিটি কিউর নিজস্ব শিডিউলিং অ্যালগরিদম থাকে এবং কিউগুলোর মধ্যে একটি স্থির অগ্রাধিকার সম্পর্ক থাকে। হলো রেডি কিউকে প্রসেসের ক্যাটাগরি অনুযায়ী আলাদা আলাদা কিউতে ভাগ করে দেওয়া — যেমন ইন্টারঅ্যাকটিভ/ফোরগ্রাউন্ড প্রসেসের জন্য একটি কিউ, ব্যাচ/ব্যাকগ্রাউন্ড প্রসেসের জন্য আরেকটি। প্রতিটি কিউর নিজস্ব শিডিউলিং অ্যালগরিদম থাকতে পারে (যেমন ফোরগ্রাউন্ডে Round Robin, ব্যাকগ্রাউন্ডে FCFS), এবং কিউগুলোর মধ্যে একটি স্থির অগ্রাধিকার থাকে (যেমন ফোরগ্রাউন্ড সবসময় ব্যাকগ্রাউন্ডকে প্রিএম্পট করে) অথবা কিউগুলোর মধ্যে একটি নির্দিষ্ট সময়-বণ্টন থাকে। গুরুত্বপূর্ণ বিষয় — একটি প্রসেস একবার যে কিউতে বসানো হয়, সে সেখানেই স্থায়ীভাবে থাকে, কিউর মধ্যে কোনো সরাসরি চলাচল নেই।
২ · মাল্টিলেভেল ফিডব্যাক কিউ — অভিযোজিত সংস্করণ
মাল্টিলেভেল ফিডব্যাক কিউ (Multilevel Feedback Queue)Multilevel Feedback Queueএকটি প্রসেসকে তার পর্যবেক্ষিত আচরণের ভিত্তিতে বিভিন্ন কিউর মধ্যে সরানোর অনুমতি দেয় এমন একটি অভিযোজিত মাল্টিলেভেল কিউ স্কিম। উপরের সীমাবদ্ধতা দূর করে — একটি প্রসেস তার আচরণের ভিত্তিতে কিউর মধ্যে সরে যেতে পারে। যদি একটি প্রসেস তার সম্পূর্ণ time quantum ব্যবহার করেও শেষ না হয় (ইঙ্গিত দেয় এটি CPU-বাউন্ড), তাকে একটি নিম্ন-অগ্রাধিকার, দীর্ঘ-quantum কিউতে ডিমোট করা হয়। আর যদি একটি প্রসেস দ্রুত CPU ছেড়ে দেয় (I/O-এর জন্য, ইঙ্গিত দেয় এটি ইন্টারঅ্যাকটিভ/I/O-বাউন্ড), সে উচ্চ-অগ্রাধিকার কিউতেই থেকে যায় বা প্রমোট হয়। এভাবেই ফিডব্যাক কিউ L10-এর SJF-এর মতো ফলাফল অর্জন করে — ছোট/ইন্টারঅ্যাকটিভ কাজকে অগ্রাধিকার — কিন্তু বার্স্ট টাইম আগে থেকে না জেনেই, শুধু আচরণ পর্যবেক্ষণ করে। এটি একটি বাস্তব, ব্যাপকভাবে ব্যবহৃত ডিজাইন — ঐতিহাসিকভাবে বেশ কিছু Unix শিডিউলার এই পদ্ধতি ব্যবহার করেছে।
প্রসেস একটি কিউতে স্থায়ীভাবে বসে থাকে, কোনো চলাচল নেই — সহজ কিন্তু কম নমনীয়।
প্রসেস আচরণের ভিত্তিতে কিউর মধ্যে সরে (ডিমোশন/প্রমোশন) — নমনীয়, অভিযোজিত, বাস্তবে বহুল ব্যবহৃত।
একটি প্রসেস উচ্চ-অগ্রাধিকার কিউতে তার পুরো quantum ব্যবহার করে ফেললে (অর্থাৎ quantum শেষ হওয়ার আগে সে স্বেচ্ছায় CPU ছাড়েনি), শিডিউলার ধরে নেয় এটি একটি দীর্ঘ, CPU-নিবিড় কাজ — তাই তাকে নিম্ন-অগ্রাধিকার কিউতে পাঠিয়ে দেওয়া হয়, যেখানে quantum দীর্ঘ কিন্তু অগ্রাধিকার কম। এভাবে ছোট কাজগুলো উচ্চ-অগ্রাধিকার কিউতেই দ্রুত শেষ হয়ে যায়, আর দীর্ঘ কাজগুলো ধীরে ধীরে নিচের দিকে নেমে যায়।
৩ · একটি সিমুলেশন — ২-লেভেল ফিডব্যাক কিউ
নিচের কোড সেলে একটি সরল ২-লেভেল ফিডব্যাক কিউ সিমুলেট করা হয়েছে — সব প্রসেস শুরু হয় হাই-প্রায়োরিটি, ছোট-quantum কিউতে; কেউ যদি সেই quantum পুরোপুরি ব্যবহার করেও শেষ না হয়, তাকে লো-প্রায়োরিটি, লম্বা-quantum কিউতে ডিমোট করা হয়।
# ২-লেভেল মাল্টিলেভেল ফিডব্যাক কিউ -- toy in-memory ডেটা, real threads/processes নয়
high_quantum = 4
low_quantum = 8
processes = [
("P1", 3), # (নাম, মোট বার্স্ট টাইম)
("P2", 6),
("P3", 10),
]
low_queue = []
finished = {}
print(f"--- রাউন্ড ১: হাই-প্রায়োরিটি কিউ (quantum = {high_quantum}) ---")
for name, remaining in processes:
if remaining <= high_quantum:
finished[name] = "high_queue (কখনও ডিমোট হয়নি)"
print(f"{name}: বার্স্ট {remaining} <= quantum({high_quantum}) -> হাই কিউতেই সম্পন্ন")
else:
used = high_quantum
left = remaining - used
low_queue.append((name, left))
print(f"{name}: বার্স্ট {remaining} > quantum({high_quantum}) -> পুরো quantum ব্যবহার, "
f"ডিমোট হলো লো কিউতে, বাকি বার্স্ট {left}")
print(f"\n--- রাউন্ড ২: লো-প্রায়োরিটি কিউ (quantum = {low_quantum}) ---")
for name, remaining in low_queue:
if remaining <= low_quantum:
finished[name] = "low_queue (ডিমোটেড, এখানেই সম্পন্ন)"
print(f"{name}: বাকি {remaining} <= quantum({low_quantum}) -> লো কিউতে সম্পন্ন")
else:
left = remaining - low_quantum
finished[name] = "low_queue (এখনও চলছে)"
print(f"{name}: quantum শেষ, আরও বাকি {left}, লো কিউতেই থাকবে")
print("\n--- চূড়ান্ত ফলাফল: কোন প্রসেস কোন কিউতে শেষ হলো ---")
for name, _ in processes:
print(f"{name}: {finished[name]}")
মাল্টিলেভেল ফিডব্যাক কিউ কোনো প্রসেসের বার্স্ট টাইম আগে থেকে না জেনেই, শুধু তার আচরণ পর্যবেক্ষণ করে (সে কি পুরো quantum ব্যবহার করছে, নাকি দ্রুত ছেড়ে দিচ্ছে) সিদ্ধান্ত নেয় কে অগ্রাধিকার পাবে — এটিই এই অ্যালগরিদমকে বাস্তব সিস্টেমে এত ব্যবহারযোগ্য করে তোলে, কারণ বাস্তবে কেউই একটি প্রসেস চলা শুরুর আগে তার সম্পূর্ণ বার্স্ট টাইম নিশ্চিতভাবে জানে না।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ মাল্টিলেভেল কিউতে কিউগুলোর মধ্যে যদি একটি স্থির অগ্রাধিকার থাকে (ফোরগ্রাউন্ড সবসময় ব্যাকগ্রাউন্ডকে প্রিএম্পট করে), এটি কোন সমস্যা তৈরি করতে পারে?
এটি L11-এর ঠিক সেই স্টারভেশন সমস্যাটি তৈরি করতে পারে — যদি ফোরগ্রাউন্ড কিউতে ক্রমাগত নতুন প্রসেস আসতেই থাকে, ব্যাকগ্রাউন্ড কিউর প্রসেসগুলো কখনোই CPU নাও পেতে পারে, কারণ কিউর মধ্যে অগ্রাধিকার স্থির ও নিরঙ্কুশ। এটিই ঠিক সেই সীমাবদ্ধতা যা মাল্টিলেভেল ফিডব্যাক কিউ-এর অভিযোজিত (adaptive) নকশা এড়াতে সাহায্য করে, যদিও সম্পূর্ণভাবে নয় — L11-এর aging কৌশলের মতো অতিরিক্ত সুরক্ষা এখানেও প্রয়োজন হতে পারে।
প্র ০২ ফিডব্যাক কিউতে একটি প্রসেস তার পুরো quantum ব্যবহার করে ফেলাটা কেন "CPU-বাউন্ড" হওয়ার একটি ভালো ইঙ্গিত?
একটি প্রসেস তখনই তার quantum শেষ হওয়ার আগে CPU স্বেচ্ছায় ছেড়ে দেয় যখন তার I/O দরকার হয় (যেমন কীবোর্ড ইনপুট বা ডিস্ক রিড অপেক্ষা করা) — এটি একটি ইন্টারঅ্যাকটিভ/I/O-বাউন্ড আচরণের লক্ষণ। বিপরীতে, যদি একটি প্রসেস পুরো quantum জুড়ে একটানা CPU-তে গণনা চালিয়ে যায় (কোনো I/O অপেক্ষা ছাড়াই), এটি স্পষ্টভাবে বোঝায় এখনও তার আরও অনেক CPU-নির্ভর কাজ বাকি আছে — অর্থাৎ এটি একটি দীর্ঘ, CPU-নিবিড় প্রসেস।
প্র ০৩ উপরের কোড সেলে P1 কখনও ডিমোট হয়নি কিন্তু P2 ও P3 হয়েছে — সংখ্যা দিয়ে কেন তা নিশ্চিত করা যায়?
হাই কিউর quantum = ৪। P1-এর মোট বার্স্ট ৩, যা quantum-এর চেয়ে কম (৩ ≤ ৪) — তাই সে একবারেই শেষ হয়ে যায়, পুরো quantum ব্যবহারই করে না। কিন্তু P2-এর বার্স্ট ৬ ও P3-এর বার্স্ট ১০ — দুটোই quantum(৪)-এর চেয়ে বড়, তাই দুজনেই পুরো quantum শেষ করে ফেলে কিন্তু কাজ শেষ করতে পারে না, ফলে উভয়েই লো কিউতে ডিমোট হয়। কোড সেলে প্রিন্ট হওয়া "বাকি বার্স্ট" সংখ্যাগুলো (P2: ২, P3: ৬) এটিই নিশ্চিত করে।
অনুশীলন
-
চিন্তা করুন: যদি সবচেয়ে নিচের কিউতে কোনো time quantum-ই না থাকত (অর্থাৎ সেখানে প্রসেস FCFS-এর মতো সম্পূর্ণ চলার সুযোগ পেত), এটি কোন পুরনো সমস্যা আবার ফিরিয়ে আনতে পারে?
এটি L10-এর "convoy effect" ফিরিয়ে আনতে পারে — সবচেয়ে নিচের কিউতে যদি একটি অত্যন্ত দীর্ঘ প্রসেস প্রবেশ করে এবং সেখানে কোনো quantum সীমা না থাকে, সে একটানা CPU দখল করে রাখবে, যার ফলে একই কিউতে থাকা অন্য (হয়তো ছোট) প্রসেসগুলোকে দীর্ঘ সময় অপেক্ষা করতে হবে। বাস্তব MLFQ বাস্তবায়নে সাধারণত সবচেয়ে নিচের কিউতেও একটি (বড় হলেও) quantum রাখা হয়, ঠিক এই কারণেই।
-
পরীক্ষা করুন: উপরের কোড সেলে
high_quantum-কে 4 থেকে 6 করে Run চেপে দেখুন P2 (বার্স্ট 6) এখনও ডিমোট হয় কি না।high_quantum= 6 করলে P2-এর বার্স্ট (৬) এখন quantum-এর সমান বা কম (৬ ≤ ৬), তাই শর্তremaining <= high_quantumসত্য হয়ে যায় এবং P2 আর ডিমোট হবে না — সে হাই কিউতেই সম্পন্ন হবে। P3-এর বার্স্ট (১০) তখনও quantum-এর চেয়ে বড় থাকবে, তাই সে এখনও ডিমোট হবে। এটি দেখায় quantum-এর আকার সরাসরি নির্ধারণ করে কোন প্রসেসকে "CPU-বাউন্ড" হিসেবে গণ্য করা হবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ১১ · প্রায়োরিটি শিডিউলিং ও Round Robin পূর্ববর্তী পাঠ ফিডব্যাক কিউর ভিত্তি — Round Robin-এর quantum ও প্রায়োরিটি শিডিউলিংয়ের স্টারভেশন সমস্যা এখানে বিস্তারিত।
- পাঠ ১৩ · মাল্টিপ্রসেসর শিডিউলিং পরবর্তী পাঠ একক CPU-এর শিডিউলিং থেকে একাধিক কোরে ছড়িয়ে থাকা শিডিউলিং সিদ্ধান্তে যাওয়া হবে এই পাঠে।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ CPU শিডিউলিং থেকে শুরু করে মেমরি, ফাইল সিস্টেম ও ভার্চুয়ালাইজেশন পর্যন্ত সম্পূর্ণ কোর্স।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks ও Operating Systems — সব এক জায়গায়।