সরাসরি পর্বে যান
পর্ব ৪০ · ৬৪-এর মধ্যে · মডিউল ৬

ডিস্ক আর SSD — গঠন আর disk scheduling

Disks and SSDs — HDD Structure, Access Time, Disk Scheduling (FCFS, SSTF, SCAN, C-SCAN, LOOK, C-LOOK), Flash, FTL and NVMe

Taught in Bangla, this lesson explains HDD structure from platters to LBA, access time with seek, rotational latency and transfer, disk scheduling with FCFS, SSTF, SCAN, C-SCAN, LOOK and C-LOOK and total head movement problems, NCQ and Linux I/O schedulers, SSD internals with NAND flash, FTL and garbage collection, NVMe speeds, and disk formatting, partitions, boot blocks and bad blocks.

এই পর্বে যা শিখবেন

  • হার্ড ডিস্কের গঠন — platter, track, sector, cylinder, head আর arm; zoned bit recording, 512 B বনাম 4 KiB sector, আর LBA
  • Access time-এর সূত্র আর পরীক্ষার ধাঁচের সংখ্যাভিত্তিক সমস্যা — rotational latency, transfer time, random বনাম sequential I/O
  • FCFS, SSTF, SCAN, C-SCAN, LOOK আর C-LOOK-এর পূর্ণ trace, কোন নিয়মে কোন উত্তর, SSTF-এর starvation আর একটি সর্বনিম্ন-সীমার প্রমাণ
  • বাস্তব সিস্টেম — NCQ, Linux-এর I/O scheduler, NVMe-তে কেন none
  • SSD-র ভেতর — NAND cell, page আর erase block, FTL, garbage collection, write amplification-এর হিসাব, wear leveling, TRIM; সঙ্গে formatting, partition, bad block আর DAS/NAS/SAN

১ · হার্ড ডিস্কের ভেতরে — platter থেকে LBA

পর্ব ৩৯-এ দেখেছেন, OS একটি ডিভাইসের সাথে কথা বলে device driver আর device controller-এর মাধ্যমে, আর বড় ডেটা সরায় DMA দিয়ে। এই পর্বে সেই ডিভাইসগুলোর মধ্যে সবচেয়ে গুরুত্বপূর্ণটি — স্থায়ী স্টোরেজ। আপনার ফাইল, ডেটাবেস, swap, এমনকি OS নিজেই থাকে এখানে। আর এখানেই একটি কম্পিউটারের সবচেয়ে বড় গতির ফারাক: RAM-এ একটি পড়া ১০০ ns-এর কাছাকাছি, ঘোরা ডিস্কে কয়েক ms — প্রায় এক লাখ গুণ বেশি।

একটি HDD খুললে দেখবেন এক বা একাধিক চকচকে গোল চাকতি, একটি অক্ষে গাঁথা — এগুলো platter. প্রতিটির দুই পিঠে চৌম্বক আবরণ, যেখানে বিট লেখা থাকে। Platter-গুলো একসাথে মিনিটে কয়েক হাজার পাক ঘোরে — ল্যাপটপে প্রায়ই 5400, ডেস্কটপে 7200, কিছু সার্ভার-ডিস্কে 10000 বা 15000 RPM. প্রতিটি পিঠের ওপর ভাসে একটি read-write head; সব head বসানো একটি arm-এর মাথায়, আর arm-গুলো একসাথে ভেতরে-বাইরে সরে। ঘোরার সময় head পিঠ থেকে মাত্র কয়েক ন্যানোমিটার উঁচুতে বাতাসের স্তরে ভাসে — তাই ধাক্কা লাগলে head পিঠে আঘাত করতে পারে (head crash).

        পাশ থেকে দেখা                     ওপর থেকে একটি platter
   arm ──┬── head 0 ▶ ═══ platter 0 (ওপর পিঠ)        .-~~~~~-.
         ├── head 1 ▶ ═══ platter 0 (নিচ পিঠ)      /  .---.  \   ← track (গোল রেখা)
         ├── head 2 ▶ ═══ platter 1 (ওপর পিঠ)     |  ( ● )  |  ● = অক্ষ (spindle)
         └── head 3 ▶ ═══ platter 1 (নিচ পিঠ)      \  '---'  /   track-এর একটি টুকরো
                                                    '-~~~~~-'      = sector
   cylinder = সব পিঠের একই ব্যাসার্ধের track-গুলো একসাথে
  • Track: একটি পিঠে একটি নির্দিষ্ট ব্যাসার্ধের গোল রেখা। একটি পিঠে লাখখানেকেরও বেশি track থাকতে পারে।
  • Sector: track-এর একটি টুকরো — পড়া-লেখার সবচেয়ে ছোট একক। প্রতিটি sector-এ ডেটার সাথে থাকে চেনার তথ্য আর ভুল ধরার ও শোধরানোর কোড (ECC).
  • Cylinder: সব পিঠের একই ব্যাসার্ধের track মিলে একটি কাল্পনিক চোঙ। Arm না সরিয়েই পুরো cylinder পড়া যায়। Disk scheduling-এর অঙ্কে “cylinder 0–199” বলতে এটাই বোঝায়।

একটি পুরোনো রেকর্ড-প্লেয়ারের কথা ভাবুন, কিন্তু অনেকগুলো রেকর্ড একটির ওপর আরেকটি সাজানো, আর প্রতিটির জন্য আলাদা সুঁই — সব সুঁই একটি দণ্ডে বাঁধা। কোনো গান শুনতে হলে আগে দণ্ডটি সঠিক দাগে সরাতে হয় (seek), তারপর রেকর্ড ঘুরে গানের শুরু সুঁইয়ের নিচে আসা পর্যন্ত অপেক্ষা (rotational latency), তারপর গান বাজা (transfer). প্রথম দুটি কাজই সময় খায়; আসল বাজানো প্রায়ই সবচেয়ে ছোট অংশ।

Zoned bit recording — বাইরের track-এ বেশি sector

বাইরের দিকের track ভেতরের track-এর চেয়ে লম্বা। পুরোনো ডিস্কে প্রতিটি track-এ সমান sector থাকত, ফলে বাইরের track-এ বিটগুলো অযথা ছড়ানো থাকত। আধুনিক ডিস্ক zoned bit recording ব্যবহার করে: track-গুলো কয়েকটি zone-এ ভাগ, আর বাইরের zone-এর প্রতিটি track-এ বেশি sector. Platter একই গতিতে ঘোরে, তাই এক পাকে বাইরের track-এর নিচ দিয়ে বেশি sector যায় — বাইরের দিকে transfer rate বেশি, প্রায়ই ভেতরের দিকের প্রায় দ্বিগুণের কাছাকাছি (ডিস্কভেদে আলাদা)। তাই একটি HDD-র শুরুর LBA-গুলো (বাইরের দিকে) শেষের দিকের চেয়ে দ্রুত পড়ে।

512 byte বনাম 4 KiB — Advanced Format

বহু দশক ধরে sector-এর আকার ছিল 512 byte. ডিস্ক বড় হওয়ায় এত ছোট sector-এ প্রতিটির সাথে আলাদা ফাঁক আর ECC-র খরচ বেশি পড়ছিল, তাই প্রায় ২০১০ সাল থেকে নির্মাতারা 4096 byte-এর physical sector চালু করেন — একে Advanced Format বলে। পুরোনো সফটওয়্যার যাতে না ভাঙে, অনেক ডিস্ক বাইরে 512 byte-এর logical sector দেখায় কিন্তু ভেতরে 4 KiB-তে লেখে (512e, “512 emulation”); কিছু ডিস্ক বাইরেও 4 KiB দেখায় (4Kn, “4K native”).

Alignment-এর ফাঁদ: 512e ডিস্কে একটি partition যদি 4 KiB-এর সীমানায় শুরু না হয় (যেমন পুরোনো নিয়মে LBA 63-এ), তবে file system-এর প্রতিটি 4 KiB block দুটি physical sector-এ ছড়িয়ে পড়ে। তখন একটি block লিখতে ডিস্ককে দুটি sector পড়ে, বদলে, আবার লিখতে হয় (read-modify-write) — লেখা লক্ষণীয়ভাবে ধীর হয়। আধুনিক partition টুল ডিফল্টে প্রথম partition 1 MiB (LBA 2048) থেকে শুরু করে, যা সব সাধারণ সীমানায় মেলে। Linux-এ lsblk -t logical আর physical sector-এর আকার দেখায়।

CHS থেকে LBA

পুরোনো দিনে OS-কে একটি sector চাইতে হতো তিনটি সংখ্যায়: cylinder, head, sector — CHS. কিন্তু zone-এ zone-এ sector-সংখ্যা বদলালে, আর ডিস্ক নিজেই খারাপ sector সরিয়ে রাখলে, এই তিন সংখ্যা আর আসল জ্যামিতির সাথে মেলে না। তাই আধুনিক ডিস্ক বাইরে দেখায় শুধু একটি ক্রমিক নম্বর — LBA 0, 1, 2, … শেষ পর্যন্ত। কোন LBA আসলে কোন platter-এর কোন track-এ, তা জানে কেবল ডিস্কের firmware.

তবু একটি মোটামুটি নিয়ম সত্য থাকে, আর disk scheduling-এর পুরো তত্ত্ব তার ওপর দাঁড়ানো: কাছাকাছি LBA সাধারণত কাছাকাছি physical জায়গায়, তাই OS LBA-র দূরত্বকে head-এর দূরত্বের আনুমানিক মাপ ধরে; অঙ্কের “cylinder নম্বর” সেই সরল মডেল।

১৯৫৬ সালে IBM বাজারে আনে প্রথম বাণিজ্যিক হার্ড ডিস্ক — IBM 350, যা RAMAC 305 কম্পিউটারের অংশ। তাতে ছিল ২৪ ইঞ্চির ৫০টি platter, আর ধারণক্ষমতা প্রায় ৫০ লাখ অক্ষর (আজকের হিসাবে মোটামুটি ৩.৭৫ MB). আকার ছিল প্রায় দুটি বড় আলমারির সমান। আজ একটি ৩.৫ ইঞ্চির ডিস্কে কয়েক কোটি গুণ বেশি ডেটা ধরে — অথচ seek আর ঘোরার মূল যান্ত্রিক নীতি প্রায় একই রয়ে গেছে। এজন্যই ঘোরা ডিস্কের গতি ধারণক্ষমতার তুলনায় সামান্যই বেড়েছে।

২ · Access time — seek, rotational latency আর transfer

একটি sector পড়তে ডিস্ককে তিনটি কাজ পরপর করতে হয়:

  1. Seek time: arm সরিয়ে head-কে ঠিক cylinder-এ নেওয়া, আর সেখানে স্থির হওয়া (settle). পাশের track-এ যেতে ১ ms-এর কম, পুরো ডিস্ক পেরোতে ১০ ms-এর বেশিও লাগতে পারে; ডেস্কটপ ডিস্কের গড় seek প্রায়ই কয়েক ms থেকে ১০ ms-এর আশপাশে বলা হয় (নির্মাতার datasheet দেখুন)।
  2. Rotational latency: চাওয়া sector ঘুরে head-এর নিচে আসা পর্যন্ত অপেক্ষা। সবচেয়ে ভালো ক্ষেত্রে ০, সবচেয়ে খারাপ ক্ষেত্রে পুরো এক পাক; এলোমেলো অবস্থানে গড়ে অর্ধেক পাক.
  3. Transfer time: sector-গুলো head-এর নিচ দিয়ে যাওয়ার সময় আসল পড়া। সময় = ডেটার আকার ÷ transfer rate.
Access time = seek time + rotational latency + transfer time
গড় rotational latency = ½ × (60 ÷ RPM) সেকেন্ড

উদাহরণ ১ (পরীক্ষার সবচেয়ে পরিচিত অঙ্ক): 7200 RPM ডিস্কের গড় rotational latency কত? এক মিনিটে ৭২০০ পাক, তাই এক পাক = ৬০,০০০ ms ÷ ৭২০০ = ৮.৩৩৩ ms. গড় latency = অর্ধেক পাক = ৪.১৬৭ ms (প্রায় ৪.১৭ ms).

উদাহরণ ২: একই ডিস্ক, গড় seek ৯ ms, transfer rate ১৫০ MB/s (১ MB = ১০৬ byte ধরে)। একটি এলোমেলো 4 KiB (৪০৯৬ byte) block পড়তে কত সময়? Transfer = ৪০৯৬ ÷ ১৫০,০০,০০,০০০ সেকেন্ড ≈ ০.০২৭ ms. মোট = ৯ + ৪.১৬৭ + ০.০২৭ ≈ ১৩.১৯৪ ms. অর্থাৎ সেকেন্ডে মোটামুটি ১,০০০ ÷ ১৩.১৯৪ ≈ ৭৬টি এলোমেলো পড়া — এই মাপকে বলে IOPS. আসল ডেটা পড়ায় গেল মাত্র ০.০২৭ ms.

উদাহরণ ৩ — random বনাম sequential: ১,০০০টি 4 KiB block পড়তে হবে। এলোমেলো জায়গায় থাকলে প্রতিটির জন্য আলাদা seek আর latency: ১,০০০ × ১৩.১৯৪ ≈ ১৩.২ সেকেন্ড। পাশাপাশি থাকলে একবার seek, একবার latency, তারপর একটানা ৪০,৯৬,০০০ byte transfer: ৯ + ৪.১৬৭ + ২৭.৩০৭ ≈ ৪০.৫ ms — প্রায় ৩২৬ গুণ দ্রুত. তাই file system ফাইলের block পাশাপাশি রাখতে চায় (পর্ব ৪৩), আর OS অনুরোধগুলো সাজিয়ে পাঠায়।

নিচের calculator এই তিনটি উদাহরণ নিজে হিসাব করে। সংখ্যাগুলো (seek, rate) বদলে নিজের বইয়ের অঙ্ক যাচাই করতে পারেন — তবে আগে হাতে করুন।

// HDD access time = seek + rotational latency + transfer (all in ms).
function hdd(rpm, seekMs, mbPerS, bytes) {
  const rotMs = 60000 / rpm;                    // one full revolution
  const latMs = rotMs / 2;                      // on average, wait half a revolution
  const xferMs = bytes / (mbPerS * 1e6) * 1000; // bytes / (bytes per ms)
  return { rotMs, latMs, xferMs, total: seekMs + latMs + xferMs };
}
const f = (x) => x.toFixed(3);
console.log("RPM     one rotation   avg rotational latency");
for (const rpm of [5400, 7200, 10000, 15000]) {
  const r = hdd(rpm, 0, 1, 0);
  console.log(String(rpm).padEnd(8) + (f(r.rotMs) + " ms").padEnd(15) + f(r.latMs) + " ms");
}
// Example drive: 7200 RPM, 9 ms average seek, 150 MB/s sustained transfer.
const SEEK = 9, RATE = 150, BLOCK = 4096, N = 1000;
const r = hdd(7200, SEEK, RATE, BLOCK);
console.log("\nOne random 4 KiB read: seek " + SEEK + " + latency " + f(r.latMs) +
  " + transfer " + f(r.xferMs) + " = " + f(r.total) + " ms");
console.log("=> about " + Math.round(1000 / r.total) + " random reads per second (IOPS)");
const random = N * r.total;                     // every block pays seek + latency
const seq = hdd(7200, SEEK, RATE, N * BLOCK).total; // one seek, then stream
console.log(N + " random 4 KiB reads: " + f(random) + " ms");
console.log(N + " blocks read sequentially: " + f(seq) + " ms");
console.log("sequential is " + (random / seq).toFixed(1) + "x faster");
console.log("effective rate, random: " + (N * BLOCK / random / 1000).toFixed(3) + " MB/s");

আউটপুট (নির্ধারিত, প্রতিবার একই):

RPM     one rotation   avg rotational latency
5400    11.111 ms      5.556 ms
7200    8.333 ms       4.167 ms
10000   6.000 ms       3.000 ms
15000   4.000 ms       2.000 ms

One random 4 KiB read: seek 9 + latency 4.167 + transfer 0.027 = 13.194 ms
=> about 76 random reads per second (IOPS)
1000 random 4 KiB reads: 13193.973 ms
1000 blocks read sequentially: 40.473 ms
sequential is 326.0x faster
effective rate, random: 0.310 MB/s

প্রথম টেবিলে প্রতিটি RPM-এর এক পাক আর গড় latency: 7200 RPM-এ ৮.৩৩৩ আর ৪.১৬৭ ms, 15000 RPM-এ ৪ আর ২ ms. দামি দ্রুত ডিস্কও latency অর্ধেকের বেশি কমাতে পারে না। তারপর একটি এলোমেলো 4 KiB পড়া: ১৩.১৯৪ ms, অর্থাৎ প্রায় ৭৬ IOPS. শেষ চার লাইনে random আর sequential-এর তুলনা — ১৩.২ সেকেন্ড বনাম ৪০.৫ ms, ৩২৬ গুণ ফারাক। “১৫০ MB/s-এর ডিস্ক” এলোমেলো 4 KiB পড়ায় বাস্তবে দেয় মাত্র ০.৩১০ MB/s. hdd() ফাংশনটি সূত্রটি হুবহু লেখা; sequential-এর বেলায় একই ফাংশনকে পুরো ৪০,৯৬,০০০ byte একবারে দেওয়া হয়েছে — একটি seek, একটি latency.

এককের সতর্কতা: পরীক্ষার প্রশ্নে transfer rate “MB/s” লেখা থাকলে ১ MB = ১০৬ byte না ২২০ byte, তা প্রশ্নেই দেখে নিন; ডিস্ক-নির্মাতারা সাধারণত দশমিক (১০৬) ব্যবহার করেন। আরেকটি পরিচিত রূপ: “প্রতি track-এ ৪০০টি 512-byte sector, 7200 RPM” — তখন একটি sector পড়ার সময় = এক পাক ÷ ৪০০ = ৮.৩৩৩ ÷ ৪০০ ≈ ০.০২০৮ ms, আর transfer rate = ৪০০ × ৫১২ byte ÷ ৮.৩৩৩ ms ≈ ২৪.৬ MB/s.

৩ · Disk scheduling — FCFS আর SSTF

একটি ব্যস্ত সার্ভারে একসাথে বহু প্রসেস ডিস্কে অনুরোধ পাঠায়। OS-এর কাছে তখন একটি অপেক্ষমাণ সারি — কোনটি আগে ডিস্কে পাঠাবে? Seek-ই সবচেয়ে বড় খরচ, তাই disk scheduling-এর লক্ষ্য: মোট head movement (কত cylinder পেরোল) কমানো, কিন্তু কোনো অনুরোধকে অনির্দিষ্টকাল বসিয়ে না রেখে। অর্থাৎ throughput বাড়ানো, আর গড় ও সবচেয়ে খারাপ response time সামলানো।

একটি বহুতল অফিসের লিফটের কথা ভাবুন। ১২ তলা, ৩ তলা, ৯ তলা, ১ তলা থেকে লোক ডাকছে। লিফট যদি ডাকার ক্রমে ছোটে (১২ → ৩ → ৯ → ১), সারাদিন ওঠানামাতেই যাবে। যদি সবসময় সবচেয়ে কাছের তলায় যায়, তবে মাঝের তলার লোকেরা খুশি, কিন্তু ছাদের কাছের কেউ হয়তো অনেকক্ষণ অপেক্ষা করবেন। আর একদিকে উঠতে উঠতে সবাইকে তুলে, তারপর নামতে নামতে নামালে — দক্ষও, ন্যায্যও। Disk scheduling-এর তিনটি মূল ধারণা ঠিক এই তিনটি।

এই পর্বের ক্লাসিক অঙ্ক (Silberschatz-এর পাঠ্যবইয়ের পরিচিত উদাহরণ): ডিস্কে cylinder 0 থেকে 199. Head এখন cylinder 53-এ। অপেক্ষমাণ অনুরোধ, আসার ক্রমে: 98, 183, 37, 122, 14, 124, 65, 67. প্রতিটি algorithm-এর জন্য বের করতে হবে কোন ক্রমে সেবা হবে আর মোট head movement কত cylinder.

FCFS — যে আগে এসেছে

FCFS (first-come, first-served) অনুরোধগুলো আসার ক্রমেই পাঠায়। ন্যায্য, সহজ, কোনো starvation নেই — কিন্তু head এদিক-ওদিক ছোটে:

53 → 98 → 183 → 37 → 122 → 14 → 124 → 65 → 67
   45 + 85 + 146 + 85 + 108 + 110 + 59 + 2 = 640 cylinder

দেখুন 122 থেকে 14, তারপর 14 থেকে 124 — প্রায় পুরো ডিস্ক দুবার পেরোনো, অথচ 122 আর 124 পাশাপাশি। 37 আর 14-র কাছে যাওয়ার সময় যদি একসাথে সেরে নেওয়া যেত, অনেক বাঁচত।

SSTF — সবচেয়ে কাছেরটি আগে

SSTF (shortest seek time first) প্রতিবার head-এর বর্তমান অবস্থান থেকে সবচেয়ে কাছের অনুরোধটি বাছে। এটি CPU scheduling-এর SJF-এর মতো একটি লোভী (greedy) কৌশল। নিচের stepper প্রতিটি ধাপে বাকি অনুরোধগুলোর দূরত্ব দেখায় — কেন ওই অনুরোধ জিতল, তা সরাসরি চোখে পড়বে।

1 start  head = 532 serve 653 serve 674 serve 375 serve 146 serve 987 serve 1228 serve 1249 serve 183
ধাপের শেষে অবস্থা
head
এই ধাপের সরা
মোট movement
অপেক্ষমাণ
  1. শুরু। Head 53-এ, আটটি অনুরোধ অপেক্ষায়। দূরত্বের তালিকায় সবচেয়ে ছোট 65 (১২ cylinder), তারপর 67 (১৪) আর 37 (১৬)।
  2. 53 → 65, সরল ১২, মোট ১২। এখন সবচেয়ে কাছে 67 — মাত্র ২ cylinder দূরে।
  3. 65 → 67, সরল ২, মোট ১৪। পরের প্রতিযোগিতা খুব কাছাকাছি: 37 আছে ৩০ দূরে, 98 আছে ৩১ দূরে। মাত্র ১ cylinder-এর ব্যবধানে 37 জেতে — আর এই সিদ্ধান্তই head-কে নিচের দিকে টেনে নেয়।
  4. 67 → 37, সরল ৩০, মোট ৪৪। এবার 14 সবচেয়ে কাছে (২৩)।
  5. 37 → 14, সরল ২৩, মোট ৬৭। নিচে আর কেউ নেই; বাকি সব অনুরোধ ওপরে, সবচেয়ে কাছে 98 (৮৪)।
  6. 14 → 98, সরল ৮৪, মোট ১৫১। পুরো trace-এর সবচেয়ে বড় লাফ — নিচে নামার দাম এখন দিতে হলো।
  7. 98 → 122, সরল ২৪, মোট ১৭৫।
  8. 122 → 124, সরল ২, মোট ১৭৭।
  9. 124 → 183, সরল ৫৯, মোট ২৩৬। সারি খালি। SSTF-এর মোট head movement ২৩৬ cylinder — FCFS-এর ৬৪০-এর তিন ভাগের এক ভাগের কিছু বেশি।

SSTF সর্বোত্তম নয় — একটি ছোট প্রমাণ

২৩৬ কি সম্ভাব্য সবচেয়ে কম? না। একটি সহজ যুক্তি দিয়ে এই সারির সর্বনিম্ন সীমা বের করা যায়। সবচেয়ে নিচের অনুরোধ 14, সবচেয়ে ওপরেরটি 183, আর head শুরু করে 53 থেকে। যেকোনো ক্রমেই head-কে 14 আর 183 দুটিই ছুঁতে হবে, তাই [14, 183] পুরো অংশটি অন্তত একবার পেরোতে হবে — ১৬৯ cylinder. তার ওপর 53 থেকে প্রথমে যেদিকের প্রান্তে যাওয়া হবে, সেখান থেকে ফিরে আসার পথের কিছু অংশ দুবার পেরোতে হবে: আগে নিচে গেলে ৫৩ − ১৪ = ৩৯, আগে ওপরে গেলে ১৮৩ − ৫৩ = ১৩০. তাই যেকোনো ক্রমের মোট ≥ ১৬৯ + min(৩৯, ১৩০) = ২০৮. আর “আগে নিচে সবগুলো, তারপর ওপরে সবগুলো” ক্রমটি ঠিক ২০৮ দেয় — অর্থাৎ এই সারির সর্বনিম্ন ২০৮, আর সেটি পরের অংশের LOOK (0-এর দিকে) পায়। SSTF ধাপ ৩-এ লোভী সিদ্ধান্তে নিচে গিয়েছিল, কিন্তু 67-এর কাছ থেকে নামায় ফেরার পথ লম্বা হয়েছে। লোভী কৌশল প্রতিটি ধাপে সেরা, পুরো পথে নয়।

SSTF-এর starvation

বড় সমস্যাটি অবশ্য সংখ্যায় নয়, ন্যায্যতায়। বাস্তবে অনুরোধ একবারে আসে না, ক্রমাগত আসে। ধরুন head 50-এর আশপাশে, আর একটি ডেটাবেস 40 থেকে 60-এর মধ্যে একটানা অনুরোধ পাঠাচ্ছে। এর মধ্যে cylinder 190-এ একটি backup প্রোগ্রামের অনুরোধ এলো। SSTF প্রতিবার কাছের নতুন অনুরোধটিই বাছবে — 190-এর অনুরোধ যতক্ষণ কাছে নতুন অনুরোধ আসছে, ততক্ষণ সেবা পাবে না। এটি starvation — CPU scheduling-এর SJF-এর মতোই। ফল: দীর্ঘ tail latency. প্রতিকার দুই রকম: অপেক্ষার সময় অনুযায়ী অগ্রাধিকার বাড়ানো (aging, বা Linux-এর mq-deadline-এর মতো প্রতিটি অনুরোধে একটি সময়সীমা), অথবা head-কে একদিকে চলতে বাধ্য করা — পরের অংশের SCAN পরিবার।

৪ · SCAN, C-SCAN, LOOK আর C-LOOK — লিফটের নিয়ম

SCAN algorithm-এ head একদিকে চলতে চলতে পথের সব অনুরোধ সেরে ডিস্কের প্রান্ত পর্যন্ত যায়, তারপর দিক উল্টে ফেরার পথে বাকিগুলো সারে। লিফটের সাথে মিলের জন্য একে elevator algorithm-ও বলে। কোনো অনুরোধ সর্বোচ্চ প্রায় দুটি পূর্ণ যাত্রার বেশি অপেক্ষা করে না — starvation নেই।

উত্তর নির্ভর করে নিয়মের ওপর — আগে লিখে নিন:

  • দিক: head এখন কোন দিকে চলছে? প্রশ্নে না থাকলে নিজের ধরে নেওয়া দিকটি উত্তরে স্পষ্ট লিখুন। Silberschatz-এর SCAN উদাহরণে head 0-এর দিকে চলে; তার C-SCAN উদাহরণে 199-এর দিকে।
  • প্রান্ত ছোঁয়া: SCAN আর C-SCAN প্রান্ত (0 বা 199) পর্যন্ত যায়, সেখানে অনুরোধ না থাকলেও; LOOK আর C-LOOK শেষ অনুরোধেই ঘোরে।
  • ফেরার লাফ: C-SCAN আর C-LOOK এক প্রান্ত থেকে আরেক প্রান্তে ফেরার সময় কোনো অনুরোধ সারে না। এই লাফ মোট movement-এ গোনা হবে কি না, বই ও প্রশ্নভেদে আলাদা — দুটি সংখ্যাই জানুন, আর কোনটি দিচ্ছেন তা লিখুন।
  • সমান দূরত্ব (SSTF): দুটি অনুরোধ head থেকে সমান দূরে হলে এই পর্বের simulator ছোট cylinder নম্বরটি আগে নেয়; প্রশ্নে অন্য নিয়ম (যেমন চলার দিকেরটি আগে) দেওয়া থাকলে সেটিই মানুন, আর উত্তরে লিখুন। ক্লাসিক সারিতে অবশ্য কোনো সমতা আসে না।

চারটির পূর্ণ trace (ক্লাসিক queue, head 53)

Algorithm ও নিয়মসেবার ক্রম আর প্রতিটি ধাপের সরামোট
SCAN, 0-এর দিকে53 → 37 → 14 → 0 → 65 → 67 → 98 → 122 → 124 → 183
16 + 23 + 14 + 65 + 2 + 31 + 24 + 2 + 59
২৩৬
LOOK, 0-এর দিকে53 → 37 → 14 → 65 → 67 → 98 → 122 → 124 → 183
16 + 23 + 51 + 2 + 31 + 24 + 2 + 59
২০৮
C-SCAN, 199-এর দিকে53 → 65 → 67 → 98 → 122 → 124 → 183 → 199 ⇢ 0 → 14 → 37
12 + 2 + 31 + 24 + 2 + 59 + 16 + [199 ফেরা] + 14 + 23
৩৮২ (ফেরা ধরে)
১৮৩ (ফেরা বাদে)
C-LOOK, 199-এর দিকে53 → 65 → 67 → 98 → 122 → 124 → 183 ⇢ 14 → 37
12 + 2 + 31 + 24 + 2 + 59 + [169 ফেরা] + 23
৩২২ (ফেরা ধরে)
১৫৩ (ফেরা বাদে)

SCAN-এর হিসাবের একটি শর্টকাট: 0-এর দিকে গেলে মোট = (৫৩ − ০) + (১৮৩ − ০) = ৫৩ + ১৮৩ = ২৩৬ — নিচে প্রান্ত পর্যন্ত, তারপর প্রান্ত থেকে সবচেয়ে ওপরের অনুরোধ পর্যন্ত। LOOK একই পথ, কিন্তু 14-এ ঘোরে: (৫৩ − ১৪) + (১৮৩ − ১৪) = ৩৯ + ১৬৯ = ২০৮ — অংশ ৩-এর সর্বনিম্ন সীমা। C-SCAN (ওপরে): (১৯৯ − ৫৩) + ১৯৯ + ৩৭ = ১৪৬ + ১৯৯ + ৩৭ = ৩৮২; লাফ বাদে ১৪৬ + ৩৭ = ১৮৩.

SCAN বনাম C-SCAN — head-এর পথ পাশাপাশি

তুলনা ন্যায্য রাখতে নিচের stepper-এ দুটিই শুরুতে ওপরে (199-এর দিকে) চলছে। দুই রেখায় H হলো head, o হলো এখনো অপেক্ষমাণ অনুরোধ (৪০ ঘরের রেখায় আনুমানিক অবস্থান, তাই 65/67 আর 122/124 এক ঘরে পড়ে)।

ধাপের শেষে head আর মোট movement
SCAN
C-SCAN
  1. শুরু। Head 53-এ, দুটিই ওপরের দিকে চলছে। নিচে অপেক্ষায় 14 আর 37; ওপরে 65, 67, 98, 122, 124, 183.
  2. 65-এ (+১২), মোট ১২। দুই algorithm-এর পথ এখনো হুবহু এক।
  3. 67-এ (+২), মোট ১৪।
  4. 98-এ (+৩১), মোট ৪৫।
  5. 122-এ (+২৪), মোট ৬৯।
  6. 124-এ (+২), মোট ৭১।
  7. 183-এ (+৫৯), মোট ১৩০। ওপরের শেষ অনুরোধ সারা হলো। LOOK বা C-LOOK হলে এখানেই ঘুরত।
  8. 199-এ (+১৬), মোট ১৪৬। 199-এ কোনো অনুরোধ নেই, তবু SCAN আর C-SCAN প্রান্ত পর্যন্ত যায় — এটিই তাদের সংজ্ঞা।
  9. পথ আলাদা হলো। SCAN দিক উল্টে নামতে নামতে প্রথমে 37 সারে (+১৬২, মোট ৩০৮)। C-SCAN কিছু না সেরে সোজা 0-এ ফেরে (১৯৯ cylinder, ধরলে মোট ৩৪৫) — আবার একই দিকে, নিচ থেকে ওপরে চলার জন্য।
  10. 14. SCAN 37 থেকে 14-এ (+২৩), মোট ৩৩১ — SCAN-এর কাজ শেষ। C-SCAN 0 থেকে 14-এ (+১৪), মোট ৩৫৯.
  11. C-SCAN শেষ করে। 14 থেকে 37 (+২৩): মোট ৩৮২, ফেরার লাফ বাদে ১৮৩. SCAN (ওপরের দিকে) ৩৩১. এই স্থির সারিতে C-SCAN বেশি পথ চলল — তার লাভ অন্য জায়গায়, নিচে দেখুন।

তাহলে C-SCAN কেন?

বাস্তবে অনুরোধ ক্রমাগত আসে, আর ডিস্কজুড়ে মোটামুটি সমানভাবে ছড়ানো থাকে। SCAN একটি প্রান্তে পৌঁছে দিক উল্টালে তার ঠিক পেছনের অংশ মাত্রই সেরে এসেছে — সেখানে নতুন অনুরোধ কম জমেছে। সবচেয়ে বেশি অনুরোধ জমে আছে অন্য প্রান্তে, যারা সবচেয়ে দীর্ঘ সময় অপেক্ষা করছে। C-SCAN (circular SCAN) ডিস্ককে একটি বৃত্তের মতো দেখে: সবসময় একদিকে সেবা, শেষে দ্রুত ফেরা। প্রতিটি cylinder প্রতি চক্রে ঠিক একবার সুযোগ পায় — অপেক্ষার সময় বেশি সমান, বিনিময়ে মোট পথ কিছুটা বেশি।

LOOK আর C-LOOK একই ধারণার ব্যবহারিক রূপ — শেষ অনুরোধ পেরিয়ে খালি প্রান্ত পর্যন্ত যাওয়ার কোনো কারণ নেই। বাস্তবে “SCAN” বা “elevator” বলে যা চালানো হয়, তা প্রায় সবসময় LOOK-এর মতো।

সব নিয়মে সব সংখ্যা — simulator

নিচের simulator ছয়টি algorithm চালায়। DIR ঠিক করে SCAN-পরিবার প্রথমে কোন দিকে যাবে, COUNT_RETURN ঠিক করে C-SCAN/C-LOOK-এর ফেরার লাফ গোনা হবে কি না। প্রথমে নির্বাচিত নিয়মে পুরো trace ছাপে, তারপর চার রকম নিয়মে সব মোটের একটি টেবিল।

// Disk-scheduling simulator: FCFS, SSTF, SCAN, C-SCAN, LOOK, C-LOOK.
const queue = [98, 183, 37, 122, 14, 124, 65, 67];
const head = 53, MAX = 199;              // cylinders 0..199
const DIR = "down";                      // "down" = move toward 0 first, "up" = toward 199
const COUNT_RETURN = true;               // count the C-SCAN / C-LOOK return jump?
const ALGS = ["FCFS", "SSTF", "SCAN", "C-SCAN", "LOOK", "C-LOOK"];
function order(alg, dir) {
  const lo = queue.filter(c => c < head).sort((a, b) => b - a);   // nearest first
  const hi = queue.filter(c => c >= head).sort((a, b) => a - b);
  const [first, second] = dir === "down" ? [lo, hi] : [hi, lo];
  const edge = dir === "down" ? 0 : MAX, far = MAX - edge;
  if (alg === "FCFS") return [...queue];
  if (alg === "SSTF") { const left = [...queue], out = []; let h = head;
    while (left.length) {                // nearest first; a tie goes to the smaller cylinder
      left.sort((a, b) => Math.abs(a - h) - Math.abs(b - h) || a - b);
      h = left.shift(); out.push(h); }
    return out; }
  if (alg === "SCAN") return [...first, edge, ...second];
  if (alg === "LOOK") return [...first, ...second];
  if (alg === "C-SCAN") return [...first, edge, "jump", far, ...second.reverse()];
  return [...first, "jump", ...second.reverse()];                 // C-LOOK
}
function run(alg, dir, countReturn, show) {
  let h = head, total = 0, jump = false; const parts = [], seq = [];
  for (const c of order(alg, dir)) {
    if (c === "jump") { jump = true; continue; }
    const d = Math.abs(c - h);
    if (jump && !countReturn) parts.push("[" + d + " not counted]");
    else { total += d; parts.push(jump ? "[" + d + " return]" : d); }
    jump = false; h = c; seq.push(c);
  }
  if (show) console.log(alg.padEnd(6) + " order: " + seq.join(" ") +
    "\n       moves: " + parts.join(" + ") + "\n       total = " + total);
  return total;
}
for (const a of ALGS) run(a, DIR, COUNT_RETURN, true);
console.log("\nTotals  up+return  up,no-return  down+return  down,no-return");
for (const a of ALGS) console.log(a.padEnd(7) + [["up", true], ["up", false], ["down", true],
  ["down", false]].map(([d, r]) => String(run(a, d, r, false)).padStart(10)).join("    "));

আউটপুট (নির্ধারিত, প্রতিবার একই):

FCFS   order: 98 183 37 122 14 124 65 67
       moves: 45 + 85 + 146 + 85 + 108 + 110 + 59 + 2
       total = 640
SSTF   order: 65 67 37 14 98 122 124 183
       moves: 12 + 2 + 30 + 23 + 84 + 24 + 2 + 59
       total = 236
SCAN   order: 37 14 0 65 67 98 122 124 183
       moves: 16 + 23 + 14 + 65 + 2 + 31 + 24 + 2 + 59
       total = 236
C-SCAN order: 37 14 0 199 183 124 122 98 67 65
       moves: 16 + 23 + 14 + [199 return] + 16 + 59 + 2 + 24 + 31 + 2
       total = 386
LOOK   order: 37 14 65 67 98 122 124 183
       moves: 16 + 23 + 51 + 2 + 31 + 24 + 2 + 59
       total = 208
C-LOOK order: 37 14 183 124 122 98 67 65
       moves: 16 + 23 + [169 return] + 59 + 2 + 24 + 31 + 2
       total = 326

Totals  up+return  up,no-return  down+return  down,no-return
FCFS          640           640           640           640
SSTF          236           236           236           236
SCAN          331           331           236           236
C-SCAN        382           183           386           187
LOOK          299           299           208           208
C-LOOK        322           153           326           157

প্রথম অংশ DIR = "down"-এ পুরো trace: FCFS ৬৪০, SSTF ২৩৬, SCAN ২৩৬, LOOK ২০৮ — উপরের টেবিলের সাথে হুবহু এক। 0-এর দিকে C-SCAN 0 থেকে 199-এ লাফ দিয়ে নামতে নামতে সারে, তাই ৩৮৬ (ফেরা বাদে ১৮৭); পাঠ্যবইয়ের ৩৮২ পেতে DIR = "up" লাগে — শেষ টেবিলের প্রথম কলাম। টেবিলটিই এই পর্বের “উত্তরপত্র”: FCFS আর SSTF দিকের ওপর নির্ভর করে না; SCAN ৩৩১ বা ২৩৬; LOOK ২৯৯ বা ২০৮; C-SCAN ৩৮২/১৮৩ (ওপরে) বা ৩৮৬/১৮৭ (নিচে); C-LOOK ৩২২/১৫৩ বা ৩২৬/১৫৭. order() শুধু সেবার ক্রম বানায়; "jump" চিহ্ন ফেরার লাফ বোঝায়, আর run() সেখানে countReturn দেখে দূরত্ব যোগ করে বা বাদ দেয়।

তুলনা এক নজরে

Algorithmক্লাসিক queue-তেসুবিধাঅসুবিধা
FCFS৬৪০সহজ, ন্যায্য, starvation নেইসবচেয়ে বেশি seek; হালকা লোডেই চলে
SSTF২৩৬গড় seek অনেক কমstarvation; সর্বোত্তমও নয়; মাঝের cylinder সুবিধা পায়
SCAN২৩৬ (নিচে) / ৩৩১ (ওপরে)starvation নেই, অপেক্ষার সীমা আছেঅকারণে প্রান্ত ছোঁয়; প্রান্তের অনুরোধের অপেক্ষা বেশি
C-SCAN৩৮২ বা ১৮৩ (ওপরে)সবচেয়ে সমান অপেক্ষাফেরার লাফ; মোট পথ বেশি
LOOK২০৮ (নিচে) / ২৯৯ (ওপরে)SCAN-এর চেয়ে কম পথপ্রান্তের অসমতা থেকেই যায়
C-LOOK৩২২ বা ১৫৩ (ওপরে)সমান অপেক্ষা, খালি প্রান্ত বাদফেরার লাফ থাকে

লোড কম হলে (সারিতে ০–১টি অনুরোধ) সব algorithm একই; ভারী লোডে LOOK/C-LOOK ভালো ভারসাম্য দেয়।

৫ · বাস্তব সিস্টেমে — NCQ, Linux-এর I/O scheduler আর NVMe

ডিস্ক নিজেই সাজায় — NCQ

OS ডিস্কের আসল জ্যামিতি জানে না, আর platter এই মুহূর্তে কোন কোণে আছে, তা তো নয়ই। ডিস্কের firmware দুটিই জানে। তাই SATA ডিস্কে NCQ (Native Command Queuing) আছে: OS একসাথে সর্বোচ্চ ৩২টি অনুরোধ ডিস্কে পাঠাতে পারে, আর ডিস্ক নিজে ঠিক করে কোনটি আগে — seek আর rotational latency দুটিই হিসাব করে। SCSI/SAS ডিস্কে একই ধারণা আগে থেকেই ছিল (tagged command queuing). ফলে আধুনিক সিস্টেমে disk scheduling দুই স্তরে হয়: OS মোটামুটি সাজায় আর ন্যায্যতা দেখে, ডিস্ক শেষ মুহূর্তের সূক্ষ্ম সাজানোটা করে।

Linux-এর I/O scheduler

Linux-এর block layer (blk-mq, multi-queue) প্রতিটি ডিভাইসের জন্য একটি I/O scheduler বেছে নিতে দেয়। Linux 5.0 (২০১৯) থেকে পুরোনো single-queue পথ আর তার CFQ, deadline, noop scheduler সরিয়ে দেওয়া হয়েছে; এখন বিকল্প চারটি:

none

কোনো সাজানো নেই, শুধু পাশাপাশি অনুরোধ জোড়া লাগানো (merge). দ্রুত SSD আর NVMe-র জন্য, যেখানে seek নেই আর ডিভাইস নিজে সমান্তরালে কাজ করে।

NVMe-তে সাধারণত ডিফল্ট

mq-deadline

অনুরোধ sector অনুযায়ী সাজানো (elevator-এর মতো), কিন্তু প্রতিটির একটি সময়সীমা — ডিফল্টে পড়া ৫০০ ms, লেখা ৫ সেকেন্ড। সীমা পেরোলে আগে সেবা — SSTF/SCAN-এর starvation-এর প্রতিকার।

SATA ডিস্কে প্রায়ই ডিফল্ট

bfq

Budget Fair Queueing: প্রতিটি প্রসেসকে ডিস্ক-সময়ের ন্যায্য ভাগ দেয়, interactive কাজকে অগ্রাধিকার — বড় কপি চলার সময়ও ডেস্কটপ সাড়া দেয়। CPU খরচ বেশি।

ডেস্কটপ, ধীর ডিস্ক

kyber

হালকা, token-ভিত্তিক: পড়া আর synchronous লেখার জন্য একটি লক্ষ্য latency রাখে, আর ডিভাইসে কতগুলো অনুরোধ একসাথে যাবে তা মানিয়ে নেয়। দ্রুত ডিভাইসের জন্য নকশা।

দ্রুত SSD

BFQ (Paolo Valente ও সহকর্মীরা) আর Kyber (Facebook-এর Omar Sandoval) দুটিই Linux 4.12-এ (২০১৭) kernel-এ আসে। কোন ডিভাইসে কোনটি ডিফল্ট, তা kernel version আর distro-র udev নিয়মের ওপর নির্ভর করে — নিজের মেশিনে দেখে নিন। নিচের bash প্রোগ্রাম ডিস্কগুলোর তালিকা, ঘোরা ডিস্ক কি না (ROTA = 1 মানে HDD), আর বর্তমান scheduler দেখায়। Container-এ চালালে ডিস্কের তথ্য লুকানো থাকতে পারে — প্রোগ্রাম সেটিও সামলায়।

echo "== lsblk =="
if command -v lsblk >/dev/null 2>&1; then
  lsblk -o NAME,ROTA,SIZE,SCHED 2>/dev/null | head -n 12 || echo "lsblk: no output here"
else
  echo "lsblk not installed"
fi
echo "== /sys/block =="
found=0
for d in /sys/block/*; do
  [ -e "$d" ] || continue
  found=1
  n=$(basename "$d")
  rot=$(cat "$d/queue/rotational" 2>/dev/null || echo "?")
  sch=$(cat "$d/queue/scheduler" 2>/dev/null || echo "?")
  echo "$n rotational=$rot scheduler=$sch"
done | head -n 12
[ -d /sys/block ] && [ -n "$(ls /sys/block 2>/dev/null)" ] || echo "/sys/block is empty or hidden here"

একটি আসল রানের আউটপুট (container ও সময়ভেদে আলাদা হতে পারে):

== lsblk ==
== /sys/block ==
loop0 rotational=1 scheduler=[none] mq-deadline
loop1 rotational=1 scheduler=[none] mq-deadline
loop2 rotational=1 scheduler=[none] mq-deadline
loop3 rotational=1 scheduler=[none] mq-deadline
loop4 rotational=1 scheduler=[none] mq-deadline
loop5 rotational=1 scheduler=[none] mq-deadline
loop6 rotational=1 scheduler=[none] mq-deadline
loop7 rotational=1 scheduler=[none] mq-deadline
loop8 rotational=1 scheduler=[none] mq-deadline
loop9 rotational=1 scheduler=[none] mq-deadline
sr0 rotational=1 scheduler=none [mq-deadline]
vda rotational=1 scheduler=[none] mq-deadline

এই container-এ lsblk কিছুই ছাপেনি — ডিভাইসের তালিকা তার কাছে লুকানো; তাই একই তথ্য সরাসরি /sys/block থেকে পড়া হয়েছে। loop0–loop9 হলো loop device, sr0 একটি CD/DVD ড্রাইভ, আর vda হলো virtual machine-এর virtio ডিস্ক। প্রতিটি লাইনে বন্ধনীর ভেতরের নামটি চলমান scheduler: বেশিরভাগে [none], sr0-তে [mq-deadline]. খেয়াল করুন, vda নিজেকে rotational=1 বলছে — virtual ডিস্ক প্রায়ই এমন দেখায়, কারণ নিচে আসলে HDD, SSD না নেটওয়ার্ক storage, তা guest OS জানে না।

নিজের Linux মেশিনে (container নয়) সাধারণত এমন কিছু দেখবেন — নমুনা, আপনার মেশিনে নাম ও সংখ্যা আলাদা হবে:

$ lsblk -o NAME,ROTA,SIZE,SCHED
NAME        ROTA   SIZE SCHED
sda            1   1.8T mq-deadline
nvme0n1        0 476.9G none
$ cat /sys/block/sda/queue/scheduler
[mq-deadline] kyber bfq none
$ echo bfq | sudo tee /sys/block/sda/queue/scheduler    # reboot হলে আগের অবস্থায় ফেরে
bfq

বন্ধনীর ভেতরেরটি এখন চলছে। স্থায়ীভাবে বদলাতে udev নিয়ম লাগে (অনুশীলন ৪)। Windows আর macOS-এ Linux-এর মতো ব্যবহারকারীর বাছাই করার scheduler-তালিকা নেই; সেখানে অনুরোধ সাজানো মূলত storage driver স্তর আর ডিস্কের firmware-এর কাজ, আর OS I/O priority-র মতো সুবিধা দেয়।

NVMe-তে কেন “none”

SSD-তে কোনো arm নেই — LBA 5 আর LBA 5,000,000 পড়ার খরচ প্রায় সমান, তাই “কাছের অনুরোধ আগে” কথাটির মানেই নেই। তার ওপর NVMe ডিভাইসে অনেকগুলো hardware queue থাকে, আর Linux সাধারণত প্রতিটি CPU core-কে নিজের queue দেয় — core-গুলো lock ছাড়াই সমান্তরালে অনুরোধ পাঠায়। Scheduler বসালে প্রতিটি অনুরোধে বাড়তি CPU খরচ আর lock-এর প্রতিযোগিতা যোগ হয়, যা কয়েক দশ µs-এর ডিভাইসে চোখে পড়ে। তাই ডিফল্ট none; তবে একই NVMe-তে কয়েকটি কাজের মধ্যে ন্যায্যতা বা latency-লক্ষ্য দরকার হলে kyber বা mq-deadline, আর cgroup-এর I/O নিয়ন্ত্রণ কাজে লাগে।

আরও গভীরে: “head movement” আসলে seek time-এর একটি সরল প্রতিনিধি। বাস্তবে seek time দূরত্বের সাথে সরলরেখায় বাড়ে না: ছোট seek-এ arm-এর ত্বরণ আর থিতু হওয়াই প্রধান, তাই সময় মোটামুটি দূরত্বের বর্গমূলের মতো বাড়ে, আর লম্বা seek-এ প্রায় সরলরৈখিক। তাই ডিস্কের firmware “shortest positioning time first” (SPTF বা SATF) ধরনের নীতি চালায় — seek আর ঘোরা মিলিয়ে কোন অনুরোধে সবচেয়ে কম সময় লাগবে, তা হিসাব করে। এমন নীতি OS চালাতে পারে না, কারণ platter-এর বর্তমান কোণ শুধু ডিস্ক জানে।

৬ · SSD-র ভেতরে — NAND flash, FTL আর garbage collection

একটি SSD-তে কোনো ঘোরা অংশ নেই। ডেটা থাকে NAND flash চিপে — কোটি কোটি ছোট cell, প্রতিটি একটি বিশেষ transistor, যার ভেতরে আটকে রাখা বৈদ্যুতিক চার্জের পরিমাণই ডেটা। বিদ্যুৎ চলে গেলেও চার্জ থেকে যায়, তাই ডেটা হারায় না।

প্রতি cell-এ কত বিট — SLC থেকে QLC

একটি cell-এ চার্জের কয়টি স্তর আলাদা করে চেনা যায়, তার ওপর নির্ভর করে সে কত বিট রাখে। বেশি বিট মানে একই চিপে বেশি ধারণক্ষমতা আর কম দাম — কিন্তু স্তরগুলো কাছাকাছি হয়ে যায়, তাই পড়া-লেখা ধীর, ভুল বেশি, আর cell কম বার লেখা সহ্য করে।

ধরনবিট / cellচার্জের স্তরসহ্যক্ষমতা (মোটামুটি মাত্রা, পণ্যভেদে আলাদা)
SLC১২সবচেয়ে বেশি — কয়েক দশ হাজার P/E cycle বা তারও বেশি
MLC২৪কয়েক হাজার
TLC৩৮প্রায় এক থেকে কয়েক হাজার; আজকের বেশিরভাগ ভোক্তা-SSD
QLC৪১৬কয়েকশো থেকে প্রায় এক হাজারের কাছাকাছি; সস্তা, বড়, পড়া-প্রধান কাজে

এখানে P/E cycle মানে একবার লেখা (program) আর একবার মোছা (erase). প্রতিটি cycle cell-এর অন্তরক স্তরকে একটু ক্ষয় করে; একসময় cell আর নির্ভরযোগ্যভাবে চার্জ ধরে রাখতে পারে না।

তিনটি নিয়ম, যা সব বদলে দেয়

  1. পড়া আর লেখা হয় page ধরে — একটি page সাধারণত কয়েক KiB থেকে ১৬ KiB.
  2. মোছা হয় শুধু erase block ধরে — একটি block-এ শত শত page, আকার কয়েক MiB পর্যন্ত। মোছা ধীর (ms-মাত্রার), পড়া মাইক্রোসেকেন্ড-মাত্রার।
  3. একটি page আবার লেখার আগে তার পুরো block মুছতে হয়। অর্থাৎ জায়গায় বসে overwrite হয় না।

ভাবুন, OS বলল “LBA 1 বদলে নতুন ডেটা লেখো।” HDD হলে head সেই sector-এ গিয়ে লিখে দেয়। SSD-তে LBA 1 যে page-এ আছে, সেটি সরাসরি আবার লেখা যায় না; পুরো block মুছতে হলে সেই block-এর বাকি শত page-এর ডেটা আগে সরাতে হবে — একটি 4 KiB লেখার জন্য কয়েক MiB নড়াচড়া! সমাধান: নতুন ডেটা অন্য একটি ফাঁকা page-এ লেখা, আর পুরোনো page-কে “বাতিল” (invalid) চিহ্ন দেওয়া। কিন্তু তাহলে LBA 1 এখন কোথায়, সেই হিসাব কেউ রাখতে হবে।

Flash Translation Layer — SSD-র নিজের “page table”

সেই হিসাব রাখে FTL (flash translation layer) — SSD-র controller-এ চলা firmware. এটি প্রতিটি LBA-র জন্য লিখে রাখে, ডেটা এখন কোন physical page-এ। পর্ব ৩২-এর page table-এর সাথে মিলটি লক্ষ করুন: সেখানে virtual page → physical frame, এখানে LBA → flash page. OS ভাবে সে একটি সাধারণ ব্লক-ডিভাইসে নির্দিষ্ট ঠিকানায় লিখছে; ভেতরে প্রতিটি লেখা নতুন জায়গায় যায়।

নতুন জায়গায় লিখতে লিখতে একসময় ফাঁকা page ফুরোয়, আর অনেক block-এ জমে বাতিল page. তখন চলে garbage collection (GC): একটি block বাছা (সাধারণত যেটিতে সবচেয়ে কম valid page), তার valid page-গুলো অন্য ফাঁকা জায়গায় কপি করা, তারপর পুরো block মুছে ফাঁকা তালিকায় ফেরত দেওয়া। কপি করা page-গুলো হলো বাড়তি লেখা — host চায়নি, তবু NAND-এ লেখা হলো। এটিই write amplification.

একটি অফিসের রেজিস্টার খাতার কথা ভাবুন, যেখানে কালিতে লেখা হয় আর কাটাকুটি নিষেধ। কোনো এন্ট্রি বদলাতে হলে পুরোনোটিতে “বাতিল” দাগ দিয়ে খাতার পরের ফাঁকা লাইনে নতুনটি লেখা হয়, আর সামনের সূচিপত্রে লেখা থাকে কোন এন্ট্রি এখন কোন পাতায় (FTL). খাতা ভরে এলে একজন কেরানি একটি পুরোনো পাতা বাছেন যেখানে বেশিরভাগই বাতিল, তার অল্প কয়টি বৈধ এন্ট্রি নতুন পাতায় তুলে নেন, তারপর পুরোনো পাতাটি ছিঁড়ে নতুন সাদা পাতা লাগান (erase). বৈধ এন্ট্রি তুলে নেওয়ার পরিশ্রমটাই বাড়তি খরচ।

নিচের stepper একটি খেলনা SSD দেখায়: ৩টি erase block (A, B, C), প্রতিটিতে ৪টি page. ঘরে সংখ্যা মানে সেই LBA-র বৈধ ডেটা, x মানে বাতিল page, . মানে ফাঁকা। FTL একটি block সবসময় GC-র জন্য খালি রাখে (বাস্তব SSD-ও এমন সংরক্ষিত জায়গা রাখে)।

Flash-এর অবস্থা (প্রতিটি block-এ ৪টি page)
Block A
Block B
Block C
host / NAND লেখা
  1. নতুন SSD. তিনটি block-ই ফাঁকা, FTL map খালি। host লিখেছে ০, NAND-এ লেখা হয়েছে ০।
  2. LBA 0–3 লেখা। চারটিই block A-তে পরপর বসল; map-এ 0→A0 … 3→A3. host ৪, NAND ৪।
  3. LBA 1 আবার লেখা (overwrite). A1-এ বসে লেখা যায় না। নতুন ডেটা গেল B0-তে, map-এ 1→B0, আর A1 এখন বাতিল (x). host ৫, NAND ৫।
  4. LBA 2 আবার লেখা। একইভাবে B1-এ, A2 বাতিল। Block A-তে এখন ২টি বৈধ (0, 3) আর ২টি বাতিল page. host ৬, NAND ৬।
  5. LBA 4, 5 লেখা। B2, B3-এ বসল; B ভরে গেল। ফাঁকা আছে শুধু C — যা GC-র জন্য সংরক্ষিত। পরের লেখার আগে জায়গা বানাতে হবে। host ৮, NAND ৮।
  6. Garbage collection: কপি। সবচেয়ে কম বৈধ page-এর ভরা block হলো A (২টি)। FTL তার বৈধ LBA 0 আর 3 কপি করল C0 আর C1-এ, map হালনাগাদ করল। এই দুটি লেখা host চায়নি — host এখনো ৮, NAND ১০।
  7. Block A মোছা। A-তে এখন সব বাতিল, তাই পুরো block একবারে erase — A আবার ফাঁকা (এবং এখন সেটিই সংরক্ষিত block). A-র P/E গণনায় একটি cycle যোগ হলো।
  8. LBA 6, 7 লেখা। C2, C3-এ বসল। মোট host লেখা ১০, NAND লেখা ১২ — write amplification = ১২ ÷ ১০ = ১.২০.

Write amplification-এর সূত্র

Write amplification (WA) = NAND-এ মোট লেখা ÷ host-এর লেখা

WA = ১ মানে কোনো বাড়তি লেখা নেই; WA = ৩ মানে host-এর প্রতি ১ GB লেখায় flash-এ ৩ GB লেখা হচ্ছে, অর্থাৎ SSD-র আয়ু তিন গুণ দ্রুত খরচ হচ্ছে, আর GC-র কাজে host-এর লেখাও ধীর হচ্ছে।

একটি সরল মডেল: ধরুন প্রতিবার GC যে block বাছে, তার page-গুলোর u অংশ বৈধ (০ ≤ u < ১)। P page-এর block থেকে u·P page কপি করলে (P − u·P) page নতুন লেখার জন্য ফাঁকা হয়। অর্থাৎ (১ − u)·P page host-লেখার জন্য NAND-এ মোট P page লিখতে হয়:

WA = P ÷ ((১ − u) · P) = ১ ÷ (১ − u)

উদাহরণ: ৬৪ page-এর একটি block-এ GC-র সময় ১৬টি বৈধ (u = ০.২৫)। ১৬টি কপি করে ৪৮টি ফাঁকা পাওয়া গেল, তাতে ৪৮টি host-লেখা — মোট NAND লেখা ৪৮ + ১৬ = ৬৪. WA = ৬৪ ÷ ৪৮ = ১ ÷ (১ − ০.২৫) ≈ ১.৩৩. Victim যদি ৭৫% বৈধ হয়, WA = ৪; ৯০% হলে ১০. Drive প্রায় ভরা থাকলে GC ভালো victim পায় না — WA লাফিয়ে বাড়ে। (বাস্তব FTL আরও জটিল; এটি শুধু প্রবণতা বোঝার মডেল।)

নিচের প্রোগ্রাম stepper-এর খেলনা SSD-টিই চালায় — page-level FTL আর “সবচেয়ে কম বৈধ page” নীতির GC — আর শেষে সূত্রের কয়েকটি মান ছাপে।

// Toy SSD: 3 erase blocks x 4 pages, page-level FTL, greedy garbage collection.
const PAGES = 4, nand = [[], [], []];       // each block: list of {lba, valid}
const map = {};                             // FTL map: LBA -> [block, page]
let host = 0, flash = 0, active = 0;
const free = () => nand.filter(b => b.length === 0).length;
function program(lba) {                     // NAND pages are written once: append only
  if (nand[active].length === PAGES) active = nand.findIndex(b => b.length === 0);
  if (map[lba]) { const [b, p] = map[lba]; nand[b][p].valid = false; }  // old copy dies
  nand[active].push({ lba, valid: true });
  map[lba] = [active, nand[active].length - 1]; flash++;
}
function gc() {                             // victim = full block with fewest valid pages
  let v = -1;
  nand.forEach((b, i) => { if (b.length === PAGES && i !== active &&
    (v < 0 || b.filter(x => x.valid).length < nand[v].filter(x => x.valid).length)) v = i; });
  const live = nand[v].filter(x => x.valid).map(x => x.lba);
  active = nand.findIndex(b => b.length === 0);  // the reserved free block
  for (const lba of live) program(lba);     // copy valid pages: extra NAND writes
  nand[v] = [];                             // erase the whole block
  console.log("  GC: block " + "ABC"[v] + " had " + live.length + " valid pages (LBA " +
    live.join(",") + ") -> copied, block erased");
}
function write(lba) {                       // keep one free block in reserve for GC
  host++;
  if (nand[active].length === PAGES && free() <= 1) gc();
  program(lba);
}
const show = (t) => console.log(t.padEnd(22) + nand.map((b, i) => "ABC"[i] + "[" +
  [0, 1, 2, 3].map(p => b[p] ? (b[p].valid ? b[p].lba : "x") : ".").join(" ") + "]").join(" ") +
  "  host=" + host + " nand=" + flash);
for (const l of [0, 1, 2, 3]) write(l); show("write LBA 0-3");
for (const l of [1, 2]) write(l); show("overwrite LBA 1,2");
for (const l of [4, 5]) write(l); show("write LBA 4,5");
for (const l of [6, 7]) write(l); show("write LBA 6,7");
console.log("WA = NAND writes / host writes = " + flash + "/" + host + " = " + (flash / host).toFixed(2));
for (const u of [0.25, 0.5, 0.75, 0.9]) console.log("victim " + u * 100 + "% valid -> WA = 1/(1-u) = " + (1 / (1 - u)).toFixed(2));

আউটপুট (নির্ধারিত, প্রতিবার একই):

write LBA 0-3         A[0 1 2 3] B[. . . .] C[. . . .]  host=4 nand=4
overwrite LBA 1,2     A[0 x x 3] B[1 2 . .] C[. . . .]  host=6 nand=6
write LBA 4,5         A[0 x x 3] B[1 2 4 5] C[. . . .]  host=8 nand=8
  GC: block A had 2 valid pages (LBA 0,3) -> copied, block erased
write LBA 6,7         A[. . . .] B[1 2 4 5] C[0 3 6 7]  host=10 nand=12
WA = NAND writes / host writes = 12/10 = 1.20
victim 25% valid -> WA = 1/(1-u) = 1.33
victim 50% valid -> WA = 1/(1-u) = 2.00
victim 75% valid -> WA = 1/(1-u) = 4.00
victim 90% valid -> WA = 1/(1-u) = 10.00

প্রতিটি লাইন stepper-এর একটি ধাপের সাথে মেলে: overwrite-এর পর A-তে দুটি x, LBA 6 লেখার আগে GC A-র দুটি বৈধ page (0, 3) C-তে কপি করে A মুছে দেয়, আর শেষে host ১০, NAND ১২ — WA ১.২০. program()-এ দুটি লাইনই FTL-এর সারকথা: পুরোনো কপিকে valid = false করা (overwrite নয়), আর নতুন জায়গার ঠিকানা map-এ লেখা। gc() GC-র সময় নিজেও program() ডাকে — তাই flash বাড়ে, host বাড়ে না। শেষ চার লাইন সূত্রের মান: ২৫% বৈধ হলে ১.৩৩, ৯০% হলে ১০.

Wear leveling, TRIM আর over-provisioning

  • Wear leveling: কিছু LBA বারবার লেখা হয় (log, metadata), কিছু বছরের পর বছর বদলায় না (সিনেমা, OS-এর ফাইল)। FTL লেখাগুলো সব block-এ ছড়িয়ে দেয়, আর মাঝে মাঝে “ঠান্ডা” ডেটাকেও বেশি-ক্ষয় হওয়া block থেকে সরিয়ে কম-ক্ষয় হওয়া block-এ রাখে (static wear leveling), যাতে সব block মোটামুটি সমানভাবে বুড়ো হয়। এর দামও বাড়তি লেখা।
  • TRIM / discard: আপনি একটি ফাইল মুছলে file system শুধু নিজের হিসাবে block-গুলো “ফাঁকা” লেখে; SSD জানে না। FTL-এর চোখে সেই page-গুলো এখনো বৈধ — GC সেগুলো বারবার কপি করবে! TRIM হলো OS-এর পক্ষ থেকে SSD-কে জানানো “এই LBA-গুলোর ডেটা আর লাগবে না।” ATA-তে TRIM, SCSI-তে UNMAP, NVMe-তে Deallocate নামে। Linux-এ fstrim (অনেক distro-তে সাপ্তাহিক fstrim.timer) বা mount-এর discard বিকল্প; Windows-এ “Optimize Drives” TRIM পাঠায়, আর fsutil behavior query DisableDeleteNotify-এর ফল 0 মানে TRIM চালু।
  • Over-provisioning: SSD-তে বিক্রির ধারণক্ষমতার চেয়ে বেশি flash থাকে — বাড়তি অংশ ব্যবহারকারী দেখেন না, FTL GC আর খারাপ block বদলানোর জন্য রাখে। যত বেশি ফাঁকা জায়গা, GC তত বেশি বাতিল-ভরা victim পায় (u কম), WA তত কম। Enterprise SSD প্রায়ই বেশি over-provisioning রাখে; ভোক্তা হিসেবে আপনিও ড্রাইভ পুরো ভরে না রেখে একই সুবিধা পান।

৭ · NVMe আর গতির মাত্রা — HDD, SATA SSD, NVMe

প্রথম দিকের SSD-গুলো SATA সংযোগে বসানো হতো — যে interface ঘোরা ডিস্কের জন্য বানানো। SATA-র AHCI নিয়মে একটি মাত্র command queue, তাতে সর্বোচ্চ ৩২টি অনুরোধ; আর SATA III-এর তারের গতি ৬ Gb/s, বাস্তবে প্রায় ৫৫০ MB/s-এর কাছাকাছিতে আটকে যায়।

২০১১ সালে NVMe (Non-Volatile Memory Express) স্পেসিফিকেশনের প্রথম সংস্করণ আসে, সরাসরি PCIe-র ওপর flash-এর জন্য নকশা করা। এর কেন্দ্রে জোড়া queue: একটি submission queue, যেখানে host অনুরোধ রাখে, আর একটি completion queue, যেখানে ডিভাইস ফল জানায় — দুটিই RAM-এ রাখা ring, ঠিক পর্ব ৫-এ দেখা io_uring-এর ধাঁচে। স্পেসিফিকেশন অনুযায়ী সর্বোচ্চ প্রায় ৬৪ হাজার I/O queue, প্রতিটিতে প্রায় ৬৪ হাজার অনুরোধ রাখা যায়; বাস্তব ডিভাইস এর অনেক কম queue দেয়, আর Linux সাধারণত প্রতিটি CPU core-কে একটি করে দেয়। কোনো core-কে অন্য core-এর সাথে lock ভাগ করতে হয় না।

নিচের টেবিল মাত্রা বোঝানোর জন্য — নির্দিষ্ট মডেল, queue depth আর workload অনুযায়ী আসল সংখ্যা অনেক বদলায়।

মাপHDD (7200 RPM)SATA SSDNVMe SSD
একটি এলোমেলো 4 KiB পড়ার latencyকয়েক ms থেকে ১০+ msকয়েক দশ থেকে ~১০০ µsপ্রায় ১০ থেকে কয়েক দশ µs
এলোমেলো পড়া, IOPSকয়েকশো পর্যন্ত (প্রায়ই ১০০–২০০)কয়েক দশ হাজারকয়েক লাখ থেকে দশ লাখ ছাড়িয়ে
ক্রমানুসারে পড়া~১০০–২৫০ MB/s~৫০০–৫৫০ MB/s (SATA-র সীমা)কয়েক GB/s (PCIe প্রজন্মভেদে)
Command queue১টি, NCQ-তে ৩২১টি, ৩২অনেকগুলো, প্রতিটি গভীর
Disk scheduling-এর গুরুত্বখুব বেশিকমপ্রায় নেই (সাধারণত none)

HDD থেকে NVMe-তে latency কমেছে প্রায় হাজার গুণ — এখন OS-এর নিজের সফটওয়্যার-পথই মোট সময়ের লক্ষণীয় অংশ (ভাবনার প্রশ্ন ০৪)।

৮ · ডিস্ক প্রস্তুত করা — formatting, partition, boot block, bad block আর সংযোগ

তিন স্তরের formatting

  1. Low-level (physical) formatting: platter-এ track আর sector-এর কাঠামো, প্রতিটি sector-এর header আর ECC-র জায়গা বসানো। আধুনিক ডিস্কে এটি কারখানাতেই হয়ে আসে; ব্যবহারকারী আর করেন না।
  2. Partitioning: ডিস্ককে এক বা একাধিক partition-এ ভাগ করা — হিসাব থাকে MBR বা GPT partition table-এ। প্রতিটি partition OS-এর কাছে আলাদা ডিস্কের মতো।
  3. Logical formatting: একটি partition-এ file system তৈরি করা — superblock, free-space-এর হিসাব, প্রথম directory (Linux-এ mkfs.ext4, Windows-এ Format). এর ভেতরের কাঠামো আসবে পর্ব ৪৩-এ। কিছু database file system ছাড়াই সরাসরি partition (raw disk) ব্যবহার করতে পারে।

Boot block

কম্পিউটার চালু হলে firmware-কে জানতে হয় কোথা থেকে OS শুরু করবে। BIOS-এর যুগে ডিস্কের প্রথম sector — boot sector, LBA 0-এর MBR — একটি ছোট boot code রাখত, যা পরের ধাপের bootloader আনত। UEFI-তে বুটের ফাইল থাকে একটি আলাদা FAT partition-এ (ESP). বিস্তারিত পর্ব ৭ · বুট প্রক্রিয়া-য়।

Bad block — খারাপ জায়গা লুকানো

কোনো চৌম্বক স্তর নিখুঁত নয়; কারখানাতেই কিছু খারাপ sector থাকে, ব্যবহারের সময় আরও তৈরি হয়। আধুনিক ডিস্ক নিজেই সামলায়: কারখানায় কিছু অতিরিক্ত (spare) sector রেখে দেওয়া হয়। কোনো sector খারাপ হলে controller তার LBA-কে একটি spare sector-এ পাঠিয়ে দেয় — sector sparing (forwarding). এতে একটি অসুবিধা: OS-এর scheduler যে LBA-কে পাশাপাশি ভাবছে, তার একটি আসলে অন্য cylinder-এ — তাই অনেক ডিস্ক প্রতিটি cylinder-এ কিছু spare রাখে। আরেক কৌশল sector slipping: খারাপ sector-এর পরের সব sector এক ঘর সরিয়ে দেওয়া, যাতে ক্রম অটুট থাকে। SSD-তে একই কাজ FTL করে — খারাপ block বাদ দিয়ে over-provisioning-এর block বসায়।

Reallocated sector-এর সংখ্যা ডিস্কের S.M.A.R.T. তথ্যে দেখা যায় (Linux-এ smartctl -a /dev/sda, root লাগে)। এই সংখ্যা বাড়তে থাকা ডিস্ক বদলানোর একটি সতর্কসংকেত — ব্যাকআপ আগে নিশ্চিত করুন।

ডিস্ক কোথায় লাগানো — DAS, NAS, SAN আর ক্লাউড

DAS

Direct-attached storage: ডিস্ক সরাসরি কম্পিউটারে — SATA, SAS, NVMe বা USB. OS একটি block device দেখে। ল্যাপটপের ভেতরের SSD এটিই।

block স্তরে, একটি host

NAS

Network-attached storage: নেটওয়ার্কে একটি file server, NFS বা SMB দিয়ে ফাইল দেয়। অফিসের শেয়ার্ড ফোল্ডার।

ফাইল স্তরে, অনেক host

SAN

Storage area network: আলাদা storage নেটওয়ার্ক (Fibre Channel বা iSCSI), যা সার্ভারকে নেটওয়ার্কের ওপারের block device দেয় — সার্ভার ভাবে ডিস্কটি তারই।

block স্তরে, নেটওয়ার্কে

ক্লাউড block storage

Cloud block storage: AWS EBS, Azure Managed Disk, Google Persistent Disk — VM-এর কাছে সাধারণ ডিস্ক, কিন্তু আসলে নেটওয়ার্কের ওপারে কপি রাখা storage; IOPS আর throughput প্রায়ই প্ল্যান অনুযায়ী সীমিত।

block স্তরে, ভাড়ায়

চাবি: NAS ফাইল দেয়, তাই file system চলে সার্ভারে; SAN আর ক্লাউড block storage ব্লক দেয়, তাই file system চলে আপনার মেশিনে। একাধিক ডিস্ক মিলিয়ে নির্ভরযোগ্যতা — RAID — পর্ব ৪৫-এ।

৯ · AI-এর যুগে ডিস্ক আর SSD

Disk scheduling-এর অঙ্ক আর storage-এর পরামর্শ — দুটিতেই AI দ্রুত উত্তর দেয়, আর দুটিতেই নিয়মিত ভুল করে:

AI-এর ভুলকেন ভুলকীভাবে যাচাই করবেন
C-SCAN-এ ফেরার লাফ নিঃশব্দে যোগ বা বাদ; দিক না বলেই SCAN-এর উত্তরএকই প্রশ্নের একাধিক সঠিক উত্তর আছে নিয়মভেদেনিয়ম আগে লিখে দিন; simulator-এর টেবিলে চারটি কলাম মিলিয়ে দেখুন
লম্বা trace-এ যোগফলে ছোট ভুল (৬৪০-এর জায়গায় ৬৩০)ভাষা-মডেল ধাপে ধাপে হিসাব রাখে নাপ্রতিটি ধাপের দূরত্ব আলাদা করে লিখিয়ে নিজে যোগ করুন
“SCAN সবসময় সর্বোত্তম” বা “SSTF সর্বোত্তম”দুটিই নয়; এই queue-তে সর্বনিম্ন ২০৮ (LOOK নিচের দিকে)অংশ ৩-এর সর্বনিম্ন-সীমার যুক্তি
Rotational latency = পুরো এক পাকগড় অর্ধেক পাক; পূর্ণ পাক হলো সবচেয়ে খারাপ60 ÷ RPM ÷ 2 হাতে হিসাব
NVMe-তে “bfq দিন, দ্রুত হবে” বা elevator= boot প্যারামিটারNVMe-তে সাধারণত none ভালো; blk-mq-র পর পুরোনো elevator প্যারামিটার কাজ করে নাcat /sys/block/*/queue/scheduler দেখুন; fio দিয়ে আগে-পরে মাপুন
“SSD-তে ফাইল overwrite করলেই নিরাপদে মুছে যায়”FTL নতুন জায়গায় লেখে, পুরোনো page থেকে যেতে পারেনির্মাতার secure erase / sanitize ডকুমেন্টেশন দেখুন
সন্দেহজনক ডিস্কে প্রথমেই sudo fsck -y বা dd চালানোর পরামর্শমরতে থাকা ডিস্কে বাড়তি লেখা ক্ষতি বাড়াতে পারে; dd-এ ভুল ডিভাইস নাম মানে পুরো ডেটা মুছে যাওয়াআগে ব্যাকআপ/ইমেজ, lsblk দিয়ে ডিভাইস নাম দুবার মেলান, SMART দেখুন

অঙ্কে নিয়ম স্পষ্ট লিখে দিন, আর যাচাইযোগ্য ধাপ চান:

নিচের disk scheduling সমস্যাটি সমাধান করো। Cylinder 0 থেকে 199. Head এখন 53-এ। অনুরোধ আসার ক্রমে: 98, 183, 37, 122, 14, 124, 65, 67. নিয়ম: SCAN আর LOOK প্রথমে 0-এর দিকে যাবে; C-SCAN আর C-LOOK প্রথমে 199-এর দিকে যাবে। C-SCAN/C-LOOK-এর ফেরার লাফ দুইভাবে হিসাব করো — গুনে আর বাদ দিয়ে। ১. FCFS, SSTF, SCAN, C-SCAN, LOOK, C-LOOK — প্রতিটির সেবার ক্রম আর প্রতিটি ধাপের দূরত্ব আলাদা লাইনে লেখো, তারপর মোট। ২. SSTF-এ যেখানে দুটি অনুরোধের দূরত্ব খুব কাছাকাছি, সেখানে দুটি দূরত্বই দেখাও। ৩. কোথাও কোনো অনুমান করলে স্পষ্ট বলো। ৪. শেষে সব মোট যাচাই করার জন্য একটি ছোট JavaScript simulator দাও।

AI-এর উত্তর মানার আগে — চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • Access time-এর তিনটি অংশ কী, আর কোনটি সবচেয়ে বড়? — seek + rotational latency + transfer; এলোমেলো ছোট I/O-তে seek আর latency প্রায় পুরোটা, transfer নগণ্য।
  • 7200 RPM-এ গড় rotational latency? — ½ × ৬০,০০০ ÷ ৭২০০ ≈ ৪.১৭ ms.
  • SSTF-এর সমস্যা কী? — starvation (দূরের অনুরোধ অনির্দিষ্টকাল অপেক্ষা করতে পারে), আর এটি সর্বোত্তমও নয়।
  • SCAN আর C-SCAN-এর পার্থক্য? — SCAN দুই দিকেই সেবা দেয়; C-SCAN এক দিকে সেবা দিয়ে লাফিয়ে শুরুতে ফেরে, তাই অপেক্ষার সময় বেশি সমান।
  • SSD-তে write amplification কেন হয়, কীভাবে কমে? — overwrite নেই, GC বৈধ page কপি করে; TRIM, over-provisioning আর ড্রাইভ পুরো না ভরানো WA কমায়।

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

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

প্র ০১SSD এত দ্রুত, তবু ঘোরা হার্ড ডিস্ক কেন আজও ডেটা সেন্টারে বিপুল সংখ্যায় কেনা হয়?

উত্তরটি এক শব্দে — দাম, তবে তার পেছনে প্রকৌশলের একটি গল্প আছে। ১৯৫৬ সালের IBM 350-এর ৫০টি ২৪-ইঞ্চি platter-এ ধরত মোটামুটি ৩.৭৫ MB. তারপর প্রায় সাত দশকে ঘোরা ডিস্কের ধারণক্ষমতা বেড়েছে কল্পনাতীত হারে — head ছোট হয়েছে, বিট আরও ঘন হয়েছে, track-গুলো আরও কাছাকাছি এসেছে। কিন্তু অংশ ২-এর অঙ্কটি মনে করুন: seek আর ঘোরা যান্ত্রিক, তাই এলোমেলো I/O-র গতি এই সময়ে বেড়েছে খুব সামান্য — 7200 RPM ডিস্ক আজও সেকেন্ডে কয়েকশো এলোমেলো পড়ার বেশি দিতে পারে না।

ফলে HDD-র প্রতি TB-এর দাম এখনো সাধারণত SSD-র চেয়ে কম, যদিও ব্যবধান সময়ের সাথে কমছে। যে ডেটা লেখা হয় একবার, পড়া হয় কালেভদ্রে — ব্যাকআপ, পুরোনো ছবি-ভিডিও, log-এর সংরক্ষণাগার, বৈজ্ঞানিক ডেটা — সেখানে এলোমেলো গতির দরকার নেই, ক্রমানুসারে লেখা-পড়াই যথেষ্ট, যেখানে HDD ভালোই চলে। তাই বড় ক্লাউড-প্রতিষ্ঠানগুলোর “ঠান্ডা” (cold) storage-স্তরের বড় অংশ প্রকাশ্য তথ্যমতে এখনো ঘোরা ডিস্কে, আর তার ওপরে একটি দ্রুত flash-স্তর গরম ডেটা রাখে — পর্ব ৪-এর memory hierarchy-র আরেকটি রূপ।

ঘনত্ব বাড়ানোর কৌশলের দামও আছে। Shingled magnetic recording (SMR)-এ track-গুলো ছাদের টালির মতো একটির ওপর আরেকটি আংশিক চাপানো, ফলে একটি track লিখলে পাশেরটি নষ্ট হয় — SSD-র erase block-এর মতোই “জায়গায় বসে overwrite নেই”, আর ডিস্কের ভেতরে একটি translation layer লাগে। ২০২০ সালে একটি বড় নির্মাতার কিছু NAS-ডিস্কে ঘোষণা ছাড়াই drive-managed SMR ব্যবহারের খবর সামনে আসে; RAID পুনর্গঠনের সময় সেগুলো খুব ধীর হয়ে যাওয়ার অভিযোগ ওঠে — ব্যবহারকারীরা শিখলেন, ডিস্কের ভেতরের প্রযুক্তি জানা জরুরি।

মূল উপলব্ধি: storage বাছাই একটি মাপজোখ — প্রতি TB-এর দাম, IOPS আর latency; ডেটা কতটা “গরম”, তা দেখে স্তর ঠিক করাই আসল নকশা, “সবচেয়ে দ্রুত” বাছা নয়।

প্র ০২ডিস্ক নিজেই NCQ দিয়ে সাজায়, SSD-তে seek-ই নেই — তাহলে SCAN, C-SCAN শেখার মূল্য কী?

প্রশ্নটি ন্যায্য, আর Linux-এর ইতিহাসই আংশিক উত্তর দেয়। দীর্ঘদিন Linux-এ I/O scheduler-কে বলা হতো “elevator”, আর পুরোনো kernel-এ boot-এর সময় elevator= প্যারামিটার দিয়ে বাছা যেত। CFQ (Completely Fair Queuing) বহু বছর অনেক distro-র ডিফল্ট ছিল। Linux 5.0 (২০১৯)-এ পুরোনো single-queue block layer সরানোর সাথে CFQ-ও বিদায় নেয়; জায়গা নেয় blk-mq-র mq-deadline, bfq, kyber আর none. অর্থাৎ নির্দিষ্ট algorithm বদলেছে, কিন্তু প্রশ্নগুলো বদলায়নি — কাকে আগে, কতক্ষণ কেউ অপেক্ষা করবে, ন্যায্যতা কীভাবে।

mq-deadline-এর ভেতরে তাকালেই দেখবেন: অনুরোধগুলো sector অনুযায়ী সাজানো একটি তালিকায় রাখা হয় আর এক দিকে চলতে চলতে পাঠানো হয় — SCAN/LOOK-এর ধারণা। পাশাপাশি প্রতিটির সময়সীমা দেখা হয় — SSTF-এর starvation-এর প্রতিকার, যা এই পর্বে দেখেছেন। BFQ-র “budget” আসলে CPU scheduling-এর ন্যায্য ভাগের ধারণা ডিস্কে আনা (পর্ব ১৮–২১)। আর NCQ-র ভেতরে ডিস্কের firmware যা করে, তা SSTF-এরই সম্প্রসারিত রূপ — seek-এর সাথে ঘোরাও হিসাব করে, আর starvation ঠেকাতে পুরোনো অনুরোধকে একসময় জোর করে সেবা দেয়।

দ্বিতীয় মূল্য: এই algorithm-গুলো একটি সাধারণ প্রশ্নের নমুনা — একটি এক-মাত্রিক জায়গায় একটি যন্ত্র চলাচল করে, আর অনুরোধ আসতে থাকে। লিফট নিয়ন্ত্রণ, গুদামে রোবটের পথ, ব্যাংকের টেপ-লাইব্রেরি, এমনকি একটি ছাপাখানার কাগজ-ফিডার — সবখানে “লোভী বনাম ঝাড়ু-চলাচল”-এর একই আপস। আর তৃতীয়ত, পরীক্ষায় এগুলো আসবেই — সংখ্যা-হিসাবের নির্ভুলতা আর নিয়ম স্পষ্ট লেখার অভ্যাস এখান থেকেই তৈরি হয়।

মূল উপলব্ধি: বাস্তব scheduler-এর নাম বদলায়, কিন্তু তার ভেতরে থাকে এই পর্বের দুটি টান — মোট পথ কমানো আর কাউকে অনির্দিষ্টকাল বসিয়ে না রাখা।

প্র ০৩Write amplification SSD-র আয়ু খায় — তাহলে file system আর database কীভাবে নিজেদের বদলেছে?

SSD নির্মাতারা আয়ু জানান TBW (terabytes written — মোট কত TB লেখা সহ্য করবে) বা DWPD (drive writes per day — নির্দিষ্ট বছর ধরে প্রতিদিন পুরো ড্রাইভ কতবার লেখা যাবে) দিয়ে। এই সংখ্যা host-এর লেখার হিসাবে; ভেতরের WA বেশি হলে আসল NAND-ক্ষয় তার চেয়ে দ্রুত। তাই সফটওয়্যারের লেখার ধরন সরাসরি হার্ডওয়্যারের আয়ুতে প্রভাব ফেলে।

প্রথম শিক্ষা এলো log-structured নকশা থেকে। ১৯৯০-এর দশকের শুরুতে Mendel Rosenblum আর John Ousterhout-এর Log-structured File System (LFS) প্রস্তাব করেছিল সব লেখা ডিস্কের শেষে ক্রমানুসারে যোগ করা — তখন উদ্দেশ্য ছিল HDD-র seek এড়ানো। মজার ব্যাপার, SSD-র FTL ভেতরে ঠিক এটিই করে, আর LFS-এর “cleaner” আসলে GC-র পূর্বসূরি। পরে Samsung-এর F2FS (Linux 3.8-এ, ২০১৩ সালের শুরুতে kernel-এ আসে) flash-এর জন্য log-structured ধারণায় নকশা করা। Database-জগতে LSM-tree (RocksDB, Cassandra-র মতো সিস্টেমে) এলোমেলো overwrite-এর বদলে ক্রমানুসারে লেখা ফাইল বানায় আর পরে compaction করে — যদিও compaction নিজেও এক ধরনের write amplification, তাই আপস আছে।

সর্বশেষ ধাপ হলো host আর ডিভাইসের দায়িত্ব নতুন করে ভাগ করা। NVMe-র Zoned Namespaces (ZNS)-এ ডিভাইস নিজেকে কয়েকটি “zone” হিসেবে দেখায়, যেখানে শুধু ক্রমানুসারে লেখা যায় আর পুরো zone একসাথে মুছতে হয় — অর্থাৎ erase block-এর নিয়ম host-এর কাছে খোলাখুলি। তখন GC কোথায় আর কখন হবে, তা file system বা database নিজে ঠিক করে, যে জানে কোন ডেটা একসাথে মরবে। এতে WA কমে আর over-provisioning কম লাগে, বিনিময়ে সফটওয়্যার জটিল হয়। একই চিন্তা প্র ০১-এর SMR ডিস্কেও (host-managed SMR).

মূল উপলব্ধি: যে স্তর জানে কোন ডেটা একসাথে বাতিল হবে, তার হাতে garbage collection থাকলে বাড়তি লেখা কমে — storage নকশার ইতিহাস হলো এই জ্ঞান ঠিক স্তরে পৌঁছে দেওয়ার চেষ্টা।

প্র ০৪NVMe-তে latency ১০ µs-এর ঘরে নেমে এলে OS-এর কাছে কী বদলায়? Interrupt, polling আর kernel এড়ানো নিয়ে ভাবুন।

HDD-র যুগে একটি I/O-তে প্রায় ১০ ms. তার মধ্যে system call, block layer, scheduler, driver, interrupt সামলানো, প্রসেস জাগানো — এসবে মিলিয়ে হয়তো কয়েক দশ µs; মোটের ১%-এরও কম, কেউ খেয়াল করত না। এখন ডিভাইস নিজেই কয়েক দশ µs-এ উত্তর দিলে একই সফটওয়্যার-খরচ মোটের বড় অংশ। Amdahl-এর নিয়ম মনে করুন: যে অংশ দ্রুত হয়নি, সেটিই এখন সীমা।

OS কয়েকটি উপায়ে সাড়া দিয়েছে। প্রথম, block layer-কে বহু-queue করা (blk-mq), যাতে প্রতিটি core নিজের queue-তে lock ছাড়াই অনুরোধ দেয় — NVMe-র নকশার সাথে মিলিয়ে; এজন্য scheduler “none”. দ্বিতীয়, system call-এর সংখ্যা কমানো — io_uring-এ অনুরোধ আর ফল shared memory-র ring-এ যায়, অনেক অনুরোধ একটি syscall-এ বা একটিও ছাড়া। তৃতীয়, interrupt-এর বদলে polling: খুব দ্রুত ডিভাইসে একটি interrupt নেওয়া, context বদলানো আর প্রসেস জাগানোর খরচ অপেক্ষার চেয়ে বেশি হতে পারে, তাই CPU কিছুক্ষণ ঘুরে ঘুরে completion queue দেখে (পর্ব ৩৯-এর polling বনাম interrupt-এর আপস নতুন রূপে)। চতুর্থ, কিছু অতি-দ্রুত সিস্টেম kernel-কেই এড়িয়ে যায় — SPDK-এর মতো কাঠামোয় user-space থেকে সরাসরি ডিভাইস চালানো হয়, বিনিময়ে নিরাপত্তা আর সহজ ভাগাভাগির সুবিধা হারায়।

একটি সতর্ক ইতিহাসও আছে। Intel আর Micron-এর 3D XPoint (Optane) প্রযুক্তি flash-এর চেয়েও কম latency আর byte-স্তরের ব্যবহার দিয়েছিল; OS-গুলো persistent memory-র জন্য নতুন পথ (যেমন DAX) বানিয়েছিল। কিন্তু ২০২২ সালে Intel Optane ব্যবসা গুটিয়ে নেওয়ার ঘোষণা দেয়। প্রযুক্তি ভালো হলেও দাম, বাস্তুতন্ত্র আর সফটওয়্যার-বদলের খরচ টিকে থাকা ঠিক করে।

মূল উপলব্ধি: হার্ডওয়্যার যত দ্রুত হয়, OS-এর “সাধারণ” খরচগুলো তত দৃশ্যমান হয় — তখন queue, syscall আর interrupt-এর নকশাই পারফরম্যান্স ঠিক করে।

প্র ০৫পুরোনো ল্যাপটপ বিক্রির আগে ফাইল মুছে দিলেন — HDD আর SSD-তে ডেটা কি সত্যিই গেল? নিরাপদে মোছার সঠিক উপায় কী?

সাধারণভাবে ফাইল মুছলে (এমনকি Recycle Bin খালি করলেও) file system শুধু নিজের হিসাবে block-গুলো “ফাঁকা” লেখে; ডেটা ডিস্কে থেকে যায়, যতক্ষণ না নতুন কিছু তার ওপর লেখা হয়। HDD-তে তাই recovery টুল দিয়ে মোছা ফাইল প্রায়ই ফেরত পাওয়া যায়। Quick format-ও মূলত file system-এর কাঠামো নতুন করে লেখে, ডেটা নয়। পুরোনো ডিস্ক বিক্রি বা দান করার আগে এটি একটি বাস্তব গোপনীয়তার ঝুঁকি — ছবি, ব্যাংকের কাগজ, ব্রাউজারের সংরক্ষিত তথ্য।

HDD-তে সমাধান তুলনামূলক সরল: পুরো ডিস্ক একবার নতুন ডেটা দিয়ে লিখে দিলে আধুনিক ডিস্কে আগের ডেটা সাধারণ উপায়ে ফেরানো যায় না (বহুবার লেখার পুরোনো পরামর্শ আজকের ঘন ডিস্কে সাধারণত অপ্রয়োজনীয় বলে মনে করা হয়; তবে reallocate হওয়া spare sector-এ পুরোনো ডেটা থেকে যেতে পারে)। SSD-তে ব্যাপারটি আলাদা, আর এই পর্বের FTL-ই কারণ। একটি ফাইলের ওপর overwrite করলে নতুন ডেটা যায় নতুন page-এ; পুরোনো page শুধু বাতিল চিহ্নিত হয়, GC না হওয়া পর্যন্ত flash-এ থাকে। over-provisioning-এর অংশে OS পৌঁছাতেই পারে না। তাই “ফাইলটির ওপর শূন্য লিখে দিন” SSD-তে নির্ভরযোগ্য নয়। TRIM চালু থাকলে মোছা অংশ সাধারণত দ্রুত অপাঠযোগ্য হয়ে যায়, কিন্তু এটি নিরাপত্তার নিশ্চয়তা নয়।

নির্ভরযোগ্য পথ হলো ডিভাইসের নিজের মোছার আদেশ, যা FTL জানে: ATA Secure Erase, NVMe-র Format বা Sanitize — নির্মাতার টুল বা OS-এর ডকুমেন্টেশন অনুযায়ী (ভুল ডিভাইস বাছলে সব ডেটা চলে যাবে, তাই ডিভাইস নাম দুবার মেলান)। আরও ভালো অভ্যাস: শুরু থেকেই পুরো ডিস্ক encryption — Windows-এ BitLocker, macOS-এ FileVault, Linux-এ LUKS. তখন key মুছে ফেললেই (cryptographic erase) সব ডেটা অর্থহীন, আর হারানো বা চুরি যাওয়া ল্যাপটপেও ডেটা সুরক্ষিত। আধুনিক ফোন এই নীতিতেই চলে — factory reset আসলে key মোছা।

মূল উপলব্ধি: SSD-তে “কোথায় লেখা হলো” ঠিক করে FTL, OS নয় — তাই নিরাপদ মোছার দায়িত্বও দিতে হয় ডিভাইসের নিজস্ব আদেশ বা encryption-কে।

অনুশীলন

হাতে হিসাব, আউটপুট অনুমান আর কমান্ড — আগে নিজে চেষ্টা করুন, তারপর উত্তর মেলান।

  1. হাতে trace: Cylinder 0–199, head 50-এ, এখন ওপরের দিকে (199-এর দিকে) চলছে। অনুরোধ আসার ক্রমে: 82, 170, 43, 140, 24, 16, 190. FCFS, SSTF, SCAN, C-SCAN, LOOK আর C-LOOK-এর মোট head movement বের করুন (C-SCAN/C-LOOK-এ ফেরার লাফ গুনে আর বাদ দিয়ে)।

    উত্তর (simulator-এ queue, head আর DIR = "up" বদলে যাচাই করা):

    FCFS   50→82→170→43→140→24→16→190
           32+88+127+97+116+8+174 = 642
    SSTF   50→43→24→16→82→140→170→190
           7+19+8+66+58+30+20 = 208
    SCAN   50→82→140→170→190→199→43→24→16
           32+58+30+20+9+156+19+8 = 332
    C-SCAN 50→…→190→199 ⇢ 0→16→24→43
           149 + [199] + 43 = 391 (লাফ বাদে 192)
    LOOK   50→82→140→170→190→43→24→16
           140 + 174 = 314
    C-LOOK 50→…→190 ⇢ 16→24→43
           140 + [174] + 27 = 341 (লাফ বাদে 167)

    খেয়াল করুন, এখানে SSTF (২০৮) সবচেয়ে কম। সর্বনিম্ন সীমা: (১৯০ − ১৬) + min(৫০ − ১৬, ১৯০ − ৫০) = ১৭৪ + ৩৪ = ২০৮ — SSTF এই সারিতে সর্বোত্তম পথই পেয়েছে, কারণ নিচের তিনটি অনুরোধ কাছেই। ওপরের দিকে বাধ্যতামূলকভাবে যাওয়া SCAN-পরিবার এখানে বেশি পথ চলে।

  2. সংখ্যাভিত্তিক: (ক) একটি 10000 RPM ডিস্কের গড় seek ৪ ms, transfer rate ২০০ MB/s (১ MB = ১০৬ byte). একটি এলোমেলো 64 KiB (৬৫,৫৩৬ byte) পড়ার গড় access time কত? (খ) একটি 7200 RPM ডিস্কের প্রতি track-এ ৪০০টি 512-byte sector. একটি sector পড়ার transfer time আর ডিস্কের সর্বোচ্চ transfer rate কত?

    (ক) এক পাক = ৬০,০০০ ÷ ১০,০০০ = ৬ ms, গড় latency = ৩ ms. Transfer = ৬৫,৫৩৬ ÷ (২০০ × ১০৬) সেকেন্ড ≈ ০.৩২৮ ms. মোট ≈ ৪ + ৩ + ০.৩২৮ = ৭.৩২৮ ms. এখানেও transfer মোটের ৫%-এরও কম।

    (খ) এক পাক = ৮.৩৩৩ ms, আর এক পাকে ৪০০টি sector head-এর নিচ দিয়ে যায়: একটি sector = ৮.৩৩৩ ÷ ৪০০ ≈ ০.০২০৮ ms. Transfer rate = ৪০০ × ৫১২ = ২,০৪,৮০০ byte প্রতি ৮.৩৩৩ ms ≈ ২৪.৫৮ MB/s. (দুটি হিসাবই access-time calculator-এর hdd() ফাংশনের সূত্রে করা যায়।)

  3. আউটপুট অনুমান: Disk-scheduling simulator-এ শুধু DIR = "up" আর COUNT_RETURN = false করে চালালে প্রথম অংশে C-SCAN আর C-LOOK-এর total = লাইনে কী ছাপা হবে, আর C-SCAN-এর moves: লাইনে ফেরার লাফটি কীভাবে দেখাবে?

    C-SCAN-এ total = 183, C-LOOK-এ total = 153 — শেষ টেবিলের “up,no-return” কলামের সাথে এক। C-SCAN-এর moves লাইন: 12 + 2 + 31 + 24 + 2 + 59 + 16 + [199 not counted] + 14 + 23 — লাফের দূরত্ব দেখানো হয়, কিন্তু মোটে যোগ হয় না (১৪৬ + ১৪ + ২৩ = ১৮৩)। বাকি চারটি algorithm-এ ফেরার লাফ নেই, তাই তাদের সংখ্যা COUNT_RETURN-এ বদলায় না: FCFS ৬৪০, SSTF ২৩৬, SCAN ৩৩১, LOOK ২৯৯.

  4. কমান্ড লিখুন: একটি Linux সার্ভারে HDD sda আর NVMe nvme0n1. (ক) দুটির বর্তমান scheduler আর ঘোরা কি না কীভাবে দেখবেন? (খ) শুধু sda-কে এখনই bfq করুন। (গ) সব ঘোরা SATA ডিস্কে reboot-এর পরেও bfq রাখার udev নিয়ম লিখুন।
    # (ক) পড়তে root লাগে না
    lsblk -d -o NAME,ROTA,SCHED
    cat /sys/block/sda/queue/scheduler /sys/block/nvme0n1/queue/scheduler
    cat /sys/block/sda/queue/rotational      # 1 = HDD
    
    # (খ) এখনই বদলানো (root), reboot-এ হারায়
    echo bfq | sudo tee /sys/block/sda/queue/scheduler
    
    # (গ) /etc/udev/rules.d/60-iosched.rules
    ACTION=="add|change", KERNEL=="sd[a-z]*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"

    নিয়ম লেখার পর sudo udevadm control --reload আর sudo udevadm trigger দিয়ে প্রয়োগ করে আবার (ক) দিয়ে মিলিয়ে নিন। ROTA শর্তের কারণে SATA SSD (rotational = 0) আর NVMe এই নিয়মে বদলাবে না — NVMe none-ই থাকবে। kernel-এ bfq module না থাকলে লেখাটি ব্যর্থ হবে; তখন scheduler ফাইলের তালিকায় bfq আছে কি না দেখুন। Production-এ বদলের আগে-পরে fio-র মতো টুলে latency মেপে সিদ্ধান্ত নিন।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমহার্ড ডিস্ক থেকে একটি block পড়ার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Driver ডিস্কের controller-কে LBA আর আকারসহ পড়ার আদেশ পাঠায়
  2. Arm সরে head ঠিক cylinder-এ পৌঁছায় (seek)
  3. চাওয়া sector ঘুরে head-এর নিচে আসা পর্যন্ত অপেক্ষা (rotational latency)
  4. Sector-গুলো পড়ে ডিস্কের buffer-এ আসে (transfer)
  5. DMA দিয়ে ডেটা RAM-এ যায়, তারপর CPU একটি interrupt পায়

Cylinder 0–199-এর ডিস্কে head 53-এ। অনুরোধ আসার ক্রমে: 98, 183, 37, 122, 14, 124, 65, 67. FCFS-এ মোট head movement কত?

  • ২৩৬
  • ৬৩৮
  • ৬৪০
  • ১৮৩

একই সারিতে (head 53; 98, 183, 37, 122, 14, 124, 65, 67; cylinder 0–199) SSTF-এর সেবার ক্রম আর মোট head movement কী?

  • 37, 14, 65, 67, 98, 122, 124, 183 — ২০৮
  • 65, 67, 37, 14, 98, 122, 124, 183 — ২৩৬
  • 65, 67, 98, 122, 124, 183, 37, 14 — ২৯৯
  • 98, 183, 37, 122, 14, 124, 65, 67 — ৬৪০

7200 RPM-এ ঘোরা একটি হার্ড ডিস্কের গড় rotational latency কত?

  • প্রায় ৪.১৭ ms
  • প্রায় ৮.৩৩ ms
  • প্রায় ২ ms
  • গড় seek time-এর সমান

SCAN আর C-SCAN disk scheduling-এর মূল পার্থক্য কোনটি?

  • SCAN প্রান্ত পর্যন্ত যায়, C-SCAN শেষ অনুরোধে ঘোরে
  • C-SCAN-এ starvation হতে পারে, SCAN-এ নয়
  • C-SCAN সবসময় কম মোট head movement দেয়
  • SCAN দুই দিকেই সেবা দেয়; C-SCAN শুধু এক দিকে সেবা দিয়ে শুরুতে ফেরে, ফলে অপেক্ষা বেশি সমান

একটি SSD-তে garbage collection-এর সময় victim block-এর ৭৫% page বৈধ থাকে। সরল মডেল WA = 1 ÷ (1 − u) অনুযায়ী write amplification কত?

  • ১.৩৩
  • ৪
  • ০.৭৫
  • ১০

SSD-তে TRIM (বা discard) আদেশের কাজ কী?

  • OS ডিভাইসকে জানায় কোন LBA-র ডেটা আর লাগবে না, যাতে GC সেগুলো কপি না করে
  • ফাইলের block পাশাপাশি সাজিয়ে defragment করে
  • নির্দিষ্ট LBA-গুলো সঙ্গে সঙ্গে নিরাপদে মুছে ফেলে, ফেরত আনা অসম্ভব করে
  • খারাপ block খুঁজে spare block দিয়ে বদলে দেয়

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. HDD-র গঠন: platter, track, sector, cylinder, head, arm; zoned bit recording-এ বাইরের track দ্রুত; 4 KiB Advanced Format আর 1 MiB alignment; OS দেখে শুধু LBA.
  2. Access time = seek + rotational latency (গড়ে অর্ধেক পাক; 7200 RPM-এ ৪.১৬৭ ms) + transfer. এলোমেলো 4 KiB ≈ ১৩.২ ms (৭৬ IOPS); ক্রমানুসারে পড়া কয়েকশো গুণ দ্রুত।
  3. ক্লাসিক সারি (head 53): FCFS ৬৪০, SSTF ২৩৬, SCAN ↓ ২৩৬, LOOK ↓ ২০৮ (সর্বনিম্ন), C-SCAN ↑ ৩৮২ / ১৮৩, C-LOOK ↑ ৩২২ / ১৫৩ — দিক আর ফেরার নিয়ম সবসময় লিখুন। SSTF-এ starvation.
  4. বাস্তবে: NCQ-তে ডিস্ক নিজে সাজায়; Linux-এ mq-deadline, bfq, kyber, none; NVMe-তে seek নেই আর queue অনেক, তাই none.
  5. SSD: SLC→QLC, page-এ লেখা, block-এ মোছা, overwrite নেই; FTL remap, GC, WA = NAND ÷ host ≈ 1 ÷ (1 − u); wear leveling, TRIM, over-provisioning; formatting, boot block, bad-block sparing, DAS/NAS/SAN/ক্লাউড।

পরের পর্বে: ডিস্ক আর SSD-র ওপর OS কীভাবে “ফাইল” নামের ধারণা দেয়? পর্ব ৪১-এ file descriptor, open/read/write আর access method.

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান