c - 使用 malloc 为上下文分配堆栈时的分段,而不是使用本地数组时的分段

标签 c arrays multithreading segmentation-fault

我正在尝试创建一个用户级线程库,但是当我尝试设置其中一个线程的上下文时,我收到了一个段错误。我制作了我所有代码的精简版本,并将其缩小(我认为)为一件奇怪的事情。如果我使用 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/

相关文章:

multithreading - Python 类管道流对象

c# - 静态类的竞争条件?

c++ - 使用 openssl 下载证书并将证书设置为 libCURL

java - 将第三方 dll 链接到我的 dll

java - 拆分字符串不包含 java 中的字符串

c++ - Cin 用于字符数组

multithreading - LOCK XCHG和MOV+MFENCE在逻辑和性能上有什么区别?

c - 命名管道的大小?进程卡住

C : Singly linked list sorting segmentation fault

java - 为什么java中的toString方法似乎不适用于数组