我正在尝试创建一个用户级线程库,但是当我尝试设置其中一个线程的上下文时,我收到了一个段错误。我制作了我所有代码的精简版本,并将其缩小(我认为)为一件奇怪的事情。如果我使用 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
:
#ifndef __cthread__
#define __cthread__
int ccreate (void* (*start)(void*), void *arg);
#endif
这是(最重要的)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
(线程控制 block )类型,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)
任何想法都将不胜感激。我大部分时间都感到困惑,并在思考我自以为知道的一切。
最佳答案
根据 this
stack_t uc_stack Stack used for this context. The value need not be (and normally is not) the stack pointer. See section 24.9 Using a Separate Signal Stack.
和this
size_t ss_size
This is the size (in bytes) of the signal stack which 'ss_sp' points to. You should set this to however much space you allocated for the stack. There are two macros defined in 'signal.h' that you should use in calculating this size:
SIGSTKSZ
This is the canonical size for a signal stack. It is judged to be sufficient for normal uses.
MINSIGSTKSZ
This is the amount of signal stack space the operating system needs just to implement signal delivery. The size of a signal stack must be greater than this. For most cases, just using
堆栈 所需的最小大小由 MINSIGSTKSZ 宏控制。
关于c - 使用 malloc 为上下文分配堆栈时的分段,而不是使用本地数组时的分段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39739792/