6

जब मैं लिनक्स कर्नेल स्रोत पढ़ा, मैं कोड के इस टुकड़े भर में आया था:समारोह पैरामीटर (सी के लिए एएसएम से) एक लिनक्स कर्नेल बाधा हैंडलर में गुजर

__visible void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs) 
{ 
    struct pt_regs *old_regs = set_irq_regs(regs); 

    entering_ack_irq(); 
    local_apic_timer_interrupt(); 
    exiting_irq(); 

    set_irq_regs(old_regs); 
} 

समारोह smp_apic_timer_interrupt() एक पैरामीटर लेता है।

ENTRY(apic_timer_interrupt) 
    RING0_INT_FRAME;  
    ASM_CLAC;   
    pushl_cfi $~(0xef); 
    SAVE_ALL;   
    TRACE_IRQS_OFF 
    movl %esp,%eax; 
    call smp_apic_timer_interrupt; // <------call high level C function  
    jmp ret_from_intr;  
    CFI_ENDPROC;   
ENDPROC(apic_timer_interrupt) 

मैं समझ नहीं कैसे उच्च स्तर सी समारोह smp_apic_timer_interrupt() अपने पैरामीटर प्राप्त (जिसके द्वारा रजिस्टर): इस समारोह के बुला विधानसभा भाषा कोड का एक टुकड़ा से है?

उत्तर

3

https://www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s06.html

SAVE_ALL मैक्रो फैलता से निम्नलिखित टुकड़ा करने का हवाला देते हुए:

cld 
push %es 
push %ds 
pushl %eax 
pushl %ebp 
pushl %edi 
pushl %esi 
pushl %edx 
pushl %ecx 
pushl %ebx 
movl $ _ _USER_DS,%edx 
movl %edx,%ds 
movl %edx,%es 

रजिस्टरों सहेजने के बाद, वर्तमान शीर्ष ढेर स्थान के पते eax रजिस्टर में सहेजा गया है [साथ movl %esp,%eax, ताकि eax स्टैंक स्थान पर बिंदु को अंतिम रजिस्टर मान वाले SAVE_ALL

पर धक्का दिया गया हो

तो eax रजिस्टर वह रजिस्टर है जिसके माध्यम से smp_apic_timer_interruptpt_regs पॉइंटर प्राप्त करता है।

+1

स्रोत लगता है कि यह है, जहां वे ढेर पर संग्रहीत कर रहे हैं करने के लिए एक सूचक चाहता है तो 'eax' समझ में आता है, लेकिन न तो सवाल और न ही उत्तर लिनक्स (कर्नेल) के बारे में कुछ भी कहा है आंतरिक रूप से एक रजिस्टर-कॉल फ़ंक्शन का उपयोग I386 के लिए भी एबीआई (amd64 नहीं)। मान लें कि आप 'mov% esp,% eax' क्यों है, यह एक बेहतर जवाब होगा यदि आप उस कर्नेल दस्तावेज़ों से लिंक कर सकते हैं जो इसे समझाते हैं। अन्यथा मैं मानता हूं कि 'smp_apic_timer_interrupt' आखिरी चीज को स्टैक ('% ebx') पर अपने पहले तर्क के रूप में धक्का देगी, जो समझ में नहीं आता है। –

+0

लिनक्स कर्नेल फास्टकॉल (ईएक्स, एक्क्स, एडएक्स) बनाया गया है। –

4

आप शायद सामान्य कॉलिंग सम्मेलन (ढेर पर तर्क) सोच रहे हैं। आधुनिक लिनक्स कर्नेल (32-बिट संस्करण) रजिस्टरों में पहले 3 पैरामीटर (ईएक्स, ईसीएक्स, EDX) ऑप्टिमाइज़ेशन के रूप में पास करते हैं। कर्नेल के आधार पर इस सम्मेलन को __attribute__(regparm(3)) का उपयोग करके फ़ंक्शंस पर एक विशेषता संशोधक के रूप में निर्दिष्ट किया गया है, या कमांड लाइन पर GCC पर कर्नेल पास -mregparm=3 विकल्प का वैकल्पिक संस्करण। जीसीसीdocumentation कि विकल्प/विशेषता के बारे में इस का कहना है:

regparm (number) 

On the Intel 386, the regparm attribute causes the compiler to pass up to 
number integer arguments in registers EAX, EDX, and ECX instead of on the 
stack. Functions that take a variable number of arguments will continue to 
be passed all of their arguments on the stack. 

प्राचीन कर्नेल में सामान्य 32-बिट ABI (और स्टैक पर बहस के सम्मेलन) के आदर्श था।

config REGPARM 
    bool "Use register arguments" 
    default y 
    help 
    Compile the kernel with -mregparm=3. This instructs gcc to use 
    a more efficient function call ABI which passes the first three 
    arguments of a function call via registers, which results in denser 
    and faster code. 

    If this option is disabled, then the default ABI of passing 
    arguments via the stack is used. 

    If unsure, say Y. 

लिनक्स कर्नेल देखरेख के साथ 2006 में इस विकल्प से छुटकारा मिला: रजिस्टरों या गिरी निर्माण विन्यास में CONFIG_REGPARM सेटिंग के माध्यम से सामान्य ढेर सम्मेलन में आखिरकार गिरी विन्यास समर्थित तर्क इस kernel commit:

-mregparm=3 has been enabled by default for some time on i386, and AFAIK 
there aren't any problems with it left. 

This patch removes the REGPARM config option and sets -mregparm=3 
unconditionally. 

इस ज्ञान के आधार पर कोई आपके द्वारा प्रस्तुत किए गए कोड को देख सकता है और मान लें कि हम कर्नेल पर हैं जहां यह रजिस्टरों में पारित होने वाले पहले 3 पैरामीटर में डिफॉल्ट हो गया है।आपके मामले में:

__visible void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs) 

तो यह EAX में पारित हो जाता है एक पैरामीटर है। कोड है कि smp_apic_timer_interrupt कहा जाता है इस तरह देखा:

ENTRY(apic_timer_interrupt) 
    RING0_INT_FRAME;  
    ASM_CLAC;   
    pushl_cfi $~(0xef); 
    SAVE_ALL;   
    TRACE_IRQS_OFF 
    movl %esp,%eax; 
    call smp_apic_timer_interrupt; // <------call high level C function  
    jmp ret_from_intr;  
    CFI_ENDPROC;   
ENDPROC(apic_timer_interrupt) 

महत्वपूर्ण बात यह है कि SAVE_ALL मैक्रो कॉल स्टैक पर सभी आवश्यक रजिस्टरों धक्का है। यह संस्करण से गिरी के संस्करण के लिए अलग अलग होंगे, लेकिन ढेर पर रजिस्टरों धकेलने का मुख्य प्रभाव समान है (मैं संक्षिप्तता के लिए बौना प्रविष्टियों को हटा दिया है):

.macro SAVE_ALL 
     cld 
     PUSH_GS 
     pushl_cfi %fs 
     pushl_cfi %es 
     pushl_cfi %ds 
     pushl_cfi %eax 
     pushl_cfi %ebp 
     pushl_cfi %edi 
     pushl_cfi %esi 
     pushl_cfi %edx 
     pushl_cfi %ecx 
     pushl_cfi %ebx 
     movl $(__USER_DS), %edx 
     movl %edx, %ds 
     movl %edx, %es 
     movl $(__KERNEL_PERCPU), %edx 
     movl %edx, %fs 
     SET_KERNEL_GS %edx 
.endm 

जब ईएसपी पूरा होगा उस स्थान पर इंगित करें जहां अंतिम रजिस्टर धक्का दिया गया था। उस पते को ईएक्स पर movl %esp,%eax के साथ कॉपी किया गया है, और ईएक्सstruct pt_regs *regs के लिए सूचक बन गया है। ढेर पर सभी धक्का रजिस्ट्रार वास्तविक pt_regs डेटा संरचना बन जाते हैं, और ईएक्स अब इसे इंगित करता है।

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

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) 

कहाँ regparm(0) का कहना है कि कोई पैरामीटर रजिस्टर के माध्यम से पारित हो जाएगा।

एक सच में पता है कि निर्माण विकल्प हैं, और कर्नेल के संस्करण सम्मेलन का सटीक आकलन इस्तेमाल किया जा रहा बनाने के लिए इस्तेमाल किया जा रहा।

संबंधित मुद्दे