VirtualAlloc-এ MEM_COMMIT দিলে সেই মুহূর্তে Commit বাড়ে। Working Set তবে একই পরিমাণ নাও বাড়তে পারে। তাহলে যে মেমোরি আপনি বরাদ্দ করেছেন ভেবেছিলেন, সেটা কোথায়?
উত্তর হলো বেশিরভাগ পেজের এখনও সংশ্লিষ্ট ভৌত RAM নেই। Windows ভৌত পেজ বরাদ্দ সেই পর্যন্ত পিছিয়ে রাখে যতক্ষণ অ্যাপ্লিকেশন সত্যি পেজ ছোঁয়। প্রথম অ্যাক্সেসে CPU page fault তুললে মেমোরি ম্যানেজার VAD, PTE, সুরক্ষা বৈশিষ্ট্য ও backing store দেখে, প্রয়োজনে RAM এক পেজ করে বাঁধে।1
এই নিবন্ধ সেই পথ অনুসরণ করে যা «আপনি যে প্রথম বাইট ছোঁয়» ভৌত RAM পর্যন্ত যায়। Working Set ও Commit-এর মতো সংখ্যার অর্থ আগে সাজাতে চাইলে পরিচয় নিবন্ধ «What Does Windows’ “Memory Usage” Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File» দেখুন। এই সিরিজ সেখানকার শব্দ আবার সংজ্ঞায়িত করে না; যন্ত্রপাতির দিক থেকে খোঁড়ে «সংখ্যা কেন এমন বের হয়»।
«Windows মেমোরির গভীরতা» — তিন পর্বই
- পর্ব ১ (এই নিবন্ধ): ভার্চুয়াল ঠিকানা ও page fault
আমরা অনুসরণ করিVirtualAllocদিয়ে নেওয়া অঞ্চল কখন ভৌত RAM পায়। - পর্ব ২: একটি ভৌত পেজের জীবন
আমরা অনুসরণ করি Working Set ছেড়ে যাওয়া পেজ Modified, Standby, Free ও Zeroed দিয়ে কীভাবে চলে। - পর্ব ৩: সেকশন অবজেক্ট ও copy-on-write
আমরা অনুসরণ করি DLL, ফাইল ম্যাপিং ও শেয়ার করা মেমোরি কেন ভৌত পেজ ভাগ করতে পারে।
পর্ব ১ যে প্রশ্ন মীমাংসা করে তা কেবল একটি।
Commit হয়ে যাওয়া ভার্চুয়াল ঠিকানা কোন মুহূর্তে ভৌত RAM হয়?
লক্ষ্য পাঠক সেই ডেভেলপার ও পরিচালক যারা যন্ত্রপাতি থেকে Windows অ্যাপ মেমোরি ব্যবহার, স্টার্টআপের ঠিক পরে page fault, 0xC0000005, এবং VMMap ও PerfMon-এর সংখ্যা বুঝতে চান। পূর্বশর্ত Windows 10/11 বা বর্তমান Windows Server, এবং প্রয়োজনীয় পটভূমি পয়েন্টার ও VirtualAlloc-এর মূল কথা; পেজ-টেবিল বিট বিন্যাস বা কার্নেল ডিবাগারের অভিজ্ঞতা লাগে না। কঠিনতা মাঝারি। আমরা অভ্যন্তরীণ কাঠামোর নাম ব্যবহার করি, কিন্তু নির্দিষ্ট Windows বিল্ডের ওপর নির্ভরশীল অনথিভুক্ত বিন্যাস ধরি না।
1. আগে উপসংহার
সাধারণ প্রাইভেট মেমোরির প্রবাহ, এক লাইনে, এমন।
Reserve ভার্চুয়াল ঠিকানার সীমা আলাদা রাখে, Commit ভবিষ্যতে বিষয়বস্তু রাখার জায়গা নিশ্চিত করতে সিস্টেমের commit সীমার বিপরীতে commit চার্জ হিসাব করে, আর প্রথম অ্যাক্সেসের page fault ভৌত পেজ বরাদ্দ করে।
অর্থাৎ MEM_COMMIT এমন আদেশ নয় যা বলে «এখনই RAM বরাদ্দ করো»। Microsoft-এর VirtualAlloc নথিও নিশ্চিত করে Commit পেজের প্রাথমিক বিষয়বস্তু শূন্য, এবং ব্যাখ্যা করে যে প্রকৃত ভৌত পেজ ভার্চুয়াল ঠিকানায় অ্যাক্সেস পর্যন্ত বরাদ্দ হয় না।1
তবু «Reserve/Commit শুধু VAD-তে লেখে» বলাও অশুদ্ধ। বাস্তবে Reserve মূলত সেই VAD তৈরি করে যা ভার্চুয়াল ঠিকানার সীমা ও বৈশিষ্ট্য প্রকাশ করে, আর Commit সিস্টেমের Commit Total বাড়ায় ও সীমার commit অবস্থা নথিভুক্ত করে। মাঝের পেজ-টেবিল স্তর ও আলাদা PTE প্রয়োজনে অলসভাবে তৈরি হয়, আর ভৌত RAM-এর চূড়ান্ত বাঁধন সাধারণত প্রথম অ্যাক্সেসে ঘটে।
Commit খালি প্রতিশ্রুতি নয়; এটা সিস্টেমব্যাপী প্রতিশ্রুতি যে বিষয়বস্তু ভবিষ্যতে RAM বা উপযুক্ত backing store-এ রাখা যাবে। সেই প্রবেশবিন্দু যা এই প্রতিশ্রুতিকে এক পেজ করে বাস্তব করে, তা page fault।
flowchart TB
accTitle: Reserve, Commit ও প্রথম অ্যাক্সেসে কী ঘটে
accDescr: MEM_RESERVE সীমা ও বৈশিষ্ট্য VAD-তে নথিভুক্ত করে, MEM_COMMIT সংরক্ষণের প্রতিশ্রুতি দিতে Commit Total খরচ করে, আর প্রথম অ্যাক্সেসের page fault ভৌত পেজ বরাদ্দ করে Working Set-এ যোগ করে
reserve["1. MEM_RESERVE"] --> commit["2. MEM_COMMIT"]
commit --> touch["3. প্রথম অ্যাক্সেস(Touch)"]
reserve -.-> vad["সীমা ও বৈশিষ্ট্য VAD-তে নথিভুক্ত"]
commit -.-> charge["Commit Total খরচ(এখনও ভৌত পেজ নেই)"]
touch --> fault["Page fault"]
fault --> zero["শূন্য করা ভৌত পেজ PTE-তে বাঁধুন"]
zero --> ws["Working Set-এ যোগ করে নির্দেশ আবার চালান"]
চিত্র ১: Reserve, Commit ও Touch আলাদা ঘটনা। ভৌত RAM কেবল শেষ ধাপে, প্রথম অ্যাক্সেসে, বাঁধা হয়।
2. তিন খাতা যা একটি ভার্চুয়াল পেজ অনুসরণ করে
ভার্চুয়াল ঠিকানা থেকে ভৌত RAM-এর পথ বুঝতে Windows যে তিন ধরনের খাতা রাখে তা আলাদা করতে হয়।
| খাতা | একক | ভূমিকা |
|---|---|---|
| VAD | ভার্চুয়াল ঠিকানার সীমা | অঞ্চল কী, Reserve/Commit, সুরক্ষা ও সেকশন মিল পরিচালনা করে |
| পেজ টেবিল / PTE | ভার্চুয়াল পেজ | ভৌত পেজে বর্তমান অনুবাদ, বা অবাস্তব অবস্থা প্রকাশ করে |
| PFN ডেটাবেস | ভৌত পেজ | প্রতিটি RAM পেজের মালিকানা, রেফারেন্স ও অবস্থা অনুসরণ করে |
VAD সীমার তথ্য রাখে, PTE ভার্চুয়াল পেজের, আর PFN ডেটাবেস ভৌত পেজের। page-fault হ্যান্ডলার এগুলো মিলিয়ে সিদ্ধান্ত নেয় অ্যাক্সেস চলতে পারে কি না।
flowchart TB
accTitle: ভার্চুয়াল ঠিকানা থেকে ভৌত RAM পর্যন্ত তিন খাতা
accDescr: ভার্চুয়াল ঠিকানা VAD সীমা কণিকায় ও PTE ভার্চুয়াল-পেজ কণিকায় পরিচালনা করে, আর PFN ডেটাবেস PTE-এর অনুবাদ লক্ষ্য ভৌত পেজকে ভৌত-পেজ কণিকায় অনুসরণ করে
va["ভার্চুয়াল ঠিকানা"] --> vad["VAD(সীমা খাতা)"]
va --> pte["PTE(ভার্চুয়াল-পেজ খাতা)"]
vad -.->|Reserve/Commit ও সুরক্ষা বিচার| pte
pte -->|বৈধ অনুবাদ| pfn["PFN ডেটাবেস(ভৌত-পেজ খাতা)"]
pfn --> ram["ভৌত RAM পেজ"]
চিত্র ২: ভিন্ন কণিকার তিন খাতা। Fault পরিচালনা VAD ও PTE মিলায়, তারপর ফল PFN পাশে প্রতিফলিত করে।
এই নিবন্ধের নায়ক VAD ও PTE। PFN ডেটাবেস আমরা পর্ব ২-এ ভৌত-পেজ পাশ থেকে দেখব।
3. Reserve, Commit ও Touch আলাদা ঘটনা
3.1. Reserve — একটি ঠিকানা দাবি করা
প্রথমে, 256MiB-এর অবিচ্ছিন্ন ভার্চুয়াল ঠিকানার সীমা সংরক্ষণ করুন।
void* base = VirtualAlloc(
nullptr,
256ull * 1024 * 1024,
MEM_RESERVE,
PAGE_NOACCESS);
এই বিন্দুতে যা ঘটেছে তা কেবল এই যে প্রক্রিয়ার ভার্চুয়াল স্থানে একটি ঠিকানা আলাদা রাখা হয়েছে যাতে অন্য বরাদ্দ এই সীমা ব্যবহার করতে না পারে। MEM_RESERVE RAM বা পেজ ফাইলে কোনো ভৌত সংরক্ষণ বরাদ্দ করে না।1
কারণ 64-বিট প্রক্রিয়ার ভার্চুয়াল স্থান বিশাল, আগে বড় সীমা Reserve করে পরে শুধু দরকারি অংশ Commit করা বাস্তবসম্মত হয়।
3.2. Commit — প্রতিশ্রুতি যে রাখা যাবে
পরবর্তীতে, সংরক্ষিত সীমা Commit করুন।
void* committed = VirtualAlloc(
base,
256ull * 1024 * 1024,
MEM_COMMIT,
PAGE_READWRITE);
সফল হলে সেই প্রতিশ্রুত পরিমাণ বাড়ে যা সিস্টেমের Commit Total — এবং সাধারণত প্রক্রিয়ার Private Bytes — এ প্রতিফলিত হয়। তবু 256MiB ভৌত পেজ একসঙ্গে দাঁড়ায় না। সাধারণ পেজ প্রথম অ্যাক্সেস পর্যন্ত ভৌতভাবে অবরাদ্দ থাকে।12
তাহলে Commit-এর মানে কী? এই যে যখন সিস্টেম প্রতিশ্রুতি নিতে পারে না, সে Commit-এর সময়েই ব্যর্থতা ফেরাতে পারে, মেমোরি ব্যবহারের মাঝখানে নয়।
3.3. Touch — যখন ভৌত পেজ দরকার হয়
অবশেষে, নিচের অ্যাসাইনমেন্ট প্রথম পেজে প্রথমবার লেখে।
static_cast<unsigned char*>(base)[0] = 1;
CPU ভার্চুয়াল ঠিকানাকে ভৌত ঠিকানায় অনুবাদ করতে চায়, কিন্তু PTE-তে এখনও ভৌত পেজের বৈধ অনুবাদ নেই। এখানে page fault হয়।
নিয়ন্ত্রণ পাওয়া মেমোরি ম্যানেজার একে «Commit ও লেখার যোগ্য প্রাইভেট পেজে প্রথম অ্যাক্সেস» বলে বিচার করে, শূন্য করা ভৌত পেজ নেয়, PTE-তে বাঁধে এবং Working Set-এ যোগ করে। তারপর ব্যর্থ লেখার নির্দেশ আবার চালায়।
অ্যাপ থেকে এটা সাধারণ অ্যাসাইনমেন্ট, কিন্তু ভিতরে অ্যাসাইনমেন্টের মাঝে নিয়ন্ত্রণ কার্নেলে যায়, ভৌত পেজ বরাদ্দ হয়, আর কার্যকরীকরণ সেই নির্দেশে ফেরে।
4. VAD — ভার্চুয়াল স্থানের সীমা খাতা
VAD মানে Virtual Address Descriptor, আর Windows একটি প্রক্রিয়ার ব্যবহৃত ঠিকানার সীমা VAD-এর গাছ হিসেবে পরিচালনা করে। WinDbg-এর !vad দিয়ে শুরু ও শেষ VPN, Commit, সুরক্ষা বৈশিষ্ট্য, Private/Mapped, Control Area ইত্যাদি দেখা যায়।3
VAD যে প্রতিনিধি তথ্য নথিভুক্ত করে তার মধ্যে এগুলো আছে।
- ঠিকানার সীমার শুরু ও শেষ
- ধরন, যেমন Private, Mapped বা Image
- Reserve/Commit অবস্থা
- পড়া, লেখা, চালানো ও copy-on-write-এর মতো সুরক্ষা
- ফাইল বা সেকশনের সঙ্গে মিল
- গার্ড পেজের মতো বিশেষ বৈশিষ্ট্য
সীমা দিয়ে পরিচালনার কারণ দক্ষতা। 256MiB 4KiB-তে 65,536 পেজ। প্রতি পেজের জন্য আগে থেকে পূর্ণ ব্যবস্থাপনা কাঠামো বানানোর বদলে VAD-তে «এই অবিচ্ছিন্ন সীমা একটি সংরক্ষণ» রাখা এবং প্রয়োজনে পেজ বাস্তবায়ন কম অপচয়।
4.1. VAD পাওয়া পুনরুদ্ধারের নিশ্চয়তা নয়
«VAD-তে থাকলে fault মীমাংসা হয়; না থাকলে অ্যাক্সেস লঙ্ঘন» সুবিধাজনক প্রাথমিক ব্যাখ্যা, কিন্তু অতি সরল। VAD পাওয়া গেলেও নিচের মতো ক্ষেত্রে সাধারণ অ্যাক্সেস চলতে পারে না।
- শুধু Reserve, আর লক্ষ্য পেজ Commit নয়
PAGE_NOACCESS- শুধু-পড়ার পেজে লেখা
- চালানো-অযোগ্য পেজ থেকে নির্দেশ চালানো
- গার্ড পেজের প্রথম স্পর্শ
- সেকশনের বৈধ সীমার বাইরে স্পর্শ
উল্টো, PTE অবৈধ হলেও VAD ও PTE-এর সফটওয়্যার অবস্থা বৈধ অ্যাক্সেস দেখালে demand-zero, Transition পুনরুদ্ধার, page-in বা CoW দিয়ে মীমাংসা হতে পারে। আরও সঠিক উত্তর VAD, PTE, সুরক্ষা বৈশিষ্ট্য ও অ্যাক্সেসের ধরন একসঙ্গে বিচার করুন।
5. পেজ টেবিল ও TLB
অ্যাপ যে পয়েন্টার রাখে তা ভার্চুয়াল ঠিকানা। CPU-এর RAM অ্যাক্সেস করতে ভার্চুয়াল পেজ সংখ্যাকে ভৌত পেজ সংখ্যায় অনুবাদ করতে হয়। সেই স্তরিত অনুবাদ টেবিল পেজ টেবিল, আর পাতার এন্ট্রি PTE (Page Table Entry)।
বৈধ PTE ধারণাগতভাবে PFN, পড়া/লেখা/চালানো সুরক্ষা, ব্যবহারকারী-মোড অনুমতি, Accessed/Dirty ও অনুরূপ তথ্য রাখে। প্রকৃত বিট বিন্যাস CPU ও Windows সংস্করণের ওপর নির্ভর করে।
প্রতিবার পেজ টেবিল হাঁটা অত্যন্ত ধীর হবে, তাই CPU সাম্প্রতিক অনুবাদ TLB (Translation Lookaside Buffer)-তে ক্যাশ করে। ঠিকানা অনুবাদ এই ক্রমে চলে।
- TLB-তে অনুবাদ থাকলে এবং অ্যাক্সেস সেই সুরক্ষার সঙ্গে মিললে সেই ফল ব্যবহার হয়।
- TLB-তে অনুবাদ না থাকলে CPU পেজ টেবিল হাঁটে।
- বৈধ PTE থাকলে এবং সুরক্ষাও মিললে তা TLB-তে নথিভুক্ত হয় ও কার্যকরীকরণ চলে।
- বৈধ অনুবাদ না থাকলে, বা সুরক্ষা লঙ্ঘন থাকলে, নিয়ন্ত্রণ page-fault প্রবেশবিন্দুতে যায়। সুরক্ষা পরীক্ষা তখনও হয় যখন অনুবাদ TLB থেকে এসেছে।
এই প্রবাহ দেখায় TLB miss আর page fault আলাদা জিনিস। একমাত্র সমস্যা যদি হয় TLB-তে অনুবাদ নেই আর PTE বৈধ, শুধু পেজ-টেবিল হাঁটা হয়। উল্টো, TLB-তে অনুবাদ থাকলেও শুধু-পড়ার পেজে লেখা বা চালানো-অযোগ্য পেজে নির্দেশ চালানোর মতো সুরক্ষা লঙ্ঘন page-fault প্রবেশবিন্দুতে যায়। তাই CoW পেজে লেখা fault করতে পারে অনুবাদ আগেই ক্যাশ থাকলেও।
flowchart TB
accTitle: ঠিকানা-অনুবাদ প্রবাহ ও page-fault প্রবেশবিন্দু
accDescr: TLB-তে অনুবাদ থাকলেও সুরক্ষা অমিল page-fault প্রবেশবিন্দুতে যায়। TLB-তে অনুবাদ না থাকলে পেজ টেবিল হাঁটা হয়; বৈধ PTE যা সুরক্ষার সঙ্গেও মেলে TLB-তে নথিভুক্ত হয় ও কার্যকরীকরণ চলে, আর অবৈধ অনুবাদ বা সুরক্ষা লঙ্ঘন page-fault প্রবেশবিন্দুতে যায়
access["মেমোরি অ্যাক্সেস"] --> tlb{"TLB-তে অনুবাদ আছে?"}
tlb -->|হ্যাঁ| perm{"অ্যাক্সেস সুরক্ষার সঙ্গে মেলে?"}
perm -->|মেলে| go["সেই অনুবাদ দিয়ে চালিয়ে যান"]
perm -->|সুরক্ষা লঙ্ঘন| entry["page-fault প্রবেশবিন্দুতে"]
tlb -->|না| walk["পেজ-টেবিল হাঁটা"]
walk --> valid{"বৈধ PTE আর সুরক্ষাও মেলে?"}
valid -->|হ্যাঁ| register["TLB-তে নথিভুক্ত করে চালিয়ে যান(fault নেই)"]
valid -->|অবৈধ বা সুরক্ষা লঙ্ঘন| entry
চিত্র ৩: TLB miss পেজ-টেবিল হাঁটায় মীমাংসা হতে পারে। অনুবাদ অবৈধ বা সুরক্ষা লঙ্ঘন থাকলে নিয়ন্ত্রণ page fault-এ যায়, আর সুরক্ষা লঙ্ঘন TLB hit-এও ঘটে।
5.1. অবৈধ PTE শুধু ফাঁকা নয়
অবৈধ PTEও খালি নয়। অবৈধ PTE-এর সফটওয়্যার অবস্থা থেকে Windows নিচের মতো ক্ষেত্র আলাদা করে।
- demand-zero পেজ যা কখনও বাস্তবায়িত হয়নি
- Transition পেজ যা RAM-এ থাকে
- Prototype PTE নির্দেশ করা শেয়ার করা পেজ
- পেজ ফাইলে সংরক্ষিত প্রাইভেট পেজ
- সুরক্ষা লঙ্ঘন বা অবৈধ অঞ্চল
CPU-এর কাজ কেবল সিদ্ধান্ত «এটা সাধারণ বৈধ অনুবাদ নয়» আর কার্নেলে হস্তান্তর; অর্থ সেখান থেকে মেমোরি ম্যানেজার দেয়।
6. শুরু থেকে শেষ পর্যন্ত একটি page fault
Commit প্রাইভেট পেজে প্রথম লেখাকে ছয় ধাপে অনুসরণ করি।
- CPU লিখতে চায়।
সে TLB ও পেজ টেবিল দেখে, কিন্তু লক্ষ্য PTE-তে বৈধ PFN নেই। - CPU page fault তোলে।
সে fault করা ভার্চুয়াল ঠিকানা, পড়া/লেখা/চালানোর ধরন, ব্যবহারকারী/কার্নেল, আর সমস্যা অনুবাদ নেই না সুরক্ষা লঙ্ঘন, কার্নেলে দেয়। - মেমোরি ম্যানেজার VAD ও PTE দেখে।
সে সিদ্ধান্ত নেয় পেজ Commit কি না, সুরক্ষা মেলে কি না, আর demand-zero, Transition, শেয়ার, page-in, CoW বা ব্যতিক্রমের কোনটি প্রযোজ্য। - demand-zero হলে শূন্য করা ভৌত পেজ নেওয়া হয়।
নতুন দেওয়া পেজ শূন্য হতে হয় যাতে অন্য প্রক্রিয়ার ডেটা না ফাঁসে। - PTE ও PFN ব্যবস্থাপনা তথ্য আপডেট হয়।
PTE-তে PFN ও সুরক্ষা বসানো হয়, ভৌত পেজ Active হয়, আর প্রক্রিয়ার Working Set-এ যোগ হয়। - ব্যর্থ নির্দেশ আবার চালানো হয়।
কারণ fault স্বাভাবিক মীমাংসা হয়েছে, ব্যবহারকারী-মোড ব্যতিক্রম দেওয়া হয় না, আর অ্যাপ স্বাভাবিকভাবে অ্যাসাইনমেন্ট চালিয়ে যায়।
ETW page-fault ঘটনাও Transition, Demand Zero, Copy-on-Write, Guard Page, Hard Page Fault ও Access Violation আলাদা ধরন হিসেবে নথিভুক্ত করে।4
তাই page fault শুরু থেকে «অস্বাভাবিক» অর্থের শব্দ নয়। এটা সাধারণ প্রবেশবিন্দু যখন CPU সাধারণ পথে অনুবাদ করতে পারেনি তখন OS-কে সিদ্ধান্ত চাইতে।
flowchart LR
accTitle: page-fault মীমাংসার শাখা
accDescr: মেমোরি ম্যানেজার VAD, PTE, সুরক্ষা বৈশিষ্ট্য ও অ্যাক্সেসের ধরন বিচার করে demand-zero, RAM-এ থাকা পেজ আবার যোগ, backing store থেকে hard fault, copy-on-write, গার্ড-পেজ বিজ্ঞপ্তি বা ব্যতিক্রমে পাঠায়
faultIn["Page fault ঘটে"] --> judge["VAD, PTE, সুরক্ষা, ধরন বিচার"]
judge -->|প্রথম অ্যাক্সেস| dz["Demand-zero(নরম)"]
judge -->|এখনও RAM-এ| soft["Standby থেকে আবার যোগ(নরম)"]
judge -->|ডিস্ক পড়া দরকার| hard["Hard fault(ডিস্ক I/O)"]
judge -->|CoW লেখা| cow["কপি করে PTE বদলান"]
judge -->|গার্ড পেজ| guard["গার্ড মুছে জানান"]
judge -->|অমীমাংসিত| av["ব্যতিক্রম(0xC0000005 ইত্যাদি)"]
চিত্র ৪: একই প্রবেশবিন্দু দিয়ে আসা fault বিচার অনুসারে ছয় ধরনের ফলে ভাগে। গার্ড পেজের বিস্তারিত ধারা ৯-এ।
7. Demand-zero — soft fault যা ডিস্ক পড়ে না
Demand-zero সেই প্রতিনিধি soft fault যা Commit প্রাইভেট পেজ প্রথম ছোঁয়ায় ঘটে। Microsoft-এর Working Set নথিও «প্রক্রিয়া বরাদ্দ ভার্চুয়াল পেজ প্রথমবার উল্লেখ করে» soft fault-এর উদাহরণ হিসেবে তালিকাভুক্ত করে।5
Demand-zero-এর বৈশিষ্ট্য এমন।
- ডিস্ক থেকে মূল ডেটা পড়ার দরকার নেই
- প্রাথমিক বিষয়বস্তু শূন্য
- উপলব্ধ ভৌত পেজ বাঁধা হয়
- Working Set ও ক্রমবর্ধমান Page Fault Count বাড়ে
- শুধু এই পরিচালনা
Memory\\Pages Input/secবাড়ায় না
তাই স্টার্টআপের ঠিক পরে Page Faults/sec-এর লাফ নিজে থেকে মানে করে না যে স্টোরেজই বাধা।
অলস বরাদ্দের আপসও সাজানো দরকার। 256MiB Commit করে সত্যি 8MiB ব্যবহার করলে বাকি 248MiB RAM-এর বাইরে রাখা যুক্তিসঙ্গত। বিনিময়ে প্রথম অ্যাক্সেসে fault পরিচালনার খরচ থাকে। বিলম্ব-সংবেদনশীল কাজের জন্য এমন নকশা আছে যা শুরুর আগে প্রতি পেজ ছুঁয়ে prefault করে, কিন্তু সেটা আপস যা আগে থেকে RAM বাস বাড়ায়।
8. Soft fault ও hard fault
8.1. Soft fault
Soft fault সেই fault যা backing store-এ পড়ার I/O ছাড়া মীমাংসা হয়। প্রতিনিধি উদাহরণে এগুলো আছে।
- Demand-zero
- Standby/Transition-এ থাকা পেজ আবার যোগ
- অন্য প্রক্রিয়ার Working Set-এ থাকা শেয়ার করা পেজ যোগ
- আগে থেকে আনা পেজ যোগ
- Copy-on-Write যার মূল পেজ বাসিন্দা
কার্নেল স্থানান্তর, লক, PTE/PFN আপডেট, TLB সামঞ্জস্য ইত্যাদির CPU খরচ আছে, কিন্তু স্টোরেজ অপেক্ষা নেই।5
8.2. Hard fault
অন্যদিকে, দরকারি পেজ RAM-এ কোথাও নেই এবং backing store থেকে পড়তে হলে তা hard fault। পড়ার উৎস শুধু পেজ ফাইল নয়।
- পেজ ফাইলে লেখা প্রাইভেট পেজ
- মেমোরি-ম্যাপ করা ফাইল
- EXE বা DLL ছবি
- ফাইল ক্যাশ উল্লেখ করা ডেটা ফাইল
ETW HardFault ঘটনায় FileObject, ReadOffset ও ByteCount থাকে, তাই প্রকৃত পড়ার উৎস অনুসরণ করা যায়।6
অতএব Hard Fault = pagefile.sys পড়া সত্য নয়।
Backing-store পড়া দরকার হলে অনুরোধ Windows I/O স্ট্যাকে যায়। IRP ও ইস্যু/সম্পন্নের প্রবাহ «The Depths of Windows I/O (Part 1)»-এ, আর ফাইল ক্যাশের সঙ্গে সংযোগ «The Depths of Windows I/O (Part 4)»-এ। পেজ RAM-এ থাকলে মেমোরি ম্যানেজার নিজে ফিরতে পারে; না থাকলে সে I/O ইস্যু করে সম্পন্ন পর্যন্ত fault করা থ্রেড অপেক্ষা করে।
9. অমীমাংসিত fault হয়ে যায় ব্যতিক্রম
VAD ও PTE দেখার পর বৈধ বরাদ্দ, page-in বা CoW হিসেবে মীমাংসা না হওয়া fault ব্যবহারকারী মোডে ব্যতিক্রম হিসেবে দেওয়া হয়।
প্রতিনিধি ক্ষেত্র STATUS_ACCESS_VIOLATION, ব্যতিক্রম কোড 0xC0000005। অবৈধ ঠিকানায় পড়া, লেখা বা চালানোয় ঘটে; প্রথম ব্যতিক্রম প্যারামিটার অ্যাক্সেসের ধরন ও দ্বিতীয় লঙ্ঘন ঠিকানা নির্দেশ করে।7
সাধারণ ধরনে এগুলো আছে।
- NULL, মুক্ত ঠিকানা বা অ্যারের বাইরের ঠিকানা পড়া
- শুধু-পড়ার পেজে লেখা
- DEP/NX চালানো-অযোগ্য করা পেজ থেকে নির্দেশ চালানো
- সংরক্ষিত কিন্তু Commit নয় এমন সীমা ছোঁয়া
PAGE_GUARD-এর অর্থ একটু আলাদা। এটা অ্যাক্সেসের একবারের বিজ্ঞপ্তি: STATUS_GUARD_PAGE_VIOLATION তোলে এবং স্ট্যাক বৃদ্ধির মতো কাজে ব্যবহৃত হয়।8
স্বাভাবিক অলস বরাদ্দ, page-in, CoW, গার্ড বিজ্ঞপ্তি ও চূড়ান্ত অ্যাক্সেস লঙ্ঘন CPU-এর দৃষ্টিতে একই page-fault প্রবেশবিন্দুতে জোটে। ফল যা নির্ধারণ করে তা VAD, PTE, সুরক্ষা বৈশিষ্ট্য ও অ্যাক্সেসের ধরনের সংমিশ্রণ।
10. নিজে দেখুন
এখন পর্যন্ত প্রবাহ নিজের মেশিনে দেখা যায়। নিচের C++ প্রোগ্রাম 256MiB Reserve করে, Commit করে, প্রতি পেজে এক বাইট লেখে, আর শেষে Release করে। প্রতি ধাপে Enter অপেক্ষা করে যাতে VMMap ও PerfMon-এ পরিবর্তন দেখতে পারেন।
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#include <cstdio>
#include <cstdlib>
#pragma comment(lib, "Psapi.lib")
constexpr SIZE_T kSize = 256ull * 1024 * 1024;
void PrintMemory(const char* stage)
{
PROCESS_MEMORY_COUNTERS_EX c{};
c.cb = sizeof(c);
if (!GetProcessMemoryInfo(
GetCurrentProcess(),
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&c),
sizeof(c))) {
std::printf("GetProcessMemoryInfo failed: %lu\n", GetLastError());
return;
}
std::printf(
"%-10s WS=%zu MiB Private=%zu MiB Faults=%lu\n",
stage,
c.WorkingSetSize / 1024 / 1024,
c.PrivateUsage / 1024 / 1024,
c.PageFaultCount);
}
void Pause(const char* message)
{
PrintMemory(message);
std::puts("Press Enter...");
(void)std::getchar();
}
int main()
{
SYSTEM_INFO si{};
GetSystemInfo(&si);
std::printf("PID=%lu, page=%lu bytes\n",
GetCurrentProcessId(), si.dwPageSize);
void* base = VirtualAlloc(nullptr, kSize, MEM_RESERVE, PAGE_NOACCESS);
if (!base) {
std::fprintf(stderr, "Reserve failed: %lu\n", GetLastError());
return EXIT_FAILURE;
}
Pause("reserved");
if (!VirtualAlloc(base, kSize, MEM_COMMIT, PAGE_READWRITE)) {
std::fprintf(stderr, "Commit failed: %lu\n", GetLastError());
VirtualFree(base, 0, MEM_RELEASE);
return EXIT_FAILURE;
}
Pause("committed");
auto* bytes = static_cast<volatile unsigned char*>(base);
for (SIZE_T offset = 0; offset < kSize; offset += si.dwPageSize) {
bytes[offset] = 1;
}
Pause("touched");
if (!VirtualFree(base, 0, MEM_RELEASE)) {
std::fprintf(stderr, "Release failed: %lu\n", GetLastError());
return EXIT_FAILURE;
}
Pause("released");
}
Visual Studio-এর x64 Native Tools Command Prompt থেকে নিচের কমান্ডে বানানো যায়।
cl /std:c++20 /EHsc /W4 memory_fault_demo.cpp
10.1. VMMap-এ কী দেখবেন
VMMap এমন সরঞ্জাম যা সংরক্ষিত ভার্চুয়াল মেমোরি, Commit, Working Set, Private ও Shareable ধরন অনুসারে দেখায়।9 প্রতি ধাপে প্রত্যাশিত পরিবর্তন এমন।
| ধাপ | প্রত্যাশিত পরিবর্তন |
|---|---|
| Reserve | Address Space Size বাড়ে, কিন্তু Commit/WS একই পরিমাণ বাড়ে না |
| Commit | Private Commit প্রায় 256MiB বাড়ে |
| Touch | Working Set ও Private WS উল্লেখযোগ্য বাড়ে, Fault Countও বাড়ে |
| Release | লক্ষ্য সীমা অদৃশ্য হয়, আর Commit ও WS কমে |
প্রকৃত সংখ্যা রানটাইম, নিরাপত্তা পণ্য, মেমোরি চাপ ও দেখার সময় অনুসারে বদলায়। দেখুন ধাপগুলোর মধ্যে সংখ্যা কোন দিকে গেল, ঠিক 256MiB বের হলো কি না নয়।
10.2. PerfMon-এ নরম ও শক্ত আলাদা করা
PerfMon-এ নিচের কাউন্টার একই সময়-অক্ষে রাখুন।
Process(<target>)\\Page Faults/secMemory\\Pages Input/secMemory\\Page Reads/secMemory\\Available MBytesProcess(<target>)\\Working Set - PrivateProcess(<target>)\\Private Bytes
Process\\Page Faults/sec-এ soft ও hard দুই faultই আছে। অন্যদিকে Memory\\Pages Input/sec hard fault মীমাংসায় ডিস্ক থেকে পড়া পেজের সংখ্যা।10
এই প্রোগ্রামের Touch ধাপে Page Faults/sec লাফানো উচিত আর Pages Input/sec খুব বাড়া উচিত নয়। নতুন Commit পেজ demand-zero-তে বাস্তবায়িত হয়, তাই ডিস্ক থেকে মূল ডেটা পড়ার দরকার নেই।
একাধিক প্রক্রিয়া এক নাম ভাগ করলে process#1-এর মতো PerfMon সংখ্যা পুনর্শুরুতে বদলাতে পারে। PID দেখানো কাউন্টারের সঙ্গে মেলান, অথবা Process V2 বা ETW/WPA দিয়ে PID দিয়ে চিহ্নিত করুন।
11. অনুশীলনে এড়াতে তিন ভুল পাঠ
11.1. «Commit বেড়েছে, তাই RAM লিক»
Commit রাখার প্রতিশ্রুত বিষয়বস্তুর পরিমাণ; অস্পৃষ্ট পেজ RAM-এ বাসিন্দা নাও হতে পারে। লিক বিচারে Private Bytes-এর সময় সিরিজ, বরাদ্দের ভাঙন, আর প্রক্রিয়াকরণ শেষে সংখ্যা বেসলাইনে ফেরে কি না দেখুন।
11.2. «Page Faults/sec উঁচু, তাই ডিস্ক ধীর»
Soft fault-এ ডিস্ক I/O নেই। Page Faults/sec, Pages Input/sec ও স্টোরেজ অপেক্ষা আলাদা করুন, আর প্রয়োজনে ETW HardFault ঘটনা দিয়ে উৎস ফাইল ও স্ট্যাক অনুসরণ করুন।
11.3. «Working Set খালি করলে লিক ঠিক হবে»
Working Set থেকে পেজ সরালে Commit বা মালিকানা ছাড়ে না। পেজ Standby বা Modified-এ যায় আর পরে আবার fault করে আসে। লিক ঠিক করতে বরাদ্দকারীকে VirtualFree, হিপ মুক্তি, অবজেক্ট ধ্বংস ইত্যাদি করতে হয়।
সেই সরানো ভৌত পেজ কোথায় যায় তা আমরা পর্ব ২-এ অনুসরণ করি।
12. সারসংক্ষেপ
MEM_RESERVEভার্চুয়াল ঠিকানার সীমা আলাদা রাখে কিন্তু RAM বা পেজ ফাইলে ভৌত অঞ্চল বরাদ্দ করে না।1MEM_COMMITCommit খরচ করে ও নিশ্চিত করে বিষয়বস্তু ভবিষ্যতে রাখা যাবে, কিন্তু সাধারণ ভৌত পেজ প্রথম অ্যাক্সেস পর্যন্ত বরাদ্দ হয় না।12- VAD সীমা খাতা, PTE ভার্চুয়াল-পেজ খাতা, আর PFN ডেটাবেস ভৌত-পেজ খাতা।
- TLB miss page fault নয়। PTE বৈধ হলে শুধু পেজ-টেবিল হাঁটা মীমাংসা করে।
- Demand-zero, Transition পুনরুদ্ধার ও শেয়ার করা পেজ যোগ soft fault যা ডিস্ক I/O ছাড়া মীমাংসা হয়।5
- পেজ ফাইল, DLL, EXE বা ম্যাপ করা ফাইল থেকে পড়তে হলে তা hard fault।6
- VAD, PTE ও সুরক্ষা বৈশিষ্ট্য পরীক্ষা fault মীমাংসা না করলে
0xC0000005-এর মতো ব্যতিক্রম পাবেন।7 - পারফরম্যান্স বিচারে শুধু
Page Faults/secদেখবেন না; একই সময়-অক্ষেPages Input/sec, Available, Working Set, Private Bytes ও স্টোরেজ অপেক্ষা দেখুন।
চলবে পর্ব ২, «একটি ভৌত পেজের জীবন: পাঁচ তালিকা ও পেজ ফাইলের সত্য»।
Commit প্রতিশ্রুতি ভৌত পেজ হওয়ার পর আমরা অনুসরণ করি সেই পেজ Working Set ছাড়লে কোথায় যায়, PFN ডেটাবেস ও পেজ তালিকা থেকে।
সম্পর্কিত নিবন্ধ
- What Does Windows’ “Memory Usage” Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File
- The Depths of Windows I/O (Part 1) — Every Read and Write Becomes an IRP: The Big Picture of the I/O System
- The Depths of Windows I/O (Part 4) — Cache Manager: When Does Your WriteFile Actually Reach the Disk?
- Reading Crash Dumps with WinDbg + SOS — A Practical Guide to Analysis After Collection
- An Introduction to Collecting Windows Crash Dumps - WER/ProcDump/WinDbg
সম্পর্কিত পরামর্শ ক্ষেত্র
KomuraSoft LLC Windows অ্যাপ্লিকেশনের মেমোরি ব্যবহার, অ্যাক্সেস লঙ্ঘন, স্টার্টআপ বিলম্ব, পেজিং ও নেটিভ কোড ত্রুটির অনুসন্ধান করে।
- Windows অ্যাপ্লিকেশন ডেভেলপমেন্ট
- বাগ অনুসন্ধান ও মূল কারণ বিশ্লেষণ
- বিদ্যমান অ্যাসেটের পুনর্ব্যবহার ও মাইগ্রেশন
- যোগাযোগ করুন
তথ্যসূত্র
-
Microsoft Learn, VirtualAlloc function.
MEM_RESERVEভৌত সংরক্ষণ না দিয়ে ভার্চুয়াল ঠিকানার সীমা সংরক্ষণ করে;MEM_COMMITসিস্টেমের সামগ্রিক মেমোরি ও পেজ ফাইলের বিপরীতে commit চার্জ হিসাব করে; Commit পেজের প্রাথমিক বিষয়বস্তু শূন্য; আর প্রকৃত ভৌত পেজ অ্যাক্সেস পর্যন্ত বরাদ্দ হয় না — এসব নিয়ে। ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Learn, PERFORMANCE_INFORMATION structure.
CommitTotalসিস্টেমের বর্তমান Commit পেজ সংখ্যা, আরCommitLimitপেজ ফাইল না বাড়িয়ে Commit করা যায় এমন উপরের সীমা — এসব নিয়ে। ↩ ↩2 -
Microsoft Learn, !vad (WinDbg).
!vadVAD গাছ দেখায় এবং শুরু ও শেষ VPN, Commit, Mapped/Private, সুরক্ষা বৈশিষ্ট্য, Control Area ইত্যাদি দেখতে দেয় — এসব নিয়ে। ↩ -
Microsoft Learn, PageFault_TypeGroup1 class. ETW Transition Fault, Demand Zero Fault, Copy-on-Write, Guard Page Fault, Hard Page Fault ও Access Violation আলাদা করে নথিভুক্ত করে — এসব নিয়ে। ↩
-
Microsoft Learn, Working Set. Soft fault backing store অ্যাক্সেস ছাড়া মীমাংসা হয়, এবং অন্য প্রক্রিয়ার Working Set, Transition, প্রথম-উল্লেখ demand-zero ইত্যাদি থেকে ঘটে — এসব নিয়ে। ↩ ↩2 ↩3
-
Microsoft Learn, PageFault_HardFault class. HardFault ঘটনায় FileObject, ReadOffset, ByteCount, VirtualAddress ও থ্রেড ID থাকে, তাই পড়ার উৎস অনুসরণ করা যায় — এসব নিয়ে। ↩ ↩2
-
Microsoft Learn, Access Violation C0000005.
0xC0000005অবৈধ মেমোরি ঠিকানায় পড়া, লেখা বা চালানোয় ঘটে, আর ব্যতিক্রম প্যারামিটার অ্যাক্সেসের ধরন ও লঙ্ঘন ঠিকানা নির্দেশ করে — এসব নিয়ে। ↩ ↩2 -
Microsoft Learn, Creating Guard Pages.
PAGE_GUARDপেজ অ্যাক্সেসের একবারের বিজ্ঞপ্তি দেয় ওSTATUS_GUARD_PAGE_VIOLATIONতোলে — এসব নিয়ে। ↩ -
Microsoft Learn, VMMap - Sysinternals. VMMap Commit ভার্চুয়াল মেমোরি ধরন অনুসারে ভাঙে এবং প্রতি ধরনের Working Set ও বিস্তারিত ঠিকানা মানচিত্র দেখায় — এসব নিয়ে। ↩
-
Microsoft Learn, Performance Analysis of Logs (PAL) Tool.
Memory\\Pages Input/sechard page fault মীমাংসায় ডিস্ক থেকে পড়া পেজের সংখ্যা — এসব নিয়ে। ↩
সম্পর্কিত নিবন্ধ
কাছাকাছি বিষয়ে গভীরে যেতে একই ট্যাগযুক্ত সাম্প্রতিক নিবন্ধ।
Windows ভার্চুয়ালাইজেশনের গভীরতা (পর্ব ৩) — সেকেন্ডে বুট হওয়া ভার্চুয়াল মেশিন: WSL2, Windows Sandbox ও কন্টেইনার এত হালকা কেন
WSL2 ও Windows Sandbox সেকেন্ডে শুরু হয়ে এত হালকা মনে হয় কেন? এই নিবন্ধ ডায়নামিক বেস ইমেজ ও ডাইরেক্ট ম্যাপ থেকে ডায়নামিক মেমরি বরাদ্দ...
Windows ভার্চুয়ালাইজেশনের গভীরতা (পর্ব ২) — কার্নেলও দেখতে পায় না এমন মেমরি: VBS, HVCI ও Credential Guard কীভাবে কাজ করে
সামঞ্জস্যপূর্ণ হার্ডওয়্যারে ক্লিন ইনস্টলে VBS ডিফল্টে চালু থাকে এবং হাইপারভাইজার ও SLAT দিয়ে কার্নেলের চেয়ে শক্তিশালী আইসোলেশন তৈরি কর...
Windows ভার্চুয়ালাইজেশনের গভীরতা (পর্ব ১) — আপনার Windows আসলে কোথায় চলছে? হাইপারভাইজার ও পার্টিশন
Hyper-V চালু করলে হোস্ট Windows নিজেই রুট পার্টিশন হিসেবে হাইপারভাইজারের উপর চলে। এই নিবন্ধ VT-x, SLAT ও VMBus-এর ভূমিকা দিয়ে ভার্চুয়াল...
Win32 থ্রেড পুল API — CreateThreadpoolWork দিয়ে থ্রেড তৈরি না করে কনকারেন্সি
নেটিভ কোডে চারদিকে CreateThread ডাকছেন? এই নিবন্ধ Vista-তে নতুন করে সাজানো Win32 থ্রেড পুল API ব্যাখ্যা করে — work, timer, wait ও io চারট...
নেমড পাইপ ব্যবহারিকভাবে — ডিজাইন থেকে নিরাপত্তা পর্যন্ত Windows-এর মানক IPC
Windows-এর মানক আন্তঃপ্রক্রিয়া যোগাযোগ নেমড পাইপের ব্যবহারিক গাইড। প্রাথমিক উৎস থেকে এই নিবন্ধ বাইট ও মেসেজ মোডের পছন্দ, একাধিক ক্লায়েন...
সম্পর্কিত বিষয়
এই পৃষ্ঠাগুলো বিষয়টিকে সেবা ও সিদ্ধান্তের বৃহত্তর প্রেক্ষাপটে স্থাপন করে।
Windows-এর প্রযুক্তিগত বিষয়
Windows ডেভেলপমেন্ট, বাগ তদন্ত ও বিদ্যমান সম্পদ ব্যবহারের প্রবেশদ্বার।
এই বিষয়ের সাথে সম্পর্কিত সেবা
নিবন্ধটি নিচের সেবাগুলোর সাথে সরাসরি সম্পর্কিত।
Windows অ্যাপ ডেভেলপমেন্ট
ব্যবসায়িক অ্যাপ, ডিভাইস ইন্টিগ্রেশন ও যোগাযোগ টুল, চাহিদা থেকে ডেভেলপমেন্ট পর্যন্ত।
প্রায়শ জিজ্ঞাসিত প্রশ্ন
এই নিবন্ধের বিষয়ে পরামর্শে প্রায়ই আসা প্রশ্ন।
- VirtualAlloc-এ MEM_COMMIT দিলে সেই মুহূর্তেই RAM বরাদ্দ হয়?
- সাধারণ প্রাইভেট মেমোরিতে Commit সিস্টেমের commit ফাঁকা জায়গা খরচ করে, কিন্তু সংশ্লিষ্ট ভৌত পেজ প্রথম অ্যাক্সেস পর্যন্ত বরাদ্দ হয় না। লেখার মাধ্যমে প্রথম স্পর্শ করা পেজ demand-zero fault পরিচালনার সময় ভৌত পেজ পায়।
- page fault মানে কি কিছু ভুল বা পারফরম্যান্স সমস্যা?
- না। ডিস্ক I/O ছাড়া soft fault — যেমন demand-zero বা Standby থেকে পেজ ফেরানো — স্বাভাবিক কাজ। পারফরম্যান্স বিচারে শুধু Page Faults/sec নয়, Pages Input/sec, স্টোরেজ অপেক্ষা ও Available MBytesও দেখুন।
- TLB miss আর page fault কি একই জিনিস?
- এরা আলাদা। TLB-তে অনুবাদ না থাকলেও পেজ টেবিলের PTE বৈধ হলে CPU শুধু টেবিল হাঁটে ও অনুবাদ আবার নথিভুক্ত করে। PTE অবৈধ বা সুরক্ষা লঙ্ঘন থাকলে page-fault প্রবেশবিন্দুতে যায়।
- ঠিকানার সীমা VAD-তে থাকলে অ্যাক্সেস লঙ্ঘন হয় না?
- অবশ্যই নয়। VAD আছে কি না তার পাশাপাশি মেমোরি ম্যানেজার Reserve বনাম Commit, পড়া/লেখা/চালানো সুরক্ষা, গার্ড পেজ, PTE অবস্থা ইত্যাদি মূল্যায়ন করে। fault মীমাংসা না হলে 0xC0000005-এর মতো ব্যতিক্রম পাবেন।
- উঁচু Page Faults/sec মানে কি সিস্টেমে RAM কম?
- শুধু এতে বোঝা যায় না। Page Faults/sec-এ অনেক soft faultও থাকে। একই সময়-অক্ষে Memory\Pages Input/sec, Memory\Page Reads/sec, Available MBytes ও ডিস্ক অপেক্ষার সঙ্গে মেলাতে হয়।