वास्तव में __attribute __ ((कंस्ट्रक्टर)) कैसे काम करता है?


348

यह स्पष्ट है कि यह चीजों को स्थापित करने वाला है।

  1. वास्तव में यह कब चलता है?
  2. दो कोष्ठक क्यों हैं?
  3. है __attribute__एक समारोह? एक मैक्रो? वाक्य - विन्यास?
  4. C में यह काम करता है? सी ++?
  5. क्या यह जिस कार्य के साथ काम करता है वह स्थिर होना चाहिए?
  6. कब __attribute__((destructor))चलता है?

उद्देश्य-सी में उदाहरण :

__attribute__((constructor))
static void initialize_navigationBarImages() {
  navigationBarImages = [[NSMutableDictionary alloc] init];
}

__attribute__((destructor))
static void destroy_navigationBarImages() {
  [navigationBarImages release];
}

जवाबों:


273
  1. यह तब चलता है जब एक साझा पुस्तकालय लोड होता है, आमतौर पर प्रोग्राम स्टार्टअप के दौरान।
  2. इस प्रकार सभी GCC विशेषताएँ हैं; संभवतः उन्हें फ़ंक्शन कॉल से अलग करना है।
  3. जीसीसी-विशिष्ट सिंटैक्स।
  4. हां, यह C और C ++ में काम करता है।
  5. नहीं, फ़ंक्शन को स्थिर होने की आवश्यकता नहीं है।
  6. जब साझा लाइब्रेरी अनलोड हो जाती है, तो विध्वंसक रन होता है, आमतौर पर प्रोग्राम से बाहर निकलने पर।

तो, जिस तरह से कंस्ट्रक्टर और डिस्ट्रक्टर्स काम करते हैं, वह यह है कि शेयर्ड ऑब्जेक्ट फाइल में विशेष सेक्शन (.ctors और ELF पर .dtors) शामिल हैं, जिसमें क्रमशः कंस्ट्रक्टर और डिस्ट्रॉक्टर विशेषताओं के साथ चिह्नित फ़ंक्शंस का संदर्भ होता है। जब लायब्रेरी को लोड / अनलोड किया जाता है तो डायनेमिक लोडर प्रोग्राम (ld.so या somesuch) चेक करता है कि क्या इस तरह के सेक्शन मौजूद हैं, और यदि ऐसा है, तो इसमें संदर्भित फ़ंक्शन को कॉल करता है।

यह सोचने के लिए आइए, सामान्य स्थिर लिंकर में शायद कुछ ऐसा ही जादू है, ताकि यदि उपयोगकर्ता स्थिर या गतिशील लिंकिंग का चयन करता है तो समान कोड स्टार्टअप / शटडाउन पर चलाया जाता है।


49
डबल ब्रैकेट उन्हें "मैक्रो आउट" ( #define __attribute__(x)) के लिए आसान बनाते हैं । यदि आपके पास कई गुण हैं, उदाहरण के लिए __attribute__((noreturn, weak)), यह "मैक्रो आउट" के लिए कठिन होगा यदि केवल ब्रैकेट्स का एक सेट था।
क्रिस जस्टर-यंग

7
यह नहीं किया जाता है .init/.fini। (आप वैध रूप से एक ही अनुवाद इकाई में कई कंस्ट्रक्टर और विध्वंसक हो सकते हैं, एक पुस्तकालय में एकाधिक को कभी नहीं - यह काम करेगा?) इसके बजाय, ELF बाइनरी प्रारूप (लिनक्स, आदि) का उपयोग करने वाले प्लेटफार्मों पर, कंस्ट्रक्टर और विध्वंसक को संदर्भित किया जाता है। में .ctorsऔर .dtorsहेडर के वर्गों। सच है, पुराने दिनों में, नामित कार्य initऔर finiगतिशील लाइब्रेरी लोड और अनलोड होने पर चलाए जाएंगे यदि वे मौजूद थे, लेकिन अब इसे बेहतर तंत्र द्वारा प्रतिस्थापित किया गया है।
ईपीमिएंट

7
@jcayzac नहीं, क्योंकि वेरैडिक मैक्रोज़ एक gcc एक्सटेंशन है, और मैक्रो करने का मुख्य कारण __attribute__यह है कि यदि आप gcc का उपयोग नहीं कर रहे हैं, तो उसके बाद से भी gcc एक्सटेंशन है।
क्रिस जस्टर-यंग

9
@ क्रिसजेस्टर-यंग वेरैडिक मैक्रो एक मानक C99 फीचर है, जीएनयू एक्सटेंशन नहीं।
jcayzac

4
"वर्तमान काल का आपका उपयोग" "बना" के बजाय "बना" - डबल परेंस अभी भी उन्हें मैक्रो से बाहर करना आसान बनाता है। आपने गलत पांडित्य के पेड़ को
काट दिया

64

.init/ .finiघटाया नहीं है। यह अभी भी ईएलएफ मानक का हिस्सा है और मैं कहूंगा कि यह हमेशा के लिए होगा। कोड इन .init/ .finiलोडर / रनटाइम-लिंकर द्वारा चलाया जाता है जब कोड लोड / अनलोड किया जाता है। प्रत्येक ईएलएफ लोड पर (उदाहरण के लिए एक साझा पुस्तकालय) कोड .initचलाया जाएगा। यह उसी तंत्र का उपयोग करना अभी भी संभव है, जैसा कि उसी चीज़ के बारे में प्राप्त करना __attribute__((constructor))/((destructor))। यह पुराना स्कूल है, लेकिन इसके कुछ फायदे हैं।

.ctors/ .dtorsउदाहरण के लिए तंत्र को सिस्टम-आरटीएल / लोडर / लिंकर-स्क्रिप्ट द्वारा समर्थन की आवश्यकता होती है। यह सभी प्रणालियों पर उपलब्ध निश्चित से बहुत दूर है, उदाहरण के लिए गहराई से एम्बेडेड सिस्टम जहां कोड नंगे धातु पर निष्पादित होता है। Ie भले ही __attribute__((constructor))/((destructor))GCC द्वारा समर्थित हो, यह निश्चित नहीं है कि इसे चलाने के लिए लिंकर तक और इसे चलाने के लिए लोडर (या कुछ मामलों में, बूट-कोड) पर चलना होगा। उपयोग करने के लिए .init/ .finiबजाय, सबसे आसान तरीका लिंकर झंडे का उपयोग करना है: -init -fini (यानी जीसीसी कमांड लाइन से, सिंटैक्स होगा -Wl -init my_init -fini my_fini)।

दोनों तरीकों का समर्थन करने वाले सिस्टम पर, एक संभावित लाभ यह है कि कोड .initपहले चलाया जाता है .ctorsऔर .finiबाद में कोड .dtors। यदि आदेश प्रासंगिक है तो कम से कम एक क्रूड लेकिन आसान तरीका है जो इनिट / एग्जिट फ़ंक्शंस के बीच अंतर करता है।

एक बड़ी कमी यह है कि आप आसानी से प्रत्येक लोड करने योग्य मॉड्यूल के प्रति एक _initऔर एक से अधिक _finiकार्य नहीं कर सकते हैं और संभवतः .soप्रेरित से अधिक में कोड को टुकड़े करना होगा । एक और यह है कि ऊपर वर्णित लिंकर विधि का उपयोग करते समय, कोई मूल _init और _finiडिफ़ॉल्ट फ़ंक्शन (द्वारा प्रदान crti.o) को प्रतिस्थापित करता है । यह वह जगह है जहां आमतौर पर सभी प्रकार के आरम्भिकरण होते हैं (लिनक्स पर यह वह जगह है जहाँ वैश्विक चर असाइनमेंट को आरंभीकृत किया जाता है)। चारों ओर एक रास्ता यहाँ वर्णित है

ऊपर दिए गए लिंक में ध्यान दें कि मूल _init()में कैस्केडिंग की आवश्यकता नहीं है क्योंकि यह अभी भी जगह में है। callइनलाइन विधानसभा तथापि में 86-स्मरक और एक समारोह विधानसभा (उदाहरण के लिए एआरएम) की तरह कई अन्य आर्किटेक्चर के लिए पूरी तरह से अलग दिखेगा से फोन कर रहा है। Ie कोड पारदर्शी नहीं है।

.init/ .finiऔर .ctors/ .detorsतंत्र समान हैं, लेकिन काफी नहीं। में कोड .init/ .finiरन "जैसा है"। यानी आपके पास .init/ में कई कार्य हो सकते हैं .fini, लेकिन कई छोटी .soफ़ाइलों में कोड को तोड़ने के बिना पूरी तरह से पारदर्शी सी में उन्हें पूरी तरह से पारदर्शी रूप से वहां रखना मुश्किल है ।

.ctors/ .dtorsसे अलग आयोजित किए जाते हैं .init/ .fini.ctors/ .dtorsअनुभाग दोनों फ़ंक्शन के साथ संकेत के साथ सिर्फ टेबल हैं, और "कॉलर" एक सिस्टम-प्रदत्त लूप है जो प्रत्येक फ़ंक्शन को अप्रत्यक्ष रूप से कॉल करता है। यानी लूप-कॉलर आर्किटेक्चर विशिष्ट हो सकता है, लेकिन जैसा कि यह सिस्टम का हिस्सा है (यदि यह सभी पर मौजूद है) तो कोई फर्क नहीं पड़ता।

निम्नलिखित स्निपेट .ctorsफ़ंक्शन सरणी में नए फ़ंक्शन पॉइंटर्स जोड़ता है , मुख्य रूप से उसी तरह से __attribute__((constructor))होता है (विधि के साथ सह-अस्तित्व हो सकता है) __attribute__((constructor)))

#define SECTION( S ) __attribute__ ((section ( S )))
void test(void) {
   printf("Hello\n");
}
void (*funcptr)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;

एक फ़ंक्शन पॉइंटर्स को एक पूरी तरह से अलग-अलग स्व-आविष्कृत अनुभाग में भी जोड़ सकता है। एक संशोधित लिंकर स्क्रिप्ट और लोडर .ctors/ .dtorsलूप की नकल करने वाले एक अतिरिक्त फ़ंक्शन की आवश्यकता होती है। लेकिन इसके साथ ही कोई भी निष्पादन आदेश पर बेहतर नियंत्रण प्राप्त कर सकता है, इन-तर्क और रिटर्न कोड हैंडलिंग एटा (उदाहरण के लिए एक C ++ परियोजना में, यह वैश्विक निर्माणकर्ताओं के पहले या बाद में चलने वाली किसी चीज़ की आवश्यकता में उपयोगी होगा)।

__attribute__((constructor))/((destructor))जहां संभव हो, मैं पसंद करूंगा , यह एक सरल और सुरुचिपूर्ण समाधान है यहां तक ​​कि यह धोखा देने जैसा लगता है। अपने जैसे नंगे-धातु कोडर के लिए, यह हमेशा एक विकल्प नहीं है।

पुस्तक लिंकर्स और लोडर में कुछ अच्छे संदर्भ ।


लोडर उन कार्यों को कैसे कह सकता है? वे फ़ंक्शन प्रक्रिया पता स्थान में ग्लोबल्स और अन्य फ़ंक्शन का उपयोग कर सकते हैं, लेकिन लोडर अपने स्वयं के पते स्थान के साथ एक प्रक्रिया है, क्या यह नहीं है?
192 बजे user2162550

@ user2162550 नहीं, ld-linux.so.2 (सामान्य "दुभाषिया", गतिशील पुस्तकालयों के लिए लोडर जो सभी गतिशील रूप से जुड़े निष्पादकों पर चलता है) निष्पादन योग्य के बहुत ही पते वाले स्थान में चलता है। सामान्य तौर पर डायनेमिक लाइब्रेरी लोडर स्वयं थ्रेड के संदर्भ में चलने वाले यूजरस्पेस के लिए कुछ विशिष्ट है, जो लाइब्रेरी संसाधन तक पहुंचने का प्रयास करता है।
पॉल स्टेलियन

जब मैं कोड को निष्पादित () से कहता हूं __attribute__((constructor))/((destructor))जिसमें विध्वंसक नहीं है। मैंने कुछ चीज़ें आज़माईं जैसे कि .dtor में एक प्रविष्टि जोड़ना जैसा कि ऊपर दिखाया गया है। लेकिन कोई सफलता नहीं मिली। कोड को संकलित करके चलाने में समस्या का सामना करना आसान है। उदाहरण के लिए, मान लें कि test_code में डिस्ट्रक्टर होता है (समस्या को डीबग करने के लिए कंस्ट्रक्टर और डिस्क्ट्रोक्टर के कार्यों में एक प्रिंटफ़ जोड़ें)। फिर चला LD_PRELOAD=./test_code numactl -N 0 sleep 1। आप देखेंगे कि कंस्ट्रक्टर को दो बार कहा जाता है लेकिन विध्वंसक केवल एक बार।
बी अबाली

39

यह पृष्ठ विशेषता और कार्यान्वयन के बारे में constructorऔर destructorईएलएफ के भीतर उन अनुभागों के बारे में बहुत अच्छी समझ प्रदान करता है जो उन्हें काम करने की अनुमति देते हैं। यहाँ प्रदान की गई जानकारी को पचाने के बाद, मैंने कुछ अतिरिक्त जानकारी संकलित की और (ऊपर से माइकल अम्ब्रस से अनुभाग उदाहरण उधार लेते हुए) ने अवधारणाओं को चित्रित करने और मेरी सीखने में मदद करने के लिए एक उदाहरण बनाया। वे परिणाम नीचे दिए गए हैं उदाहरण स्रोत के साथ।

जैसा कि इस थ्रेड में बताया गया है, constructorऔर destructorविशेषताएँ ऑब्जेक्ट फ़ाइल के .ctorsऔर .dtorsअनुभाग में प्रविष्टियाँ बनाते हैं । आप किसी भी कार्य के संदर्भों को तीन में से किसी एक भाग में रख सकते हैं। (1) या तो sectionविशेषता का उपयोग करना ; (2) constructorऔर destructorइनलाइन-असेंबली कॉल के साथ विशेषताएँ (या 3) (जैसा कि अम्ब्रस के उत्तर में लिंक को संदर्भित किया गया है)।

के उपयोग constructorऔर destructorविशेषताओं से आप अतिरिक्त रूप से निर्माता / विध्वंसक को प्राथमिकता प्रदान कर सकते हैं कि main()वह वापस आने से पहले या बाद में इसके निष्पादन के क्रम को नियंत्रित कर सके । दी गई प्राथमिकता का मूल्य जितना कम होगा, निष्पादन की प्राथमिकता उतनी ही कम होगी (कम प्राथमिकताएं मुख्य प्राथमिकताओं से पहले निष्पादित होती हैं) - (और बाद में उच्च प्राथमिकताएं)। प्राथमिकता मान जो आप देते हैं वह संकलक से अधिक होना चाहिए100 क्योंकि कार्यान्वयन के लिए संकलक 0-100 के बीच प्राथमिकता मान रखता है। प्राथमिकता के साथ ए constructorया destructorनिर्दिष्ट प्राथमिकता के बिना निर्दिष्ट constructorया पहले निष्पादित होता destructorहै।

'अनुभाग' विशेषता के साथ या के साथ इनलाइन विधानसभा, आप में भी जगह समारोह संदर्भ कर सकते हैं .initऔर .finiELF कोड अनुभाग है कि किसी भी निर्माता से पहले और किसी भी नाशक के बाद क्रमश: निष्पादित करेंगे। .initअनुभाग में रखे गए फ़ंक्शन संदर्भ द्वारा बुलाए गए किसी भी फ़ंक्शन को फ़ंक्शन संदर्भ से पहले ही निष्पादित किया जाएगा (हमेशा की तरह)।

मैंने नीचे दिए उदाहरण में उनमें से प्रत्येक को चित्रित करने की कोशिश की है:

#include <stdio.h>
#include <stdlib.h>

/*  test function utilizing attribute 'section' ".ctors"/".dtors"
    to create constuctors/destructors without assigned priority.
    (provided by Michael Ambrus in earlier answer)
*/

#define SECTION( S ) __attribute__ ((section ( S )))

void test (void) {
printf("\n\ttest() utilizing -- (.section .ctors/.dtors) w/o priority\n");
}

void (*funcptr1)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;

/*  functions constructX, destructX use attributes 'constructor' and
    'destructor' to create prioritized entries in the .ctors, .dtors
    ELF sections, respectively.

    NOTE: priorities 0-100 are reserved
*/
void construct1 () __attribute__ ((constructor (101)));
void construct2 () __attribute__ ((constructor (102)));
void destruct1 () __attribute__ ((destructor (101)));
void destruct2 () __attribute__ ((destructor (102)));

/*  init_some_function() - called by elf_init()
*/
int init_some_function () {
    printf ("\n  init_some_function() called by elf_init()\n");
    return 1;
}

/*  elf_init uses inline-assembly to place itself in the ELF .init section.
*/
int elf_init (void)
{
    __asm__ (".section .init \n call elf_init \n .section .text\n");

    if(!init_some_function ())
    {
        exit (1);
    }

    printf ("\n    elf_init() -- (.section .init)\n");

    return 1;
}

/*
    function definitions for constructX and destructX
*/
void construct1 () {
    printf ("\n      construct1() constructor -- (.section .ctors) priority 101\n");
}

void construct2 () {
    printf ("\n      construct2() constructor -- (.section .ctors) priority 102\n");
}

void destruct1 () {
    printf ("\n      destruct1() destructor -- (.section .dtors) priority 101\n\n");
}

void destruct2 () {
    printf ("\n      destruct2() destructor -- (.section .dtors) priority 102\n");
}

/* main makes no function call to any of the functions declared above
*/
int
main (int argc, char *argv[]) {

    printf ("\n\t  [ main body of program ]\n");

    return 0;
}

उत्पादन:

init_some_function() called by elf_init()

    elf_init() -- (.section .init)

    construct1() constructor -- (.section .ctors) priority 101

    construct2() constructor -- (.section .ctors) priority 102

        test() utilizing -- (.section .ctors/.dtors) w/o priority

        test() utilizing -- (.section .ctors/.dtors) w/o priority

        [ main body of program ]

        test() utilizing -- (.section .ctors/.dtors) w/o priority

    destruct2() destructor -- (.section .dtors) priority 102

    destruct1() destructor -- (.section .dtors) priority 101

उदाहरण ने निर्माता / विध्वंसक व्यवहार को मजबूत करने में मदद की, उम्मीद है कि यह दूसरों के लिए भी उपयोगी होगा।


आपने कहां पाया कि "आपके द्वारा दिए जाने वाले प्राथमिकता मूल्य 100 से अधिक होने चाहिए"? यह जानकारी GCC फ़ंक्शन विशेषता प्रलेखन
जस्टिन

4
IIRC, संदर्भ के कुछ जोड़े थे, PATCH: निर्माण / विध्वंसक तर्कों ( MAX_RESERVED_INIT_PRIORITY) के लिए प्राथमिकता प्राथमिकता तर्क , और वे C ++ ( init_priority) 7.7 C ++ - विशिष्ट चर, कार्य और प्रकार गुण के समान थे । तो फिर मैं इसके साथ की कोशिश की 99: warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
डेविड सी। रैंकिन

1
आह। मैंने क्लैंग के साथ प्राथमिकताएं <100 की कोशिश की और यह काम करने लगा, लेकिन मेरा सरल परीक्षण मामला (एक संकलन इकाई) बहुत सरल था
जस्टिन

1
स्थिर वैश्विक चरों (स्टैटिक सीटर्स) की प्राथमिकता क्या है?
द्वादशी d

2
एक स्थिर वैश्विक का प्रभाव और दृश्यता इस बात पर निर्भर करेगी कि आपका कार्यक्रम कैसे संरचित है (उदाहरण के लिए एकल फ़ाइल, कई फाइलें ( अनुवाद इकाइयां )) और जिसमें वैश्विक घोषित किया गया है देखें: स्टेटिक (कीवर्ड) , विशेष रूप से स्टेटिक वैश्विक चर विवरण।
डेविड सी। रंकिन

7

यहाँ एक "ठोस" (और संभवतः उपयोगी ) उदाहरण है कि कैसे, क्यों, और कब इन काम का उपयोग करना है, फिर भी भद्दा निर्माण करता है ...

Xcode एक "वैश्विक" "उपयोगकर्ता डिफ़ॉल्ट" का उपयोग करता है यह तय करने के लिए कि कौन से XCTestObserverवर्ग के लिए यह शोकसंतृप्त कंसोल से बाहर है ।

इस उदाहरण में ... जब मैंने इस पुसीडो-लाइब्रेरी को स्पष्ट रूप से लोड किया है, तो आइए इसे कॉल करें ... libdemure.a, अपने परीक्षण लक्ष्य में एक ध्वज के माध्यम से।

OTHER_LDFLAGS = -ldemure

में चाहता हूं..

  1. लोड होने पर (यानी जब XCTestमेरा परीक्षण बंडल लोड होता है), "डिफ़ॉल्ट" XCTest"ऑब्जर्वर" वर्ग को ओवरराइड करें ... ( constructorफ़ंक्शन के माध्यम से ) PS: जहां तक ​​मैं बता सकता हूं .. यहां किया गया कुछ भी मेरे अंदर समान प्रभाव के साथ किया जा सकता है कक्षा ' + (void) load { ... }विधि।

  2. मेरे परीक्षण चलाएं .... इस मामले में, लॉग में कम अपर्याप्तता के साथ (अनुरोध पर कार्यान्वयन)

  3. "वैश्विक" XCTestObserverवर्ग को यह प्राचीन स्थिति में लौटाएं .. ताकि अन्य XCTestरनों को बेईमानी न करें जो कि बैंडवागन (उर्फ से जुड़ा हुआ libdemure.a) पर नहीं हुआ है । मुझे लगता है कि यह ऐतिहासिक रूप से किया गया था dealloc.. लेकिन मैं उस पुराने हग के साथ खिलवाड़ शुरू करने वाला नहीं हूं।

इसलिए...

#define USER_DEFS NSUserDefaults.standardUserDefaults

@interface      DemureTestObserver : XCTestObserver @end
@implementation DemureTestObserver

__attribute__((constructor)) static void hijack_observer() {

/*! here I totally hijack the default logging, but you CAN
    use multiple observers, just CSV them, 
    i.e. "@"DemureTestObserverm,XCTestLog"
*/
  [USER_DEFS setObject:@"DemureTestObserver" 
                forKey:@"XCTestObserverClass"];
  [USER_DEFS synchronize];
}

__attribute__((destructor)) static void reset_observer()  {

  // Clean up, and it's as if we had never been here.
  [USER_DEFS setObject:@"XCTestLog" 
                forKey:@"XCTestObserverClass"];
  [USER_DEFS synchronize];
}

...
@end

लिंकर ध्वज के बिना ... (फैशन-पुलिस झुंड क्यूपर्टिनो प्रतिशोध की मांग करते हैं , फिर भी एप्पल की डिफ़ॉल्ट प्रबल होती है, जैसा कि वांछित है, यहां देखें )

यहां छवि विवरण दर्ज करें

साथ -ldemure.aलिंकर झंडा ... (सुबोध परिणाम, हांफी ... "धन्यवाद constructor/ destructor" ... भीड़ चियर्स ) यहां छवि विवरण दर्ज करें


1

यहाँ एक और ठोस उदाहरण दिया गया है। यह एक साझा पुस्तकालय के लिए है। साझा लाइब्रेरी का मुख्य कार्य स्मार्ट कार्ड रीडर के साथ संवाद करना है। लेकिन यह udp पर रनटाइम पर 'कॉन्फ़िगरेशन जानकारी' भी प्राप्त कर सकता है। Udp को एक थ्रेड द्वारा नियंत्रित किया जाता है जिसे Mit समय पर शुरू किया जाना चाहिए

__attribute__((constructor))  static void startUdpReceiveThread (void) {
    pthread_create( &tid_udpthread, NULL, __feigh_udp_receive_loop, NULL );
    return;

  }

पुस्तकालय c में लिखा गया था।


1
लाइब्रेरी में C ++ में लिखा जाने वाला एक अजीब विकल्प है, क्योंकि साधारण ग्लोबल वैरिएबल कंस्ट्रक्टर्स C ++ में कोड प्री-मेन चलाने के लिए मुहावरेदार तरीका है।
निकोलस विल्सन

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