Windows मेमोरी की गहराई (भाग 3) — सेक्शन ऑब्जेक्ट और कॉपी-ऑन-राइट: DLL और फ़ाइल मैपिंग वास्तव में क्या हैं
· Go Komura · Windows, मेमोरी प्रबंधन, साझा मेमोरी, फ़ाइल मैपिंग, कॉपी-ऑन-राइट, DLL, Cache Manager
पिछले लेख «Windows मेमोरी की गहराई (भाग 2) — एक भौतिक पृष्ठ का जीवन» में हमने देखा कि Working Set छोड़ने वाला भौतिक पृष्ठ Modified, Standby, Free और Zeroed से कैसे गुज़रता है। उसी Standby में DLL, EXE, मैप की गई फ़ाइलें और फ़ाइल कैश के पृष्ठ भी रहते हैं।
यहाँ एक प्रश्न उठता है। जब 100 प्रोसेस एक ही kernel32.dll इस्तेमाल करते हैं, क्या Windows RAM में कोड पृष्ठों के 100 सेट रखता है? जब दो प्रोसेस एक ही फ़ाइल को मेमोरी-मैप करते हैं, क्या दूसरा उस पृष्ठ का उपयोग कर सकता है जिसे एक ने पढ़ा?
उत्तर है कई वर्चुअल पतों को एक ही भौतिक पृष्ठ पर मैप करना। उस साझा इकाई का केंद्र सेक्शन ऑब्जेक्ट है, और लिखने के समय ही साझेदारी तोड़ने वाला तंत्र कॉपी-ऑन-राइट (Copy-on-Write, CoW) है।
यह लेख देखता है कि EXE और DLL, डेटा फ़ाइलें, पेजफ़ाइल-समर्थित साझा मेमोरी और फ़ाइल कैश एक ही फ़ाइल स्ट्रीम से कैसे जुड़ते हैं, और प्रसंस्करण पथ कहाँ बँटते हैं। संख्याएँ पढ़ना स्वयं परिचयात्मक लेख «What Does Windows’ “Memory Usage” Actually Mean?» को आधार मानता है।
«Windows मेमोरी की गहराई» — तीनों भाग
- भाग 1: वर्चुअल पते और पेज फ़ॉल्ट
हम उस क्षण का अनुसरण करते हैं जब Commit किया गया वर्चुअल पृष्ठ भौतिक RAM पाता है। - भाग 2: एक भौतिक पृष्ठ का जीवन
हम Working Set छोड़ने वाले पृष्ठ की अवस्था-परिवर्तनों का अनुसरण करते हैं। - भाग 3 (यह लेख): सेक्शन ऑब्जेक्ट और कॉपी-ऑन-राइट
हम उस तंत्र का अनुसरण करते हैं जिससे DLL, फ़ाइल मैपिंग और साझा मेमोरी भौतिक पृष्ठ साझा करते हैं।
भाग 3 जिस प्रश्न का उत्तर देता है वह केवल एक है।
कई प्रोसेस एक ही DLL या फ़ाइल को भौतिक पृष्ठों के एक सेट के रूप में क्यों इस्तेमाल कर सकते हैं?
लक्षित पाठक वे डेवलपर और संचालक हैं जो DLL साझेदारी, CreateFileMapping, MapViewOfFile, साझा मेमोरी, CoW और फ़ाइल कैश से संबंध को केवल API उपयोग नहीं बल्कि आंतरिक संरचना से समझना चाहते हैं। पूर्वापेक्षाएँ Windows 10/11 या वर्तमान Windows Server हैं, और आवश्यक पृष्ठभूमि वर्चुअल पते, पेज फ़ॉल्ट और Working Set की बुनियाद है। कठिनाई मध्यम है; हम Control Area और Prototype PTE जैसे आंतरिक शब्द भी इस्तेमाल करते हैं, लेकिन अवलोकन VMMap, Process Explorer और QueryWorkingSetEx से दोहराए जा सकते हैं।
1. पहले निष्कर्ष
शुरुआत में पूरी तस्वीर।
- सेक्शन ऑब्जेक्ट साझा करने योग्य मेमोरी रेंज दर्शाता है।
हर प्रोसेस उस सेक्शन का भाग अपने वर्चुअल स्पेस में «व्यू» के रूप में मैप करता है।1 - एक ही सेक्शन के व्यू एक ही वर्चुअल पते पर होने आवश्यक नहीं।
प्रोसेस A का0x000001...और प्रोसेस B का0x000002...एक ही सेक्शन ऑफ़सेट और एक ही भौतिक पृष्ठ की ओर इशारा कर सकते हैं।2 - फ़ाइल-समर्थित और पेजफ़ाइल-समर्थित सेक्शन होते हैं।
पहले वास्तविक फ़ाइल इस्तेमाल करते हैं; दूसरे स्पष्ट फ़ाइल के बिना साझा मेमोरी आदि के लिए।2 - EXE/DLL लोड करना इमेज सेक्शन है; साधारण फ़ाइल मैपिंग डेटा सेक्शन है।
SEC_IMAGEके साथ PE के अंदर सेक्शन विशेषताएँ पृष्ठ सुरक्षा तय करती हैं।3 - पढ़ते समय एक ही भौतिक पृष्ठ साझा हो सकता है।
जब एक पक्ष CoW पृष्ठ पर लिखता है, केवल वही पृष्ठ कॉपी होता है और लिखने वाले प्रोसेस का PTE बदल जाता है।4 - एक ही फ़ाइल स्ट्रीम पर भी कैश, डेटा और इमेज पथ अलग होते हैं।
Cache Manager का कैश्ड I/OSharedCacheMapइस्तेमाल करता है, डेटा मैपिंगDataSectionObject, और EXE/DLLImageSectionObject। तीनोंSECTION_OBJECT_POINTERSसे एक ही स्ट्रीम से जुड़ते हैं, लेकिन इमेज फ़ॉल्ट Cache Manager से नहीं जाता।5 - अकेले Private Bytes यह पुष्टि नहीं कर सकते कि CoW हुआ।
FILE_MAP_COPYमैप के समय पूरे व्यू का Commit पहले ही लगाता है, इस आशंका से कि बाद में हर पृष्ठ निजी हो जाए। प्रति-पृष्ठ जाँच के लिएQueryWorkingSetExका Shared बिट इस्तेमाल करें।67
एक वाक्य में: साझा वर्चुअल पता नहीं, बल्कि सेक्शन के अंदर की सामग्री और उस क्षण का संगत भौतिक पृष्ठ है।
2. सेक्शन ऑब्जेक्ट और व्यू
Microsoft की परिभाषा में सेक्शन ऑब्जेक्ट साझा करने योग्य मेमोरी क्षेत्र दर्शाता है और वह तंत्र भी है जो फ़ाइल को प्रोसेस के एड्रेस स्पेस में मैप करता है।1
समझने की कुंजी सेक्शन को उस व्यू से अलग करना है जो हर प्रोसेस देखता है।
| अवधारणा | भूमिका |
|---|---|
| सेक्शन ऑब्जेक्ट | साझा करने की सामग्री, आकार, बैकिंग स्टोर और सुरक्षा की ऊपरी सीमा दर्शाता है |
| व्यू | सेक्शन का भाग किसी प्रोसेस में वर्चुअल पते की रेंज के रूप में दिखाता है |
| PTE | व्यू के हर वर्चुअल पृष्ठ को वर्तमान भौतिक पृष्ठ या अवास्तविक अवस्था से बाँधता है |
| PFN | वह भौतिक पृष्ठ दर्शाता है जो वास्तव में RAM में मौजूद है |
Win32 में CreateFileMapping फ़ाइल-मैपिंग ऑब्जेक्ट का हैंडल लौटाता है, और MapViewOfFile प्रोसेस के वर्चुअल स्पेस में व्यू बनाता है।3
HANDLE mapping = CreateFileMappingW(
file,
nullptr,
PAGE_READONLY,
0,
0,
nullptr);
void* view = MapViewOfFile(
mapping,
FILE_MAP_READ,
0,
0,
0);
केवल CreateFileMapping बुलाने से प्रोसेस को पढ़ने योग्य पता अभी नहीं मिलता। व्यू बनाना भी हर पृष्ठ तुरंत RAM में नहीं डालता। पहले छुए गए पृष्ठ से पेज फ़ॉल्ट फ़ाइल सामग्री पढ़ता है और PTE से भौतिक पृष्ठ बाँधता है।8 अर्थात् भाग 1 का डिमांड पेजिंग सेक्शन व्यू पर भी लागू होता है।
2.1. एक ही सेक्शन, अलग वर्चुअल पते
भले प्रोसेस A और प्रोसेस B एक ही सेक्शन का एक ही ऑफ़सेट मैप करें, व्यू का आरंभ पता भिन्न हो सकता है।
flowchart LR
accTitle: अलग वर्चुअल पतों को एक ही भौतिक पृष्ठ पर मैप करना
accDescr: प्रोसेस A और प्रोसेस B प्रत्येक के पास अलग वर्चुअल पते पर व्यू है, लेकिन वे एक ही सेक्शन ऑफ़सेट से एक ही भौतिक पृष्ठ PFN X तक पहुँचते हैं
viewA["प्रोसेस A: 0x000001A00000 + 0x3000"] --> offset["सेक्शन ऑफ़सेट 0x3000"]
viewB["प्रोसेस B: 0x000002700000 + 0x3000"] --> offset
offset --> pfnX["एक ही भौतिक पृष्ठ PFN X"]
चित्र 1: साझा सेक्शन के अंदर की सामग्री और भौतिक पृष्ठ है, वर्चुअल पता नहीं।
इसीलिए साझा मेमोरी में कच्चा पॉइंटर न रखें। प्रोसेस A का पॉइंटर मान प्रोसेस B में असंबंधित पता हो सकता है।
साझा संरचना में व्यू के आरंभ से ऑफ़सेट, स्थिर-चौड़ाई पूर्णांक, तथा स्पष्ट संस्करण और संरेखण इस्तेमाल करें। Microsoft का MapViewOfFileEx दस्तावेज़ भी पॉइंटर के बजाय बेस से ऑफ़सेट रखने की सलाह देता है, क्योंकि भविष्य में वही पता उपलब्ध रहे इसकी गारंटी नहीं।6
3. फ़ाइल-समर्थित और पेजफ़ाइल-समर्थित
सेक्शन इस आधार पर दो बड़े प्रकारों में बँटते हैं कि सामग्री कहाँ से पुनर्स्थापित हो सकती है।
flowchart TB
accTitle: फ़ाइल-समर्थित और पेजफ़ाइल-समर्थित कैसे बँटते हैं
accDescr: CreateFileMapping को वास्तविक फ़ाइल देने से फ़ाइल-समर्थित सेक्शन बनता है, और स्वच्छ पृष्ठ मूल फ़ाइल से फिर पढ़ा जा सकता है। INVALID_HANDLE_VALUE देने से पेजफ़ाइल-समर्थित सेक्शन बनता है; पेज फ़ाइल सामग्री थामती है, जो ऑब्जेक्ट नष्ट होने पर गायब हो जाती है
create["CreateFileMapping"] -->|वास्तविक फ़ाइल हैंडल दें| fileBacked["फ़ाइल-समर्थित सेक्शन"]
create -->|INVALID_HANDLE_VALUE दें| pfBacked["पेजफ़ाइल-समर्थित सेक्शन"]
fileBacked --> restore1["स्वच्छ पृष्ठ मूल फ़ाइल से फिर पढ़ा जा सकता है"]
pfBacked --> restore2["पेज फ़ाइल सामग्री थामती है, जो नष्ट होने पर गायब होती है"]
चित्र 2: बैकिंग स्टोर का अंतर तय करता है कि सामग्री कहाँ से लौटे और कितनी देर जिए।
3.1. फ़ाइल-समर्थित सेक्शन
CreateFileMapping को वास्तविक फ़ाइल देने से फ़ाइल-समर्थित सेक्शन बनता है।
- केवल-पठन व्यू आवश्यक पृष्ठ फ़ाइल से पढ़ता है।
- पढ़ने/लिखने वाले व्यू के बदलाव उस फ़ाइल के डेटा माने जाते हैं।
- CoW व्यू के बदलाव मूल फ़ाइल में नहीं लिखे जाते; वे निजी पृष्ठ बन जाते हैं।
यदि फ़ाइल-समर्थित पृष्ठ स्वच्छ है, भौतिक पृष्ठ त्यागा जा सकता है और मूल फ़ाइल से फिर पढ़ा जा सकता है। यही गुण भाग 2 में देखी Standby और फ़ाइल कैश की दक्षता थामता है।
3.2. पेजफ़ाइल-समर्थित सेक्शन
CreateFileMapping के hFile में INVALID_HANDLE_VALUE देना और आकार बताना पेजफ़ाइल-समर्थित सेक्शन बनाता है।
HANDLE mapping = CreateFileMappingW(
INVALID_HANDLE_VALUE,
nullptr,
PAGE_READWRITE,
0,
64 * 1024,
L"Local\\KomuraMemoryDemo");
यह स्पष्ट डेटा फ़ाइल के बिना सेक्शन है, जिसे पेज फ़ाइल थामती है। आरंभिक सामग्री शून्य है, और कई प्रोसेस नाम, हैंडल विरासत, DuplicateHandle आदि से एक ही ऑब्जेक्ट खोल सकते हैं।93 बदलाव उसी साझा पृष्ठ को मैप करने वाले प्रोसेस को दिखते हैं। दूसरी ओर, सेक्शन ऑब्जेक्ट नष्ट होने पर सामग्री नहीं रहती, इसलिए स्थायी फ़ाइल छोड़ने के लिए उपयुक्त नहीं।2
सावधानी: साझा मेमोरी अपने आप पारस्परिक बहिष्कार नहीं लाती। म्यूटेक्स, सेमाफोर, इवेंट, लॉक-फ़्री प्रोटोकॉल आदि अलग से डिज़ाइन करें।9
4. इमेज मैपिंग और डेटा मैपिंग
जब हम कहते हैं «EXE या DLL भी फ़ाइल मैपिंग है», साधारण डेटा फ़ाइल से अंतर फिर भी रखना चाहिए।
| मद | इमेज मैपिंग | डेटा मैपिंग |
|---|---|---|
| मुख्य उपयोग | EXE या DLL लोड करना | साधारण फ़ाइलें, साझा डेटा |
| निर्माण विशेषता | SEC_IMAGE |
PAGE_READONLY, PAGE_READWRITE आदि |
| पृष्ठ सुरक्षा | PE इमेज के अंदर विशेषताएँ तय करती हैं | मैपिंग और व्यू विनिर्देश तय करते हैं |
| लेखन | लिखने योग्य सेक्शन या CoW से निजी किया जा सकता है | साझा लेखन या CoW चुना जा सकता है |
VirtualQuery Type |
MEM_IMAGE |
MEM_MAPPED |
SEC_IMAGE के साथ व्यू की पृष्ठ सुरक्षा मुख्यतः निष्पादित इमेज की अपनी सेक्शन विशेषताएँ तय करती हैं, न कि CreateFileMapping को दिए साधारण सुरक्षा मान।3
इस तंत्र से कोड जैसे अपरिवर्तित पृष्ठ कई प्रोसेस में एक ही भौतिक पृष्ठ साझा कर सकते हैं, और केवल प्रोसेस-निजी बदलाव वाले पृष्ठ CoW से शाखा लेते हैं। फिर भी हर DLL पृष्ठ आवश्यक रूप से साझा नहीं होता — ASLR पुनर्स्थापन, लोडर सुधार, हॉटपैच, वास्तविक PE सेक्शन विशेषताएँ आदि के कारण।
महत्वपूर्ण डिज़ाइन है पहले साझा करने योग्य पृष्ठ साझा करें, और केवल बदलाव वाले पृष्ठ देर से कॉपी करें।
5. कॉपी-ऑन-राइट शुरू से अंत तक
दो प्रोसेस एक ही CoW पृष्ठ पढ़ रहे हों, उस अवस्था से प्रोसेस A के एक बाइट लिखने तक चलें।
5.1. लिखने से पहले
लिखत होने से पहले दोनों प्रोसेस के PTE अवधारणात्मक रूप से एक ही साझा पृष्ठ तक पहुँचते हैं, और पठन वैसे ही सफल होता है।
flowchart LR
accTitle: कॉपी-ऑन-राइट से पहले की साझा अवस्था
accDescr: लिखत होने से पहले प्रोसेस A का PTE और प्रोसेस B का PTE दोनों एक ही साझा पृष्ठ PFN X तक पहुँचते हैं, और पठन वैसे ही सफल होता है
pteA["प्रोसेस A PTE"] --> pfnX["साझा PFN X(पठन / कॉपी-ऑन-राइट)"]
pteB["प्रोसेस B PTE"] --> pfnX
चित्र 3: लिखने से पहले दोनों प्रोसेस के PTE एक ही भौतिक पृष्ठ की ओर इशारा करते हैं।
5.2. लिखते समय सुरक्षा फ़ॉल्ट
CoW पृष्ठ शुरू से साधारण साझा लिखने योग्य पृष्ठ नहीं होता। जब प्रोसेस A लिखने का प्रयास करता है, CPU सुरक्षा फ़ॉल्ट उठाता है। नियंत्रण पाने वाला मेमोरी मैनेजर तय करता है कि यह अवैध लिखत नहीं बल्कि CoW विशेषता पर लिखत है।
5.3. नया भौतिक पृष्ठ बनाना
उस निर्णय पर Windows यह करता है।
- प्रोसेस A के लिए एक भौतिक पृष्ठ प्राप्त करें।
- PFN X की सामग्री नए पृष्ठ PFN Y पर कॉपी करें।
- प्रोसेस A का PTE PFN Y से बदलें।
- प्रोसेस A की सुरक्षा साधारण पढ़ना/लिखना करें।
- असफल लिखने के निर्देश को फिर चलाएँ।
flowchart LR
accTitle: कॉपी-ऑन-राइट के बाद की विभाजित अवस्था
accDescr: प्रोसेस A की लिखत के बाद केवल प्रोसेस A का PTE उस निजी पृष्ठ PFN Y से बदला जाता है जिसे सामग्री की कॉपी मिली, जबकि प्रोसेस B का PTE मूल साझा पृष्ठ PFN X की ओर इशारा करता रहता है
pteA2["प्रोसेस A PTE"] --> pfnY["निजी PFN Y(R/W, लिखने के बाद)"]
pteB2["प्रोसेस B PTE"] --> pfnX2["साझा PFN X(मूल)"]
pfnX2 -.->|लिखते समय कॉपी| pfnY
चित्र 4: केवल लिखने वाले प्रोसेस का PTE नए निजी पृष्ठ से बदला जाता है; दूसरा पक्ष मूल सामग्री पढ़ता रहता है।
प्रोसेस B मूल सामग्री पढ़ता रहता है और प्रोसेस A का बदलाव नहीं देखता। यही Copy-on-Write है। DLL साझेदारी और FILE_MAP_COPY एक ही सिद्धांत इस्तेमाल करते हैं: जब तक लिखें नहीं, कॉपी न करें।46
5.4. FILE_MAP_WRITE से अंतर
FILE_MAP_WRITE की साझा लिखत से लिखा पृष्ठ इस तरह डिज़ाइन है कि एक पक्ष का बदलाव उसी फ़ाइल मैपिंग वाले दूसरे व्यू से भी दिखे। FILE_MAP_COPY में इसके विपरीत केवल लिखे गए पृष्ठ प्रोसेस-निजी होते हैं; बदलाव मूल फ़ाइल में वापस नहीं लिखे जाते और व्यू अनमैप होते ही खो जाते हैं।6
क्या आप चाहते हैं «साझा मेमोरी से अद्यतन पहुँचाना», या «हर प्रोसेस साझा आरंभिक डेटा से निजी बदलाव करे»? उद्देश्य के अनुसार सही चुनाव उलटा होता है।
6. CoW के बाद भी MEM_MAPPED / MEM_IMAGE
CoW के बाद पृष्ठ भौतिक रूप से Private हो चुका है। तब लगता है VirtualQuery का Type भी MEM_PRIVATE हो जाएगा, लेकिन वास्तव में डेटा व्यू MEM_MAPPED और निष्पादन योग्य इमेज MEM_IMAGE रहता है। VirtualQuery बताता है कि क्षेत्र किस आरंभिक आवंटन से आया।7
प्रति पृष्ठ यह देखने के लिए कि CoW पहले ही हुआ या नहीं, यह प्रक्रिया इस्तेमाल करें।
- लक्ष्य पृष्ठ तक पहुँचें और उसे रेज़िडेंट बनाएँ।
QueryWorkingSetExसे पृष्ठ की Working Set जानकारी लें।Sharedबिट देखें।- यदि
Shared == 0तो वह रेज़िडेंट पृष्ठ Private है।
VMMap में पुष्टि करते समय भी केवल क्षेत्र के Type नहीं, Working Set के Private/Shareable विभाजन देखें।
6.1. Private Bytes बढ़ें नहीं भी सकते
FILE_MAP_COPY में प्रोसेस बाद में व्यू के हर पृष्ठ पर लिख सकता है। इसलिए Windows मैप के समय पूरे व्यू के बराबर Commit शुल्क लेता है।6 परिणामस्वरूप पहला पृष्ठ लिखने पर उसी क्षण Private Bytes 4KiB नहीं बढ़ सकते।
CoW देखते समय ये मेट्रिक प्राथमिकता दें।
QueryWorkingSetExका Shared बिट- VMMap का Private WS / Shareable WS
- RAMMap की भौतिक-पृष्ठ जानकारी
- पूरक जानकारी के रूप में Private Bytes
यदि «लिखने के क्षण Private Bytes बढ़े या नहीं» को उत्तीर्ण/अनुत्तीर्ण परीक्षा मानें, तो सही काम कर रहे CoW छूट जाएँगे।
7. Cache Manager से संपर्क — तीन पथ अलग रखें
«EXE/DLL लोड, फ़ाइल कैश और साझा मेमोरी सब सेक्शन हैं» कहना पूरी तस्वीर देता है, लेकिन कार्यान्वयन को एक ऑब्जेक्ट में न समेटें।
फ़ाइल स्ट्रीम के पास SECTION_OBJECT_POINTERS होते हैं, जिन्हें मेमोरी मैनेजर और Cache Manager इस्तेमाल करते हैं।
typedef struct _SECTION_OBJECT_POINTERS {
PVOID DataSectionObject;
PVOID SharedCacheMap;
PVOID ImageSectionObject;
} SECTION_OBJECT_POINTERS;
DataSectionObject: डेटा फ़ाइल की सेक्शन अवस्थाSharedCacheMap: Cache Manager द्वारा ट्रैक किया गया कैश व्यूImageSectionObject: निष्पादन योग्य इमेज की सेक्शन अवस्था
Microsoft दस्तावेज़ बताता है कि यह संरचना फ़ाइल ऑब्जेक्ट को फ़ाइल स्ट्रीम के सेक्शन से बाँधती है और मेमोरी में सामग्री तथा कैश जानकारी ट्रैक करती है।5
यहाँ I/O श्रृंखला और मेमोरी श्रृंखला मिलती हैं। फिर भी तीन पथ अलग समझें।
- कैश्ड
ReadFile/WriteFileCache Manager केSharedCacheMapऔर कैश व्यू इस्तेमाल करते हैं। - डेटा फ़ाइल पर मैपिंग फ़ॉल्ट मेमोरी मैनेजर
DataSectionObjectपक्ष पर संभालता है। वह उसी स्ट्रीम के कैश्ड I/O से सहयोग कर सामग्री सुसंगत रखता है। - EXE/DLL पर इमेज फ़ॉल्ट मेमोरी मैनेजर
ImageSectionObjectऔर पेजिंग I/O से संभालता है। यह Cache Manager केSharedCacheMapसे जाने वाला पथ नहीं।
flowchart TB
accTitle: एक ही फ़ाइल स्ट्रीम से जुड़ने वाले तीन पथ
accDescr: कैश्ड ReadFile/WriteFile SharedCacheMap इस्तेमाल करते हैं, डेटा-मैपिंग फ़ॉल्ट DataSectionObject, और EXE/DLL इमेज फ़ॉल्ट ImageSectionObject; तीनों SECTION_OBJECT_POINTERS से एक ही फ़ाइल स्ट्रीम से जुड़ते हैं
cached["कैश्ड ReadFile / WriteFile"] --> scm["SharedCacheMap"]
dataFault["डेटा-मैपिंग फ़ॉल्ट"] --> dso["DataSectionObject"]
imageFault["EXE/DLL इमेज फ़ॉल्ट"] --> iso["ImageSectionObject"]
scm --> stream["एक ही फ़ाइल स्ट्रीम(SECTION_OBJECT_POINTERS)"]
dso --> stream
iso --> stream
चित्र 5: तीन पथ अलग संसाधित होते हैं, लेकिन एक ही फ़ाइल स्ट्रीम से जुड़ते हैं।
तीनों में समान यह नहीं कि «सब Cache Manager में जाता है», बल्कि यह कि एक ही फ़ाइल स्ट्रीम कैश, डेटा सेक्शन और इमेज सेक्शन जैसी अलग अवस्थाएँ SECTION_OBJECT_POINTERS से बाँधती है।5 कैश पठन-लेखन, Lazy Writer, तथा Cc और Mm का संबंध «The Depths of Windows I/O (Part 4) — Cache Manager: When Does Your WriteFile Actually Reach the Disk?» में है।
ध्यान दें कि मेमोरी-मैप व्यू को ReadFile/WriteFile से मिलाएँ तो हमेशा एक ही क्षण की सामग्री दिखने की गारंटी नहीं। डिज़ाइन में सिंक्रनाइज़ेशन, फ्लश और फ़ाइल-साझा मोड होना चाहिए।36
8. ऑब्जेक्ट और व्यू का जीवनकाल
केवल CreateFileMapping हैंडल बंद करने से मौजूदा व्यू नष्ट नहीं होता। व्यू सेक्शन का आंतरिक संदर्भ रखता है, और हर व्यू के UnmapViewOfFile तथा हर हैंडल के CloseHandle के बाद ही ऑब्जेक्ट नष्ट योग्य होता है।3
UnmapViewOfFile(view);
CloseHandle(mapping);
CloseHandle(file);
जीवनकाल का यह अलगाव «फ़ाइल बंद कर दी, फिर भी उपयोग में है» घटना का कारण है। फ़ाइल हैंडल बंद करने के बाद भी यदि इमेज सेक्शन या डेटा व्यू फ़ाइल स्ट्रीम का संदर्भ रखे, फ़ाइल का अंतिम बंद बाद में आता है।
I/O पक्ष की सफ़ाई/बंद से संबंध «The Depths of Windows I/O (Part 1)» में है, और कार्यान्वयन जाल «Shared Memory Pitfalls and Practical Best Practices» में।
9. स्वयं देखें
9.1. दो प्रोसेस से एक ही DLL देखना
पहले मौजूदा प्रोसेस से DLL साझेदारी पुष्टि करें।
- व्यवस्थापक के रूप में Process Explorer शुरू करें।
- दो
cmd.exeप्रोसेस शुरू करें। - View > Lower Pane View > DLLs चुनें।
- दोनों प्रोसेस में एक ही DLL का पथ और मैपिंग पुष्टि करें।
- VMMap में प्रत्येक
cmd.exeखोलें और Images Working Set, Private तथा Shareable तुलना करें।
Process Explorer में एक ही DLL दिखना प्रमाण है कि दोनों ने एक ही इमेज मैप की। अकेले इससे यह सिद्ध नहीं होता कि हर पृष्ठ का PFN मेल खाता है। प्रति-पृष्ठ साझेदारी पुष्टि के लिए VMMap का Shareable विभाजन, RAMMap और QueryWorkingSetEx मिलाएँ। Process Explorer और VMMap Sysinternals देते हैं।1011
9.2. दो प्रोसेस से FILE_MAP_COPY देखना
निम्न प्रोग्राम एक ही फ़ाइल को CoW व्यू के रूप में मैप करता है और QueryWorkingSetEx का Shared बिट दिखाता है। फ़ाइल-मैपिंग ऑब्जेक्ट PAGE_READONLY से बनता है, लेकिन वह सुरक्षा FILE_MAP_COPY व्यू से संगत है, और व्यू पक्ष की पहली लिखत CoW पैदा करती है।3
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#include <cstdio>
#include <cwchar>
#pragma comment(lib, "Psapi.lib")
void PrintPage(const char* stage, void* address)
{
MEMORY_BASIC_INFORMATION mbi{};
if (!VirtualQuery(address, &mbi, sizeof(mbi))) {
std::printf("VirtualQuery failed: %lu\n", GetLastError());
return;
}
for (int attempt = 0; attempt < 3; ++attempt) {
// The page may have been trimmed while the user was waiting.
// Touch it immediately before querying the working-set attributes.
volatile unsigned char resident =
*static_cast<volatile unsigned char*>(address);
(void)resident;
PSAPI_WORKING_SET_EX_INFORMATION ws{};
ws.VirtualAddress = address;
if (!QueryWorkingSetEx(GetCurrentProcess(), &ws, sizeof(ws))) {
std::printf("QueryWorkingSetEx failed: %lu\n", GetLastError());
return;
}
if (!ws.VirtualAttributes.Valid) {
Sleep(0);
continue;
}
std::printf(
"%s: Type=0x%lx Valid=1 Shared=%llu ShareCount=%llu\n",
stage,
static_cast<unsigned long>(mbi.Type),
static_cast<unsigned long long>(ws.VirtualAttributes.Shared),
static_cast<unsigned long long>(ws.VirtualAttributes.ShareCount));
return;
}
std::printf(
"%s: page is not resident; Shared/ShareCount were not interpreted\n",
stage);
}
int wmain(int argc, wchar_t** argv)
{
if (argc != 3) {
std::fwprintf(stderr, L"usage: cow_demo <file> <read|write>\n");
return 2;
}
HANDLE file = CreateFileW(
argv[1], GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE) return 3;
HANDLE mapping = CreateFileMappingW(
file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (!mapping) {
CloseHandle(file);
return 4;
}
auto* view = static_cast<unsigned char*>(
MapViewOfFile(mapping, FILE_MAP_COPY, 0, 0, 0));
if (!view) {
CloseHandle(mapping);
CloseHandle(file);
return 5;
}
volatile unsigned char value = view[0];
(void)value;
std::puts("Start the other process. When both are waiting, press Enter...");
(void)std::getchar();
PrintPage("before", view);
if (std::wcscmp(argv[2], L"write") == 0) {
std::puts("Press Enter to trigger copy-on-write...");
(void)std::getchar();
view[0] ^= 0x5a;
PrintPage("after write", view);
} else {
std::puts("After the writer changes its page, press Enter...");
(void)std::getchar();
PrintPage("reader after peer write", view);
}
std::puts("Press Enter to exit...");
(void)std::getchar();
UnmapViewOfFile(view);
CloseHandle(mapping);
CloseHandle(file);
}
बनाएँ और तैयार करें।
cl /std:c++20 /EHsc /W4 cow_demo.cpp
$path = "$env:TEMP\\cow-demo.bin"
[IO.File]::WriteAllBytes($path, [byte[]]::new(65536))
फिर दो कंसोल से एक ही फ़ाइल खोलें।
.\\cow_demo.exe "$env:TEMP\\cow-demo.bin" read
.\\cow_demo.exe "$env:TEMP\\cow-demo.bin" write
दोनों शुरू करने के बाद पहले पठन पक्ष फिर लेखन पक्ष पर Enter दबाएँ, और पुष्टि करें कि दोनों के before में Shared सेट है। लेखन पक्ष पर फिर Enter दबाएँ तो उस प्रोसेस का पृष्ठ Shared 0 हो जाता है। फिर पठन पक्ष पर Enter दबाएँ और पुष्टि करें कि पाठक मूल पृष्ठ पढ़ता रहता है। लिखने के बाद भी VirtualQuery का Type MEM_MAPPED रहता है।
ध्यान दें कि PrintPage पूछने से ठीक पहले लक्ष्य पृष्ठ को फिर छूता है, और Valid == 0 हो तो Shared तथा ShareCount की व्याख्या नहीं करता और तीन बार तक पुनः प्रयास करता है। यदि पृष्ठ अभी भी रेज़िडेंट नहीं तो परिणाम नहीं देता और यह बताता है। ShareCount समय और मेमोरी दबाव से बदल सकता है, इसलिए Valid == 1 पर पुष्ट Shared बिट का बदलाव देखें, कोई स्थिर मान नहीं।
10. व्यवहार में बचने योग्य पाँच गलत पाठ
10.1. «साझा मेमोरी एक ही वर्चुअल पते पर पहुँचती है»
साझा सेक्शन और भौतिक पृष्ठ है। व्यू का वर्चुअल पता प्रोसेस के अनुसार भिन्न हो सकता है, इसलिए कच्चे पॉइंटर के बजाय ऑफ़सेट रखें।
10.2. «यदि वही DLL है तो हर पृष्ठ आवश्यक रूप से साझा है»
स्वच्छ कोड पृष्ठ साझा करना आसान है, जबकि पुनर्स्थापन, लिखने योग्य सेक्शन, CoW और माप के क्षण की रेज़िडेंसी भी Private पृष्ठ पैदा करते हैं।
10.3. «CoW के बाद यह MEM_PRIVATE हो जाता है»
VirtualQuery का Type MEM_MAPPED या MEM_IMAGE रहता है। वास्तविक साझेदारी QueryWorkingSetEx से पुष्टि करें।7
10.4. «यदि Private Bytes नहीं बढ़े तो CoW नहीं हुआ»
FILE_MAP_COPY पूरे व्यू का Commit पहले लगाता है। Private WS और Shared बिट प्राथमिकता दें।6
10.5. «यदि पृष्ठ साझा है तो सिंक्रनाइज़ेशन अनावश्यक है»
एक ही भौतिक पृष्ठ देखना और कई CPU कोर से सुरक्षित अद्यतन करना अलग समस्याएँ हैं। परमाणुकता, मेमोरी क्रम, पारस्परिक बहिष्कार, क्रैश के समय मध्य अवस्था, और संस्करण संगतता डिज़ाइन करें।
संदर्भ और जीवनकाल को अलग-अलग अनुसरण करने का विचार Excel COM इंटरऑप के बाद बचे प्रोसेस की समस्या पर भी लागू होता है। देखें «Why EXCEL.EXE Processes Remain After C# Excel COM Automation — Reference Release Patterns and the Replacement Decision»।
11. सार
- सेक्शन ऑब्जेक्ट साझा करने योग्य मेमोरी रेंज दर्शाता है, और हर प्रोसेस उसे अपने वर्चुअल स्पेस में व्यू के रूप में मैप करता है।1
- एक ही सेक्शन का एक ही ऑफ़सेट अलग वर्चुअल पतों से एक ही भौतिक पृष्ठ पर मैप होता है।2
- फ़ाइल-समर्थित सेक्शन वास्तविक फ़ाइल थामता है; पेजफ़ाइल-समर्थित सेक्शन नामित साझा मेमोरी आदि थामता है।9
- EXE/DLL इमेज सेक्शन और साधारण फ़ाइल डेटा सेक्शन मानी जाती है; सुरक्षा और वापस लिखने का गंतव्य भिन्न हैं।3
- CoW पठन के दौरान भौतिक पृष्ठ साझा करता है और पहली लिखत पर केवल वही पृष्ठ कॉपी कर PTE बदलता है।4
- CoW के बाद भी
VirtualQueryMEM_MAPPED/MEM_IMAGEलौटाता है, इसलिएQueryWorkingSetExके Shared बिट से पुष्टि करें।7 FILE_MAP_COPYमें पूरे व्यू का Commit पहले लगता है, इसलिए अकेले Private Bytes से CoW नहीं आँका जा सकता।6- Cache Manager का कैश्ड I/O, डेटा मैपिंग और इमेज मैपिंग एक ही फ़ाइल स्ट्रीम से अलग पथों के रूप में जुड़ते हैं जो क्रमशः
SharedCacheMap,DataSectionObjectऔरImageSectionObjectइस्तेमाल करते हैं।5 - साझा मेमोरी तभी सुरक्षित होती है जब व्यू और हैंडल का जीवनकाल, सिंक्रनाइज़ेशन, ACL और ऑफ़सेट डिज़ाइन शामिल हों।
इससे «Windows मेमोरी की गहराई» के तीनों भाग पूरे होते हैं। आप वर्चुअल पता Reserve/Commit करते हैं, पेज फ़ॉल्ट से भौतिक पृष्ठ पाते हैं, पृष्ठ को Working Set से पृष्ठ सूची पर ले जाते हैं, सेक्शन से साझा करते हैं, और केवल लिखे पृष्ठ CoW से बाँटते हैं — Windows मेमोरी प्रबंधन इसी एक प्रवाह से जुड़ा है।
संबंधित लेख
- Windows मेमोरी की गहराई (भाग 1) — वह क्षण जब वर्चुअल पता भौतिक RAM बनता है: पेज फ़ॉल्ट शुरू से अंत तक
- Windows मेमोरी की गहराई (भाग 2) — एक भौतिक पृष्ठ का जीवन: पाँच सूचियाँ और पेज फ़ाइल की सच्चाई
- The Depths of Windows I/O (Part 4) — Cache Manager: When Does Your WriteFile Actually Reach the Disk?
- Shared Memory Pitfalls and Practical Best Practices
- Why EXCEL.EXE Processes Remain After C# Excel COM Automation — Reference Release Patterns and the Replacement Decision
- Process Explorer / Handle / VMMap in Practice — Chasing Hangs, Leaks, and “File in Use” from the State Right Now
संबंधित परामर्श क्षेत्र
KomuraSoft LLC Windows अनुप्रयोगों की साझा मेमोरी, फ़ाइल मैपिंग, DLL लोडिंग, फ़ाइल लॉकिंग, अंतर-प्रोसेस संचार और मेमोरी उपयोग की दोष जाँच संभालती है।
संदर्भ लिंक
-
Microsoft Learn, Section Objects and Views. सेक्शन ऑब्जेक्ट के साझा करने योग्य मेमोरी रेंज दर्शाने, और हर प्रोसेस के सेक्शन का भाग व्यू के रूप में मैप करने पर। ↩ ↩2 ↩3
-
Microsoft Learn, File-Backed and Page-File-Backed Sections. फ़ाइल-समर्थित और पेजफ़ाइल-समर्थित सेक्शन, CoW, तथा अलग प्रोसेस के वर्चुअल पतों से एक ही भौतिक मेमोरी साझा करने पर। ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, CreateFileMappingW function. फ़ाइल-मैपिंग ऑब्जेक्ट, पेजफ़ाइल-समर्थित सेक्शन,
SEC_IMAGE, व्यू और हैंडल का जीवनकाल, तथा एक ही फ़ाइल थामने वाले व्यू की सुसंगति पर। ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Microsoft Learn, Memory Protection. कई प्रोसेस के एक ही DLL के भौतिक पृष्ठ साझा करने, और एक पक्ष के लिखने पर CoW के नए भौतिक पृष्ठ पर कॉपी कर PTE अद्यतन करने पर। ↩ ↩2 ↩3
-
Microsoft Learn, SECTION_OBJECT_POINTERS structure. DataSectionObject, SharedCacheMap और ImageSectionObject के फ़ाइल स्ट्रीम मैपिंग तथा कैश जानकारी को मेमोरी मैनेजर / Cache Manager से बाँधने पर। ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MapViewOfFileEx function.
FILE_MAP_COPYके साथ CoW; पेज फ़ाइल से समर्थित निजी पृष्ठ; पूरे व्यू का Commit शुल्क; तथा वर्चुअल पते के बजाय ऑफ़सेट रखने पर। ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Microsoft Learn, VirtualQuery function. CoW के बाद Type के
MEM_MAPPED/MEM_IMAGEरहने, तथाQueryWorkingSetExके Shared बिट से निजीकरण पुष्टि पर। ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Managing Memory Sections. व्यू तक पहुँचने तक भौतिक मेमोरी न दिए जाने, और पहली पहुँच के पेज फ़ॉल्ट के फ़ाइल सामग्री पढ़ने पर। ↩
-
Microsoft Learn, Sharing Files and Memory. नाम या हैंडल से एक ही फ़ाइल-मैपिंग ऑब्जेक्ट साझा करने;
INVALID_HANDLE_VALUEसे पेजफ़ाइल-समर्थित साझा मेमोरी बनाने; तथा सिंक्रनाइज़ेशन अलग आवश्यक होने पर। ↩ ↩2 ↩3 -
Microsoft Learn, Process Explorer - Sysinternals. Process Explorer के प्रोसेस हैंडल तथा लोड की गई DLL / मेमोरी-मैप फ़ाइलें दिखाने पर। ↩
-
Microsoft Learn, VMMap - Sysinternals. VMMap के प्रोसेस वर्चुअल मेमोरी को Image, Mapped File, Private आदि में बाँटने, तथा Working Set का Private/Shareable विभाजन दिखाने पर। ↩
संबंधित लेख
निकटवर्ती विषयों में गहराई से जाने के लिए समान टैग वाले नवीनतम लेख।
DllMain और Loader Lock — DLL आरंभीकरण में "कुछ न करें" कहे जाने का वास्तविक कारण
DllMain से LoadLibrary क्यों न बुलाएँ या दूसरे थ्रेड से सिंक्रोनाइज़ न करें। प्राथमिक स्रोतों से यह लेख समझाता है कि लोडर लॉक हर DLL सूचन...
Windows मेमोरी की गहराई (भाग 2) — भौतिक पेज का जीवन: पाँच सूचियाँ और पेज फ़ाइल की सच्चाई
यह लेख PFN डेटाबेस, Standby, Modified, मेमोरी संपीड़न और पेज फ़ाइल को जोड़कर बताता है कि Working Set छोड़ने के बाद भौतिक पेज कहाँ जाता है।
Windows मेमोरी की गहराई (भाग 1) — वह क्षण जब आभासी पता भौतिक RAM बन जाता है: शुरू से अंत तक page fault
यह लेख VirtualAlloc, VAD, पेज टेबल, TLB, demand-zero और hard fault को जोड़कर उस क्षण को समझाता है जब किसी आभासी पते को भौतिक RAM मिलती है।
Windows वर्चुअलाइज़ेशन की गहराई (भाग 3) — सेकंडों में बूट होने वाली वर्चुअल मशीनें: WSL2, Windows Sandbox और कंटेनर इतने हल्के क्यों हैं
WSL2 और Windows Sandbox सेकंडों में शुरू होकर इतने हल्के क्यों लगते हैं? यह लेख डायनामिक बेस इमेज और डायरेक्ट मैप से डायनामिक मेमोरी आवंट...
Windows वर्चुअलाइज़ेशन की गहराई (भाग 2) — वह मेमोरी जिसे कर्नेल भी नहीं देख सकता: VBS, HVCI और Credential Guard कैसे काम करते हैं
संगत हार्डवेयर पर क्लीन इंस्टॉल पर VBS डिफ़ॉल्ट से सक्षम होता है और हाइपरवाइज़र तथा SLAT से कर्नेल से मज़बूत अलगाव बनाता है। यह लेख VTL, ...
संबंधित विषय
ये पृष्ठ विषय को सेवाओं और निर्णयों के व्यापक संदर्भ में रखते हैं।
Windows के तकनीकी विषय
Windows विकास, बग जाँच और मौजूदा संपत्तियों के उपयोग का प्रवेश-द्वार।
इस विषय से जुड़ी सेवाएँ
यह लेख निम्नलिखित सेवाओं से सीधे जुड़ा है।
Windows ऐप विकास
व्यावसायिक ऐप, डिवाइस एकीकरण और संचार उपकरण, आवश्यकताओं से विकास तक।
अक्सर पूछे जाने वाले प्रश्न
इस लेख के विषय पर परामर्श में अक्सर पूछे जाने वाले प्रश्न।
- क्या एक ही DLL इस्तेमाल करने वाला हर प्रोसेस RAM में उस DLL की पूरी कॉपी पाता है?
- आमतौर पर नहीं। उसी इमेज के अपरिवर्तित पृष्ठ हर प्रोसेस के अलग वर्चुअल पते से एक ही भौतिक पृष्ठ पर मैप होते हैं। केवल वे पृष्ठ जिन्हें लिखना हो, कॉपी-ऑन-राइट आदि से प्रोसेस-निजी भौतिक पृष्ठ बनते हैं।
- क्या CreateFileMapping उसी क्षण प्रोसेस को मेमोरी आवंटित करता है?
- CreateFileMapping फ़ाइल-मैपिंग ऑब्जेक्ट बनाता है, लेकिन उसे प्रोसेस के वर्चुअल स्पेस में दिखाने वाला MapViewOfFile है। इसके अलावा व्यू के भौतिक पृष्ठ आमतौर पर पहली बार छुए गए पृष्ठ से पेज फ़ॉल्ट द्वारा साकार होते हैं।
- FILE_MAP_WRITE और FILE_MAP_COPY में क्या अंतर है?
- FILE_MAP_WRITE के ज़रिए बदलाव साझा फ़ाइल-डेटा पक्ष पर दिखने वाली लिखत है। FILE_MAP_COPY आरंभिक पृष्ठ साझा करता है, लेकिन केवल लिखे गए पृष्ठ प्रोसेस-निजी कॉपी बनते हैं; बदलाव मूल फ़ाइल में वापस नहीं लिखे जाते और व्यू अनमैप होते ही खो जाते हैं।
- कॉपी-ऑन-राइट के बाद क्या VirtualQuery MEM_PRIVATE लौटाता है?
- नहीं। डेटा व्यू MEM_MAPPED और इमेज व्यू MEM_IMAGE रहता है। यह देखने के लिए कि पृष्ठ वास्तव में निजी हुआ या नहीं, पृष्ठ को रेज़िडेंट बनाएँ और QueryWorkingSetEx का Shared बिट देखें।
- क्या साझा मेमोरी में कच्चा पॉइंटर रखना चाहिए?
- आमतौर पर नहीं। एक ही सेक्शन होने पर भी कोई गारंटी नहीं कि हर प्रोसेस का व्यू एक ही वर्चुअल पते पर होगा। साझा संरचना में बेस से ऑफ़सेट, स्थिर-चौड़ाई पूर्णांक, तथा स्पष्ट लेआउट और सिंक्रनाइज़ेशन योजना इस्तेमाल करें।