2013-04-22 13 views
5

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

मैं एक मुख्य प्रोग्राम शुरू करने के लिए क्या करना चाहता हूं, एक स्थान malloc, उपयोगकर्ता स्तर के धागे को शुरू करने के लिए इसे एक ढेर के रूप में उपयोग करें। मेरी समस्या वापसी पते के साथ है। यहाँ कोड अब तक बताया गया है:

[मैं अपने कोड का संपादन कर रहा हूँ इसे करने की तारीख मेरा उत्तर की वर्तमान स्थिति के लिए बनाने के लिए]

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

#define STACK_SIZE 512 

void switch_thread(int*,int*); 

int k = 0; 

void simple_function() 
{ 
    printf("I am the function! k is: %d\n",k); 
    exit(0); 
} 

void create_thread(void (*function)()) 
{ 
    int* stack = malloc(STACK_SIZE + 32); 
    stack = (int*)(((long)stack & (-1 << 4)) + 0x10); 
    stack = (int*) ((long)stack + STACK_SIZE); 
    *stack = (long) function; 
    switch_thread(stack,stack); 
} 

int main() 
{ 
    create_thread(simple_function); 
    assert(0); 
    return 0; 
} 

switch_thread एक विधानसभा कोड मैं के रूप में लिखा गया है इस प्रकार है:

.text 
    .globl switch_thread 
switch_thread: 
    movq %rdi, %rsp 
    movq %rsi, %rbp 
    ret 

इस कोड GDB के तहत अच्छी तरह से वास्तव में चलाता है और उम्मीद उत्पादन देता है (जो, simple_function और मुद्रण करने के लिए नियंत्रण गुजर "मैं समारोह हूँ कश्मीर है: 0"। लेकिन जब अलग से चलाने, इस एक सेगमेंटेशन गलती देता है। मैं इस परिणाम से परेशान हूं।

किसी भी मदद की सराहना की जाएगी। अग्रिम में धन्यवाद।

+0

क्या आपको नहीं लगता कि आपको फ़ंक्शन कॉल पर अन्य रजिस्टरों की परवाह करनी चाहिए? – Alex

+2

अपने स्टैक को 16 तक संरेखित करना न भूलें। –

+0

@AkiSuihkonen यह एक बिंदु है जिसे मैं स्पष्ट करना चाहता हूं। क्या आप कृपया बता सकते हैं कि इसे 16 तक क्यों गठबंधन किया जाना चाहिए? – user2290802

उत्तर

7

दो अपने कोड के साथ समस्याओं:

  1. जब तक आपकी धागा एक उचित प्रक्रिया (या एक नेस्टेड प्रक्रिया) के अंदर वास्तव में है, वहाँ "आधार सूचक" जैसी कोई चीज नहीं है। यह% आरबीपी अप्रासंगिक का मान बनाता है क्योंकि थ्रेड प्रारंभिक बिंदु पर किसी विशेष प्रक्रिया के अंदर नहीं है।

  2. आपके विचार के विपरीत, जब ret निर्देश निष्पादित हो जाता है, तो वह मान जो% आरएसपी संदर्भित करता है वह प्रोग्राम काउंटर का नया मान बन जाता है। इसका मतलब यह है कि , *(base_pointer) के बजाय इसे निष्पादित होने पर परामर्श दिया जाएगा। फिर,% आरबीपी का मान यहां अप्रासंगिक है।

आपका कोड (कम से कम संशोधन के साथ इसे चलाने बनाने के लिए) इस तरह दिखना चाहिए:

void switch_thread(int* stack_pointer,int* entry_point); 

void create_thread(void (*function)()) 
{ 
    int* stack_pointer = malloc(STACK_SIZE + 8); 
    stack_pointer += STACK_SIZE; //you'd probably want to back up the original allocated address if you intend to free it later for any reason. 
    switch_thread(stack_pointer,function);  
} 

आपका switch_thread दिनचर्या इस तरह दिखना चाहिए:

.text 
    .globl switch_thread 
switch_thread: 
    mov  %rsp, %rax //move the original stack pointer to a scratch register 
    mov  %rdi, %rsp //set stack pointer 
    push %rax  //back-up the original stack pointer 
    call %rsi  //call the function 
    pop  %rsp  //restore the original stack pointer 
    ret    //return to create_thread 

जानकारी के लिए: यदि आप ' अपने आप पर एक थ्रेड शुरू कर रहे हैं, मेरा सुझाव है कि आप पहले एक उचित ट्रैम्पोलिन बनाएं जो थ्रेड एंट्री पॉइंट (जैसे एनटीएलएल के आरटीएलयूसर थ्रेडस्टार्ट) के रूप में कार्य करता है। यह चीजों को अधिक स्वच्छ बना देगा, खासकर यदि आप अपने प्रोग्राम को बहुप्रचारित बनाना चाहते हैं और प्रारंभिक दिनचर्या में किसी भी पैरामीटर में भी पास करना चाहते हैं।

+1

आपको सी प्रोग्राम में 'malloc' के वापसी मूल्य को डालने की आवश्यकता नहीं है। –

+0

आपकी व्याख्या बहुत स्पष्ट नहीं है। क्या आप इसे फिर से लिख सकते हैं? साथ ही, कॉल% आरएसआई निर्देश का उपयोग क्या है? – user2290802

+1

@ user2290802% ebp केवल तभी परामर्श किया जाता है जब आप वास्तव में किसी विशेष प्रक्रिया के लिए स्टैक फ्रेम का निर्माण/नष्ट कर रहे होते हैं, यानी जब आप 'ENTER/LEAVE' निर्देशों (या समकक्ष प्रक्रिया प्रस्तावना/epilogue) निष्पादित कर रहे हैं। आपके switch_thread फ़ंक्शन में, ऐसा कोई फ्रेम नहीं है (और आपको किसी भी तरह की आवश्यकता नहीं है)। % आरएसआई रजिस्टर के मूल्य के लिए, यह सूचक को 'simple_function()' पर रखेगा। – JosephH

0

base_pointer को void (*)() मानों को स्टोर करने के लिए उपयुक्त रूप से गठबंधन करने की आवश्यकता है, अन्यथा आप अपरिभाषित व्यवहार से निपट रहे हैं। मुझे लगता है कि आपका मतलब कुछ है:

void create_thread(void (*function)()) 
{ 
    size_t offset = STACK_SIZE + sizeof function - STACK_SIZE % sizeof function; 
    char *stack_pointer = malloc(offset + sizeof *base_pointer); 
    void (**base_pointer)() = stack_pointer + offset; 
    *base_pointer = function; 
    switch_thread(stack_pointer,base_pointer);  
} 

मॉलोक डालने की कोई आवश्यकता नहीं है। पॉइंटर्स प्रकारों को ऑब्जेक्ट करने के लिए पॉइंटर्स को पूर्णांक प्रकारों या फ़ंक्शन पॉइंटर्स पर डालना आम तौर पर एक बुरा विचार है।

मैं समझता हूं कि यह सभी पोर्टेबल-सी नाइट-पिक सलाह है, लेकिन यह वास्तव में अपरिभाषित व्यवहार पर निर्भर होने के बजाय पोर्टेबल कोड में जितना संभव हो उतना सॉफ्टवेयर लिखने में मदद करता है।

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