2016-09-28 32 views
1

我正在嘗試創建用戶級別線程庫,但是當我嘗試設置其中一個線程的上下文時,我收到了分段錯誤錯誤。我做了我所有代碼的簡化版,並縮小了它(我認爲)到一個奇怪的事情。如果我使用malloc()爲線程上下文提供堆棧,則不起作用。奇怪的是,如果我在當前函數的堆棧中聲明一個相同大小的數組,它將按預期工作。我認爲,除了內存中的不同區域外,這兩種方法完全相同的將會是。畢竟,它們都是指向可自由使用的某些內存區域的指針。使用malloc爲上下文分配堆棧時分段,但在使用本地數組時分段

重要行是:

  • char cstack[1024];,和
  • char *cstack = (char *)malloc(1024);

無論在thread.c,如下所示。如果我評論第一個而不是第二個,則會出現分段錯誤。如果我做相反的事情,一切都好(好像)。

這是我用來測試它的程序,test.c

#include <stdlib.h> 
#include "thread.h" 

int main(int argc, char** argv) { 
    ccreate(NULL, NULL); 
    return 0; 
} 

這裏是thread.h

​​

這裏是(最重要的)thread.c

#include <stdlib.h> 
#include <stdio.h> 
#include <ucontext.h> 
#include "logging.c" 
#include "cdata.h" 
#include "thread.h" 

#undef LOGLEVEL 
#define LOGLEVEL 5 

ucontext_t m; 
int been_here; 

static TCB_t *TCB_init(int tid) { 
    floginfo("initializing TCB %d", tid); 
    TCB_t *thr = (TCB_t *)malloc(sizeof(TCB_t)); 

    thr->tid = tid; 
    thr->state = PROCST_APT; 

    flogdebug("initializing context at address %p", &(thr->context)); 
    if (getcontext(&(thr->context)) == -1) logerror("error initializing context"); 
    thr->context.uc_link = &m; 

    return thr; 
} 

static void say_hey(void) { 
    been_here = 1; 
    loginfo("hey"); 
} 

int ccreate (void* (*start)(void*), void *arg) { 
    static int last_tid = -1; 

    //char cstack[1024]; /* doesn't segfault */ 
    char *cstack = (char *)malloc(1024); /* segfaults */ 

    floginfo("creating thread %d.", last_tid + 1); 
    TCB_t *thr; 
    thr = TCB_init(++last_tid); 
    thr->context.uc_stack.ss_sp = cstack; 
    thr->context.uc_stack.ss_size = 1024; 

    been_here = 0; 
    getcontext(&m); 

    if (!been_here) { 

     flogdebug("making context at address %p", &(thr->context)); 
     logdebug("calling: makecontext(&(thr->context), say_hey, 0);"); 
     makecontext(&(thr->context), say_hey, 0); 

     flogdebug("setting context at address %p", &(thr->context)); 
     logdebug("calling: setcontext(&(thr->context))"); 
     setcontext(&(thr->context)); 
    } 
    logdebug("came back from setcontext"); 

    logdebug("exiting"); 

    return 0; 
} 

這就是TCB_t(線程控制塊)類型定義,cdata.h

#ifndef __cdata__ 
#define __cdata__ 

#define PROCST_CRE 0 
#define PROCST_APT 1 
#define PROCST_EXEC 2 
#define PROCST_BLOC 3 
#define PROCST_TERM 4 

typedef struct s_TCB { 
    int  tid; 
    int  state; 
    ucontext_t context; 
} TCB_t; 

#endif 

這裏是(非常重要,我認爲)logging.c

#include <stdio.h> 
#include <stdarg.h> 

#define LOGLEVEL 6 
#define LVL_DEBUG 5 
#define LVL_INFO 4 
#define LVL_WARNING 3 
#define LVL_ERROR 2 
#define LVL_CRITICAL 1 

void logdebug(const char *msg) { 
    if (LOGLEVEL >= LVL_DEBUG) { 
     fprintf(stderr, "DEBUG: %s\n", msg); 
    } 
} 

void loginfo(const char *msg) { 
    if (LOGLEVEL >= LVL_INFO) { 
     fprintf(stderr, "INFO:  %s\n", msg); 
    } 
} 

void logwarning(const char *msg) { 
    if (LOGLEVEL >= LVL_WARNING) { 
     fprintf(stderr, "WARNING: %s\n", msg); 
    } 
} 

void logerror(const char *msg) { 
    if (LOGLEVEL >= LVL_ERROR) { 
     fprintf(stderr, "ERROR: %s\n", msg); 
    } 
} 

void logcritical(const char *msg) { 
    if (LOGLEVEL >= LVL_CRITICAL) { 
     fprintf(stderr, "CRITICAL: %s\n", msg); 
    } 
} 

void flogdebug(const char *fmt, ...) { 
    char buff[1024]; 
    va_list args; 
    va_start(args, fmt); 
    vsprintf(buff, fmt, args); 
    va_end(args); 
    logdebug((const char *)buff); 
} 

void floginfo(const char *fmt, ...) { 
    char buff[1024]; 
    va_list args; 
    va_start(args, fmt); 
    vsprintf(buff, fmt, args); 
    va_end(args); 
    loginfo((const char *)buff); 
} 

void flogwarning(const char *fmt, ...) { 
    char buff[1024]; 
    va_list args; 
    va_start(args, fmt); 
    vsprintf(buff, fmt, args); 
    va_end(args); 
    logwarning((const char *)buff); 
} 

void flogerror(const char *fmt, ...) { 
    char buff[1024]; 
    va_list args; 
    va_start(args, fmt); 
    vsprintf(buff, fmt, args); 
    va_end(args); 
    logerror((const char *)buff); 
} 
void flogcritical(const char *fmt, ...) { 
    char buff[1024]; 
    va_list args; 
    va_start(args, fmt); 
    vsprintf(buff, fmt, args); 
    va_end(args); 
    logcritical((const char *)buff); 
} 

最後,編譯所有gcc -Wall thread.c test.c給沒有警告。如果char cstack[1024];是註釋掉,我得到這個:

INFO:  creating thread 0. 
INFO:  initializing TCB 0 
DEBUG: initializing context at address 0x16d5018 
DEBUG: making context at address 0x16d5018 
DEBUG: calling: makecontext(&(thr->context), say_hey, 0); 
DEBUG: setting context at address 0x16d5018 
DEBUG: calling: setcontext(&(thr->context)) 
INFO:  hey 
DEBUG: came back from setcontext 
DEBUG: exiting 

如果char *cstack = (char *)malloc(1024);是註釋掉,我得到這個:

INFO:  creating thread 0. 
INFO:  initializing TCB 0 
DEBUG: initializing context at address 0x12f8428 
DEBUG: making context at address 0x12f8428 
DEBUG: calling: makecontext(&(thr->context), say_hey, 0); 
DEBUG: setting context at address 0x12f8428 
DEBUG: calling: setcontext(&(thr->context)) 
Segmentation fault (core dumped) 

任何想法在所有讚賞。我大多都很困惑,並在思考我所認識的一切。

+2

因爲1024字節可能不足以滿足堆棧的要求,所以程序會因堆棧溢出而覆蓋malloc()的內部數據。另一方面,當你使用當前棧作爲緩衝區時,你的程序仍然會導致溢出,但沒有可見的效果,因爲主棧通常很大(〜4 MiB),並且溢出不會造成任何傷害。 – GreenScape

+1

在'gdb'(或者你最喜歡的調試器)和'valgrind'下運行這個函數,看看結果如何。它是你唯一沒有做過的事情,也是你可能應該做的第一件事。 – WhozCraig

+0

你正在對齊堆棧嗎?沒有「已經過」需要變化嗎? – kfsone

回答

1

根據用於此背景下this

stack_t uc_stack堆棧。該值不需要是 (通常不是)堆棧指針。請參閱第24.9節使用 單獨的信號堆棧。

this

爲size_t ss_size

這是 'ss_sp' 點到信號棧 的大小(以字節計)。您應該將其設置爲分配給堆棧的多少空間。有在「signal.h中」 定義兩個宏,你應該在計算這個尺寸使用:

SIGSTKSZ

這是一個信號棧規範大小。它被判斷爲 足以用於正常使用。

MINSIGSTKSZ

這是信號堆棧空間的操作 系統需要只是實現信號傳遞的量。信號的大小必須大於此值。在大多數情況下,只用

最小堆棧所需的大小是由MINSIGSTKSZ宏觀管理。

+0

這是什麼工作。非常感謝你。 –

+0

@dr_ate不客氣。 – GreenScape

0

如果cstack位於正常堆棧上,它會正常工作,因爲您錯誤地設置了堆棧指針,並且您正在重寫真實堆棧上的內存而不是覆蓋從malloc獲取的內存。

堆棧在您的機器上增長的方向是什麼?如果它像一個普通的CPU一樣增長(幾乎除了PA-RISC以外的其他任何東西),那麼你的內存塊中的初始堆棧指針指向哪裏?如果你不能用「哦,當然這是一個愚蠢的錯誤」來回答這兩個問題,你應該重新考慮這個項目,在這之後它只會變得更加困難。

+0

我可以看到你的觀點,堆棧向上增長,並且可能應該將堆棧指針設置爲分配的內存區域的低端。然而,'makecontext'手冊頁上的例子就是我所做的,我也在其他來源看到過。我錯過了什麼嗎? –

0

根據手冊頁,您應該在SIGSTKSZ上分配堆棧大小。 在我的電腦,這個常數等於8192在計算機這個常數可能大於1024

+0

謝謝。 'SIGSTKSZ'在我的電腦上也是8192,所以我現在用它作爲堆棧的大小。但問題仍然存在。 –