পাঠ ৪৩ · ৫৬-এর মধ্যে · মডিউল ১০
Home / Courses / Operating Systems (OS) / ডিস্ক শিডিউলিং

ডিস্ক শিডিউলিং অ্যালগরিদম

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

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

  • কেন seek time ডিস্ক পারফরম্যান্সের সবচেয়ে বড় নিয়ন্ত্রক ফ্যাক্টর
  • FCFS, SSTF, SCAN ও C-SCAN অ্যালগরিদমের কাজের পদ্ধতি ও ট্রেড-অফ
  • বাস্তব কোড দিয়ে একই অনুরোধ সেটের উপর তিনটি অ্যালগরিদমের মোট হেড মুভমেন্ট গণনা ও তুলনা
  • SSTF-এর starvation ঝুঁকি ও SCAN কীভাবে সেটি এড়ায়

১ · Seek Time কেন গুরুত্বপূর্ণ

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

২ · FCFS ও SSTF

FCFS (First-Come, First-Served)
অনুরোধগুলো ঠিক যে ক্রমে এসেছে সেই ক্রমেই সার্ভিস করা হয় — সহজ ও ন্যায্য, কিন্তু হেডকে বারবার ডিস্কের এক প্রান্ত থেকে অন্য প্রান্তে ছোটাছুটি করাতে পারে, প্রচুর অপ্রয়োজনীয় মুভমেন্ট তৈরি করে।
SSTFShortest Seek Time Firstহেডের বর্তমান অবস্থান থেকে সবচেয়ে কাছের অনুরোধ সবার আগে সার্ভিস করা হয়। (Shortest Seek Time First)
হেডের বর্তমান অবস্থান থেকে সবচেয়ে কাছের অনুরোধ আগে সার্ভিস করা হয় — তাৎক্ষণিকভাবে কম মুভমেন্ট দেয়, কিন্তু কাছের অনুরোধ ক্রমাগত এলে দূরের অনুরোধ চিরকাল অপেক্ষা করতে পারে (starvation, M3-এর SJF-এর সাথে সরাসরি সাদৃশ্যপূর্ণ)।

৩ · SCAN ("এলিভেটর") ও C-SCAN

SCAN হেডকে একটি বিল্ডিং-এর এলিভেটরের মতো আচরণ করায় — হেড একটি দিকে চলতে থাকে, পথে পড়া সব অনুরোধ সার্ভিস করতে করতে ডিস্কের শেষ প্রান্তে পৌঁছায়, তারপর দিক উল্টে বিপরীত দিকে একই কাজ করে। এতে SSTF-এর starvation সমস্যা হয় না (প্রতিটি অনুরোধ হেডের পথে একবার না একবার পড়বেই), অথচ FCFS-এর মতো অপ্রয়োজনীয় ছোটাছুটিও এড়ানো যায়। C-SCAN (Circular SCAN) একটি বৈচিত্র্য — শেষ প্রান্তে পৌঁছে দিক না উল্টিয়ে হেড সরাসরি শুরুর প্রান্তে "লাফ" দিয়ে আবার একই দিকে ঝাড়ু দেয় — এতে ডিস্কের মাঝখানের ট্র্যাকগুলো প্রান্তের ট্র্যাকগুলোর তুলনায় অন্যায্যভাবে বেশি সুবিধা পায় না (SCAN-এ মাঝখানের ট্র্যাক গড়ে দুবার তাড়াতাড়ি সার্ভিস পায়, C-SCAN এই অসামঞ্জস্য দূর করে সব ট্র্যাকের জন্য অপেক্ষার সময় আরও সমান করে)।

M3-এর সাথে সরাসরি সমান্তরাল

FCFS/SSTF/SCAN আসলে M3-এর CPU শিডিউলিং সমস্যারই একটি ভিন্ন রূপ — এখানে "রিসোর্স" হলো ফিজিক্যাল ডিস্ক হেড, "কাজ" হলো ট্র্যাক নম্বরে পৌঁছানো, এবং "খরচ" হলো CPU টাইমের বদলে হেড মুভমেন্টের দূরত্ব। FCFS-এর convoy effect, SSTF-এর starvation, আর SCAN-এর ন্যায্যতা — সবগুলোই M3-এর ধারণার হুবহু প্রতিফলন, শুধু ভিন্ন প্রেক্ষাপটে।

ট্র্যাক ০ ট্র্যাক ১৯৯ 14 37 65 67 98 122 124 183 হেড = ৫৩
উদাহরণ: হেড অবস্থান ৫৩, পেন্ডিং অনুরোধ {14, 37, 65, 67, 98, 122, 124, 183} — নিচের কোড সেলে FCFS/SSTF/SCAN তিনটিই এই একই সেটের উপর চালানো হয়েছে।

নিচের কোডে FCFS, SSTF ও SCAN — তিনটি অ্যালগরিদমই বাস্তবে ইমপ্লিমেন্ট করা হয়েছে এবং একই অনুরোধ সেট ও হেড অবস্থানের উপর চালিয়ে মোট হেড মুভমেন্ট গণনা করা হয়েছে — কোনো ফলাফল হাতে ধরে লেখা হয়নি, প্রতিটি সংখ্যা কোড চালিয়েই বের করা।

Python
# FCFS, SSTF ও SCAN ডিস্ক শিডিউলিং -- একই অনুরোধ সেট ও হেড অবস্থানের উপর বাস্তব গণনা
# (toy in-memory ডেটা -- কোনো আসল ডিস্ক অ্যাক্সেস নেই)

requests = [98, 183, 37, 122, 14, 124, 65, 67]
head_start = 53
disk_min, disk_max = 0, 199


def fcfs_total_movement(requests, head):
    """আসার ক্রমেই সার্ভিস -- ক্রম পরিবর্তন হয় না।"""
    sequence = [head] + requests
    total = sum(abs(sequence[i] - sequence[i - 1]) for i in range(1, len(sequence)))
    return total, requests[:]


def sstf_total_movement(requests, head):
    """প্রতি ধাপে হেডের বর্তমান অবস্থান থেকে সবচেয়ে কাছের পেন্ডিং অনুরোধ বেছে নেওয়া হয়।"""
    remaining = requests[:]
    current = head
    order = []
    total = 0
    while remaining:
        closest = min(remaining, key=lambda track: abs(track - current))
        total += abs(closest - current)
        current = closest
        order.append(closest)
        remaining.remove(closest)
    return total, order


def scan_total_movement(requests, head, disk_max, disk_min=0, direction="up"):
    """হেড এক দিকে ঝাড়ু দিয়ে ডিস্কের শেষ প্রান্ত পর্যন্ত যায়, তারপর দিক পাল্টায়।"""
    current = head
    total = 0
    order = []
    if direction == "up":
        going_up = sorted(t for t in requests if t >= head)
        going_down = sorted((t for t in requests if t < head), reverse=True)
        path = going_up + [disk_max] + going_down
    else:
        going_down = sorted((t for t in requests if t <= head), reverse=True)
        going_up = sorted(t for t in requests if t > head)
        path = going_down + [disk_min] + going_up

    for track in path:
        total += abs(track - current)
        current = track
        if track not in (disk_max, disk_min):
            order.append(track)
    return total, order


fcfs_moves, fcfs_order = fcfs_total_movement(requests, head_start)
sstf_moves, sstf_order = sstf_total_movement(requests, head_start)
scan_moves, scan_order = scan_total_movement(requests, head_start, disk_max, disk_min, direction="up")

print(f"হেড শুরু অবস্থান: {head_start}, পেন্ডিং অনুরোধ: {requests}\n")
print(f"FCFS  -> সার্ভিস ক্রম: {fcfs_order}")
print(f"FCFS  -> মোট হেড মুভমেন্ট: {fcfs_moves}\n")
print(f"SSTF  -> সার্ভিস ক্রম: {sstf_order}")
print(f"SSTF  -> মোট হেড মুভমেন্ট: {sstf_moves}\n")
print(f"SCAN  -> সার্ভিস ক্রম (ডিস্ক-প্রান্ত ভ্রমণসহ): {scan_order}")
print(f"SCAN  -> মোট হেড মুভমেন্ট: {scan_moves}\n")

print("--- সারসংক্ষেপ ---")
print(f"FCFS মোট মুভমেন্ট: {fcfs_moves}")
print(f"SSTF মোট মুভমেন্ট: {sstf_moves} (FCFS-এর চেয়ে কম: {sstf_moves < fcfs_moves})")
print(f"SCAN মোট মুভমেন্ট: {scan_moves} (FCFS-এর চেয়ে কম: {scan_moves < fcfs_moves})")

    
এই নির্দিষ্ট উদাহরণে (হেড ৫৩, অনুরোধ {98, 183, 37, 122, 14, 124, 65, 67}, ডিস্ক ০-১৯৯) কোডটি প্রকৃতপক্ষে গণনা করে দেখায় — FCFS = ৬৪০, SSTF = ২৩৬, SCAN = ৩৩১ ট্র্যাক মুভমেন্ট। SSTF ও SCAN দুটোই FCFS-এর চেয়ে উল্লেখযোগ্যভাবে কম মুভমেন্ট দেয় — কারণ FCFS-কে ৯৮ থেকে ১৮৩-তে গিয়ে আবার ৩৭-এ ফিরে আসতে হয় (আসার ক্রম অনুযায়ী), যেখানে SSTF ও SCAN উভয়ই কাছাকাছি ট্র্যাকগুলো একসাথে সার্ভিস করে এই অপ্রয়োজনীয় ছোটাছুটি এড়ায়। SSTF এখানে SCAN-এর চেয়েও কম মুভমেন্ট দিচ্ছে (কারণ SCAN বাধ্যতামূলকভাবে ডিস্কের শেষ প্রান্ত ১৯৯ পর্যন্ত যায়, এমনকি সেখানে কোনো অনুরোধ না থাকলেও) — এটিই ঠিক SSTF-এর সুবিধা, তবে বাস্তব সিস্টেমে অনবরত নতুন কাছের অনুরোধ এলে SSTF দূরের অনুরোধকে (যেমন ট্র্যাক ১৮৩) অনির্দিষ্টকালের জন্য আটকে রাখতে পারত — এটিই SCAN-এর মূল সুবিধা।

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

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

প্র ০১ উপরের উদাহরণে SSTF-এর মোট মুভমেন্ট (২৩৬) SCAN-এর (৩৩১) চেয়ে কম হলো কেন — তাহলে SSTF কি সবসময় ভালো?

না। এই নির্দিষ্ট, একবারের অনুরোধ সেটে SSTF কম মুভমেন্ট দিয়েছে কারণ SCAN-কে বাধ্যতামূলকভাবে ডিস্কের শেষ প্রান্ত (১৯৯) পর্যন্ত ভ্রমণ করতে হয়েছে, যদিও সেখানে কোনো অনুরোধ ছিল না। কিন্তু বাস্তব সিস্টেমে অনুরোধ ক্রমাগত আসতে থাকে — যদি হেডের কাছাকাছি ট্র্যাকে নতুন অনুরোধ বারবার আসতে থাকে, SSTF দূরের ট্র্যাক ১৮৩-এর অনুরোধকে চিরকাল উপেক্ষা করতে পারে (starvation)। SCAN-এর সামান্য বেশি মুভমেন্ট আসলে ন্যায্যতার একটি গ্যারান্টিড খরচ — প্রতিটি অনুরোধ হেডের সুইপে একবার না একবার পড়বেই।

প্র ০২ C-SCAN কেন SCAN-এর চেয়ে "বেশি ন্যায্য" — মাঝখানের ট্র্যাকের সাথে প্রান্তের ট্র্যাকের ঠিক কী পার্থক্য হয়?

SCAN-এ হেড একবার উপরে ঝাড়ু দিয়ে আবার নিচে নামে — মাঝখানের একটি ট্র্যাক প্রতি "চক্রে" গড়ে দুবার হেডের কাছাকাছি পড়ে (একবার উপরে যাওয়ার পথে, একবার নিচে নামার পথে), কিন্তু প্রান্তের একটি ট্র্যাক শুধু একবারই পড়ে। ফলে মাঝখানের ট্র্যাকগুলোর গড় অপেক্ষার সময় প্রান্তের তুলনায় কম হয়। C-SCAN সবসময় একই দিকে ঝাড়ু দিয়ে শুরুতে ফিরে আসায় প্রতিটি ট্র্যাক ঠিক একবারই "পাস" পায় প্রতি চক্রে — সব ট্র্যাকের জন্য অপেক্ষার সময় বেশি অভিন্ন হয়।

প্র ০৩ SSTF ইমপ্লিমেন্টেশনে প্রতি ধাপে min(remaining, key=...) ব্যবহার করা হয়েছে — এর টাইম কমপ্লেক্সিটি নিয়ে কী বলা যায়?

প্রতি ধাপে remaining-এর সব এন্ট্রি স্ক্যান করে সবচেয়ে কাছেরটি খুঁজে বের করা হয় — n-টি অনুরোধের জন্য এটি O(n) সময় নেয়, এবং মোট n ধাপ চলে, তাই সম্পূর্ণ অ্যালগরিদম O(n²)। বাস্তব সিস্টেমে অনেক বেশি পেন্ডিং অনুরোধ থাকলে এই দ্বিঘাত খরচ উল্লেখযোগ্য হতে পারে — একটি সর্টেড ডেটা স্ট্রাকচার (যেমন একটি balanced tree) ব্যবহার করে এটি O(n log n)-এ নামিয়ে আনা সম্ভব, যদিও এই পাঠের সরল সিমুলেশনে স্পষ্টতার জন্য সরল স্ক্যান ব্যবহার করা হয়েছে।

অনুশীলন

  1. চিন্তা করুন: যদি হেড শুরুর অবস্থান ৫৩-এর বদলে সরাসরি ৯৮ (সবচেয়ে ছোট অনুরোধের একটির খুব কাছে) থেকে শুরু হতো, FCFS-এর মোট মুভমেন্টে কী পরিবর্তন হতো বলে মনে করেন?

    FCFS-এর মোট মুভমেন্ট সম্পূর্ণভাবে নির্ভর করে অনুরোধের আসার ক্রমের উপর, হেডের শুরুর অবস্থান শুধু প্রথম লাফের দূরত্ব পরিবর্তন করে — বাকি ক্রম (183, 37, 122, 14, 124, 65, 67 — এই ক্রমেই) অপরিবর্তিত থাকে, যা এখনও ডিস্কের এক প্রান্ত থেকে আরেক প্রান্তে বহুবার ছোটাছুটি করাবে। তাই মোট মুভমেন্ট কিছুটা পরিবর্তিত হলেও FCFS-এর মূল দুর্বলতা (অসংগঠিত ছোটাছুটি) থেকেই যাবে।

  2. পরীক্ষা করুন: উপরের কোড সেলে scan_total_movement(...)-এর direction প্যারামিটার "up" থেকে "down" করে Run চাপুন — মোট মুভমেন্ট বদলায় কি না দেখুন।

    দিক পরিবর্তন করলে হেড প্রথমে নিচের দিকে (৩৭, ১৪) গিয়ে ডিস্কের শুরুর প্রান্ত (০) স্পর্শ করবে, তারপর উপরে উঠে বাকি অনুরোধগুলো (৬৫, ৬৭, ৯৮, ১২২, ১২৪, ১৮৩) সার্ভিস করবে — মোট মুভমেন্টের সংখ্যাটি "up" দিকের থেকে ভিন্ন হবে, কারণ ডিস্কের কোন প্রান্ত পর্যন্ত ভ্রমণ করতে হচ্ছে তা বদলে যায়। এটি দেখায় SCAN-এর প্রাথমিক দিক নির্বাচনও মোট কর্মক্ষমতাকে প্রভাবিত করে।

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

আগের পাঠ
I/O হার্ডওয়্যার ও I/O সফটওয়্যার লেয়ার