C में _start () का क्या उपयोग है?


125

मैंने अपने सहकर्मी से सीखा कि एक सी प्रोग्राम को बिना किसी main()फंक्शन को लिखे और निष्पादित कर सकता है। इसे इस तरह किया जा सकता है:

my_main.c

/* Compile this with gcc -nostartfiles */

#include <stdlib.h>

void _start() {
  int ret = my_main();
  exit(ret); 
}

int my_main() {
  puts("This is a program without a main() function!");
  return 0; 
}

इसे इस कमांड के साथ संकलित करें:

gcc -o my_main my_main.c nostartfiles

इसे इस कमांड से चलाएं:

./my_main

इस तरह की बात करने की आवश्यकता कब होगी? क्या कोई वास्तविक विश्व परिदृश्य है जहां यह उपयोगी होगा?



7
क्लासिक लेख जो प्रोग्रामों को शुरू करने के कुछ आंतरिक कामकाज को प्रदर्शित करता है: लिनक्स के लिए वास्तव में किशोर ईएलएफ निष्पादन बनाने पर एक बवंडर ट्यूटोरियल । यह एक अच्छा पढ़ा गया है जो _start()बाहर और अन्य सामानों के कुछ बेहतर बिंदुओं पर चर्चा करता है main()

1
C भाषा खुद कहती है _start, या इसके अलावा किसी भी प्रविष्टि बिंदु के बारे में कुछ भी नहीं है main(सिवाय इसके कि प्रविष्टि बिंदु का नाम कार्यान्वयन-परिभाषित है जो फ्रीस्टैंडिंग (एम्बेडेड) कार्यान्वयन के लिए है)।
कीथ थॉम्पसन

जवाबों:


107

प्रतीक _startहै प्रवेश बिंदु अपने कार्यक्रम की। यही है, उस प्रतीक का पता प्रोग्राम स्टार्ट पर जम्प किया हुआ पता है। आम तौर पर, नाम के साथ फ़ंक्शन को _startफ़ाइल नाम से आपूर्ति की जाती है crt0.oजिसमें सी रनटाइम वातावरण के लिए स्टार्टअप कोड होता है। यह कुछ सामान सेट करता है, तर्क सरणी को पॉप्युलेट करता है argv, गिनता है कि कितने तर्क हैं, और फिर कॉल करता है mainmainरिटर्न के बाद , exitकहा जाता है।

यदि कोई प्रोग्राम C रनटाइम वातावरण का उपयोग नहीं करना चाहता है, तो उसे इसके लिए अपने कोड की आपूर्ति करने की आवश्यकता है _start। उदाहरण के लिए, गो प्रोग्रामिंग भाषा का संदर्भ कार्यान्वयन इसलिए होता है क्योंकि उन्हें एक गैर-मानक थ्रेडिंग मॉडल की आवश्यकता होती है जिसमें स्टैक के साथ कुछ जादू की आवश्यकता होती है। _startजब आप वास्तव में छोटे कार्यक्रम या ऐसे कार्यक्रम लिखना चाहते हैं, जो अपारंपरिक चीजें करते हैं, तो अपनी आपूर्ति करना भी उपयोगी है ।


2
एक अन्य उदाहरण लिनक्स का डायनेमिक लिंकर / लोडर है जिसका अपना स्वयं का _start परिभाषित है।
पीपी

2
@BlueMoon लेकिन वह भी _startऑब्जेक्ट फ़ाइल से आता crt0.oहै।
फ़ूज

2
@ThomasMatthews मानक निर्दिष्ट नहीं करता है _start; वास्तव में, यह निर्दिष्ट नहीं करता है कि पहले क्या होता है, इसे बिल्कुल mainभी कहा जाता है, यह केवल निर्दिष्ट करता है कि जब mainकहा जाता है तो किन शर्तों को पूरा करना चाहिए । यह एंट्री पॉइंट के लिए एक कन्वेंशन है _startजो पुराने दिनों की है।
फ़ूज

1
"गो प्रोग्रामिंग भाषा का संदर्भ कार्यान्वयन इसलिए होता है क्योंकि उन्हें एक गैर-मानक थ्रेडिंग मॉडल की आवश्यकता होती है" crt0.o C विशिष्ट (crt-> C क्रम) है। यह उम्मीद करने का कोई कारण नहीं है कि इसका उपयोग किसी अन्य भाषा के लिए किया जाए। और गो का थ्रेडिंग मॉडल पूरी तरह से मानक अनुपालन है
स्टीव कॉक्स

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

45

जबकि mainप्रोग्रामर के दृष्टिकोण से आपके कार्यक्रम के लिए _startप्रवेश बिंदु, ओएस परिप्रेक्ष्य से सामान्य प्रविष्टि बिंदु है (आपके प्रोग्राम को ओएस से शुरू करने के बाद निष्पादित होने वाला पहला निर्देश)

एक विशिष्ट सी और विशेष रूप से सी ++ प्रोग्राम में, निष्पादन में प्रवेश करने से पहले बहुत सारे काम किए गए हैं। विशेष रूप से वैश्विक चर के आरंभीकरण जैसे सामान। यहां आप उन सभी चीजों की अच्छी व्याख्या पा सकते हैं जो बीच _start()- बीच में चल रही हैं main()और मुख्य रूप से फिर से बाहर निकलने के बाद (नीचे टिप्पणी देखें)।
इसके लिए आवश्यक कोड आमतौर पर कंपाइलर लेखकों द्वारा एक स्टार्टअप फ़ाइल में प्रदान किया जाता है, लेकिन ध्वज के साथ–nostartfiles आप अनिवार्य रूप से कंपाइलर को बताते हैं: "मुझे मानक स्टार्टअप फाइल देने में परेशान न करें, मुझे इस बात पर पूरा नियंत्रण दें कि क्या सही हो रहा है। शुरू"।

यह कभी-कभी आवश्यक होता है और अक्सर एम्बेडेड सिस्टम पर उपयोग किया जाता है। उदाहरण के लिए, यदि आपके पास OS नहीं है और आपको अपनी वैश्विक वस्तुओं के आरंभ से पहले अपने मेमोरी सिस्टम के कुछ हिस्सों (जैसे कैश) को मैन्युअल रूप से सक्षम करना होगा।


वैश्विक संस्करण डेटा अनुभाग का हिस्सा हैं और इस प्रकार कार्यक्रम की लोडिंग के दौरान सेटअप होते हैं (यदि वे हैं तो वे पाठ अनुभाग का हिस्सा हैं, वही कहानी)। _Start फ़ंक्शन पूरी तरह से असंबंधित है।
चीयर्स

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

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

@zwol यह केवल आंशिक रूप से सही है। उदाहरण के लिए, ऐसा फ़ंक्शन मेमोरी को आवंटित कर सकता है। स्मृति को आवंटित करना तब समस्याग्रस्त होता है जब आंतरिक डेटा संरचनाओं को mallocप्रारंभ नहीं किया जाता है।
फ़ूज

1
@FUZxxl ही कहा है, मुझे लगता है कि async-संकेत सुरक्षित कार्यों नोटिस कर रहे हैं संशोधित करने की अनुमति errno(जैसे readऔर writeasync-संकेत सुरक्षित हैं और उन्हें सेट कर सकते हैं errno) और कहा कि क़यास बिल्कुल के आधार पर एक समस्या है जब प्रति-धागा हो सकता है errnoस्थान आवंटित किया जाता है ।
zwol

2

यहाँ कार्यक्रम स्टार्टअप के दौरान पहले क्या होता है का एक अच्छा अवलोकन है main। विशेष रूप से, यह पता चलता है कि __startहै वास्तविक प्रवेश बिंदु ओएस दृष्टिकोण से अपने कार्यक्रम के लिए।

यह पहला पता है जिसमें से निर्देश सूचक आपके प्रोग्राम में गिनना शुरू कर देगा।

वहां का कोड कुछ हाउस रनिंग करने के लिए कुछ C रनटाइम लाइब्रेरी रूटीन को इनवॉइस करता है, फिर अपनी कॉल करें main, और फिर चीजों को नीचे लाएं और exitजो भी एग्जिट कोड mainवापस आए उसे कॉल करें ।


एक तस्वीर एक हजार शब्दों के बराबर होती है:

सी रनटाइम स्टार्टअप आरेख


पुनश्च: इस उत्तर को एक अन्य प्रश्न से प्रत्यारोपित किया जाता है जो एसओ ने इस एक के डुप्लिकेट के रूप में मदद की है।


उत्कृष्ट विश्लेषण और अच्छी तस्वीर को संरक्षित करने के लिए क्रॉस-पोस्ट किया गया ।
ulidtko

1

इस तरह की बात करने की आवश्यकता कब होगी?

जब आप अपने प्रोग्राम के लिए अपना खुद का स्टार्टअप कोड चाहते हैं।

mainसी कार्यक्रम के _startलिए पहली प्रविष्टि नहीं है, पर्दे के पीछे पहली प्रविष्टि है।

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

_start: # _start is the entry point known to the linker
    xor %ebp, %ebp            # effectively RBP := 0, mark the end of stack frames
    mov (%rsp), %edi          # get argc from the stack (implicitly zero-extended to 64-bit)
    lea 8(%rsp), %rsi         # take the address of argv from the stack
    lea 16(%rsp,%rdi,8), %rdx # take the address of envp from the stack
    xor %eax, %eax            # per ABI and compatibility with icc
    call main                 # %edi, %rsi, %rdx are the three args (of which first two are C standard) to main

    mov %eax, %edi    # transfer the return of main to the first argument of _exit
    xor %eax, %eax    # per ABI and compatibility with icc
    call _exit        # terminate the program

क्या कोई वास्तविक विश्व परिदृश्य है जहां यह उपयोगी होगा?

यदि आप मतलब है, हमारे अपने लागू करें _start:

हां, मेरे द्वारा काम किए गए अधिकांश वाणिज्यिक एम्बेडेड सॉफ़्टवेयर में, हमें _startअपनी विशिष्ट मेमोरी और प्रदर्शन आवश्यकताओं के बारे में अपने स्वयं के कार्यान्वयन की आवश्यकता है।

यदि आपका मतलब है, mainफ़ंक्शन को छोड़ें और इसे किसी और चीज़ में बदलें:

नहीं, मुझे ऐसा करने में कोई लाभ नहीं दिख रहा है।

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