我正在嘗試創建用戶級別線程庫,但是當我嘗試設置其中一個線程的上下文時,我收到了分段錯誤錯誤。我做了我所有代碼的簡化版,並縮小了它(我認爲)到一個奇怪的事情。如果我使用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)
任何想法在所有讚賞。我大多都很困惑,並在思考我所認識的一切。
因爲1024字節可能不足以滿足堆棧的要求,所以程序會因堆棧溢出而覆蓋malloc()的內部數據。另一方面,當你使用當前棧作爲緩衝區時,你的程序仍然會導致溢出,但沒有可見的效果,因爲主棧通常很大(〜4 MiB),並且溢出不會造成任何傷害。 – GreenScape
在'gdb'(或者你最喜歡的調試器)和'valgrind'下運行這個函數,看看結果如何。它是你唯一沒有做過的事情,也是你可能應該做的第一件事。 – WhozCraig
你正在對齊堆棧嗎?沒有「已經過」需要變化嗎? – kfsone