X86 पेजिंग कैसे काम करता है?


91

यह प्रश्न इस विषय पर अच्छी मुफ्त जानकारी के रिक्त स्थान को भरने के लिए है।

मेरा मानना ​​है कि एक अच्छा उत्तर एक बड़े SO उत्तर में या कम से कम कुछ उत्तरों में फिट होगा।

मुख्य लक्ष्य यह है कि पूरी तरह से शुरुआती लोगों को केवल पर्याप्त जानकारी दी जाए ताकि वे स्वयं से मैनुअल ले सकें, और पेजिंग से संबंधित बुनियादी ओएस अवधारणाओं को समझने में सक्षम हो सकें।

सुझाए गए दिशानिर्देश:

  • उत्तर शुरुआती के अनुकूल होने चाहिए:
    • ठोस, लेकिन संभवतः सरलीकृत उदाहरण बहुत महत्वपूर्ण हैं
    • दिखाए गए अवधारणाओं के अनुप्रयोग स्वागत योग्य हैं
  • उपयोगी संसाधनों का हवाला देना अच्छा है
  • ओएस के पेजिंग फीचर्स का उपयोग करने वाले छोटे डिग्रियों का स्वागत है
  • PAE और PSE स्पष्टीकरण स्वागत योग्य हैं
  • x86_64 में छोटे उत्थान का स्वागत है

संबंधित प्रश्न और मुझे लगता है कि वे नकल नहीं कर रहे हैं:


1
इसे "faq" टैग किया जाना चाहिए और "समुदाय-विकी" के रूप में चिह्नित किया जाना चाहिए।
केरेक एसबी

@KerrekSB मैं वास्तव में नहीं जानता कि इस तरह के सवालों का सामना कैसे किया जाए। उत्तर होना चाहिए समुदाय विकी यह है कि? मुझे कोई faqटैग नहीं मिला ।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

3
मैं कहूंगा कि संक्षिप्त उत्तर है, " वॉल्यूम 3, अध्याय 4: इंटेल मैनुअल में पेजिंग पढ़ें "। यह बहुत स्पष्ट, संक्षिप्त और अच्छी तरह से लिखा गया है, और यह किसी भी अधिक आधिकारिक नहीं मिलता है।
केरेक एसबी

4
@KerrekSB मैं मानता हूं कि मैनुअल स्पष्ट और आधिकारिक है, लेकिन यह मेरे लिए पहले पढ़ने के रूप में थोड़ा कठोर था, मुझे चीजों को बेहतर समझने के लिए कुछ सरल और ठोस उदाहरण + तर्क की आवश्यकता थी।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

जवाबों:


144

एक अच्छा टीओसी और अधिक सामग्री के साथ इस उत्तर का संस्करण

मैं रिपोर्ट की गई किसी भी त्रुटि को ठीक करूंगा। यदि आप बड़े संशोधन करना चाहते हैं या एक लापता पहलू जोड़ना चाहते हैं, तो उन्हें अपने जवाबों पर अच्छी तरह से योग्य प्रतिनिधि प्राप्त करें। माइनर एडिट को सीधे मर्ज किया जा सकता है।

नमूना कोड

न्यूनतम उदाहरण: https://github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579cbcd77721/paging.S

प्रोग्रामिंग में सब कुछ की तरह, वास्तव में यह समझने का एकमात्र तरीका न्यूनतम उदाहरणों के साथ खेलना है।

यह एक "कठिन" विषय बनाता है कि न्यूनतम उदाहरण बड़ा है क्योंकि आपको अपना खुद का छोटा ओएस बनाने की आवश्यकता है।

इंटेल मैनुअल

हालांकि मन में उदाहरणों के बिना समझना असंभव है, जितनी जल्दी हो सके मैनुअल से परिचित होने का प्रयास करें।

इंटेल इंटेल मैनुअल वॉल्यूम 3 सिस्टम प्रोग्रामिंग गाइड में पेजिंग का वर्णन करता है - 325384-056US सितंबर 2015 अध्याय 4 "पेजिंग"।

विशेष रूप से दिलचस्प है चित्रा 4-4 "सीआर 3 के प्रारूप और 32-बिट पेजिंग के साथ पेजिंग-स्ट्रक्चर एंट्रीज", जो प्रमुख डेटा संरचनाएं देता है।

MMU

पेजिंग CPU के मेमोरी मैनेजमेंट यूनिट (MMU) भाग द्वारा किया जाता है । कई अन्य (जैसे x87 सह-प्रोसेसर , APIC ) की तरह, यह शुरुआती दिनों में अलग चिप के द्वारा हुआ करता था, जिसे बाद में सीपीयू में एकीकृत किया गया था। लेकिन यह शब्द अभी भी प्रयोग किया जाता है।

सामान्य तथ्य

तार्किक पते "नियमित" उपयोगकर्ता-भूमि कोड (जैसे सामग्री rsiमें mov eax, [rsi]) में उपयोग किए जाने वाले मेमोरी पते हैं ।

पहले विभाजन उन्हें रैखिक पतों में अनुवादित करता है, और फिर पेजिंग फिर रैखिक पतों का भौतिक पतों में अनुवाद करता है।

(logical) ------------------> (linear) ------------> (physical)
             segmentation                 paging

अधिकांश समय, हम वास्तविक पतों को वास्तविक रैम हार्डवेयर मेमोरी कोशिकाओं को अनुक्रमित करने के रूप में सोच सकते हैं, लेकिन यह 100% सच नहीं है क्योंकि:

पेजिंग केवल संरक्षित मोड में उपलब्ध है। संरक्षित मोड में पेजिंग का उपयोग वैकल्पिक है। यदि रजिस्टर PGथोड़ा सा cr0सेट है तो पेजिंग चालू है।

पेजिंग बनाम विभाजन

पेजिंग और विभाजन के बीच एक बड़ा अंतर यह है कि:

  • पेजिंग रैम पेज के बराबर आकार के विखंडू में विभाजित हो जाता है
  • विभाजन, स्मृति को मनमाने आकार के टुकड़ों में विभाजित करता है

यह पेजिंग का मुख्य लाभ है, क्योंकि समान आकार के चोक चीजों को अधिक प्रबंधनीय बनाते हैं।

पेजिंग इतना अधिक लोकप्रिय हो गया है कि 64-बिट मोड में विभाजन के लिए समर्थन x86-64 में गिरा दिया गया था, नए सॉफ्टवेयर के लिए ऑपरेशन का मुख्य मोड, जहां यह केवल संगतता मोड में मौजूद है, जो IA32 का अनुकरण करता है।

आवेदन

पेजिंग का उपयोग आधुनिक ओएस पर प्रक्रियाओं को संबोधित करने के लिए किया जाता है। आभासी पतों के साथ ओएस एक ही रैम पर दो या अधिक समवर्ती प्रक्रियाओं को इस तरह से फिट कर सकता है:

  • दोनों कार्यक्रमों को दूसरे के बारे में कुछ भी जानने की जरूरत नहीं है
  • दोनों कार्यक्रमों की स्मृति आवश्यकतानुसार बढ़ सकती है और सिकुड़ सकती है
  • कार्यक्रमों के बीच स्विच बहुत तेज है
  • एक कार्यक्रम कभी भी किसी अन्य प्रक्रिया की मेमोरी तक नहीं पहुँच सकता है

ऐतिहासिक रूप से पेजिंग विभाजन के बाद आया, और बड़े पैमाने पर इसे आधुनिक ओएस जैसे लिनक्स में वर्चुअल मेमोरी के कार्यान्वयन के लिए बदल दिया गया क्योंकि चर लंबाई के खंडों के बजाय पृष्ठों की स्मृति के निश्चित आकार के टुकड़ों का प्रबंधन करना आसान है।

हार्डवेयर कार्यान्वयन

संरक्षित मोड में विभाजन की तरह (जहां एक सेगमेंट रजिस्टर को संशोधित करना GDT या LDT से लोड को ट्रिगर करता है), पेजिंग हार्डवेयर अपना काम करने के लिए मेमोरी में डेटा संरचनाओं का उपयोग करता है (पेज टेबल, पेज डायरेक्टरी, आदि)।

उन डेटा संरचनाओं का प्रारूप हार्डवेयर द्वारा तय किया गया है , लेकिन यह रैम पर उन डेटा संरचनाओं को सही ढंग से स्थापित करने और प्रबंधित करने के लिए ओएस पर निर्भर है, और हार्डवेयर को यह बताने के लिए कि उन्हें (कहां cr3) ढूंढना है ।

कुछ अन्य आर्किटेक्चर सॉफ्टवेयर के हाथों में लगभग पूरी तरह से पेजिंग छोड़ देते हैं, इसलिए पेज टेबल पर चलने और टीएलबी में नई मैपिंग डालने के लिए एक टीएलबी मिस एक ओएस-आपूर्ति फ़ंक्शन चलाता है। यह ओएस द्वारा चुने जाने वाले पेज टेबल फॉर्मेट्स को छोड़ देता है, लेकिन यह हार्डवेयर के लिए पेज-वॉक को अन्य निर्देशों के आउट-ऑफ-ऑर्डर निष्पादन के साथ ओवरलैप करने में सक्षम होने की संभावना नहीं बनाता है , जिस तरह से x86 कर सकता है

उदाहरण: सरलीकृत एकल-स्तरीय पेजिंग योजना

यह एक उदाहरण है कि वर्चुअल मेमोरी स्पेस को लागू करने के लिए x86 आर्किटेक्चर के सरलीकृत संस्करण पर पेजिंग कैसे संचालित होती है।

पेज टेबल

ओएस उन्हें निम्नलिखित पेज टेबल दे सकता है:

ओएस द्वारा प्रक्रिया 1 को दी गई पृष्ठ तालिका:

RAM location        physical address   present
-----------------   -----------------  --------
PT1 + 0       * L   0x00001            1
PT1 + 1       * L   0x00000            1
PT1 + 2       * L   0x00003            1
PT1 + 3       * L                      0
...                                    ...
PT1 + 0xFFFFF * L   0x00005            1

ओएस द्वारा 2 प्रक्रिया करने के लिए दी गई पृष्ठ तालिका:

RAM location       physical address   present
-----------------  -----------------  --------
PT2 + 0       * L  0x0000A            1
PT2 + 1       * L  0x0000B            1
PT2 + 2       * L                     0
PT2 + 3       * L  0x00003            1
...                ...                ...
PT2 + 0xFFFFF * L  0x00004            1

कहाँ पे:

  • PT1और PT2: रैम पर तालिका 1 और 2 की प्रारंभिक स्थिति।

    मानों का नमूना: 0x00000000, 0x12345678, आदि

    यह ओएस है जो उन मूल्यों को तय करता है।

  • L: पृष्ठ तालिका प्रविष्टि की लंबाई।

  • present: इंगित करता है कि पृष्ठ स्मृति में मौजूद है।

पेज टेबल रैम पर स्थित हैं। वे उदाहरण के लिए स्थित हो सकते हैं:

--------------> 0xFFFFFFFF


--------------> PT1 + 0xFFFFF * L
Page Table 1
--------------> PT1


--------------> PT2 + 0xFFFFF * L
Page Table 2
--------------> PT2

--------------> 0x0

दोनों पृष्ठ तालिकाओं के लिए RAM पर प्रारंभिक स्थान OS द्वारा मनमानी और नियंत्रित किए जाते हैं। यह सुनिश्चित करने के लिए ओएस पर निर्भर है कि वे ओवरलैप नहीं करते हैं!

प्रत्येक प्रक्रिया किसी भी पेज टेबल को सीधे नहीं छू सकती है, हालांकि यह ओएस के लिए अनुरोध कर सकती है जिससे पेज टेबल संशोधित हो सकती है, उदाहरण के लिए बड़े स्टैक या हीप सेगमेंट के लिए पूछना।

एक पृष्ठ 4KB (12 बिट्स) का एक हिस्सा है, और चूंकि पते में 32 बिट्स हैं, केवल 20 बिट्स (20 + 12 = 32, इस प्रकार हेक्साडेसिमल नोटेशन में 5 वर्ण) को प्रत्येक पृष्ठ की पहचान करना आवश्यक है। यह मान हार्डवेयर द्वारा तय किया जाता है।

पृष्ठ तालिका प्रविष्टियाँ

एक पृष्ठ तालिका है ... पृष्ठों की तालिका प्रविष्टियों की एक तालिका!

तालिका प्रविष्टियों का सटीक प्रारूप हार्डवेयर द्वारा तय किया गया है

इस सरलीकृत उदाहरण में, पृष्ठ तालिका प्रविष्टियों में केवल दो फ़ील्ड हैं:

bits   function
-----  -----------------------------------------
20     physical address of the start of the page
1      present flag

इसलिए इस उदाहरण में हार्डवेयर डिजाइनरों को चुना जा सकता था L = 21

अधिकांश वास्तविक पृष्ठ तालिका प्रविष्टियों में अन्य फ़ील्ड हैं।

यह 21 बिट्स पर चीजों को संरेखित करने के लिए अव्यवहारिक होगा क्योंकि मेमोरी बाइट्स द्वारा संबोधित की जाती है और बिट्स नहीं। इसलिए, इस मामले में भी केवल 21 बिट्स की आवश्यकता होती है, हार्डवेयर डिजाइनर शायद L = 32तेजी से पहुंच बनाने के लिए चुनते हैं , और बाद में उपयोग के लिए शेष बिट्स को आरक्षित करते हैं। LX86 पर वास्तविक मान 32 बिट्स है।

एकल-स्तरीय योजना में पता अनुवाद

एक बार ओएस द्वारा पृष्ठ तालिकाओं की स्थापना की गई है, रैखिक और भौतिक पते के बीच का अनुवाद हार्डवेयर द्वारा किया जाता है

जब OS प्रक्रिया 1 को सक्रिय करना चाहता है, तो यह प्रक्रिया एक के लिए, तालिका की शुरुआत cr3को सेट करता है PT1

यदि प्रक्रिया 1 रैखिक पते का उपयोग करना चाहती है 0x00000001, तो पेजिंग हार्डवेयर सर्किट ओएस के लिए स्वचालित रूप से निम्न कार्य करता है:

  • रैखिक पता को दो भागों में विभाजित करें:

    | page (20 bits) | offset (12 bits) |
    

    तो इस मामले में हमारे पास होगा:

    • पेज = 0x00000
    • ऑफसेट = 0x001
  • पृष्ठ 1 में देखें क्योंकि cr3यह इंगित करता है।

  • प्रविष्टि देखें 0x00000क्योंकि वह पृष्ठ भाग है।

    हार्डवेयर जानता है कि यह प्रविष्टि रैम पते पर स्थित है PT1 + 0 * L = PT1

  • चूंकि यह मौजूद है, पहुंच वैध है

  • पृष्ठ तालिका द्वारा, पृष्ठ संख्या 0x00000का स्थान पर है 0x00001 * 4K = 0x00001000

  • अंतिम भौतिक पता खोजने के लिए हमें केवल ऑफ़सेट जोड़ने की आवश्यकता है:

      00001 000
    + 00000 001
      -----------
      00001 001
    

    क्योंकि 00001पृष्ठ का भौतिक पता तालिका पर देखा गया 001है और ऑफसेट है।

    जैसा कि नाम से संकेत मिलता है, ऑफसेट को हमेशा पृष्ठ का भौतिक पता जोड़ा जाता है।

  • हार्डवेयर तब उस भौतिक स्थान पर मेमोरी प्राप्त करता है।

उसी तरह, निम्नलिखित अनुवाद प्रक्रिया 1 के लिए होगा:

linear     physical
---------  ---------
00000 002  00001 002
00000 003  00001 003
00000 FFF  00001 FFF
00001 000  00000 000
00001 001  00000 001
00001 FFF  00000 FFF
00002 000  00002 000
FFFFF 000  00005 000

उदाहरण के लिए, पता एक्सेस करते समय 00001000, पृष्ठ भाग 00001हार्डवेयर होता है, यह जानता है कि इसकी पृष्ठ तालिका प्रविष्टि RAM पते पर स्थित है: PT1 + 1 * L( 1पृष्ठ भाग के कारण), और वह वह जगह है जहाँ यह इसके लिए दिखेगा।

जब OS 2 को संसाधित करने के लिए स्विच करना चाहता है, तो उसे केवल cr3पृष्ठ 2 पर बिंदु बनाना होगा। यह इतना आसान है!

अब निम्नलिखित अनुवाद प्रक्रिया 2 के लिए होंगे:

linear     physical
---------  ---------
00000 002  00001 002
00000 003  00001 003
00000 FFF  00001 FFF
00001 000  00000 000
00001 001  00000 001
00001 FFF  00000 FFF
00003 000  00003 000
FFFFF 000  00004 000

एक ही रैखिक पता अलग-अलग प्रक्रियाओं के लिए अलग-अलग भौतिक पते में अनुवाद करता है , केवल अंदर के मूल्य पर निर्भर करता है cr3

इस तरह हर कार्यक्रम सटीक भौतिक पतों की चिंता किए बिना अपने डेटा को शुरू 0और खत्म होने की उम्मीद कर सकता है FFFFFFFF

पृष्ठ दोष

क्या होगा यदि प्रक्रिया 1 एक पृष्ठ के अंदर एक पते तक पहुंचने की कोशिश करती है जो मौजूद नहीं है?

हार्डवेयर एक पृष्ठ दोष अपवाद के माध्यम से सॉफ्टवेयर को सूचित करता है।

यह आमतौर पर ओएस पर निर्भर होता है कि एक अपवाद हैंडलर को यह दर्ज करने के लिए कि क्या किया जाना है।

यह संभव है कि पृष्ठ पर नहीं है एक पृष्ठ तक पहुँचने के लिए एक प्रोग्रामिंग त्रुटि है:

int is[1];
is[2] = 1;

लेकिन ऐसे मामले हो सकते हैं जिनमें यह स्वीकार्य है, उदाहरण के लिए जब लिनक्स में:

  • कार्यक्रम अपने स्टैक को बढ़ाना चाहता है।

    यह बस एक निश्चित सीमा में एक निश्चित बाइट तक पहुंचने की कोशिश करता है, और अगर ओएस खुश है तो यह उस पेज को प्रोसेस एड्रेस स्पेस में जोड़ता है।

  • पृष्ठ को डिस्क पर स्वैप किया गया था।

    पेज को रैम में वापस लाने के लिए ओएस को प्रक्रियाओं के पीछे कुछ काम करना होगा।

    OS यह पता लगा सकता है कि यह पृष्ठ तालिका प्रविष्टि के बाकी हिस्सों की सामग्री पर आधारित है, क्योंकि यदि वर्तमान ध्वज स्पष्ट है, तो पृष्ठ तालिका प्रविष्टि की अन्य प्रविष्टियाँ पूरी तरह से OS के लिए छोड़ दी जाती हैं कि वह क्या चाहती है।

    लिनक्स पर उदाहरण के लिए, जब वर्तमान = 0:

    • यदि पृष्ठ तालिका प्रविष्टि के सभी क्षेत्र 0, अमान्य पते हैं।

    • और, पृष्ठ को डिस्क पर स्वैप किया गया है, और उन फ़ील्ड के वास्तविक मान डिस्क पर पृष्ठ की स्थिति को कूटबद्ध करते हैं।

किसी भी मामले में, ओएस को यह जानने की जरूरत है कि समस्या से निपटने में सक्षम होने के लिए कौन सा पता पेज फॉल्ट उत्पन्न करता है। यही कारण है कि अच्छा IA32 डेवलपर्स cr2जब भी पृष्ठ दोष होता है, तो उस पते का मूल्य निर्धारित करता है। अपवाद हैंडलर तब केवल cr2पता प्राप्त करने के लिए देख सकता है।

सरलीकरण

वास्तविकता का सरलीकरण जो इस उदाहरण को समझने में आसान बनाता है:

  • सभी वास्तविक पेजिंग सर्किट अंतरिक्ष को बचाने के लिए बहु-स्तरीय पेजिंग का उपयोग करते हैं, लेकिन यह एक साधारण एकल-स्तरीय योजना दिखाती है।

  • पृष्ठ तालिकाओं में केवल दो फ़ील्ड शामिल थे: एक 20 बिट पता और 1 बिट वर्तमान ध्वज।

    वास्तविक पृष्ठ तालिकाओं में कुल 12 फ़ील्ड होते हैं, और इसलिए अन्य सुविधाएँ जिन्हें छोड़ दिया गया है।

उदाहरण: बहु-स्तरीय पेजिंग योजना

एकल-स्तरीय पेजिंग योजना के साथ समस्या यह है कि इसमें बहुत अधिक रैम होगी: प्रति प्रक्रिया 4 जी / 4K = 1M प्रविष्टियां । यदि प्रत्येक प्रविष्टि 4 बाइट्स लंबी है, तो यह 4M प्रति प्रक्रिया बना देगा , जो कि डेस्कटॉप कंप्यूटर के लिए भी बहुत अधिक है: ps -A | wc -lकहते हैं कि मैं अभी 244 प्रक्रियाएँ चला रहा हूँ, ताकि मेरी RAM लगभग 1GB हो जाए!

इस कारण से, x86 डेवलपर्स ने बहु-स्तरीय योजना का उपयोग करने का निर्णय लिया जो रैम उपयोग को कम करता है।

इस प्रणाली का नकारात्मक पक्ष यह है कि इसका उपयोग थोड़ा अधिक है।

पीएई के बिना 32 बिट प्रोसेसर के लिए उपयोग की जाने वाली सरल 3 स्तरीय पेजिंग योजना में, 32 पते बिट्स को निम्नानुसार विभाजित किया गया है:

| directory (10 bits) | table (10 bits) | offset (12 bits) |

प्रत्येक प्रक्रिया में एक और केवल एक पेज डायरेक्टरी जुड़ी होनी चाहिए, इसलिए इसमें कम से कम 2^10 = 1Kपेज डायरेक्टरी एंट्रीज होंगी , जो कि सिंगल-लेवल स्कीम पर आवश्यक न्यूनतम 1M से बहुत बेहतर है।

पेज टेबल केवल ओएस द्वारा आवश्यकतानुसार आवंटित किए जाते हैं। प्रत्येक पृष्ठ तालिका में 2^10 = 1Kपृष्ठ निर्देशिका प्रविष्टियाँ हैं

पृष्ठ निर्देशिकाओं में शामिल हैं ... पृष्ठ निर्देशिका प्रविष्टियाँ! पृष्ठ निर्देशिका प्रविष्टियाँ पृष्ठ तालिका प्रविष्टियों के समान होती हैं सिवाय इसके कि वे सारणी के भौतिक पतों के बजाय पृष्ठ तालिकाओं के RAM पतों की ओर इंगित करती हैं । चूंकि वे पते केवल 20 बिट्स चौड़े हैं, इसलिए पेज टेबल 4KB पेजों की शुरुआत में होनी चाहिए।

cr3 अब पृष्ठ तालिकाओं के बजाय वर्तमान प्रक्रिया के पृष्ठ निर्देशिका की रैम पर स्थान को इंगित करता है।

एकल-स्तरीय योजना से पृष्ठ तालिका प्रविष्टियाँ बिल्कुल नहीं बदलती हैं।

एकल-स्तरीय योजना से पृष्ठ तालिकाएँ बदल जाती हैं क्योंकि:

  • प्रत्येक प्रक्रिया में 1K पेज टेबल, एक पेज डायरेक्टरी प्रविष्टि हो सकती है।
  • प्रत्येक पृष्ठ तालिका में 1M प्रविष्टियों के बजाय 1K प्रविष्टियाँ होती हैं।

पहले दो स्तरों (और नहीं, कहते हैं 12 | 8 | 12) पर 10 बिट्स का उपयोग करने का कारण यह है कि प्रत्येक पृष्ठ तालिका प्रविष्टि 4 बाइट्स लंबी है। तब पृष्ठ निर्देशिकाओं और पेज टेबल्स की 2 ^ 10 प्रविष्टियां 4K पेज में अच्छी तरह से फिट होंगी। इसका मतलब यह है कि यह उस उद्देश्य के लिए पृष्ठों को आवंटित करने और उनसे निपटने के लिए तेज़ और सरल है।

बहु-स्तरीय योजना में पता अनुवाद

OS द्वारा पेज 1 को संसाधित करने के लिए दी गई पृष्ठ निर्देशिका:

RAM location     physical address   present
---------------  -----------------  --------
PD1 + 0     * L  0x10000            1
PD1 + 1     * L                     0
PD1 + 2     * L  0x80000            1
PD1 + 3     * L                     0
...                                 ...
PD1 + 0x3FF * L                     0

OS PT1 = 0x10000000( 0x10000* 4K) पर 1 द्वारा प्रोसेस करने के लिए दी गई पेज टेबल :

RAM location      physical address   present
---------------   -----------------  --------
PT1 + 0     * L   0x00001            1
PT1 + 1     * L                      0
PT1 + 2     * L   0x0000D            1
...                                  ...
PT1 + 0x3FF * L   0x00005            1

OS PT2 = 0x80000000( 0x80000* 4K) पर 1 द्वारा प्रोसेस करने के लिए दी गई पेज टेबल :

RAM location      physical address   present
---------------   -----------------  --------
PT2 + 0     * L   0x0000A            1
PT2 + 1     * L   0x0000C            1
PT2 + 2     * L                      0
...                                  ...
PT2 + 0x3FF * L   0x00003            1

कहाँ पे:

  • PD1: RAM पर प्रक्रिया 1 की पृष्ठ निर्देशिका की प्रारंभिक स्थिति।
  • PT1और PT2: रैम पर प्रक्रिया 1 के लिए पेज टेबल 1 और पेज टेबल 2 की प्रारंभिक स्थिति।

तो इस उदाहरण में पेज डायरेक्टरी और पेज टेबल को कुछ इस तरह रैम में स्टोर किया जा सकता है:

----------------> 0xFFFFFFFF


----------------> PT2 + 0x3FF * L
Page Table 1
----------------> PT2

----------------> PD1 + 0x3FF * L
Page Directory 1
----------------> PD1


----------------> PT1 + 0x3FF * L
Page Table 2
----------------> PT1

----------------> 0x0

आइए, रैखिक पता 0x00801004चरण का अनुवाद करें ।

हमें लगता है कि cr3 = PD1, यह सिर्फ वर्णित पृष्ठ निर्देशिका को इंगित करता है।

बाइनरी में रैखिक पता है:

0    0    8    0    1    0    0    4
0000 0000 1000 0000 0001 0000 0000 0100

समूह के रूप में 10 | 10 | 12देता है:

0000000010 0000000001 000000000100
0x2        0x1        0x4

जो देता है:

  • पृष्ठ निर्देशिका प्रविष्टि = 0x2
  • पृष्ठ तालिका प्रविष्टि = 0x1
  • ऑफसेट = 0x4

इसलिए हार्डवेयर पेज डायरेक्टरी के एंट्री 2 के लिए दिखता है।

पेज डायरेक्टरी टेबल कहती है कि पेज टेबल पर स्थित है 0x80000 * 4K = 0x80000000। यह प्रक्रिया का पहला रैम एक्सेस है।

चूँकि पेज टेबल एंट्री है 0x1, हार्डवेयर पेज टेबल के एंट्री 1 पर दिखता है 0x80000000, जो बताता है कि भौतिक पेज एड्रेस पर स्थित है 0x0000C * 4K = 0x0000C000। यह प्रक्रिया का दूसरा रैम एक्सेस है।

अंत में, पेजिंग हार्डवेयर ऑफसेट जोड़ता है, और अंतिम पता है 0x0000C004

अनुवादित पतों के अन्य उदाहरण हैं:

linear    10 10 12 split   physical
--------  ---------------  ----------
00000001  000 000 001      00001001
00001001  000 001 001      page fault
003FF001  000 3FF 001      00005001
00400000  001 000 000      page fault
00800001  002 000 001      0000A001
00801008  002 001 008      0000C008
00802008  002 002 008      page fault
00B00001  003 000 000      page fault

पृष्ठ दोष तब होते हैं यदि पृष्ठ निर्देशिका प्रविष्टि या पृष्ठ तालिका प्रविष्टि मौजूद नहीं है।

यदि OS दूसरी प्रक्रिया को समवर्ती रूप से चलाना चाहता है, तो यह दूसरी प्रक्रिया को एक अलग पृष्ठ निर्देशिका प्रदान करेगा, और उस निर्देशिका को अलग-अलग पृष्ठ तालिकाओं से लिंक करेगा।

64-बिट आर्किटेक्चर

वर्तमान बिट आकार के लिए 64 बिट अभी भी बहुत अधिक पता है, इसलिए अधिकांश आर्किटेक्चर कम बिट का उपयोग करेंगे।

x86_64 48 बिट्स (256 TiB) का उपयोग करता है, और विरासत मोड का PAE पहले से ही 52-बिट पते (4 PiB) की अनुमति देता है।

उन 48 बिट्स में से 12 पहले से ही ऑफसेट के लिए आरक्षित हैं, जो 36 बिट्स को छोड़ देता है।

यदि 2 स्तर का दृष्टिकोण लिया जाता है, तो सबसे अच्छा विभाजन दो 18 बिट स्तर होगा।

लेकिन इसका मतलब यह होगा कि पेज डायरेक्टरी में 2^18 = 256Kप्रविष्टियां होंगी , जिसमें बहुत अधिक रैम होगी: 32 बिट आर्किटेक्चर के लिए एकल-स्तरीय पेजिंग के करीब!

इसलिए, 64 बिट आर्किटेक्चर और भी पेज स्तर बनाते हैं, आमतौर पर 3 या 4।

x86_64 किसी 9 | 9 | 9 | 12योजना में 4 स्तरों का उपयोग करता है , ताकि ऊपरी स्तर केवल 2^9उच्च स्तर की प्रविष्टियाँ लेता है ।

पीएई

भौतिक पता विस्तार।

32 बिट्स के साथ, केवल 4GB रैम को संबोधित किया जा सकता है।

यह बड़े सर्वरों के लिए एक सीमा बनना शुरू हो गया, इसलिए इंटेल ने पीएई तंत्र को पेंटियम प्रो में पेश किया।

समस्या से छुटकारा पाने के लिए, इंटेल ने 4 नई एड्रेस लाइन्स जोड़ीं, ताकि 64GB को संबोधित किया जा सके।

पृष्ठ तालिका संरचना भी बदल दी जाती है यदि PAE चालू है। जिस तरीके से इसे बदला गया है, वह इस बात पर निर्भर करता है कि PSE चालू या बंद है।

पीएई को PAEबिट के माध्यम से चालू और बंद किया जाता है cr4

भले ही कुल पता करने योग्य मेमोरी 64GB हो, लेकिन व्यक्तिगत प्रक्रिया अभी भी केवल 4GB तक उपयोग करने में सक्षम है। हालाँकि OS विभिन्न प्रक्रियाओं को अलग-अलग 4GB विखंडू पर रख सकता है।

सार्वजनिक उपक्रम

पृष्ठ आकार विस्तार।

4K के बजाय पृष्ठों की लंबाई 4M (या PAM पर है तो 2M) है।

PSE को PAEबिट के माध्यम से चालू और बंद किया जाता है cr4

पीएई और पीएसई पेज टेबल योजनाएं

यदि पीएई और पीएसई सक्रिय हैं, तो विभिन्न पेजिंग स्तर की योजनाओं का उपयोग किया जाता है:

  • कोई PAE और कोई PSE: 10 | 10 | 12

  • कोई पीएई और पीएसई: नहीं 10 | 22

    22 बिट्स 4Mb पृष्ठ के भीतर ऑफसेट है, क्योंकि 22 बिट्स 4Mb को संबोधित करते हैं।

  • पीएई और कोई पीएसई: 2 | 9 | 9 | 12

    डिज़ाइन का कारण 10 के बजाय 9 का उपयोग किया जाता है, क्योंकि अब प्रविष्टियाँ 32 बिट्स में फिट नहीं हो सकती हैं, जो सभी 20 पते बिट्स और 12 सार्थक या आरक्षित फ़्लैग बिट्स द्वारा भरे गए थे।

    कारण यह है कि पृष्ठ तालिकाओं के पते का प्रतिनिधित्व करने के लिए 20 बिट्स पर्याप्त नहीं हैं: प्रोसेसर में 4 अतिरिक्त तारों को जोड़ने के कारण अब 24 बिट्स की आवश्यकता होती है।

    इसलिए, डिजाइनरों ने प्रवेश का आकार 64 बिट तक बढ़ाने का फैसला किया, और उन्हें एक ही पृष्ठ तालिका में फिट करने के लिए प्रविष्टियों की संख्या 2 ^ 10 के बजाय 2 ^ 9 तक कम करना आवश्यक है।

    के बाद से यह शुरू करने से 2, एक नया पृष्ठ के स्तर का कहा जाता है पृष्ठ निर्देशिका सूचक तालिका (PDPT) है बताते पेज निर्देशिका और पता रैखिक 32 बिट में भरने के लिए। PDPT भी 64 बिट्स चौड़े हैं।

    cr3अब पीडीपीटी की ओर इशारा करता है जो मुट्ठी की 4 4GB मेमोरी पर होना चाहिए और दक्षता को संबोधित करने के लिए 32 बिट गुणकों पर संरेखित किया जाना चाहिए। इसका मतलब यह है कि अब cr3पहले के 4GB के 2 ^ 32 को पूरा करने के लिए 32 गुणकों के लिए 20: 2 ^ 5 के बजाय 27 महत्वपूर्ण बिट्स हैं।

  • PAE और PSE: 2 | 9 | 21

    डिजाइनरों ने एक एकल पृष्ठ में फिट होने के लिए एक 9 बिट चौड़े मैदान को रखने का फैसला किया।

    इससे 23 बिट्स निकलते हैं। पीएसई के लिए पीएई मामले के साथ समान रखने के लिए पीएसपी के लिए 2 छोड़कर, ऑफसेट के लिए 21 छोड़ देता है, जिसका अर्थ है कि पृष्ठ 4 एम के बजाय 2 एम व्यापक हैं।

TLB

अनुवाद लुकाहेड बफर (TLB) पेजिंग पतों के लिए एक कैश है।

चूंकि यह एक कैश है, इसलिए यह सीपीयू कैश के कई डिजाइन मुद्दों को साझा करता है, जैसे कि एसोसिएटिविटी स्तर।

यह खंड 4 एकल पते प्रविष्टियों के साथ एक सरलीकृत पूरी तरह से साहचर्य TLB का वर्णन करेगा। ध्यान दें कि अन्य कैश की तरह, असली टीएलबी आमतौर पर पूरी तरह से सहयोगी नहीं होते हैं।

मूल परिचालन

रैखिक और भौतिक पते के बीच अनुवाद होने के बाद, इसे टीएलबी पर संग्रहीत किया जाता है। उदाहरण के लिए, 4 प्रविष्टि टीएलबी निम्नलिखित अवस्था में शुरू होती है:

  valid   linear   physical
  ------  -------  ---------
> 0       00000    00000
  0       00000    00000
  0       00000    00000
  0       00000    00000

>वर्तमान प्रविष्टि को बदला जाएगा इंगित करता है।

और एक पृष्ठ के बाद रैखिक पता 00003एक भौतिक पते पर अनुवादित किया 00005जाता है, टीएलबी बन जाता है:

  valid   linear   physical
  ------  -------  ---------
  1       00003    00005
> 0       00000    00000
  0       00000    00000
  0       00000    00000

और का एक दूसरा अनुवाद के बाद 00007के लिए 00009यह हो जाता है:

  valid   linear   physical
  ------  -------  ---------
  1       00003    00005
  1       00007    00009
> 0       00000    00000
  0       00000    00000

अब अगर 00003फिर से अनुवाद करने की आवश्यकता है, तो हार्डवेयर पहले टीएलबी को देखता है और एक सिंगल रैम एक्सेस के साथ अपना पता ढूंढता है 00003 --> 00005

बेशक, 00000टीएलबी पर नहीं है 00000क्योंकि एक कुंजी के रूप में कोई वैध प्रविष्टि शामिल नहीं है ।

प्रतिस्थापन नीति

जब टीएलबी भरा जाता है, तो पुराने पते लिखे जाते हैं। सीपीयू कैश के लिए की तरह, प्रतिस्थापन नीति एक संभावित जटिल ऑपरेशन है, लेकिन एक सरल और उचित अनुमानी हाल ही में उपयोग की गई प्रविष्टि (LRU) को हटा देना है।

LRU के साथ, राज्य से शुरू:

  valid   linear   physical
  ------  -------  ---------
> 1       00003    00005
  1       00007    00009
  1       00009    00001
  1       0000B    00003

जोड़ना 0000D -> 0000Aहोगा:

  valid   linear   physical
  ------  -------  ---------
  1       0000D    0000A
> 1       00007    00009
  1       00009    00001
  1       0000B    00003

सीएएम

टीएलबी का उपयोग करने से अनुवाद में तेजी आती है, क्योंकि प्रारंभिक अनुवाद प्रति टीएलबी स्तर तक एक पहुंच लेता है , जिसका अर्थ है कि एक साधारण 32 बिट योजना पर 2, लेकिन 64 बिट आर्किटेक्चर पर 3 या 4।

टीएलबी को आम तौर पर एक महंगी प्रकार की रैम के रूप में लागू किया जाता है जिसे सामग्री-पता योग्य मेमोरी (सीएएम) कहा जाता है। सीएएम हार्डवेयर पर एक साहचर्य मानचित्र को लागू करता है, अर्थात, एक संरचना जो एक कुंजी (रैखिक पता) दी गई है, एक मान प्राप्त करता है।

मैपिंग को रैम पतों पर भी लागू किया जा सकता है, लेकिन सीएएम मैपिंग में रैम मैपिंग की तुलना में बहुत कम प्रविष्टियों की आवश्यकता हो सकती है।

उदाहरण के लिए, एक नक्शा जिसमें:

  • दोनों कुंजियों और मूल्यों में 20 बिट्स हैं (एक साधारण पेजिंग योजनाओं का मामला)
  • प्रत्येक समय पर अधिकतम 4 मूल्यों को संग्रहीत करने की आवश्यकता होती है

4 प्रविष्टियों के साथ एक TLB में संग्रहीत किया जा सकता है:

linear   physical
-------  ---------
00000    00001
00001    00010
00010    00011
FFFFF    00000

हालाँकि, RAM के साथ इसे लागू करने के लिए, 2 ^ 20 पते होना आवश्यक होगा :

linear   physical
-------  ---------
00000    00001
00001    00010
00010    00011
... (from 00011 to FFFFE)
FFFFF    00000

जो कि TLB का उपयोग करने से भी अधिक महंगा होगा।

प्रविष्टियों को अमान्य कर रहा है

जब cr3परिवर्तन होते हैं, तो सभी टीएलबी प्रविष्टियां अमान्य हो जाती हैं, क्योंकि एक नई प्रक्रिया के लिए एक नई पृष्ठ तालिका का उपयोग किया जाने वाला है, इसलिए यह संभव नहीं है कि पुरानी प्रविष्टियों में से किसी का कोई अर्थ हो।

X86 invlpgनिर्देश भी प्रदान करता है जो स्पष्ट रूप से एकल TLB प्रविष्टि को अमान्य करता है। अन्य आर्किटेक्चर टीएलबी प्रविष्टियों को अमान्य करने के लिए और भी अधिक निर्देश प्रदान करते हैं, जैसे किसी दिए गए सीमा पर सभी प्रविष्टियों को अमान्य करना।

कुछ x86 सीपीयू x86 विनिर्देश की आवश्यकताओं से परे जाते हैं और एक पृष्ठ तालिका प्रविष्टि को संशोधित करने और इसका उपयोग करने के बीच , इसकी गारंटी से अधिक सामंजस्य प्रदान करते हैं , जब यह टीएलबी में पहले से ही कैश नहीं था । जाहिरा तौर पर विंडोज 9x ने शुद्धता के लिए उस पर भरोसा किया, लेकिन आधुनिक एएमडी सीपीयू सुसंगत पेज-वॉक प्रदान नहीं करते हैं। इंटेल सीपीयू करते हैं, भले ही उन्हें ऐसा करने के लिए गलत-अटकलों का पता लगाना हो। इसका फायदा उठाना शायद एक बुरा विचार है, क्योंकि संभवतः बहुत कुछ हासिल करने के लिए नहीं है, और सूक्ष्म समय-संवेदनशील समस्याओं को पैदा करने का एक बड़ा जोखिम है जो डिबग करना कठिन होगा।

लिनक्स कर्नेल उपयोग

लिनक्स कर्नेल छोटे डेटा विखंडन के साथ तेज़ प्रक्रिया स्विच की अनुमति देने के लिए x86 की पेजिंग सुविधाओं का व्यापक उपयोग करता है।

के v4.2तहत, देखो arch/x86/:

  • include/asm/pgtable*
  • include/asm/page*
  • mm/pgtable*
  • mm/page*

ऐसा लगता है कि पृष्ठों को दर्शाने के लिए परिभाषित कोई संरचना नहीं है, केवल मैक्रोज़: include/asm/page_types.hविशेष रूप से दिलचस्प है। अंश:

#define _PAGE_BIT_PRESENT   0   /* is present */
#define _PAGE_BIT_RW        1   /* writeable */
#define _PAGE_BIT_USER      2   /* userspace addressable */
#define _PAGE_BIT_PWT       3   /* page write through */

arch/x86/include/uapi/asm/processor-flags.hपरिभाषित करता है CR0, और विशेष रूप से PGबिट स्थिति:

#define X86_CR0_PG_BIT      31 /* Paging */

ग्रन्थसूची

नि: शुल्क:

  • रटगर्स- pxk-416 अध्याय "मेमोरी प्रबंधन: व्याख्यान नोट्स"

    पुराने ओएस द्वारा उपयोग की जाने वाली मेमोरी संगठन तकनीकों की अच्छी ऐतिहासिक समीक्षा।

गैर-मुक्त:

  • bovet05 अध्याय "मेमोरी एड्रेसिंग"

    X86 मेमोरी एड्रेसिंग के लिए उचित परिचय। कुछ अच्छे और सरल उदाहरण याद आ रहे हैं।


महान जवाब, लेकिन मैं अभी भी स्पष्ट नहीं हूं कि एलआरयू कैसे तय किया जाता है। हर बार OS को इनवॉइस करना एमआरयू के अलावा एक पेज तक पहुंचना महंगा लगता है। वैकल्पिक रूप से मैं LRU के लिए पृष्ठ तालिका को फिर से व्यवस्थित करने वाले हार्डवेयर को देख सकता था, जो समवर्ती कार्यक्रमों के लिए खतरनाक हो सकता है। क्या ये दोनों सही हैं? जब पृष्ठ में कोई गलती होती है तो OS को यह पता कैसे होता है कि LRU कौन सा पृष्ठ है।
कीनन

@Keynan मुझे लगता है कि यह हार्डवेयर है जो इसे करता है, इसलिए लिया गया समय चिंता का विषय नहीं है। समवर्ती के लिए मुझे नहीं पता कि यह कैसे प्रबंधित किया जाता है। मुझे लगता है कि प्रति प्रोसेसर एक CR3 और कैश है, और OS को यह सुनिश्चित करना चाहिए कि मेमोरी पेज ओवरलैप न हों।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '19

1
real TLBs are not usually fully associativeThe TLB is usually implemented as … CAMक्या ये दो कथन विरोधाभासी नहीं हैं?
a3f

>>> x86_64 एक 9 में 4 स्तरों का उपयोग करता है 9 | 9 | 12 स्कीम यह होनी चाहिए 9 | 9 | 9 | 9 | 12 9
मोनक्लोफ

@monklof मुझे लगता है कि यह सही है: 9 9 9 12 पहले से ही 512 जीबी रैम की अनुमति देता है। 5 स्तर की योजना केवल सर्वरों पर लक्षित एक अधिक हालिया विकास है, यह मेरी वेबसाइट में उत्तर में उल्लिखित है जो आज तक अधिक है।
सिरो सांटिल्ली 郝海东 i i i ''

22

यहाँ एक बहुत छोटा, उच्च-स्तरीय उत्तर दिया गया है:

एक x86 प्रोसेसर कई संभावित मोड (मोटे तौर पर: वास्तविक, संरक्षित, 64-बिट) में से एक में काम करता है। प्रत्येक मोड कई संभावित मेमोरी एड्रेसिंग मॉडल में से एक का उपयोग कर सकता है (लेकिन हर मोड हर मॉडल का उपयोग नहीं कर सकता है), अर्थात्: रियल-मोड एड्रेसिंग, सेगमेंट एड्रेसिंग और फ्लैट-लीनियर एड्रेसिंग।

आधुनिक दुनिया में, संरक्षित या 64-बिट मोड में केवल फ्लैट-रैखिक पते प्रासंगिक हैं, और दो मोड अनिवार्य रूप से समान हैं, जिनमें मुख्य अंतर मशीन शब्द के आकार और इस प्रकार स्मृति की पता योग्य मात्रा है।

अब, मेमोरी एड्रेसिंग मोड मशीन निर्देशों के मेमोरी ऑपरेंड्स (जैसे कि mov DWORD PTR [eax], 25, जो 32-बिट (aka dword) पूर्णांक के मान को 25 मेमोरी में स्टोर करता है, जिसका पता eax32-बिट रजिस्टर में संग्रहीत होता है) को अर्थ देता है । फ्लैट-लीनियर एड्रेसिंग में, इस संख्या को eaxएक सिंगल, रिग्रेसिव रेंज पर चलाने की अनुमति है, शून्य से अधिकतम मान तक (हमारे मामले में यह 2 32  - 1 है)।

हालाँकि, फ़्लैट-लीनियर एड्रेसिंग को न तो पृष्ठांकित किया जा सकता है और न ही पृष्ठांकित किया जा सकता है । पेजिंग के बिना, पता सीधे भौतिक स्मृति को संदर्भित करता है। पेजिंग के साथ , प्रोसेसर की मेमोरी मैनेजमेंट यूनिट (या एमएमयू) पारदर्शी रूप से वांछित पते (अब वर्चुअल एड्रेस कहलाता है ) को लुकअप मेकेनिज्म, तथाकथित पेज टेबल में फीड करती है और एक नया मान प्राप्त करती है, जिसकी व्याख्या फिजिकल एड्रेस के रूप में की जाती है। मूल ऑपरेशन अब भौतिक स्मृति में इस नए, अनुवादित पते पर संचालित होता है, भले ही उपयोगकर्ता केवल आभासी पते को देखता है।

पेजिंग का मुख्य लाभ यह है कि पेज टेबल को ऑपरेटिंग सिस्टम द्वारा प्रबंधित किया जाता है। इस प्रकार ऑपरेटिंग सिस्टम पृष्ठ तालिकाओं को मनमाने ढंग से संशोधित और प्रतिस्थापित कर सकता है, जैसे कि "स्विचिंग कार्य"। यह पृष्ठ तालिकाओं का एक पूरा संग्रह रख सकता है, प्रत्येक "प्रक्रिया" के लिए, और जब भी यह निर्णय लेता है कि किसी विशेष प्रक्रिया को दिए गए सीपीयू पर चलना है, तो यह प्रक्रिया के पृष्ठ तालिकाओं को उस CPU के MMU में लोड करता है (प्रत्येक CPU का अपना स्वयं का है पृष्ठ तालिकाओं का सेट)। इसका परिणाम यह है कि प्रत्येक प्रक्रिया अपना स्वयं का वर्चुअल एड्रेस स्पेस देखती है, जो बिना देखे कि भौतिक पेज मुफ्त थे जब ओएस को इसके लिए मेमोरी आवंटित करनी थी। यह किसी भी अन्य प्रक्रिया की मेमोरी के बारे में कभी नहीं जानता है, क्योंकि यह सीधे भौतिक मेमोरी तक नहीं पहुंच सकता है।

पृष्ठ सारणी नेस्टेड ट्री जैसी डेटा संरचनाएं हैं जो सामान्य मेमोरी में संग्रहीत होती हैं, ओएस द्वारा लिखी जाती हैं, लेकिन सीधे हार्डवेयर द्वारा पढ़ी जाती हैं, इसलिए प्रारूप तय हो जाता है। वे "सीपीयू" को एक विशेष सीपीयू नियंत्रण रजिस्टर सेट-टॉप-टेबल पर इंगित करने के लिए "लोड" करते हैं। सीपीयू लुकअप को याद रखने के लिए टीएलबी नामक कैश का उपयोग करता है, इसलिए समान पृष्ठों तक बार-बार पहुंच बिखरे हुए एक्सेस की तुलना में बहुत तेज है, टीएलबी-मिस कारणों के साथ-साथ सामान्य डेटा कैश कारणों के लिए। यह देखने के लिए आम है कि शब्द "टीएलबी प्रविष्टि" का उपयोग पृष्ठ तालिका प्रविष्टियों को संदर्भित करने के लिए किया जाता है, जब वे टीएलबी में कैश नहीं होते हैं।

और यदि आप चिंता करते हैं कि एक प्रक्रिया पेजिंग को निष्क्रिय या कोशिश कर सकती है या पृष्ठ तालिकाओं को संशोधित कर सकती है: यह अनुमति नहीं है, क्योंकि x86 विशेषाधिकार विशेषाधिकार स्तर ("रिंग्स") कहा जाता है, और उपयोगकर्ता कोड एक विशेषाधिकार स्तर पर निष्पादित होता है जो अनुमति देने के लिए बहुत कम है यह सीपीयू के पेज टेबल को संशोधित करने के लिए है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.