পাঠ ১১ · ৫৬-এর মধ্যে · মডিউল ৩
Home / Courses / Operating Systems (OS) / প্রায়োরিটি ও RR

প্রায়োরিটি শিডিউলিং ও Round Robin

Priority scheduling & Round Robin
৯ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

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

  • প্রায়োরিটি শিডিউলিং, স্টার্ভেশন সমস্যা এবং aging সমাধান
  • Round Robin অ্যালগরিদম এবং quantum size-এর trade-off
  • Python দিয়ে একটি real Round Robin সিমুলেটর — ready-queue rotation ও প্রতিটি প্রসেসের completion/waiting time
  • Python দিয়ে aging-সহ প্রায়োরিটি শিডিউলিং — একটি নিম্ন-প্রায়োরিটি প্রসেস কীভাবে ধীরে ধীরে "জিতে" যায়

১ · প্রায়োরিটি শিডিউলিং

প্রতিটি প্রসেসকে একটি প্রায়োরিটি নম্বর দেওয়া হয়; scheduler সবসময় READY প্রসেসগুলোর মধ্যে সর্বোচ্চ প্রায়োরিটিরটি চালায় (এই পাঠে আমরা সাধারণ কনভেনশন ব্যবহার করছি — কম সংখ্যা মানে বেশি প্রায়োরিটি)। এর একটি গুরুত্বপূর্ণ, বাস্তবে গুরুতর সমস্যা আছে — স্টার্ভেশন (Starvation)Starvationএকটি নিম্ন-প্রায়োরিটি প্রসেস কখনোই CPU না পাওয়ার ঝুঁকি, যদি উচ্চ-প্রায়োরিটি প্রসেস ক্রমাগত আসতে থাকে। — একটি নিম্ন-প্রায়োরিটি প্রসেস কখনোই না চলতে পারে যদি উচ্চ-প্রায়োরিটি প্রসেস আসতেই থাকে।

মানক সমাধান — AgingAgingএকটি অপেক্ষারত প্রসেসের প্রায়োরিটি সময়ের সাথে সাথে ধীরে ধীরে বাড়ানো, যাতে শেষ পর্যন্ত এটি চলার নিশ্চয়তা পায়। — একটি অপেক্ষারত প্রসেসের প্রায়োরিটি ক্রমশ বাড়াতে থাকা (আমাদের কনভেনশনে, effective priority-এর সংখ্যা কমাতে থাকা) যতক্ষণ না এটি অবশেষে সর্বোচ্চ প্রায়োরিটি পায় এবং চলার নিশ্চয়তা পায়।

২ · Round Robin

Round Robin প্রতিটি প্রসেসকে একটি নির্দিষ্ট ছোট time quantum দেয়; সেই quantum-এর মধ্যে প্রসেস শেষ না হলে এটি preempt হয়ে ready queue-এর একদম পেছনে গিয়ে বসে — বিশেষভাবে time-sharing/ইন্টারঅ্যাক্টিভ সিস্টেমের জন্য ডিজাইন করা, যাতে প্রতিটি প্রসেস নিয়মিত বিরতিতে CPU পায় (ন্যায্য, বাউন্ডেড response time)। এর মূল্য — কিছুটা context-switching overhead (L06)।

Quantum size-এর trade-off

Quantum খুব বড় হলে Round Robin কার্যত FCFS-এর মতো আচরণ করে (response time খারাপ হয়)। quantum খুব ছোট হলে context-switching overhead (প্রতিটি সুইচে পিসিবি save/load, L06) সামগ্রিক কাজের তুলনায় অনেক বড় হয়ে যায় (throughput খারাপ হয়)। ব্যবহারিক সিস্টেমে quantum এমনভাবে বেছে নেওয়া হয় যাতে বেশিরভাগ ইন্টারঅ্যাক্টিভ বার্স্ট একটি quantum-এর মধ্যেই শেষ হয়ে যায়, কিন্তু খুব বড় নয় যে responsiveness নষ্ট হয়।

৩ · Round Robin সিমুলেশন — real কোডে

নিচের কোড সেলে quantum=2 নিয়ে ৩টি প্রসেসের একটি real Round Robin শিডিউলার বাস্তবায়ন করা হয়েছে — একটি deque-ভিত্তিক ready queue প্রতি রাউন্ডে rotate হয়, এবং নতুন arrival-দের সঠিক মুহূর্তে queue-তে যোগ করা হয়।

Python
# Round Robin শিডিউলার -- সত্যিকারের অ্যালগরিদম, টয় প্রসেস ডেটার উপর
from collections import deque

processes = [
    # (নাম, arrival_time, burst_time)
    ("P1", 0, 5),
    ("P2", 1, 4),
    ("P3", 2, 2),
]
quantum = 2

def round_robin(processes, quantum):
    remaining = {name: burst for name, arrival, burst in processes}
    arrival_of = {name: arrival for name, arrival, burst in processes}
    order_by_arrival = sorted(processes, key=lambda p: p[1])

    time = 0
    queue = deque()
    schedule = []
    completion = {}
    i = 0

    def admit_new_arrivals(current_time):
        nonlocal i
        while i < len(order_by_arrival) and order_by_arrival[i][1] <= current_time:
            queue.append(order_by_arrival[i][0])
            i += 1

    admit_new_arrivals(time)
    if not queue:
        time = order_by_arrival[0][1]
        admit_new_arrivals(time)

    while queue:
        name = queue.popleft()
        start = max(time, arrival_of[name])
        run_time = min(quantum, remaining[name])
        end = start + run_time
        schedule.append((name, start, end))
        remaining[name] -= run_time
        time = end
        admit_new_arrivals(time)          # রান শেষে নতুন arrival যোগ, তারপরই বর্তমান প্রসেস (যদি বাকি থাকে) ফেরত
        if remaining[name] > 0:
            queue.append(name)
        else:
            completion[name] = time

    waiting = {}
    for name, arrival, burst in processes:
        turnaround = completion[name] - arrival
        waiting[name] = turnaround - burst
    return schedule, completion, waiting

schedule, completion, waiting = round_robin(processes, quantum)

print(f"--- Round Robin (quantum={quantum}) ---")
print("এক্সিকিউশন অর্ডার (নাম, শুরু, শেষ):")
for name, start, end in schedule:
    print(f"  {name}: [{start} -> {end}]")

print("\nপ্রতিটি প্রসেসের completion ও waiting time:")
for name, arrival, burst in processes:
    print(f"  {name}: completion={completion[name]}, waiting={waiting[name]}")

avg_wait = sum(waiting.values()) / len(waiting)
print(f"\nগড় waiting time = {avg_wait:.2f}")

    
লক্ষ্য করুন — P1 (burst ৫) প্রথম quantum-এ ২ একক চলে, তারপর P2 ও P3-কেও চলার সুযোগ দিয়ে আবার ফিরে আসে — এটিই Round Robin-এর মূল চরিত্র: কোনো প্রসেস একটানা পুরো burst শেষ করে না, বরং পালা করে ছোট ছোট অংশে চলে, যতক্ষণ না prosesটির পুরো burst শেষ হয়ে যায়।

৪ · প্রায়োরিটি + Aging সিমুলেশন — স্টার্ভেশন প্রতিরোধ

এবার একটি নিম্ন-প্রায়োরিটি প্রসেস C (প্রায়োরিটি সংখ্যা ৮, খারাপ) কল্পনা করুন, যখন প্রতি রাউন্ডে একটি নতুন উচ্চ-প্রায়োরিটি প্রসেস (প্রায়োরিটি সংখ্যা ৩) এসেই যাচ্ছে। Aging ছাড়া C কখনোই CPU পেতো না — প্রতিটি নতুন জব সবসময় এর চেয়ে ভালো প্রায়োরিটিতে থাকতো। aging প্রয়োগ করে দেখা যাক C-র effective priority ধীরে ধীরে কীভাবে বাড়ে (সংখ্যা কমে), এবং কখন এটি অবশেষে জেতে।

Python
# প্রায়োরিটি শিডিউলিং + aging -- কম সংখ্যা = বেশি প্রায়োরিটি
# প্রতি রাউন্ডে একটি নতুন উচ্চ-প্রায়োরিটি জব (priority 3) আসে -- aging ছাড়া C কখনোই CPU পেতো না
c_priority = 8
aging_step = 2
new_job_priority = 3
round_num = 0

while True:
    round_num += 1
    new_job = f"J{round_num}"
    ready = {new_job: new_job_priority, "C": c_priority}
    chosen = min(ready, key=lambda n: ready[n])
    print(f"রাউন্ড {round_num}: রেডি -> {ready}  |  নির্বাচিত: {chosen}")
    if chosen == "C":
        print(f"\n{round_num} রাউন্ড পর C-এর aged priority ({c_priority}) নতুন জবের priority "
              f"({new_job_priority})-এর চেয়ে ভালো হয়ে গেলো -> C অবশেষে CPU পেলো (স্টার্ভেশন প্রতিরোধ হলো)")
        break
    c_priority -= aging_step
    print(f"   C স্কিপড হলো -> aging প্রয়োগ, C-এর নতুন effective priority = {c_priority}")
    if round_num > 10:
        print("সেফটি লিমিট -- থামানো হলো")
        break

    
লক্ষ্য করুন — C-এর প্রায়োরিটি সংখ্যা ৮ থেকে ৬, ৪, তারপর ২-এ নেমে আসে (প্রতিবার aging_step=2 কমে), এবং যখন এটি নতুন জবের প্রায়োরিটি সংখ্যা (৩)-এর চেয়ে কম (অর্থাৎ ভালো) হয়ে যায়, C অবশেষে জিতে যায় — এমনকি ক্রমাগত নতুন উচ্চ-প্রায়োরিটি জব আসতে থাকা সত্ত্বেও।
মূল কথা · Key takeaway

প্রায়োরিটি শিডিউলিং সহজ কিন্তু aging ছাড়া starvation-এর ঝুঁকিতে থাকে। Round Robin এই সমস্যা ভিন্নভাবে এড়ায় — কোনো প্রায়োরিটি প্রয়োজন নেই, শুধু নিয়মিত ঘূর্ণন দিয়ে প্রতিটি প্রসেসকে বাউন্ডেড সময়ের মধ্যে CPU দেওয়ার নিশ্চয়তা দেয়। L12-এর মাল্টিলেভেল ফিডব্যাক কিউ এই দুটি ধারণাকে একত্রিত করে আরও নমনীয় একটি ডিজাইন তৈরি করে।

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

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

প্র ০১ উপরের Round Robin উদাহরণে P1-এর প্রথম রান ২ একক (quantum) দিয়ে শেষ হয়, পুরো burst (৫) দিয়ে নয় কেন?

Round Robin-এর মূল নিয়মই হলো কোনো প্রসেস তার পুরো burst একটানা চালাতে পারে না, quantum-এর বেশি হলে preempt হয়ে যায়। P1-এর burst (৫) quantum (২)-এর চেয়ে বড়, তাই এটি প্রথম রাউন্ডে মাত্র ২ একক চলে, বাকি ৩ একক নিয়ে queue-এর পেছনে গিয়ে বসে, এবং পরে আবার তার পালা এলে বাকিটা চালায়।

প্র ০২ যদি quantum-কে অনেক বড় করে দেওয়া হয় (যেমন ১০০), Round Robin আচরণে কী পরিবর্তন হবে?

যদি quantum প্রতিটি প্রসেসের burst time-এর চেয়েও বড় হয়ে যায়, প্রতিটি প্রসেস তার প্রথম রানেই সম্পূর্ণ শেষ হয়ে যাবে — কোনো preemption ঘটবে না, এবং ফলাফল কার্যত FCFS-এর সমান হয়ে যাবে (arrival অনুযায়ী ক্রম)। এটিই দেখায় quantum-এর আকার Round Robin-এর আচরণকে সরাসরি নিয়ন্ত্রণ করে।

প্র ০৩ Aging প্রয়োগ করার সময় aging_step (আমাদের উদাহরণে ২) খুব ছোট বা খুব বড় করলে কী সমস্যা হতে পারে?

aging_step খুব ছোট হলে C-এর প্রায়োরিটি বাড়তে অনেক রাউন্ড লাগবে — starvation দীর্ঘ সময় ধরে চলবে, aging কার্যত ধীর হবে। aging_step খুব বড় হলে C খুব দ্রুত (হয়তো ১-২ রাউন্ডেই) সর্বোচ্চ প্রায়োরিটি পেয়ে যেতে পারে, যা উল্টো দিকে অন্য (হয়তো সত্যিই জরুরি) উচ্চ-প্রায়োরিটি কাজকে বঞ্চিত করতে শুরু করবে — aging_step-ও তাই একটি বাস্তব trade-off, quantum size-এর মতোই।

অনুশীলন

  1. চিন্তা করুন: আপনার কম্পিউটারের অপারেটিং সিস্টেম কেন সাধারণত pure Round Robin-এর বদলে একটি মাল্টিলেভেল ফিডব্যাক কিউ (L12) ব্যবহার করে, শুধু Round Robin ব্যবহার না করে?

    Pure Round Robin সব প্রসেসকে সমানভাবে treat করে, তারা ইন্টারঅ্যাক্টিভ (দ্রুত রেসপন্স দরকার) হোক বা ব্যাচ/CPU-বাউন্ড (throughput দরকার) হোক। মাল্টিলেভেল ফিডব্যাক কিউ প্রসেসের প্রকৃত আচরণ পর্যবেক্ষণ করে (কে দ্রুত yield করছে বনাম কে পুরো quantum ব্যবহার করছে) এবং সেই অনুযায়ী প্রায়োরিটি/quantum সমন্বয় করে — বাস্তব ব্যবহারকারীর অভিজ্ঞতার জন্য এটি সাধারণত বেশি কার্যকর।

  2. পরীক্ষা করুন: উপরের Round Robin কোড সেলে quantum-কে ২ থেকে ১ করে দিন এবং দেখুন এক্সিকিউশন অর্ডারে কতগুলো (ছোট) ধাপ তৈরি হয় এবং গড় waiting time বদলায় কিনা।

    quantum=1 করলে প্রতিটি প্রসেস প্রতিবার মাত্র ১ একক করে চলবে, ফলে schedule তালিকায় অনেক বেশি (ছোট ছোট) এন্ট্রি দেখা যাবে — অর্থাৎ বাস্তবে অনেক বেশি context switch ঘটতো। গড় waiting time সামান্য বদলাতে পারে (কম quantum প্রায়ই responsiveness বাড়ায় কিন্তু overhead-ও বাড়ায়, যা এই সরলীকৃত সিমুলেশনে মডেল করা হয়নি)।

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

আগের পাঠ
FCFS ও SJF শিডিউলিং