এই পর্বে যা শিখবেন
- আগের পর্বগুলোয় ছড়িয়ে থাকা Windows-এর টুকরো তথ্য জুড়ে একটি পূর্ণ মানচিত্র
- একটি
CreateFilecall কীভাবে kernel32 থেকে ntdll, syscall, I/O manager আর IRP হয়ে driver পর্যন্ত যায় - Handle, reference count, token, IRQL, DPC আর APC — নিজে simulator চালিয়ে আর ধাপে ধাপে দেখে
- Windows memory manager-এর page list, registry-র hive আর service, বুটের ক্রম আর WSL1 বনাম WSL2
- Task Manager, Sysinternals আর PowerShell দিয়ে নিজের যন্ত্রে নিরাপদে এগুলো পর্যবেক্ষণ করা
১ · NT কেন আলাদা — Cutler, VMS আর ১৯৯৩
বাংলাদেশের বেশিরভাগ অফিস, ব্যাংকের কাউন্টার, সাইবার ক্যাফে আর বাসার ল্যাপটপে যে OS চলে, সেটি Windows. কিন্তু “Windows” নামের নিচে আসলে দুটি সম্পূর্ণ আলাদা বংশ ছিল। একটি MS-DOS-এর ওপর দাঁড়ানো — Windows 3.x, 95, 98, ME. অন্যটি শূন্য থেকে লেখা নতুন একটি kernel — Windows NT. ২০০১ সালে Windows XP থেকে সাধারণ ব্যবহারকারীর Windows-ও NT-তে চলে আসে, আর DOS-বংশ শেষ হয়। আজকের Windows 10, Windows 11, Windows Server — সবই NT.
১৯৮৮ সালের শেষ দিকে Dave Cutler Digital Equipment Corporation (DEC) ছেড়ে Microsoft-এ যোগ দেন, সাথে আনেন DEC-এর কয়েকজন প্রকৌশলী। DEC-এ তিনি RSX-11M আর VMS অপারেটিং সিস্টেমের নকশায় নেতৃত্ব দিয়েছিলেন। Microsoft-এ প্রকল্পটি শুরুতে OS/2-এর পরের সংস্করণ হিসেবে ভাবা হয়েছিল; ১৯৯০ সালে Windows 3.0-এর বিপুল সাফল্যের পর মূল API হয় Win32. ১৯৯৩ সালের জুলাইয়ে বেরোয় Windows NT 3.1. “NT”-এর প্রচলিত ব্যাখ্যা “New Technology”; নামের উৎস নিয়ে আরও কয়েকটি গল্প চালু আছে, তবে সেগুলোকে লোককথা হিসেবেই নেওয়া ভালো।
NT-এর নকশাকারীরা শুরুতেই কয়েকটি লক্ষ্য লিখে নিয়েছিলেন। Microsoft-এর প্রকাশিত বিবরণ আর Windows Internals বইয়ের ধারায় সাধারণত এগুলো বলা হয় (তালিকার শব্দচয়ন সূত্রভেদে একটু আলাদা):
বহনযোগ্যতা (portability)
বেশিরভাগ কোড C-তে; হার্ডওয়্যার-নির্ভর অংশ আলাদা স্তরে। প্রথম সংস্করণই x86, MIPS আর পরে Alpha-তে চলত; আজ x64 আর ARM64.
HAL এই লক্ষ্যের ফলনির্ভরযোগ্যতা ও নিরাপত্তা
প্রতিটি প্রসেসের আলাদা address space, kernel-এর সুরক্ষিত মেমরি, প্রতিটি object-এ ACL. মার্কিন সরকারের তখনকার নিরাপত্তা-মান (C2) মাথায় রেখে নকশা।
DOS-বংশে এগুলো ছিল নাসামঞ্জস্য (compatibility)
এক kernel-এর ওপর একাধিক “environment subsystem” — Win32, OS/2 1.x আর POSIX; সাথে পুরোনো DOS ও 16-bit Windows প্রোগ্রাম চালানোর ব্যবস্থা।
আজ শুধু Win32 টিকে আছেগতি ও প্রসারযোগ্যতা
Multiprocessor-এ চলার উপযোগী, preemptive kernel; নতুন driver আর file system যোগ করা যায় এমন স্তরভাগ।
SMP শুরু থেকেইএই কোর্সে Windows এর আগেও বারবার এসেছে, কিন্তু টুকরো টুকরো ভাবে। এই পর্ব সেগুলো এক জায়গায় জোড়া লাগাবে — একই কথা আবার বলবে না, বরং কোন টুকরো কোথায় বসে, সেটি দেখাবে:
| যেখানে দেখেছেন | Windows-এর যে টুকরো | এই পর্বে যেখানে বসবে |
|---|---|---|
| পর্ব ০৫ · System call | Win32 API বনাম ntdll-এর syscall | অংশ ২, ৭ |
| পর্ব ০৬ · OS-এর গঠন | Hybrid NT kernel: HAL, kernel, Executive | অংশ ২ |
| পর্ব ০৭ · বুট | Windows Boot Manager | অংশ ৮ |
| পর্ব ১২ · IPC | ALPC, named pipe, file mapping | অংশ ২ (csrss, lsass-এর সাথে কথা) |
| পর্ব ১৩ · Signal | Unix signal নেই; console event, APC | অংশ ৫ (APC) |
| পর্ব ১৯ · MLFQ | ৩২টি priority, quantum, boost | অংশ ৪ |
| পর্ব ৩৭ · Working set | Working set manager | অংশ ৬ |
| পর্ব ৩৮ · Linux-এ মেমরি | Commit charge, memory compression | অংশ ৬ |
| পর্ব ৩৯ · I/O system | IRQL, DPC, UMDF/KMDF | অংশ ৫, ৭ |
| পর্ব ৪৩ · File system | NTFS-এর MFT | অংশ ৭ |
| পর্ব ৪৬ · Protection | Security descriptor, DACL, ACE | অংশ ৩ (token) |
NT-কে একটি বড় সরকারি অফিস ভবন হিসেবে ভাবুন। নিচতলায় (kernel mode) আছে রেকর্ড রুম, ক্যাশ সেকশন আর নিরাপত্তা দপ্তর — সাধারণ মানুষ সেখানে ঢুকতে পারে না। ওপরতলায় (user mode) আছে সাধারণ ডেস্ক, যেখানে মানুষ ফর্ম জমা দেয়। মাঝখানে একটিমাত্র কাউন্টার — system call — যেখানে ফর্ম দিলে নিচতলার কর্মীরা কাজ করে দেন। আর প্রতিটি জিনিসের — ফাইল, চেয়ার, চাবি — একটি টোকেন নম্বর আছে; সেই নম্বরই handle.
২ · বড় ছবি — user mode আর kernel mode-এ কে কোথায়
পর্ব ০৬-এ দেখেছেন, Windows-কে বলা হয় hybrid kernel: নকশায় আলাদা আলাদা “ব্যবস্থাপক”, কিন্তু গতির জন্য তারা সবাই একই kernel address space-এ চলে। এবার পুরো ভবনের নকশাটি এক ছবিতে দেখা যাক — ওপর থেকে নিচে।
Windows NT-এর স্তর (সরলীকৃত)
After the architecture diagram in Windows Internals, 7th ed.
- প্রোগ্রাম ও সিস্টেম প্রসেস
explorer, smss, csrss, lsass, servicesuser mode - Subsystem DLL
kernel32, kernelbase, user32, gdi32Win32 API - ntdll.dll
NtCreateFile → syscallnative API, দরজা - ntoskrnl.exe
Executive + Kernelkernel mode - Driver, win32k, HAL
ntfs.sys, win32k.sys, hal.dllহার্ডওয়্যারের কাছে
User mode — সিস্টেম প্রসেসের দল
Task Manager-এর Details ট্যাব খুললে কয়েকটি প্রসেস সব Windows-এ দেখবেন। এরা কে, কী করে:
| প্রসেস | কাজ |
|---|---|
System (PID 4) | আসলে kernel-এর নিজের thread-গুলোর ঘর — driver আর executive-এর system thread এখানে থাকে। কোনো user-mode কোড নেই। |
smss.exe | Session Manager — বুটের পর প্রথম user-mode প্রসেস; session তৈরি করে, pagefile সাজায়, প্রতিটি session-এর জন্য csrss আর wininit/winlogon চালায়। |
csrss.exe | Client/Server Runtime Subsystem — প্রতি session-এ একটি; Win32 subsystem-এর user-mode অংশ (প্রসেস-thread তৈরির খবর রাখা, console-এর কিছু কাজ)। |
wininit.exe | Session 0-এর সূচনাকারী; services.exe আর lsass.exe চালু করে। |
services.exe | Service Control Manager — সব service চালু-বন্ধ করে (অংশ ৮)। |
lsass.exe | Local Security Authority — পাসওয়ার্ড বা অন্য পরিচয় যাচাই করে, সফল হলে access token তৈরি করায়। |
winlogon.exe | প্রতিটি interactive session-এর লগইন সামলায়; Ctrl+Alt+Del চেনে, LogonUI দেখায়, লগইনের পর userinit → explorer চালায়। |
svchost.exe | এক বা একাধিক service-এর আশ্রয়-প্রসেস; তাই এর অনেকগুলো কপি থাকে। |
সাধারণ প্রোগ্রাম Windows-এর সাথে কথা বলে Win32 API দিয়ে — kernel32.dll, kernelbase.dll (Windows 7 থেকে kernel32-এর অনেক কাজ এখানে সরেছে), user32.dll, gdi32.dll. এগুলোকে বলে environment subsystem-এর DLL: একটি নির্দিষ্ট “OS-এর চেহারা” তারা প্রোগ্রামকে দেখায়। নিচের আসল দরজা কিন্তু একটিই — ntdll.dll. এখানে থাকে native API (NtCreateFile, NtReadFile, NtAllocateVirtualMemory…), image loader, heap-এর অংশ আর প্রতিটি system call-এর ছোট্ট “stub”:
ntdll!NtCreateFile:
mov r10, rcx ; 1st argument: syscall clobbers rcx, so keep a copy
mov eax, 55h ; system service number for NtCreateFile (this build)
syscall ; CPU jumps to kernel mode: nt!KiSystemCall64
ret ; back with an NTSTATUS in eax (0 = STATUS_SUCCESS)পর্ব ০৫-এর Linux-এর syscall-এর সাথে প্রায় হুবহু মিল — নম্বর eax-এ, তারপর syscall instruction. পার্থক্য হলো নীতিতে: Linux-এর syscall নম্বর স্থির, প্রকাশ্য ABI. Windows-এর নম্বর build-ভেদে বদলায়, আর Microsoft শুধু Win32 API-কেই স্থির চুক্তি হিসেবে ধরে। তাই Windows-এ “সরাসরি syscall” লেখা প্রোগ্রাম পরের আপডেটে ভেঙে যেতে পারে।
Kernel mode — ntoskrnl, HAL, driver আর win32k
ntoskrnl.exe-এর ওপরের অংশ — Executive: object manager, security reference monitor, process manager, memory manager, I/O manager, cache manager, configuration manager (registry), Plug and Play ও power manager, ALPC. এরা নীতি (policy) ঠিক করে।- নিচের অংশ — “kernel”: thread dispatching ও scheduling, interrupt আর exception বিতরণ, multiprocessor সমন্বয়ের মৌলিক যন্ত্র (spinlock, dispatcher object). এটি mechanism দেয়।
- HAL (
hal.dll): interrupt controller, timer, মাদারবোর্ডের পার্থক্য লুকায়। (নতুন Windows-এ HAL-এর অনেক অংশ kernel-এর ভেতরেই জুড়ে দেওয়া হয়েছে বলে Microsoft-এর সূত্রে উল্লেখ আছে।) - Driver (
.sys): ডিভাইস driver, file system driver (ntfs.sys), filter driver — সবই kernel mode-এ, একই অধিকারে। - win32k.sys: জানালা ব্যবস্থাপক আর গ্রাফিক্সের kernel অংশ। NT 4.0 (১৯৯৬) গতির জন্য এটিকে csrss থেকে kernel-এ নামিয়ে আনে — এর দাম নিয়ে ভাবনার প্রশ্ন ২ দেখুন।
Windows-এ “system call” মানে Win32 ফাংশন নয়। CreateFileW একটি সাধারণ library ফাংশন; সেটি ntdll-এর NtCreateFile ডাকে, আর কেবল সেখানেই CPU kernel mode-এ ঢোকে। Linux-এর fopen → open → syscall-এর মতো একই তিন-ধাপের ছক।
৩ · Object manager আর security — সবকিছুই object, হাতে handle
NT-এর সবচেয়ে সুন্দর নকশা-সিদ্ধান্ত সম্ভবত এটি: kernel যা কিছু সামলায় — ফাইল, প্রসেস, thread, event, mutex, semaphore, registry key, token, job, section (ভাগ করা মেমরি) — সবই একই কাঠামোর object. প্রতিটি object-এর একটি সাধারণ header থাকে (নাম, ধরন, security descriptor, গণনা) আর ধরনভেদে আলাদা body. এদের দেখভাল করে object manager.
প্রসেস কখনো object-এর আসল ঠিকানা পায় না। পায় একটি সংখ্যা — handle — যা আসলে প্রসেসের নিজস্ব handle table-এর একটি সারির নম্বর। প্রতিটি সারিতে থাকে object-এর pointer আর খোলার সময় অনুমোদিত অধিকার (granted access). Handle-এর মান ৪-এর গুণিতক (নিচের দুটি বিট অন্য কাজে লাগে)।
ব্যাংকের লকার-রুমের কথা ভাবুন। লকারগুলো ভল্টে, গ্রাহক সেখানে সরাসরি যেতে পারেন না। কাউন্টারে পরিচয়পত্র দেখালে কর্মকর্তা একবার যাচাই করে একটি টোকেন দেন — “৪ নম্বর, শুধু দেখার অনুমতি”। এরপর প্রতিবার টোকেন দেখালেই কাজ হয়, পরিচয় আর যাচাই হয় না। টোকেন ফেরত দিলে খাতায় গণনা এক কমে; শেষ টোকেন ফেরত এলে লকার খালি করা যায়। Handle ঠিক এই টোকেন।
এখান থেকে দুটি গুরুত্বপূর্ণ নিয়ম বেরোয়:
- অধিকার যাচাই হয় খোলার সময়।
CreateFileবাOpenProcess-এর সময় security reference monitor একবার যাচাই করে; এরপর handle দিয়ে প্রতিটি কাজে শুধু দেখা হয় চাওয়া কাজটি granted access-এর মধ্যে কি না। Unix-এর file descriptor-ও (পর্ব ৪১) একই নীতিতে চলে। - দুটি গণনা: handle count (কতগুলো handle খোলা) আর pointer বা reference count (handle ছাড়াও kernel-এর ভেতরের কতগুলো জায়গা object-টিকে ধরে আছে)। Handle count শূন্য হলে নামওয়ালা object namespace থেকে সরে যায় (ফাইলের ক্ষেত্রে driver “cleanup” বার্তা পায়); reference count শূন্য হলে তবেই object মুছে যায়।
নামওয়ালা object থাকে একটি গাছের মতো object namespace-এ — Unix-এর ফাইল-গাছের মতো, কিন্তু kernel-এর ভেতরে। কয়েকটি পরিচিত ডাল: \Device\HarddiskVolume3 (একটি volume), \GLOBAL??\C: (একটি symbolic link, যা C:-কে আসল device-এ নিয়ে যায়), \Sessions\1\BaseNamedObjects\ (প্রোগ্রামের তৈরি নামওয়ালা event, mutex), \REGISTRY\MACHINE (registry). Sysinternals-এর WinObj দিয়ে এই গাছ ঘুরে দেখা যায়।
Win32 থেকে handle দেখতে এমন — কোডটি Windows-এর জন্য (এখানের Linux runner-এ চলবে না), আউটপুটও একটি উদাহরণ:
#include <windows.h>
#include <stdio.h>
int main(void) {
HANDLE h = CreateFileW(L"C:\\data\\report.txt", GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
printf("CreateFile failed, error %lu\n", GetLastError()); /* 2 = not found */
return 1;
}
HANDLE dup;
DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &dup,
0, FALSE, DUPLICATE_SAME_ACCESS); /* a second handle, same object */
printf("handle %p, duplicate %p\n", h, dup);
CloseHandle(h);
CloseHandle(dup); /* last handle closed */
return 0;
}
/* example output: handle 00000000000000A4, duplicate 00000000000000A8 */এবার ভেতরের হিসাবটি নিজে চালিয়ে দেখুন। নিচের simulator দুটি প্রসেস A আর B-এর handle table আর প্রতিটি object-এর H (handle count) ও P (pointer count) রাখে। সরলতার জন্য handle শুরু হয়েছে 0x4 থেকে; আসল প্রসেসে loader আগেই অনেক handle খোলে, তাই মান বড় হয়।
// Simplified NT object manager: per-process handle tables, two counts per object
const objs = {}, tab = { A: [], B: [] };
const hv = i => "0x" + ((i + 1) * 4).toString(16); // handle = (slot+1)*4
function open(p, name, access) { // create or open by name
if (!objs[name]) objs[name] = { H: 0, P: 0 };
let i = tab[p].indexOf(null);
if (i < 0) i = tab[p].push(null) - 1;
tab[p][i] = { name, access }; objs[name].H++; objs[name].P++;
return hv(i);
}
function deref(name) { // drop one pointer reference
if (--objs[name].P === 0) { delete objs[name]; return " -> " + name + " DELETED"; }
return "";
}
function close(p, h) {
const i = parseInt(h, 16) / 4 - 1, e = tab[p][i];
tab[p][i] = null; objs[e.name].H--; return deref(e.name);
}
function show(what, note = "") {
console.log(what + note);
for (const p in tab) console.log(" " + p + ": " + (tab[p].map((e, i) =>
e ? hv(i) + "=" + e.name + "(" + e.access + ")" : hv(i) + "=free").join(" ") || "(empty)"));
console.log(" objects: " + Object.keys(objs).map(n => n + " H=" + objs[n].H + " P=" + objs[n].P).join(", "));
}
let a1 = open("A", "data.txt", "RW"); show("1. A opens data.txt -> " + a1);
let a2 = open("A", "JobDone", "ALL"); show("2. A creates event JobDone -> " + a2);
let b1 = open("B", "JobDone", "SYNC"); show("3. B opens JobDone by name -> " + b1);
let b2 = open("B", "data.txt", "R"); show("4. A duplicates its handle into B -> " + b2);
objs["data.txt"].P++; show("5. kernel references data.txt (I/O in flight)");
show("6. A closes " + a1, close("A", a1));
show("7. B closes " + b2, close("B", b2));
show("8. I/O completes, kernel drops its reference", deref("data.txt"));
let a3 = open("A", "log.txt", "W"); show("9. A opens log.txt -> " + a3 + " (slot reused)");
show("10. A closes " + a2 + ", B closes " + b1, close("A", a2) + close("B", b1));আউটপুটের মূল অংশ:
3. B opens JobDone by name -> 0x4
A: 0x4=data.txt(RW) 0x8=JobDone(ALL)
B: 0x4=JobDone(SYNC)
objects: data.txt H=1 P=1, JobDone H=2 P=2
5. kernel references data.txt (I/O in flight)
objects: data.txt H=2 P=3, JobDone H=2 P=2
7. B closes 0x8
A: 0x4=free 0x8=JobDone(ALL)
B: 0x4=JobDone(SYNC) 0x8=free
objects: data.txt H=0 P=1, JobDone H=2 P=2
8. I/O completes, kernel drops its reference -> data.txt DELETED
9. A opens log.txt -> 0x4 (slot reused)
10. A closes 0x8, B closes 0x4 -> JobDone DELETED
(১) Handle মান প্রসেস-নিজস্ব: ধাপ ৩-এ A-এর 0x4 হলো data.txt, আর B-এর 0x4 হলো JobDone — একই সংখ্যা, ভিন্ন object. তাই handle-এর মান অন্য প্রসেসে পাঠিয়ে লাভ নেই; লাগে DuplicateHandle (ধাপ ৪), আর সেখানে অধিকার কমানো যায় — B পেল শুধু R. (২) দুই গণনার পার্থক্য: ধাপ ৭-এ সব handle বন্ধ (H=0), তবু object টিকে আছে (P=1), কারণ kernel-এর একটি অসমাপ্ত I/O এটিকে ধরে রেখেছে। ধাপ ৮-এ P শূন্য হতেই object মুছে গেল। (৩) Slot আবার ব্যবহার: ধাপ ৯-এ খালি হওয়া 0x4 নতুন ফাইল পেল। কোনো প্রোগ্রাম ভুল করে পুরোনো handle আবার ব্যবহার করলে (use-after-close) সে নীরবে অন্য object-এ হাত দেবে — এজন্য Driver Verifier আর Application Verifier-এর মতো টুল handle-এর ভুল ধরার ব্যবস্থা রাখে।
Linux-এ এর সবচেয়ে কাছের জিনিস file descriptor table. এখানের runner Linux, তাই তুলনার জন্য নিচের bash block চালিয়ে দেখুন — এটি Windows নয়, Linux-এর সমতুল্য দৃশ্য:
echo "== Linux 'handle table': /proc/self/fd of this ls process =="
exec 3</etc/passwd 4>notes.txt # the shell opens two more files
ls -l /proc/self/fd | awk 'NR > 1 {print $9, $10, $11}'
echo "== dup: fd 5 shares fd 3's open file (like DuplicateHandle) =="
exec 5<&3
ls -l /proc/$$/fd | awk 'NR > 1 && $9 >= 3 {print $9, $10, $11}'
echo "== per-process limit on open fds =="
ulimit -n
echo "== threads and resident memory (Windows: thread count, working set) =="
grep -E '^(Name|Threads|VmSize|VmRSS|VmHWM):' /proc/self/status
ps -o pid,nlwp,rss,vsz,comm -p $$
exec 3<&- 4>&- 5<&-একটি আসল run-এর আউটপুট:
== Linux 'handle table': /proc/self/fd of this ls process ==
0 -> pipe:[921268467]
1 -> pipe:[921268508]
2 -> pipe:[921268469]
3 -> /etc/passwd
4 -> /home/wandbox/notes.txt
5 -> /proc/3/fd
== dup: fd 5 shares fd 3's open file (like DuplicateHandle) ==
255 -> /home/wandbox/prog.sh
3 -> /etc/passwd
4 -> /home/wandbox/notes.txt
5 -> /etc/passwd
== per-process limit on open fds ==
1024
== threads and resident memory (Windows: thread count, working set) ==
Name: grep
VmSize: 3660 kB
VmHWM: 1864 kB
VmRSS: 1864 kB
Threads: 1
PID NLWP RSS VSZ COMMAND
2 1 3408 4324 bash
(১) /proc/self মানে “যে প্রসেস এখন পড়ছে” — প্রথম তালিকাটি ls-এর নিজের: ০–২ উত্তরাধিকারসূত্রে পাওয়া pipe, ৩ আর ৪ shell থেকে পাওয়া ফাইল, আর ৫ হলো ls-এর নিজের খোলা /proc/3/fd ডিরেক্টরি — ঠিক যেমন Windows-এ প্রতিটি প্রসেসের handle table আলাদা আর child প্রসেস inheritable handle পায়। (২) exec 5<&3-এর পর shell-এর ($$) তালিকায় fd ৩ আর ৫ দুটোই /etc/passwd — একই খোলা ফাইল, দুটি সূচক; DuplicateHandle-এর সমতুল্য। ২৫৫ হলো bash-এর নিজের script ফাইল। (৩) ulimit -n ১০২৪ — প্রতি-প্রসেস সীমা; Windows-এর handle সীমা অনেক বড় হলেও অসীম নয়। (৪) Threads: 1 আর VmRSS (এখানে ১৮৬৪ kB, grep-এর) হলো Task Manager-এর thread সংখ্যা আর working set-এর Linux-রূপ; ps-এর NLWP আর RSS একই জিনিস shell-এর জন্য দেখায়। PID আর pipe-এর নম্বর প্রতিবার আলাদা হবে। মনে রাখবেন, এটি উপমা: Linux-এর fd শুধু “ফাইলের মতো” জিনিসের জন্য, আর RSS-এ ভাগ করা page-ও গোনা হয়, যেখানে Task Manager-এর Memory কলাম দেখায় শুধু private working set.
Security reference monitor — token, SID, privilege আর UAC
“কে খুলছে?” — এই প্রশ্নের উত্তর রাখে প্রতিটি প্রসেসের access token. লগইনের সময় lsass পরিচয় যাচাই করার পর token তৈরি হয়, আর প্রতিটি নতুন প্রসেস সাধারণত parent-এর token-এর কপি পায়। Token-এ থাকে ব্যবহারকারীর SID, তার সব group-এর SID, privilege-এর তালিকা (যেমন SeShutdownPrivilege, SeDebugPrivilege), আর integrity level. Object খোলার সময় security reference monitor token-কে object-এর DACL-এর সাথে মেলায় — DACL-এর গঠন আর ACE-এর ক্রম পর্ব ৪৬-এ বিস্তারিত দেখেছেন, এখানে আর বলছি না।
Windows Vista (২০০৭) থেকে UAC চালু: administrator group-এর ব্যবহারকারী লগইন করলে দুটি token তৈরি হয় — একটি ছাঁটা (Medium integrity, admin-অধিকার নিষ্ক্রিয়) আর একটি পূর্ণ (High integrity). সাধারণ প্রোগ্রাম চলে ছাঁটাটিতে; পূর্ণ অধিকার লাগলে সম্মতির জানালা আসে, যা আলাদা “secure desktop”-এ দেখানো হয়, যাতে অন্য প্রোগ্রাম নকল ক্লিক পাঠাতে না পারে। Sandbox, AppContainer আর integrity level-এর ব্যবহার পর্ব ৪৯-এ।
৪ · Process, thread আর job — আর scheduler-এর এক ঝলক
Windows-এ process হলো একটি পাত্র: একটি address space, একটি handle table, একটি access token আর অন্তত একটি thread. CPU পায় শুধু thread — scheduler কখনো “প্রসেস” বাছে না, বাছে thread. Linux-এর মতো (পর্ব ১৪) Windows-ও one-to-one মডেল: প্রতিটি user thread-এর পেছনে একটি kernel thread.
Kernel-এর ভেতরে প্রতিটি প্রসেস একটি EPROCESS কাঠামো, যার শুরুতে বসানো থাকে kernel-স্তরের KPROCESS; প্রতিটি thread একটি ETHREAD, যার শুরুতে KTHREAD. Executive-এর ব্যবস্থাপকেরা E-অংশ দেখে, নিচের kernel (dispatcher) দেখে শুধু K-অংশ — স্তরভাগের সুন্দর উদাহরণ। User space-এ আছে তাদের ছায়া: PEB (process environment block) আর প্রতি thread-এ TEB — loader, heap আর thread-local storage এগুলো ব্যবহার করে।
প্রসেস তৈরি — fork নেই
CreateProcess এক call-এ নতুন address space বানায়, exe ফাইলটি map করে, প্রথম thread তৈরি করে, csrss-কে জানায়, তারপর ntdll-এর loader DLL-গুলো লোড করে। Unix-এর fork + exec-এর মতো দুই ধাপ নয় (পর্ব ১০)।
Job — প্রসেসের দল
একটি job object কয়েকটি প্রসেসকে এক দলে বাঁধে: মোট মেমরি বা CPU-র সীমা, “job বন্ধ হলে সবাইকে শেষ করো”, UI-এর বিধিনিষেধ। ব্রাউজারের sandbox আর Windows container এর ওপর দাঁড়ানো — Linux-এর cgroup-এর আত্মীয় (পর্ব ৫১)।
CreateJobObjectFiber আর UMS
Fiber হলো একটি thread-এর ভেতরে প্রোগ্রাম নিজে বদলায় এমন হালকা কাজের ধারা — kernel এদের চেনে না (user-level thread, পর্ব ১৪)। 64-bit Windows 7-এ আসা User-Mode Scheduling (UMS) Microsoft-এর ডকুমেন্টেশন অনুযায়ী Windows 11-এ আর সমর্থিত নয়।
ConvertThreadToFiberScheduler — পর্ব ১৯-এর সংক্ষিপ্ত পুনরাবৃত্তি
পর্ব ১৯-এ বিস্তারিত দেখেছেন: Windows-এর scheduler ৩২ স্তরের preemptive priority scheduler — ১৬–৩১ “real-time”, ১–১৫ variable, ০ শুধু zero page thread-এর জন্য (অংশ ৬-এ একে আবার দেখবেন)। প্রতিটি স্তরে Round Robin, আর variable স্তরে I/O শেষ হলে, foreground জানালায় আর অনাহারে থাকা thread-এ সাময়িক boost. এখানে শুধু দুটি নতুন কথা যোগ করি:
- Ready queue-র কাঠামো: প্রতিটি processor-এর জন্য ৩২টি ready queue আর একটি ৩২-বিটের “ready summary” — যে স্তরে কেউ অপেক্ষায়, সেই বিট ১. সর্বোচ্চ priority-র thread খুঁজতে একটি bit-scan instruction-ই যথেষ্ট, তাই সিদ্ধান্ত O(1).
- Quantum: client Windows-এ ডিফল্ট মোটামুটি ২টি clock tick, server-এ ১২টি — সার্ভারে লম্বা quantum মানে কম context switch, বেশি throughput. Clock tick-এর দৈর্ঘ্য যন্ত্রভেদে আলাদা; অনেক x64 যন্ত্রে প্রায় ১৫.৬ ms (তবে timer resolution প্রোগ্রাম বদলাতে পারে)। Client-এ foreground প্রসেসের quantum লম্বা করার একটি সেটিংও আছে।
নিজের Windows যন্ত্রে প্রসেস, handle আর working set একসাথে দেখতে PowerShell খুলুন (নিচের আউটপুট একটি উদাহরণ — আপনার সংখ্যা ভিন্ন হবে):
PS> Get-Process -Name explorer, notepad
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
2861 112 80332 169164 45.67 6844 1 explorer
247 18 5028 22908 0.12 11320 1 notepad
PS> (Get-Process -Id 6844).Threads.Count
98
PS> Get-Process | Sort-Object Handles -Descending | Select-Object -First 3 Name, Id, Handles
Name Id Handles
---- -- -------
System 4 5412
explorer 6844 2861
lsass 912 2210কলামগুলোর অর্থ: Handles = এই প্রসেসের handle table-এ খোলা handle; PM(K) = private bytes, অর্থাৎ এই প্রসেসের commit-এর হিসাব; WS(K) = working set, এখন RAM-এ থাকা page; SI = session নম্বর (১ = প্রথম লগইন করা ব্যবহারকারী; service-গুলো session 0-এ)। খেয়াল করুন, System প্রসেসেরও হাজার হাজার handle — kernel আর driver-এর নিজের খোলা object.
৫ · IRQL, DPC আর APC — CPU-র অগ্রাধিকারের সিঁড়ি
Thread-এর priority বলে “কোন thread আগে CPU পাবে”। কিন্তু interrupt তো কোনো thread নয় — নেটওয়ার্ক কার্ড যেকোনো মুহূর্তে বাধা দিতে পারে। এজন্য NT-তে দ্বিতীয় একটি সিঁড়ি আছে: প্রতিটি core এই মুহূর্তে কোন IRQL-এ চলছে। পর্ব ৩৯-এ সংজ্ঞা দেখেছেন; এখানে পুরো সিঁড়িটি (x64):
| IRQL (x64) | নাম | কে চলে | কী নিষেধ |
|---|---|---|---|
| 15 | HIGH_LEVEL / PROFILE | machine check, profiling | প্রায় সবকিছু |
| 14 | IPI / POWER | core থেকে core-এ বার্তা | — |
| 13 | CLOCK | clock interrupt: সময়, quantum-এর হিসাব | — |
| 3–11 | DIRQL | ডিভাইসের ISR | ঘুমানো, page fault |
| 2 | DISPATCH_LEVEL | DPC, scheduler নিজে, spinlock ধরা কোড | ঘুমানো/wait, pageable মেমরি ছোঁয়া, thread switch |
| 1 | APC_LEVEL | kernel APC | অন্য APC |
| 0 | PASSIVE_LEVEL | সাধারণ thread — user ও kernel কোড | কিছুই না |
নিয়ম একটাই: একটি core যে IRQL-এ চলছে, তার সমান বা নিচের সব interrupt অপেক্ষা করে। x64-এ বর্তমান IRQL CPU-র CR8 register-এ থাকে। আর scheduler নিজে DISPATCH_LEVEL-এ চলে বলে সেই স্তরে বা ওপরে কোনো thread switch সম্ভব নয় — তাই সেখানে page fault হলে (যার মেটাতে ডিস্কের অপেক্ষা লাগে) Windows এগোতে পারে না, আর IRQL_NOT_LESS_OR_EQUAL বা DRIVER_IRQL_NOT_LESS_OR_EQUAL নামের নীল পর্দা (bugcheck) দেখায়। এটি ভুল driver-এর সবচেয়ে পরিচিত লক্ষণ।
ISR-কে খুব ছোট রাখতে হয়, তাই ভারী কাজ যায় DPC-তে (Linux-এর softirq-এর সমতুল্য, পর্ব ৩৯)। আর কোনো নির্দিষ্ট thread-এর context-এ কাজ করাতে লাগে APC — যেমন I/O শেষে ফলাফল সেই thread-এর address space-এ পৌঁছে দেওয়া। User APC পৌঁছায় কেবল তখন, যখন thread “alertable” অপেক্ষায় (SleepEx, WaitForSingleObjectEx) — Unix signal-এর মতো চলমান কোডে হঠাৎ ঢুকে পড়ে না (পর্ব ১৩)। নিচে একটি নেটওয়ার্ক প্যাকেট আসার পুরো ঘটনা দেখুন (মানগুলো উদাহরণ, প্রবাহটি সরলীকৃত):
| ঘটনা | |
| IRQL | |
| এখন চলছে | |
| DPC queue | |
| অপেক্ষায় (masked) | |
| T1 |
- সাধারণ অবস্থা। T1 নামের একটি সাধারণ thread (priority 8) PASSIVE_LEVEL-এ (০) চলছে। কোনো interrupt আটকানো নেই, DPC queue খালি।
- NIC interrupt. নেটওয়ার্ক কার্ড interrupt দিল; CPU T1-কে থামিয়ে IRQL তুলল ডিভাইসের DIRQL-এ (এখানে ৬ ধরা হয়েছে)। ISR শুধু জরুরি কাজটুকু করে — ডিভাইসকে জানায় “পেয়েছি”, আর বাকি কাজের জন্য একটি DPC queue-তে রাখে। এখন ৬ বা তার নিচের সব অপেক্ষায়।
- Clock interrupt — আরও উঁচু। ISR চলার মাঝেই timer বাজল। CLOCK (১৩) > ৬, তাই clock ISR NIC-এর ISR-কেও থামিয়ে দিল — interrupt-এর ভেতরে interrupt. এটি সময় হালনাগাদ করে আর T1-এর quantum থেকে সময় কাটে।
- পেছনে ফেরা। Clock ISR শেষ, IRQL আবার ৬; NIC ISR তার বাকি অংশ শেষ করল। এখনো T1 থেমে আছে — interrupt চলাকালীন কোনো thread চলে না।
- DPC চলছে, DISPATCH_LEVEL (২). ISR ফিরলে IRQL নামতে গিয়ে kernel দেখে DPC queue খালি নয়; তাই ২-এ থেমে NicDpc চালায়। Packet প্রক্রিয়া হচ্ছে, ডিভাইসের interrupt আবার আসতে পারে (৬ > ২), কিন্তু thread switch আর APC অপেক্ষায়। এখানে pageable মেমরি ছোঁয়া নিষেধ।
- IRP complete, T2 জাগে। DPC অপেক্ষমাণ
recv()-এর IRP সম্পন্ন করল। সেই অনুরোধের মালিক T2 ready হলো; নেটওয়ার্ক I/O-র জন্য তার base priority ৮-এর ওপর ২ boost — এখন ১০, যা T1-এর ৮-এর চেয়ে বেশি। Dispatcher, এখনো DISPATCH_LEVEL-এ, T1-কে preempt করার সিদ্ধান্ত নিল। - Context switch আর APC. Core-এ T2 এলো। প্রচলিত পথে I/O manager T2-এর জন্য একটি special kernel APC রেখেছিল; IRQL ২ থেকে নামার পথে সেটি APC_LEVEL-এ (১) T2-এর নিজের address space-এ চলে — ফলাফল কপি, event সেট। এ কাজটি DPC পারত না, কারণ DPC যেকোনো প্রসেসের context-এ চলে।
- আবার PASSIVE. IRQL ০; T2 user mode-এ ফিরে
recv()-এর ফল পেল। T1 তার বাকি quantum নিয়ে ready queue-তে অপেক্ষায়। পুরো ঘটনাটি সাধারণত কয়েক মাইক্রোসেকেন্ডের।
Task Manager-এ “System interrupts” বা Process Explorer-এ “Interrupts” আর “DPCs” সারি উঁচু থাকলে সন্দেহের তালিকায় প্রথমে আসে কোনো driver. অডিও ভেঙে যাওয়া বা মাউস আটকে যাওয়ার মতো উপসর্গে Microsoft-এর Windows Performance Toolkit দিয়ে DPC-র সময় মাপা যায়। Driver বদলের আগে নির্মাতার সই করা সংস্করণই নিন।
৬ · Memory manager — VAD, reserve-commit আর page-এর তালিকা
Address space-এর খাতা — VAD
প্রতিটি প্রসেসের address space-এর কোন অংশ কী কাজে বরাদ্দ, তা memory manager রাখে VAD (virtual address descriptor)-এর একটি self-balancing গাছে — প্রতিটি node একটি টানা এলাকা: কোথা থেকে কোথা, private না ফাইল-ম্যাপ করা, কী অধিকার। Linux-এর VMA-র (পর্ব ৩৮) প্রায় হুবহু সমতুল্য। Page table-এ তখনো কিছু না থাকলেও VAD দেখে page fault-এর সময় বোঝা যায় ঠিকানাটি বৈধ কি না।
Reserve বনাম commit — জায়গা ঠিক করা আর প্রতিশ্রুতি দেওয়া
Windows-এ মেমরি চাওয়ার দুটি আলাদা ধাপ, যা VirtualAlloc-এ স্পষ্ট দেখা যায়:
- Reserve (
MEM_RESERVE): শুধু virtual ঠিকানার একটি এলাকা আটকে রাখা — VAD-এ একটি node; কোনো RAM বা pagefile খরচ নেই। ধরুন কমিউনিটি সেন্টারে বিয়ের জন্য তারিখ বুক করলেন, কিন্তু অতিথির সংখ্যা এখনো জানাননি। - Commit (
MEM_COMMIT): OS প্রতিশ্রুতি দেয় যে প্রয়োজনে এই page-গুলোর জন্য RAM বা pagefile-এ জায়গা থাকবে। প্রতিটি commit যোগ হয় সিস্টেমের commit charge-এ, যা commit limit (মোটামুটি RAM + সব pagefile-এর আকার) ছাড়াতে পারে না। - প্রথম ছোঁয়া: commit করলেও physical page আসে কেবল প্রথম ছোঁয়ায় — demand-zero fault (পর্ব ৩৫-এর zero-fill-on-demand).
Thread-এর stack এর সুন্দর উদাহরণ: ডিফল্টে সাধারণত ১ MiB reserve, শুরুতে শুধু কয়েকটি page commit, আর তার নিচে একটি guard page — stack বাড়তে বাড়তে guard page ছুঁলে আরও commit হয়। Linux-এর সাথে দর্শনের পার্থক্যটি পর্ব ৩৮-এ দেখেছেন: Linux overcommit করে আর সংকটে OOM killer ডাকে; Windows প্রতিশ্রুতির মুহূর্তেই হিসাব মেলায়, সীমা ছাড়ালে VirtualAlloc ব্যর্থ হয় (pagefile নিজে থেকে বাড়ার সুযোগ থাকলে আগে সেটি বাড়ে)।
RAM ১৬ GiB, pagefile ৮ GiB → commit limit ≈ ১৬ + ৮ = ২৪ GiB. এখনকার commit charge ২০ GiB হলে ৫ GiB-এর নতুন commit চাওয়া মানে ২৫ > ২৪ — অনুরোধ ব্যর্থ, যদি না pagefile বড় হতে পারে। অথচ ৫ GiB শুধু reserve করলে সফল হবে, কারণ reserve commit charge বাড়ায় না। (Kernel-এর নিজের ব্যবহার ইত্যাদি মিলিয়ে বাস্তব সংখ্যা একটু কম হয়।)
Working set আর page frame-এর খাতা
পর্ব ৩৭-এ দেখেছেন, Windows প্রতিটি প্রসেসের working set আলাদা রাখে আর working set manager চাপ বাড়লে সেখান থেকে page ছাঁটে। কিন্তু ছাঁটা page যায় কোথায়? উত্তর: PFN database — RAM-এর প্রতিটি physical page frame-এর জন্য একটি entry, যাতে লেখা frame-টি এখন কোন অবস্থায়। অবস্থা অনুযায়ী frame-গুলো কয়েকটি তালিকায় সাজানো:
| তালিকা | ভেতরে কী | কে ভরে, কে নেয় |
|---|---|---|
| Free | পুরোনো ডেটা-সহ খালি frame | প্রসেস শেষ হলে আসে; ডিস্ক থেকে পড়ার জন্য নেওয়া হয় (পড়া ডেটা পুরোনোটা মুছে দেবে) |
| Zeroed | শূন্য দিয়ে ভরা খালি frame | zero page thread (priority ০) ভরে; demand-zero fault নেয় |
| Standby | পরিষ্কার page — ডেটার কপি ডিস্কে আছে | ছাঁটা পরিষ্কার page আসে; আবার চাইলে soft fault, নইলে অন্য কাজে নেওয়া হয় |
| Modified | “ময়লা” page — ডিস্কে লেখা হয়নি | ছাঁটা dirty page আসে; modified page writer pagefile-এ লিখে Standby-তে পাঠায় |
| Active | কোনো working set-এ ব্যবহৃত | তালিকা নয় — page table-এ valid |
ছাঁটা page-এর page table entry “invalid” হয়, কিন্তু তখনো frame-টিকে চিনে রাখে — একে বলে transition অবস্থা। তাই প্রসেস আবার চাইলে ডিস্ক না ছুঁয়েই page ফেরত আসে: soft fault. Standby তালিকা আসলে Vista থেকে ৮টি priority-ভিত্তিক তালিকা, আর SysMain (আগের SuperFetch) service আগেভাগে প্রায়ই-ব্যবহৃত ডেটা ডিস্ক থেকে Standby-তে এনে রাখে। এজন্যই Windows-এ “free” মেমরি কম দেখালেও ভয়ের কিছু নেই — Standby হলো ঝুঁকিহীন cache, Linux-এর page cache-এর মতো। এবার একটি frame-এর পুরো জীবনচক্র দেখুন:
| ঘটনা | |
| F0 কোথায় | |
| F0-এর ভেতরে | |
| A-এর PTE (H) | |
| pagefile-এ H | |
| খরচ |
- Free তালিকায়। F0-তে আগের কোনো মালিকের ডেটা পড়ে আছে। এই অবস্থায় এটি কাউকে নতুন page হিসেবে দেওয়া যায় না — আগের ডেটা ফাঁস হবে।
- Zero page thread. সবচেয়ে নিচু priority-র (০) এই thread CPU অলস থাকলে Free frame শূন্য করে Zeroed তালিকায় রাখে, যাতে পরে fault-এর সময় দেরি না হয়।
- প্রথম ছোঁয়া — demand-zero. A আগেই H commit করেছিল, কিন্তু ছোঁয়নি। এখন ছুঁতেই fault; memory manager Zeroed তালিকা থেকে F0 নিয়ে A-এর working set-এ দিল, PTE valid → F0. ডিস্কের কোনো কাজ নেই।
- লেখা। A H-এ ডেটা লিখল। এই page-এর কোনো কপি ডিস্কে নেই, তাই এটি “ময়লা” (dirty) — ফেলে দিলে ডেটা হারাবে।
- ছাঁটা → Modified. মেমরির চাপে working set manager H-কে A-এর working set থেকে সরাল। ডেটা ডিস্কে নেই, তাই F0 গেল Modified তালিকায়। PTE এখন invalid, কিন্তু “transition” — এখনো F0-কে চেনে।
- Modified page writer. একটি system thread F0-এর ডেটা pagefile-এ লিখল। এখন কপি ডিস্কে আছে, তাই F0 পরিষ্কার — চলে এলো Standby তালিকায়। ডেটা তবু RAM-এ রয়ে গেছে।
- Soft fault — ফেরত আসা। A আবার H ছুঁল। PTE transition অবস্থায় F0-কে চেনে, তাই F0 Standby থেকে সরাসরি আবার A-এর working set-এ — কোনো ডিস্ক-পড়া নেই, খরচ মাইক্রোসেকেন্ডের ঘরে।
- আবার ছাঁটা — এবার সরাসরি Standby. এর মধ্যে A H-এ আর লেখেনি, আর pagefile-এর কপিটি এখনো ঠিক আছে। তাই এবার Modified-এ না গিয়ে সোজা Standby-তে; আবার লেখার খরচ বাঁচল।
- পুনর্ব্যবহার (repurpose). প্রসেস B-এর একটি নতুন page লাগবে; Zeroed আর Free দুটোই খালি। তাই memory manager Standby-র F0 নিল: আগে A-এর PTE বদলে pagefile-এর ঠিকানা লিখল, তারপর F0 শূন্য করে B-কে দিল। A-এর ডেটা এখন শুধু pagefile-এ।
- Hard fault. B শেষ হলো — তার private page-গুলো Free তালিকায় (F0-ও)। এবার A আবার H ছুঁলে আর soft fault সম্ভব নয়: pagefile থেকে পড়তে হলো একটি Free frame-এ। এটিই আসল ডিস্ক-খরচ; Task Manager-এর “Hard faults/sec” এটিই গোনে।
একই নিয়ম এবার চারটি frame আর দুটি প্রসেসে চালিয়ে দেখুন। এখানে A-এর একটি code page C-ও আছে, যা app.exe ফাইল থেকে আসে — ফাইল-ব্যাকড পরিষ্কার page, তাই ছাঁটা হলে সোজা Standby-তে যায়।
// Simplified Windows page lists: 4 physical frames, processes A and B
const L = { Free: ["F0", "F1", "F2", "F3"], Zeroed: [], Standby: [], Modified: [] };
const ws = { A: {}, B: {} }, owner = {}, onDisk = new Set(), dirty = new Set();
const pop = (...ls) => { for (const l of ls) if (L[l].length) return [L[l].shift(), l]; };
function give(p, pg, f) { ws[p][pg] = f; owner[f] = p + ":" + pg; }
function touch(p, pg) { // page not in working set -> fault
const id = p + ":" + pg;
for (const l of ["Standby", "Modified"]) {
const i = L[l].findIndex(f => owner[f] === id);
if (i >= 0) { give(p, pg, L[l].splice(i, 1)[0]); return "SOFT fault, back from " + l; }
}
if (onDisk.has(id)) { const [f, l] = pop("Free", "Zeroed", "Standby");
give(p, pg, f); return "HARD fault, disk read into " + f + " (from " + l + ")"; }
const [f, l] = pop("Zeroed", "Free", "Standby"), old = owner[f]; give(p, pg, f); dirty.add(id);
return "demand-zero: " + f + " from " + l + (l === "Standby" ? " (was " + old + ")" : "")
+ (l === "Zeroed" ? "" : ", zeroed first");
}
function trim(p) { // working set manager removes all pages
for (const pg in ws[p]) { const f = ws[p][pg]; L[dirty.has(p + ":" + pg) ? "Modified" : "Standby"].push(f); }
ws[p] = {}; return "clean -> Standby, dirty -> Modified";
}
function writer() { for (const f of L.Modified) { onDisk.add(owner[f]); dirty.delete(owner[f]); }
L.Standby.push(...L.Modified.splice(0)); return "written to pagefile, now Standby"; }
function exit(p) { for (const pg in ws[p]) { L.Free.push(ws[p][pg]); delete owner[ws[p][pg]]; }
ws[p] = {}; return "private pages -> Free"; }
const lst = l => l + "[" + L[l].map(f => l === "Standby" || l === "Modified" ? f + "=" + owner[f] : f) + "]";
const wsS = p => p + "{" + Object.entries(ws[p]).map(e => e.join("=")) + "}";
const show = (s, r) => console.log(s + ": " + r + "\n " + ["Free", "Zeroed", "Standby", "Modified"].map(lst).join(" ") + " WS " + wsS("A") + " " + wsS("B"));
show("1 zero page thread", (L.Zeroed.push(...L.Free.splice(0, 2)), "F0,F1 zeroed"));
show("2 A touches heap H", touch("A", "H"));
onDisk.add("A:C"); show("3 A runs code page C", touch("A", "C"));
show("4 trim A", trim("A")); show("5 A touches C", touch("A", "C")); show("6 modified page writer", writer());
show("7 B touches X", touch("B", "X")); show("8 B touches Y", touch("B", "Y"));
show("9 B touches Z", touch("B", "Z")); show("10 B exits", exit("B"));
show("11 A touches H", touch("A", "H"));আউটপুট:
1 zero page thread: F0,F1 zeroed
Free[F2,F3] Zeroed[F0,F1] Standby[] Modified[] WS A{} B{}
2 A touches heap H: demand-zero: F0 from Zeroed
Free[F2,F3] Zeroed[F1] Standby[] Modified[] WS A{H=F0} B{}
3 A runs code page C: HARD fault, disk read into F2 (from Free)
Free[F3] Zeroed[F1] Standby[] Modified[] WS A{H=F0,C=F2} B{}
4 trim A: clean -> Standby, dirty -> Modified
Free[F3] Zeroed[F1] Standby[F2=A:C] Modified[F0=A:H] WS A{} B{}
5 A touches C: SOFT fault, back from Standby
Free[F3] Zeroed[F1] Standby[] Modified[F0=A:H] WS A{C=F2} B{}
6 modified page writer: written to pagefile, now Standby
Free[F3] Zeroed[F1] Standby[F0=A:H] Modified[] WS A{C=F2} B{}
7 B touches X: demand-zero: F1 from Zeroed
Free[F3] Zeroed[] Standby[F0=A:H] Modified[] WS A{C=F2} B{X=F1}
8 B touches Y: demand-zero: F3 from Free, zeroed first
Free[] Zeroed[] Standby[F0=A:H] Modified[] WS A{C=F2} B{X=F1,Y=F3}
9 B touches Z: demand-zero: F0 from Standby (was A:H), zeroed first
Free[] Zeroed[] Standby[] Modified[] WS A{C=F2} B{X=F1,Y=F3,Z=F0}
10 B exits: private pages -> Free
Free[F1,F3,F0] Zeroed[] Standby[] Modified[] WS A{C=F2} B{}
11 A touches H: HARD fault, disk read into F1 (from Free)
Free[F3,F0] Zeroed[] Standby[] Modified[] WS A{C=F2,H=F1} B{}
(১) ধাপ ৪-এ দুই পথ: heap page H (কোনো ডিস্ক-কপি নেই) গেল Modified-এ, আর code page C (কপি আছে app.exe-এ) গেল Standby-তে — একই ছাঁটা, ভিন্ন গন্তব্য। (২) ধাপ ৫: C ফিরল soft fault-এ, ডিস্ক ছাড়াই। (৩) ধাপ ৮: Zeroed খালি, তাই Y পেল Free-এর F3 — আগে শূন্য করে। (৪) ধাপ ৯: Zeroed আর Free দুটোই খালি — এবার Standby-র F0 (যাতে A-এর H ছিল) পুনর্ব্যবহার হলো। (৫) ধাপ ১১: তাই A H আবার চাইলে hard fault — pagefile থেকে F1-এ পড়া। খেয়াল করুন, H আর আগের frame-এ নেই, এখন F1-এ; virtual ঠিকানা একই থাকলেও physical frame বদলাতে পারে। আসল Windows-এ এর সাথে আরও অনেক কিছু আছে — ৮টি standby priority, প্রতি-NUMA-node তালিকা, large page, ফাইলের জন্য আলাদা mapped page writer — কিন্তু মূল চক্রটি এটাই।
Memory compression আর pagefile
Windows 10-এর একটি প্রথম দিকের বড় আপডেট (২০১৫) থেকে memory compression চালু: Modified তালিকার অনেক page সরাসরি pagefile-এ না লিখে চেপে ছোট করে RAM-এর একটি “compression store”-এ রাখা হয়। এই store থাকে “Memory Compression” নামের একটি বিশেষ প্রসেসের working set-এ; Task Manager-এর Performance → Memory অংশে “In use (Compressed)” লেখায় এর আকার দেখা যায়। চেপে রাখা page ফেরত আনা ডিস্ক-পড়ার চেয়ে সাধারণত অনেক দ্রুত, তবে CPU খরচ হয়। Pagefile তবু দরকারি: commit limit-এর অংশ সে, আর crash dump লিখতেও প্রায়ই লাগে। তাই “pagefile বন্ধ করে দিন” পরামর্শটি সাধারণত ক্ষতিকর — অনেক প্রোগ্রাম commit সীমায় আটকে ব্যর্থ হতে পারে।
৭ · I/O manager, IRP, driver আর NTFS
Windows-এর I/O হলো packet-চালিত। প্রতিটি অনুরোধের জন্য I/O manager একটি IRP (I/O request packet) বানায় — অনুরোধের ধরন (create, read, write, device control…), buffer, ফলাফলের জায়গা, আর stack-এর প্রতিটি driver-এর জন্য একটি করে “stack location”. IRP তারপর driver stack-এর ওপর থেকে নিচে যায়: প্রতিটি driver নিজের অংশ করে, দরকারে নিচে পাঠায়, শেষে কেউ সম্পন্ন করলে ফলাফল ওপরে ফেরে। অফিসের ফাইল-চালানের মতো — একটি ফাইল টেবিল থেকে টেবিলে যায়, প্রত্যেকে নোট লিখে স্বাক্ষর দেন।
অংশ ৩-এর CreateFileW call-টি এবার শুরু থেকে শেষ পর্যন্ত অনুসরণ করুন:
- kernel32 / kernelbase
CreateFileWWin32 - ntdll
NtCreateFile → syscallদরজা - Executive
KiSystemCall64kernel mode - Object manager
\??\C: → \Device\…নাম খোঁজা - I/O manager
IRP_MJ_CREATEIRP তৈরি - Filter → NTFS → disk
ntfs.sysdriver stack
CreateFileW(L"C:\\data\\report.txt", GENERIC_READ, ...) // DOS path → NT path (ntdll-এর সহায়ক ফাংশন) "\??\C:\data\report.txt" NtCreateFile(&h, FILE_GENERIC_READ, &objAttr, &iosb, ...)
mov r10, rcx mov eax, 55h ; NtCreateFile-এর নম্বর (এই build) syscall
KiSystemCall64: kernel stack-এ যাওয়া, register রাখা table[0x55] → NtCreateFile → IoCreateFile user-এর pointer যাচাই (ProbeForRead) — বিশ্বাস নয়
\??\C: → symbolic link
→ \Device\HarddiskVolume3 (device object)
বাকি অংশ "\data\report.txt" → device-এর parse routine
FILE_OBJECT { FileName = "\data\report.txt", ... }
IRP { MajorFunction = IRP_MJ_CREATE,
StackLocation[filter], StackLocation[ntfs], ... }
IoCallDriver(top of stack, irp)
fltmgr.sys + minifilter (যেমন antivirus) দেখে, চাইলে আটকায় ntfs.sys directory index → MFT record, DACL যাচাই volmgr → partmgr → disk.sys → storport → NVMe miniport
IRP.IoStatus = STATUS_SUCCESS → ওপরে ফেরে
অথবা STATUS_OBJECT_NAME_NOT_FOUND → kernelbase → ERROR_FILE_NOT_FOUND (2)
0x00A4 → FILE_OBJECT (report.txt) granted: FILE_GENERIC_READ প্রোগ্রাম পেল HANDLE 0xA4; পরের ReadFile-এ আর নাম খোঁজা নেই
- Win32 স্তর। প্রোগ্রাম
CreateFileWডাকল; kernel32 কাজটি kernelbase-এ পাঠায়। kernelbase পরিচিতC:\...পথকে NT-এর নিজের পথে বদলায় —\??\C:\data\report.txt— আর Win32-এর flag-গুলোকে native অধিকারে অনুবাদ করেNtCreateFileডাকে। - ntdll-এর দরজা। Stub syscall নম্বরটি
eax-এ রেখেsyscallচালায়। এই মুহূর্তে CPU user mode থেকে kernel mode-এ যায় — পর্ব ০৫-এর সেই রেখা। - System service dispatch.
KiSystemCall64kernel stack-এ যায়, register রাখে, নম্বর দিয়ে service table থেকেNtCreateFileখুঁজে নেয়। User-এর দেওয়া প্রতিটি pointer আগে যাচাই হয় — user mode-কে kernel কখনো বিশ্বাস করে না। - নাম খোঁজা। Object manager নামটি ভাঙে:
\??\C:একটি symbolic link, যা নিয়ে যায়\Device\HarddiskVolume3-এ। সেটি একটি device object, তাই বাকি অংশ\data\report.txtতার parse routine-এ, অর্থাৎ I/O manager-এর হাতে তুলে দেওয়া হয়। - File object আর IRP. I/O manager একটি নতুন file object বানায় (খোলা ফাইলের প্রতিনিধি — offset, mode) আর
IRP_MJ_CREATEধরনের একটি IRP, যাতে stack-এর প্রতিটি driver-এর ঘর আছে। তারপর IRP-টি volume-এর stack-এর সবচেয়ে ওপরের driver-এ পাঠায়। - Driver stack. প্রথমে Filter Manager আর তার minifilter-গুলো (যেমন antivirus) অনুরোধটি দেখে — চাইলে আটকাতে পারে। তারপর
ntfs.sysdirectory-র index ঘেঁটে MFT record খোঁজে, ফাইলের DACL-এর সাথে প্রসেসের token মেলায়। দরকার হলে নিজেই নিচের disk stack-এ নতুন IRP পাঠিয়ে MFT-র অংশ পড়ে। - Complete. NTFS
IoCompleteRequestডাকে; ফলাফল stack বেয়ে ওপরে ফেরে। ব্যর্থ হলে NTSTATUS আসে, যেমনSTATUS_OBJECT_NAME_NOT_FOUND, যা kernelbase Win32-এর error 2-এ বদলায় —GetLastError()এটিই দেখায়। - Handle. সফল হলে object manager প্রসেসের handle table-এ একটি নতুন সারি বসায় — file object-এর pointer আর অনুমোদিত অধিকার (এখানে শুধু পড়া)। প্রোগ্রাম পেল
0xA4-এর মতো একটি সংখ্যা। পরের প্রতিটিReadFileএই handle দিয়ে সোজা file object-এ পৌঁছেIRP_MJ_READবানায় — নাম খোঁজা বা পূর্ণ DACL যাচাই আর হয় না।
Asynchronous I/O Windows-এর শক্তির জায়গা: ফাইল “overlapped” মোডে খুললে ReadFile সাথে সাথে ফেরে, IRP পেছনে চলতে থাকে, আর শেষ হলে খবর আসে event, APC বা I/O completion port-এ। বহু সার্ভার-সফটওয়্যার IOCP-র ওপর দাঁড়ানো; Linux-এর io_uring-এর সাথে তুলনা পর্ব ৩৯-এ।
Driver মডেল — WDM থেকে KMDF/UMDF
WDM (Windows Driver Model, Windows 98/2000-এর যুগ থেকে) হলো IRP-ভিত্তিক মূল মডেল, কিন্তু সঠিকভাবে লেখা কঠিন — Plug and Play, power, বাতিল হওয়া IRP, সবকিছু নিজে সামলাতে হয়। তাই Microsoft পরে Windows Driver Frameworks দেয়: KMDF (kernel-mode, WDM-এর ওপর একটি নিরাপদ কাঠামো) আর UMDF (user-mode — driver crash করলেও kernel পড়ে না; পর্ব ৩৯)। 64-bit Windows-এ kernel-mode driver সই করা (signed) হতে হয়, আর x64 kernel-এ Kernel Patch Protection (“PatchGuard”) kernel-এর গুরুত্বপূর্ণ কাঠামো বদলানো ধরলে সিস্টেম থামিয়ে দেয়। তবু একটি সই করা driver-এর ভুলও পুরো সিস্টেম ফেলে দিতে পারে — ভাবনার প্রশ্ন ৫ দেখুন।
NTFS আর ReFS — সংক্ষেপে
NTFS-এর কেন্দ্র MFT (পর্ব ৪৩) আর metadata journal $LogFile (পর্ব ৪৪)। বাকি বৈশিষ্ট্যগুলো এক নজরে:
- নিরাপত্তা: প্রতিটি ফাইল-ফোল্ডারের security descriptor (DACL/SACL) — অংশ ৩-এর token এখানেই মেলানো হয়।
- Alternate data stream: একটি ফাইলের একাধিক নামওয়ালা ডেটা-ধারা। ইন্টারনেট থেকে নামানো ফাইলে Windows
Zone.Identifierনামের একটি stream রাখে — “এটি বাইরে থেকে এসেছে” সতর্কতা এখান থেকেই। - Compression আর EFS: প্রতি-ফাইল সংকোচন, আর ব্যবহারকারীর certificate-এ বাঁধা প্রতি-ফাইল এনক্রিপশন। পুরো ডিস্কের জন্য আলাদা ব্যবস্থা BitLocker.
- আরও: hard link, reparse point (symbolic link, junction), sparse file, আর USN change journal (কোন ফাইল কবে বদলাল তার খাতা — backup ও search-এর কাজে)।
ReFS (Resilient File System) এসেছে Windows Server 2012-এর সাথে: metadata-র checksum, পুরোনো জায়গায় না লিখে নতুন জায়গায় লেখা (copy-on-write ধাঁচ, পর্ব ৪৪), খুব বড় volume আর Storage Spaces-এর সাথে মিলে নিজে মেরামত। তবে এতে NTFS-এর সব বৈশিষ্ট্য নেই (যেমন EFS), আর সাধারণ Windows সাধারণত ReFS থেকে বুট করে না; Windows 11-এর “Dev Drive” ReFS ব্যবহার করে। সংস্করণভেদে সমর্থন বদলেছে, তাই নির্দিষ্ট সংস্করণের Microsoft ডকুমেন্টেশন দেখে নেওয়া ভালো।
৮ · Registry, service আর বুট
Registry — একটি কেন্দ্রীয় সেটিং-খাতা
Linux-এ সেটিং ছড়িয়ে থাকে /etc-এর শত শত text ফাইলে। Windows রাখে একটি গাছের মতো ডেটাবেসে — registry. গাছের ভেতরের ঘর key (ফোল্ডারের মতো), আর প্রতিটি key-তে নামওয়ালা value — ধরন REG_SZ (লেখা), REG_DWORD (৩২-বিট সংখ্যা), REG_QWORD, REG_BINARY, REG_MULTI_SZ ইত্যাদি। Kernel-এর configuration manager এটি সামলায়, আর এটিও object — \REGISTRY\MACHINE namespace-এ।
| যেভাবে দেখেন | আসলে কী | ডিস্কে কোথায় (সাধারণত) |
|---|---|---|
HKLM\SYSTEM | driver, service, বুটের সেটিং | C:\Windows\System32\config\SYSTEM |
HKLM\SOFTWARE | যন্ত্র-জুড়ে প্রোগ্রামের সেটিং | …\config\SOFTWARE |
HKLM\SAM, HKLM\SECURITY | স্থানীয় অ্যাকাউন্ট আর নিরাপত্তা-নীতি (সাধারণ admin-ও পড়তে পারে না) | …\config\SAM, SECURITY |
HKLM\HARDWARE | বুটের সময় চেনা হার্ডওয়্যার | ডিস্কে নেই — প্রতি বুটে মেমরিতে তৈরি |
HKCU | লগইন করা ব্যবহারকারীর সেটিং | C:\Users\<নাম>\NTUSER.DAT |
HKCR | ফাইলের ধরন আর COM — HKLM ও HKCU-র মিশ্র দৃশ্য | দুই জায়গার Classes |
ডিস্কের প্রতিটি ফাইলকে বলে hive. Hive-এর পাশে .LOG1, .LOG2 ফাইল থাকে — বদল আগে log-এ লেখা হয়, তারপর hive-এ, যাতে বিদ্যুৎ চলে গেলে hive নষ্ট না হয় (পর্ব ৪৪-এর journaling-এর আরেক রূপ)। Windows Vista-তে transactional registry-ও আসে, তবে Microsoft এখন নতুন প্রোগ্রামে এটি ব্যবহার না করার পরামর্শ দেয়। Windows 10-এর ২০১৮ সালের একটি আপডেট থেকে hive-গুলো “Registry” নামের একটি ছোট প্রসেসের address space-এ map করা থাকে। পড়া নিরাপদ — PowerShell-এ (আউটপুট উদাহরণ):
PS> Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' |
>> Select-Object ProductName, DisplayVersion, CurrentBuild, UBR, InstallationType
ProductName : Windows 10 Pro
DisplayVersion : 23H2
CurrentBuild : 22631
UBR : 4317
InstallationType : Client
PS> Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\hivelist' |
>> Select-Object '\REGISTRY\MACHINE\SYSTEM', '\REGISTRY\MACHINE\SOFTWARE' | Format-List
\REGISTRY\MACHINE\SYSTEM : \Device\HarddiskVolume3\Windows\System32\config\SYSTEM
\REGISTRY\MACHINE\SOFTWARE : \Device\HarddiskVolume3\Windows\System32\config\SOFTWAREদুটি মজার জিনিস চোখে পড়ে। প্রথমত, build 22631 আসলে Windows 11 (23H2), অথচ ProductName এখনো “Windows 10 Pro” — বহু পুরোনো প্রোগ্রামের সামঞ্জস্য রক্ষায় এই value বদলানো হয়নি বলে ব্যাপকভাবে উল্লেখ করা হয়; তাই সংস্করণ বুঝতে build নম্বর দেখুন। দ্বিতীয়ত, hivelist দেখায় hive-এর ঠিকানা object namespace-এর ভাষায় — \Device\HarddiskVolume3, অংশ ৩-এর সেই গাছ।
“Registry cleaner” প্রায়ই অপ্রয়োজনীয়। Registry-তে অনাথ (orphan) key থাকলে সেগুলো জায়গা নেয় খুব সামান্য, আর key খোঁজা হয় সূচিবদ্ধ কাঠামোয়, তাই কয়েক হাজার বাড়তি key মুছে সাধারণত মাপার মতো গতি বাড়ে না। অথচ ভুল key মুছলে প্রোগ্রাম বা পুরো সিস্টেম না চলতে পারে। Microsoft-এর support নীতিতে registry cleaner ব্যবহারের ফলে হওয়া সমস্যা সমর্থন না করার কথা বলা আছে। Registry বদলানোর আগে সবসময় সেই key export করে রাখুন, আর নির্দেশনা Microsoft-এর ডকুমেন্টেশন থেকে নিন।
Service আর Service Control Manager
Service হলো লগইন ছাড়াই পেছনে চলা প্রোগ্রাম — Linux-এর daemon বা systemd unit-এর (পর্ব ০৭) সমতুল্য। প্রতিটি service-এর বিবরণ registry-তে: HKLM\SYSTEM\CurrentControlSet\Services\<নাম> — Start value (0 = boot, 1 = system, 2 = automatic, 3 = manual, 4 = disabled), কোন ফাইল চালাতে হবে, কোন অ্যাকাউন্টে (LocalSystem, LocalService, NetworkService). মজার কথা, driver-এর বিবরণও এই একই key-তে থাকে। services.exe-এর SCM এগুলো চালায়; অনেক service একসাথে থাকে svchost.exe-এর ভেতরে। Vista থেকে service চলে session 0-এ, আর ব্যবহারকারী session 1 বা তার পরে — যাতে service-এর জানালা ব্যবহারকারীর ডেস্কটপে মিশে আক্রমণের পথ না খোলে।
PS> Get-Service -Name EventLog, Spooler, WSearch, wuauserv
Status Name DisplayName
------ ---- -----------
Running EventLog Windows Event Log
Running Spooler Print Spooler
Running WSearch Windows Search
Stopped wuauserv Windows Update
PS> Get-CimInstance Win32_Service -Filter "Name='Spooler'" |
>> Select-Object Name, StartMode, StartName, PathName
Name StartMode StartName PathName
---- --------- --------- --------
Spooler Auto LocalSystem C:\Windows\System32\spoolsv.exeWindows Update “Stopped” দেখে ঘাবড়াবেন না — এটি প্রয়োজনমতো চালু হয় (trigger-start). আর Print Spooler চলছে LocalSystem-এ, অর্থাৎ সর্বোচ্চ অধিকারে — এজন্যই ২০২১ সালের “PrintNightmare” নামে পরিচিত দুর্বলতাগুলো এত গুরুতর ছিল, আর প্রিন্টার না থাকা সার্ভারে অনেক প্রতিষ্ঠান এই service বন্ধ রাখে। প্রতিরক্ষার সাধারণ নীতি: যে service দরকার নেই, তা নীতিমালা মেনে বন্ধ করা, আর আপডেট নিয়মিত রাখা।
বুট — পর্ব ০৭-এর Windows অংশ
UEFI firmware \EFI\Microsoft\Boot\bootmgfw.efi — Windows Boot Manager — চালায় (পর্ব ০৭)। এটি BCD (boot configuration data — নিজেও registry-র ফরম্যাটের একটি hive) পড়ে কোন Windows চালাবে ঠিক করে, তারপর winload.efi চালায়। Winload মেমরিতে আনে ntoskrnl.exe, HAL, SYSTEM hive আর Start = 0 লেখা boot driver-গুলো (যেমন ডিস্কের driver — নইলে kernel ডিস্কই পড়তে পারত না)। Kernel ধাপে ধাপে executive চালু করে, System প্রসেস বানায়, বাকি driver লোড করে, তারপর প্রথম user-mode প্রসেস smss.exe চালায়। smss session 0-এ csrss আর wininit (→ services, lsass), আর session 1-এ csrss আর winlogon চালায়; লগইনের পর userinit → explorer. Secure Boot এই শিকলের প্রতিটি ধাপের সই যাচাই করে (পর্ব ০৭, ৪৯)। আর “Fast startup” চালু থাকলে Shut down আসলে kernel session-এর একটি hibernate — তাই driver-সমস্যায় “Restart” আর “Shut down”-এর আচরণ আলাদা হতে পারে।
৯ · WSL আর পর্যবেক্ষণের টুল
WSL1 বনাম WSL2 — অনুবাদক বনাম আসল kernel
WSL-এর দুটি প্রজন্ম NT-এর দুটি আলাদা ক্ষমতা দেখায়:
WSL1 (২০১৬) — pico process
Linux-এর ELF প্রোগ্রাম চলে একটি pico process-এ — এমন একটি ন্যূনতম NT প্রসেস, যার ভেতরে ntdll বা Win32 কিছুই নেই। এর Linux system call ধরে একটি kernel driver (pico provider) NT kernel-এর কাজে অনুবাদ করে। কোনো Linux kernel নেই — NT-এর মূল “একাধিক subsystem” নকশার আধুনিক উত্তরাধিকার। দুর্বলতা: সব syscall অনুবাদ হয়নি, আর ফাইল-ভারী কাজ (যেমন git, npm install) NTFS-এর ওপর ধীর ছিল।
WSL2 (২০১৯ সালে ঘোষণা) — আসল Linux kernel
Microsoft-এর build করা একটি আসল Linux kernel চলে Hyper-V-এর ওপরের একটি হালকা utility VM-এ (পর্ব ৫০)। তাই syscall-এর সামঞ্জস্য পূর্ণ — Docker, signal, /proc সব Linux-এর মতো। Linux ফাইল থাকে ext4 virtual disk-এ, দ্রুত; কিন্তু /mnt/c দিয়ে Windows-এর ফাইল ছোঁয়া একটি নেটওয়ার্ক-ধাঁচের প্রোটোকলের ওপর দিয়ে যায়, তুলনামূলক ধীর।
ব্যবহারিক পরামর্শ: WSL2-এ কাজ করলে প্রজেক্টের ফাইল Linux-এর দিকে (~/project) রাখুন, /mnt/c/...-তে নয়। আর মনে রাখুন, এই কোর্সের Linux-কমান্ড WSL2-এ প্রায় হুবহু চলবে, কিন্তু তারা দেখাবে VM-এর ভেতরের Linux kernel — Windows-এর NT kernel নয়।
Windows-কে চোখে দেখার টুল — শুধু নিজের যন্ত্রে, পর্যবেক্ষণের জন্য
| টুল | কী দেখায় | এই পর্বের কোন অংশ |
|---|---|---|
| Task Manager, Resource Monitor | প্রসেস, thread, handle সংখ্যা, working set, commit, compressed মেমরি, hard fault | ৪, ৬ |
| Sysinternals Process Explorer | প্রতিটি প্রসেসের handle আর DLL-এর তালিকা, token, thread-এর stack, DPC/interrupt-এর সময় | ৩, ৫ |
| Process Monitor | ফাইল, registry আর প্রসেসের প্রতিটি কাজ সময়ের ক্রমে — পর্ব ০৫-এর strace-এর আত্মীয় | ৭, ৮ |
| RAMMap, VMMap | Free/Zeroed/Standby/Modified তালিকার আকার; একটি প্রসেসের reserve আর commit | ৬ |
| WinObj | Object namespace-এর গাছ | ৩ |
| WinDbg | Crash dump বিশ্লেষণ (!analyze -v), kernel-এর কাঠামো (!process, !handle, !vad) | সব |
Sysinternals-এর টুলগুলো Mark Russinovich ও Bryce Cogswell-এর তৈরি; ২০০৬ সালে Microsoft প্রতিষ্ঠানটি কিনে নেয়, আর টুলগুলো এখন Microsoft-এর সাইট থেকে বিনামূল্যে পাওয়া যায় — শুধু সেখান থেকেই নামাবেন। এগুলো দিয়ে নিজের যন্ত্র বা প্রতিষ্ঠানের অনুমোদিত যন্ত্র পর্যবেক্ষণ করুন; kernel debugging শিখতে একটি আলাদা পরীক্ষামূলক VM ব্যবহার করাই নিরাপদ।
আরও গভীরে: আধুনিক Windows-এ Hyper-V চালু থাকলে Virtualization-Based Security (VBS) একটি দ্বিতীয় “secure kernel” চালায়, যা সাধারণ NT kernel-এরও নাগালের বাইরে — Microsoft একে Virtual Trust Level (VTL) 1 বলে, সাধারণ kernel চলে VTL 0-তে। HVCI এই ব্যবস্থা দিয়ে নিশ্চিত করে যে kernel-এ কেবল সই-যাচাই করা কোডই executable হতে পারে, আর Credential Guard lsass-এর গোপন তথ্য সেই সুরক্ষিত দিকে (LsaIso) সরিয়ে রাখে। অর্থাৎ “kernel mode = সর্বোচ্চ অধিকার” কথাটি আজকের Windows-এ পুরোপুরি সত্য নয়; বিস্তারিত Windows Internals (৭ম সংস্করণ, খণ্ড ১)-এর নিরাপত্তা অধ্যায়ে।
পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:
- Windows-এ process আর thread-এর সম্পর্ক? — Process একটি পাত্র (address space, handle table, token); scheduling-এর একক thread, ৩২টি priority স্তরে।
- Handle আর Unix file descriptor-এর মিল ও অমিল? — দুটোই প্রসেস-নিজস্ব সূচক, অধিকার যাচাই খোলার সময়; handle শুধু ফাইল নয়, প্রসেস, thread, event, registry key — যেকোনো kernel object-এর জন্য।
- DISPATCH_LEVEL-এ page fault কেন নিষেধ? — Fault মেটাতে ডিস্কের অপেক্ষা লাগে, কিন্তু ঐ স্তরে thread switch হয় না; তাই bugcheck (IRQL_NOT_LESS_OR_EQUAL).
- Reserve আর commit-এর পার্থক্য? — Reserve শুধু ঠিকানার এলাকা আটকায়; commit RAM বা pagefile-এর প্রতিশ্রুতি দেয় আর commit charge বাড়ায়; physical page আসে প্রথম ছোঁয়ায়।
- WSL1 বনাম WSL2? — WSL1 pico process-এ Linux syscall NT-তে অনুবাদ করে; WSL2 হালকা Hyper-V VM-এ আসল Linux kernel চালায়।
১০ · AI-এর যুগে Windows internals
Windows-এর সমস্যা নিয়ে AI-কে প্রশ্ন করলে উত্তর দ্রুত আসে, কিন্তু Windows-এর বিশেষ কিছু ফাঁদ আছে। এক, সংস্করণের গোলমাল — Windows 7, 10, 11 আর Server-এর আচরণ মিশিয়ে ফেলা, বা অনেক আগে বাদ পড়া ফিচারকে বর্তমান বলা। দুই, ভুল OS — প্রশ্ন Windows নিয়ে, উত্তরে Linux-এর chmod বা sudo. তিন, “performance tweak”-এর নামে ক্ষতিকর পরামর্শ — pagefile বন্ধ, অজানা service বন্ধ, registry cleaner, এমনকি Defender বা UAC বন্ধ করা, বা Set-ExecutionPolicy Unrestricted. চার, kernel-এর আত্মবিশ্বাসী কিন্তু ভুল তথ্য — IRQL-এর নিয়ম, quantum-এর দৈর্ঘ্য, syscall নম্বর।
নিচের কাঠামোয় প্রশ্ন করলে AI-কে সংস্করণ, প্রমাণ আর ঝুঁকি — তিনটিই বলতে বাধ্য করা হয়:
AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১NT-কে প্রায়ই VMS-এর “উত্তরসূরি” বলা হয়। কোন অর্থে এটি সত্য, আর সাধারণ ব্যবহারকারীর Windows NT-তে আসতে ১৯৯৩ থেকে ২০০১ পর্যন্ত সময় লাগল কেন?
কোড অর্থে নয় — NT নতুন করে লেখা, VMS-এর কোড এতে নেই। মিলটা নকশার চিন্তায়, কারণ নকশা করেছিলেন একই মানুষ। Cutler-এর দল VMS-এ যেসব ধারণা নিয়ে কাজ করেছিল, NT-তে তাদের অনেকগুলোর আত্মীয় চোখে পড়ে, আর বহু লেখায় এই তুলনা করা হয়েছে: VMS-এর interrupt priority level (IPL) ↔ NT-এর IRQL; VMS-এর AST (asynchronous system trap) ↔ NT-এর APC; I/O request packet-ভিত্তিক driver; প্রতি-প্রসেস working set আর ছাঁটা page রাখার free ও modified তালিকা। অর্থাৎ অংশ ৫ আর ৬-এ যা শিখলেন, তার শিকড় ১৯৭০-এর দশকের শেষের মিনিকম্পিউটারে।
তাহলে সাধারণ মানুষের Windows NT-তে আসতে এত দেরি কেন? কারণ সামঞ্জস্য আর সম্পদ। NT 3.1-এর জন্য তখনকার হিসাবে বেশ বেশি RAM লাগত, যখন বেশিরভাগ বাসার কম্পিউটারে তা ছিল না। তার চেয়েও বড় সমস্যা ছিল পুরোনো সফটওয়্যার: DOS-এর গেম আর প্রোগ্রাম সরাসরি হার্ডওয়্যার ছুঁত, মেমরির যেকোনো জায়গায় লিখত — NT-এর সুরক্ষিত মডেলে ঠিক যা নিষেধ। Hardware নির্মাতাদের driver-ও ছিল DOS/Windows 95-এর জন্য। তাই Microsoft দুই লাইন চালিয়ে যায়: অফিস ও সার্ভারের জন্য নির্ভরযোগ্য NT (3.5, 4.0, Windows 2000), আর বাসার জন্য সামঞ্জস্যপূর্ণ Windows 95/98/ME. Windows XP (২০০১) পর্যন্ত লেগে যায় — ততদিনে RAM সস্তা, driver মডেল (WDM) দুই লাইনে মিলেছে, আর NT-তে পুরোনো প্রোগ্রাম চালানোর অনুকরণ-স্তর যথেষ্ট ভালো।
এই গল্পের আরেকটি শিক্ষা: নির্ভরযোগ্যতার দাম সাধারণ ব্যবহারকারী সবসময় সঙ্গে সঙ্গে দিতে রাজি থাকেন না। DOS-বংশের Windows প্রায়ই crash করত, তবু চলত, কারণ পুরোনো সবকিছু সেখানে চলত।
মূল উপলব্ধি: ভালো নকশা প্রজন্ম পেরিয়ে টেকে — IRQL, APC, working set আজও আছে। কিন্তু একটি OS-এর সাফল্য শুধু kernel-এর মানে নয়; সামঞ্জস্য, driver আর হার্ডওয়্যারের দামই অনেক সময় ঠিক করে কোন kernel মানুষের হাতে পৌঁছাবে।
প্র ০২NT 4.0-তে জানালা আর গ্রাফিক্সের কোড (win32k) user mode থেকে kernel-এ নামানো হয়। লাভ কী ছিল, দাম কী দিতে হলো, আর পরে Windows কীভাবে ভারসাম্য খুঁজেছে?
NT 3.x-এ window manager আর GDI (graphics) থাকত csrss.exe-এ, user mode-এ। প্রতিটি আঁকার কাজে প্রোগ্রাম থেকে csrss-এ বার্তা যেত — অর্থাৎ প্রসেস বদল, context switch, ডেটা কপি। তখনকার হার্ডওয়্যারে GUI ধীর লাগত, আর Windows 95-এর সাথে তুলনায় এটি বড় দুর্বলতা ছিল। NT 4.0 (১৯৯৬) এই কোড win32k.sys নামে kernel-এ নামিয়ে আনে: এখন প্রোগ্রাম একটি system call দিয়েই সরাসরি আঁকতে পারে। গতি স্পষ্টভাবে বাড়ে। এটি পর্ব ০৬-এর microkernel বনাম monolithic বিতর্কের বাস্তব উদাহরণ — Windows এখানে গতির দিকে ঝুঁকেছিল।
দাম দুটি। প্রথমত নির্ভরযোগ্যতা: গ্রাফিক্সের বা display driver-এর একটি ভুল এখন পুরো সিস্টেম ফেলে দিতে পারে। দ্বিতীয়ত নিরাপত্তা: win32k একটি বিশাল, পুরোনো কোডের ভাণ্ডার, যাকে যেকোনো GUI প্রোগ্রাম system call দিয়ে ডাকতে পারে। বছরের পর বছর privilege escalation-এর অনেক দুর্বলতা এখানে পাওয়া গেছে — kernel-এর attack surface অনেক বড় হয়ে যায়।
পরে ভারসাম্য খোঁজা হয়েছে কয়েক দিক থেকে। Vista-র WDDM display driver মডেলে display driver-এর বড় অংশ user mode-এ সরে, আর GPU আটকে গেলে driver পুনরায় চালু করা যায় (পর্দা এক মুহূর্ত কালো হয়ে ফেরে, নীল পর্দা নয়)। Windows 8 থেকে একটি প্রসেস নিজের জন্য win32k system call নিষিদ্ধ করতে পারে; Chrome আর Edge-এর মতো ব্রাউজার তাদের renderer প্রসেসে এটি ব্যবহার করে বলে জানা যায় — ওয়েবপেজ চালানোর কোডের তো জানালা আঁকার দরকার নেই। Windows 10-এ win32k কয়েকটি অংশে ভাগ করা হয়। অর্থাৎ নকশা পুরোপুরি উল্টানো হয়নি, বরং যাদের দরকার নেই তাদের জন্য দরজা বন্ধ করা হয়েছে।
মূল উপলব্ধি: “kernel-এ সরালে দ্রুত” আর “user mode-এ রাখলে নিরাপদ” — এই দোলক OS-এর ইতিহাসে বারবার ফেরে। আধুনিক উত্তর প্রায়ই মাঝামাঝি: দ্রুত পথ রাখা, কিন্তু least privilege দিয়ে কে সেই পথ ছুঁতে পারবে তা সীমিত করা।
প্র ০৩Linux overcommit করে, Windows commit charge গুনে আগেই “না” বলে। কোনটি ভালো নকশা? দুই পদ্ধতিতে ব্যর্থতা দেখতে কেমন?
দুটোই একই সমস্যার দুই উত্তর: প্রসেসগুলো যত মেমরি চায়, সবাই একসাথে তা পুরো ব্যবহার করে না। Linux (ডিফল্ট heuristic মোডে, পর্ব ৩৮) প্রায় সব চাওয়ায় “হ্যাঁ” বলে, আর আশা করে সবাই সব ছোঁবে না। লাভ: fork()-এর পর copy-on-write-এ বিশাল প্রসেসের কপিও সস্তা, sparse ডেটা-কাঠামো সহজ, মেমরির ব্যবহার দক্ষ। ক্ষতি: হিসাব না মিললে ব্যর্থতা আসে অনেক পরে, অপ্রত্যাশিত জায়গায় — OOM killer একটি প্রসেস মেরে ফেলে, হয়তো এমন একটি, যার কোনো দোষ নেই। Container-এ “exit code 137” এর পরিচিত চেহারা।
Windows প্রতিটি commit-কে সত্যিকারের প্রতিশ্রুতি ধরে: commit charge যেন commit limit (RAM + pagefile) না ছাড়ায়। লাভ: ব্যর্থতা আসে সঠিক জায়গায় আর সঠিক সময়ে — VirtualAlloc বা malloc NULL ফেরত দেয়, প্রোগ্রাম চাইলে ভদ্রভাবে সামলাতে পারে; কেউ হঠাৎ খুন হয় না। ক্ষতি: অনেক প্রোগ্রাম যতটা commit করে, তার সামান্যই ছোঁয় — তবু সেই পুরোটার জন্য pagefile-এ জায়গা রাখতে হয়। তাই Windows-এ বড় pagefile দরকার হয়, আর “low on virtual memory” সতর্কতা দেখা দিতে পারে যখন RAM-এ আসলে অনেক জায়গা খালি। Windows-এ fork নেই বলেই এই কড়া নীতি চালানো সহজ — fork থাকলে প্রতিটি কপির পুরো commit গুনতে হতো।
দুই সিস্টেমই মাঝামাঝি যেতে পারে: Linux-এ vm.overcommit_memory=2 দিলে Windows-ধাঁচের কড়া হিসাব হয় (ডেটাবেস সার্ভারে কখনো কখনো এটি পছন্দ করা হয়); Windows-এ system-managed pagefile প্রয়োজনে বড় হয়ে চাপ সামলায়। Memory compression দুই জগতেই চাপ কমায়।
মূল উপলব্ধি: প্রশ্নটি “কোনটি ভালো” নয়, “ব্যর্থতা কোথায় আর কার ঘাড়ে পড়বে”। আগেভাগে ব্যর্থ হওয়া (fail early) প্রোগ্রামের জন্য সৎ, দেরিতে ব্যর্থ হওয়া মেমরির জন্য দক্ষ। সিস্টেম ডিজাইনে এই trade-off বারবার দেখবেন।
প্র ০৪সব সেটিং একটি কেন্দ্রীয় registry-তে, নাকি Linux-এর মতো ছড়ানো text ফাইলে — কোনটির কী সুবিধা? আর “registry cleaner” নিয়ে সংশয় কেন?
Windows 3.x-এর যুগে সেটিং থাকত অসংখ্য .INI text ফাইলে — প্রতিটি প্রোগ্রামের নিজের ফরম্যাট, একসাথে লেখার নিয়ম নেই, কোনটি কার তা জানা কঠিন। NT আর Windows 95 registry দিয়ে এর সমাধান খোঁজে। Registry-র সুবিধা: এক রকম API আর ধরনযুক্ত value (সংখ্যা সংখ্যাই, লেখা লেখাই); প্রতিটি key-তে ACL, তাই কে কী বদলাতে পারবে তা নির্দিষ্ট; log ফাইল দিয়ে crash-সহনশীল লেখা; নেটওয়ার্কে Group Policy দিয়ে হাজার যন্ত্রে একই সেটিং চাপানো; বদলের খবর পাওয়ার ব্যবস্থা (RegNotifyChangeKeyValue). একটি বড় প্রতিষ্ঠানের IT বিভাগের জন্য এগুলো অনেক কাজের।
অসুবিধা: এটি একটি binary ডেটাবেস, তাই সাধারণ text editor, diff বা git দিয়ে দেখা ও মেলানো কঠিন; নষ্ট হলে পুরো সিস্টেম বিপদে পড়ে; আর প্রোগ্রাম uninstall-এর পর নিজের key প্রায়ই রেখে যায়। Linux-এর /etc-এর text ফাইল মানুষ পড়তে পারে, version control-এ রাখা যায়, ফাইল কপি করেই backup — কিন্তু প্রতিটি প্রোগ্রামের আলাদা ফরম্যাট, আর একসাথে লেখার বা অধিকারের সূক্ষ্ম নিয়ন্ত্রণ কম। মজার ব্যাপার, Linux ডেস্কটপও কিছু ক্ষেত্রে registry-ধাঁচের ডেটাবেস ব্যবহার করে (GNOME-এর dconf), আর macOS রাখে plist ফাইল — দুই পথের মাঝামাঝি।
Registry cleaner-এর সংশয়: এই সফটওয়্যারগুলো অনাথ key খুঁজে মোছে আর গতি বাড়ানোর দাবি করে। কিন্তু অনাথ key-র আকার সাধারণত সামান্য, আর configuration manager key খোঁজে সূচিবদ্ধভাবে — কয়েক হাজার বাড়তি key খোঁজার সময়ে মাপার মতো পার্থক্য আনে বলে নির্ভরযোগ্য প্রমাণ সাধারণত দেখানো হয় না। উল্টো দিকে, কোন key “অপ্রয়োজনীয়” তা নিশ্চিতভাবে জানা কঠিন; ভুল মুছলে প্রোগ্রাম, driver বা বুট নষ্ট হতে পারে। Microsoft-এর support নীতিও এ ধরনের টুল ব্যবহারের ফলে হওয়া সমস্যার দায় নেয় না।
মূল উপলব্ধি: কেন্দ্রীয় ডেটাবেস নিয়ন্ত্রণ আর ব্যবস্থাপনা সহজ করে, ছড়ানো text ফাইল স্বচ্ছতা আর সরলতা দেয়। আর যেকোনো “এক ক্লিকে দ্রুত” টুলের দাবি মাপজোখ দিয়ে যাচাই না করে বিশ্বাস করবেন না।
প্র ০৫২০২৪ সালের জুলাইয়ে একটি নিরাপত্তা-সফটওয়্যারের আপডেট লাখ লাখ Windows যন্ত্রকে নীল পর্দায় আটকে দেয়। একটি driver-এর ভুল কেন এত বড় হয়, আর OS-নকশায় এর শিক্ষা কী?
১৯ জুলাই ২০২৪ CrowdStrike-এর Falcon sensor-এর জন্য একটি “content update” (একটি কনফিগারেশন ফাইল, নতুন driver নয়) প্রকাশিত হয়। Falcon-এর kernel-mode driver সেই ফাইল পড়তে গিয়ে সীমার বাইরের মেমরি পড়ে — kernel-এ এমন ভুলের একটাই ফল: bugcheck, নীল পর্দা। আর driver-টি boot-start ছিল (অংশ ৮-এর Start = 0-এর মতো আগেভাগে চালু হয়), তাই যন্ত্র চালু হতেই আবার crash — বুটের চক্রে আটকে যাওয়া। Microsoft-এর হিসাবে প্রায় ৮৫ লাখ Windows যন্ত্র আক্রান্ত হয়েছিল; বিমানবন্দর, ব্যাংক, হাসপাতালে কাজ থেমে যায়। অনেক যন্ত্রে হাতে-হাতে safe mode-এ ঢুকে ফাইলটি মুছতে হয়েছিল, আর BitLocker চালু থাকলে আগে recovery key লাগত।
কেন এত বড়? পর্ব ০৬-এর কথা মনে করুন: Windows-এ driver kernel-এর একই address space-এ, একই সর্বোচ্চ অধিকারে চলে। নিরাপত্তা-সফটওয়্যার kernel-এ থাকে কারণ সেখান থেকে প্রতিটি প্রসেস, ফাইল আর নেটওয়ার্ক কাজ দেখা ও আটকানো যায় (অংশ ৭-এর minifilter). কিন্তু সেই ক্ষমতার মানে, তার একটি ভুল pointer পুরো সিস্টেমের মৃত্যু। Driver signing আর PatchGuard আক্রমণকারীর বিরুদ্ধে কাজ করে, বিশ্বস্ত বিক্রেতার নিজের ভুলের বিরুদ্ধে নয়। আর যে আপডেট একসাথে সব গ্রাহকের কাছে যায়, তার ভুলও একসাথে সবার কাছে যায় — ধাপে ধাপে (staged) প্রকাশ না করার দাম।
পরে কী হলো? ২০২৪-এর শেষে Microsoft একটি “Windows Resiliency Initiative” ঘোষণা করে, যার অংশ হিসেবে নিরাপত্তা-সফটওয়্যারকে kernel-এর বাইরে চালানোর সুযোগ তৈরি আর বুট-ব্যর্থ যন্ত্র দূর থেকে মেরামতের ব্যবস্থার কথা বলা হয়; এগুলোর খুঁটিনাটি ও সময়সূচি এখনো বিকশিত হচ্ছে বলে Microsoft-এর সর্বশেষ ঘোষণা দেখে নেওয়া ভালো। দিকটি পরিচিত: macOS আগেই নিরাপত্তা-সফটওয়্যারকে kernel extension থেকে user-space-এর Endpoint Security কাঠামোয় সরিয়েছে, আর Linux-এ eBPF যাচাই-করা ছোট প্রোগ্রাম দিয়ে kernel পর্যবেক্ষণ করতে দেয় (পর্ব ৫৫)।
মূল উপলব্ধি: “কোড কোথায় চলে” — এটি শুধু তাত্ত্বিক প্রশ্ন নয়। Kernel-এ যত কম তৃতীয়-পক্ষের কোড, ব্যর্থতার পরিধি তত ছোট; আর যেকোনো স্বয়ংক্রিয় আপডেট ধাপে ধাপে ছড়ানো, যাতে ভুল প্রথম কয়েকটি যন্ত্রেই ধরা পড়ে।
অনুশীলন
হাতে হিসাব, ধাপে ধাপে অনুসরণ আর একটি কমান্ড লেখা — উত্তর দেখার আগে নিজে চেষ্টা করুন।
-
Commit-এর হিসাব: একটি যন্ত্রে RAM ৮ GiB আর pagefile ৪ GiB (আকার স্থির)। বর্তমান commit charge ৯ GiB. একটি প্রোগ্রাম (ক) ২ GiB reserve করে; (খ) তার মধ্যে ৫১২ MiB commit করে; (গ) commit-করা অংশের ৩০০ MiB ছোঁয়; (ঘ) আরও ৩ GiB commit চায়। প্রতি ধাপের পর commit charge কত, প্রোগ্রামের working set সর্বোচ্চ কত, আর (ঘ) সফল হবে কি?
Commit limit ≈ ৮ + ৪ = ১২ GiB. (ক) Reserve commit বাড়ায় না → ৯ GiB. (খ) ৫১২ MiB = ০.৫ GiB যোগ → ৯.৫ GiB. (গ) ছোঁয়ায় commit বদলায় না (আগেই প্রতিশ্রুত) → ৯.৫ GiB; এখন প্রোগ্রামের এই অংশের working set সর্বোচ্চ ৩০০ MiB (ছাঁটা হলে কম)। (ঘ) ৯.৫ + ৩ = ১২.৫ > ১২ → ব্যর্থ (pagefile স্থির, তাই বাড়তে পারবে না)। প্রোগ্রাম NULL বা
ERROR_COMMITMENT_LIMITপাবে। একই ৩ GiB শুধু reserve চাইলে সফল হতো। (বাস্তবে kernel-এর নিজের commit-এর কারণে সীমা একটু কম।) -
আউটপুট অনুমান করুন — handle table: অংশ ৩-এর simulator-এর নিয়মে (খালি সবচেয়ে ছোট slot আগে, handle = (slot + 1) × 4) প্রসেস P পরপর করে:
open X,open Y,open Z,close 0x8,open W,duplicate 0x4 → নিজের মধ্যেই. প্রতিটি handle-এর মান আর শেষে X-এর H কত?X → 0x4, Y → 0x8, Z → 0xc.
close 0x8→ Y-এর H = ০, (কারও pointer না থাকলে) Y মুছে যায়, slot ১ (০ থেকে গুনে) খালি।open W→ খালি সবচেয়ে ছোট slot ১ → 0x8 (পুরোনো Y-এর মান আবার ব্যবহার)। Duplicate 0x4 → নতুন slot ৩ → 0x10, একই object X. শেষে X-এর H = ২ (0x4 আর 0x10). শিক্ষা: Y বন্ধের পর কেউ ভুল করে 0x8 ব্যবহার করলে সে এখন W-তে কাজ করবে। -
IRQL-এর নিয়ম: একটি driver-এর DPC (DISPATCH_LEVEL-এ) নিচের কোন কাজ করতে পারবে? (ক) spinlock নেওয়া, (খ) একটি event-এ timeout ছাড়া
KeWaitForSingleObject, (গ) paged pool-এর একটি buffer পড়া, (ঘ) আরেকটি DPC queue করা, (ঙ) একটি work item queue করা যাতে ফাইল লেখা হবে।পারবে: (ক) spinlock — এটি DISPATCH_LEVEL-এর জন্যই তৈরি; (ঘ) আরেকটি DPC queue; (ঙ) work item queue — ফাইল লেখা পরে PASSIVE_LEVEL-এর একটি system thread-এ হবে, যেখানে অপেক্ষা ও page fault বৈধ। পারবে না: (খ) অপেক্ষা মানে thread switch, যা DISPATCH_LEVEL-এ অসম্ভব; (গ) paged pool-এর page RAM-এ না থাকলে page fault হবে, যা মেটাতে অপেক্ষা লাগে — ফল bugcheck
IRQL_NOT_LESS_OR_EQUAL. বিপজ্জনক দিক: (গ) কখনো কখনো চলে যায় (page তখন RAM-এ ছিল), তাই বাগটি পরীক্ষায় লুকিয়ে থাকে — Driver Verifier ইচ্ছা করে pageable মেমরি বের করে দিয়ে এটি ধরে। -
কমান্ড লিখুন: (ক) PowerShell-এ সবচেয়ে বেশি handle খোলা ৫টি প্রসেসের নাম, Id, handle সংখ্যা আর working set (MB) দেখান। (খ) Linux-এ (এই কোর্সের runner-এর মতো) একটি PID-এর খোলা fd-এর সংখ্যা আর thread সংখ্যা দেখান।
(ক)
Get-Process | Sort-Object Handles -Descending | Select-Object -First 5 Name, Id, Handles, @{n='WS(MB)'; e={[math]::Round($_.WorkingSet64 / 1MB)}}— এখানেWorkingSet64byte-এ, তাই 1MB দিয়ে ভাগ। (খ)ls /proc/1234/fd | wc -lআরgrep Threads /proc/1234/status(নিজের shell-এর জন্য 1234-এর জায়গায়$$)। অন্য ব্যবহারকারীর প্রসেসেরfdপড়তে অনুমতি লাগে —sudoলাগানোর আগে ভাবুন, আসলেই দরকার কি না। দুই আউটপুট পাশাপাশি রাখলে অংশ ৩-এর তুলনাটি স্পষ্ট হয়: handle table ↔ fd table, WS ↔ RSS.
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি UEFI যন্ত্রে Windows-এর বুটের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- UEFI firmware ESP থেকে
bootmgfw.efiচালায় - Windows Boot Manager BCD পড়ে
winload.efiচালায় - winload
ntoskrnl.exe, HAL, SYSTEM hive আর boot driver লোড করে - Kernel executive চালু করে প্রথম user-mode প্রসেস
smss.exeচালায় - smss csrss, wininit আর winlogon চালায়; লগইনের পর explorer
Windows-এ একটি প্রোগ্রাম CreateFileW ডাকলে CPU ঠিক কোন DLL-এর কোডে syscall instruction চালিয়ে kernel mode-এ ঢোকে?
- kernel32.dll
- ntdll.dll
- user32.dll
- ntoskrnl.exe
একটি ফাইল object-এর সব handle বন্ধ হয়েছে (handle count = 0), কিন্তু kernel-এর একটি অসমাপ্ত I/O তখনো object-টির একটি pointer reference ধরে আছে। কী ঘটবে?
- Object সঙ্গে সঙ্গে মুছে যাবে
- Object আবার একটি handle পাবে
- Object টিকে থাকবে, I/O শেষে reference count শূন্য হলে মুছবে
- সিস্টেম bugcheck করবে
Windows-এ DISPATCH_LEVEL (IRQL 2)-এ চলা কোডের জন্য কোনটি নিষিদ্ধ?
- Pageable মেমরি ছোঁয়া বা কোনো event-এ অপেক্ষা করা
- Spinlock নেওয়া
- একটি DPC queue করা
- উঁচু IRQL-এর interrupt দ্বারা থামানো
Windows-এ একটি প্রসেসের ছাঁটা page Standby তালিকায় আছে। প্রসেসটি আবার সেই page ছুঁলে কী হয়?
- Hard fault — pagefile থেকে পড়া
- Access violation — প্রোগ্রাম বন্ধ
- নতুন শূন্য-ভরা page দেওয়া হয়
- Soft fault — frame Standby থেকে সরাসরি working set-এ ফেরে
একটি Windows যন্ত্রে RAM ১৬ GiB, pagefile ৮ GiB (স্থির), বর্তমান commit charge ২০ GiB. একটি প্রোগ্রাম VirtualAlloc দিয়ে ৫ GiB MEM_COMMIT চাইলে কী হওয়ার কথা?
- সফল, কারণ physical page তো প্রথম ছোঁয়ায় আসবে
- ব্যর্থ, কারণ commit charge commit limit ছাড়িয়ে যাবে
- সফল, তবে পরে একটি প্রসেসকে মেরে ফেলা হবে
- স্বয়ংক্রিয়ভাবে শুধু reserve হয়ে যাবে
WSL1 আর WSL2-এর মূল পার্থক্য কোনটি?
- WSL1 Windows-এর exe চালায়, WSL2 Linux-এর প্রোগ্রাম
- WSL1 আসল Linux kernel চালায়, WSL2 syscall অনুবাদ করে
- WSL1 pico process-এ syscall অনুবাদ করে; WSL2 হালকা VM-এ আসল Linux kernel চালায়
- শুধু লাইসেন্সের পার্থক্য, ভেতরে একই
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- গঠন: user mode-এ সিস্টেম প্রসেস + subsystem DLL +
ntdll(syscall stub); kernel mode-এntoskrnl(executive + kernel), HAL, driver,win32k. Win32 ফাংশন syscall নয়। - Object ও handle: সবকিছু object; handle = প্রসেস-নিজস্ব সারি, অধিকার যাচাই খোলার সময়; handle count শূন্যে নাম সরে, reference count শূন্যে object মোছে। Token-এ SID, privilege, integrity level.
- Process-thread: process পাত্র, thread scheduling-এর একক (৩২ স্তর, quantum, boost); job দল বাঁধে। IRQL: ০ PASSIVE, ১ APC, ২ DISPATCH, ওপরে DIRQL — ২ বা ওপরে wait ও page fault নিষেধ; ISR → DPC → APC.
- মেমরি: VAD-এ এলাকা; reserve বনাম commit (limit ≈ RAM + pagefile); working set থেকে ছাঁটা page: dirty → Modified → (লেখা) → Standby; Standby থেকে ফেরত soft fault, পুনর্ব্যবহারের পর hard fault.
- বাকি সব: I/O = IRP-এর stack-যাত্রা; NTFS (MFT, $LogFile, ACL, ADS); registry = hive ফাইল + log; service = SCM; বুট bootmgfw → winload → ntoskrnl → smss; WSL1 অনুবাদ, WSL2 আসল kernel.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৫৭ · পারফরম্যান্স আর observability — top, vmstat, perf আর তদন্তের পদ্ধতিপরবর্তী পর্বধীর সিস্টেমের কারণ মেপে খুঁজে বের করা।
- অপারেটিং সিস্টেম · কোর্স হোম৬৪টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- পর্ব ৫৫ · Linux kernel-এর ভেতর — গঠন, module আর eBPFপাশাপাশি তুলনাএকই প্রশ্ন, monolithic kernel-এর উত্তর।
- পর্ব ০৬ · OS-এর গঠনপেছনে ফিরেHybrid kernel আর Tanenbaum–Torvalds বিতর্ক।
- C · Process আর IPCপ্রোগ্রামারের দিকfork, pipe আর file descriptor — handle-এর Unix আত্মীয়।
- DBMS · RecoveryডেটাবেসWrite-ahead log — registry আর NTFS-এর log-এর একই চিন্তা।