gpt4 book ai didi

c - 交换到 ucontext_t 的 uc_link 时发生 swapcontext 段错误

转载 作者:行者123 更新时间:2023-11-30 15:33:01 69 4
gpt4 key购买 nike

我正在使用通常的 makecontext/swapcontext 例程,用 C 语言编写一个小型的概念验证 Fiber 库,但这给我带来了一些麻烦(我的平台是 OSX 10.9 Mavericks,使用 clang-503.0.40 )。

这是我正在处理的数据结构:

typedef enum {
/// Fiber is waiting to start execution
FIBER_PENDING,

/// Fiber is in the midst of executing
FIBER_EXECUTING,

/// Fiber has finished executing
FIBER_FINISHED,

/// Fiber is in the process of yielding
FIBER_YIELDING
} fiber_state;

typedef struct {
char *stack;
fiber_state state;
ucontext_t context;
} fiber;

这是迷你库(三个函数, fiber_initfiber_runfiber_yield :

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

// Generic log w/ file name and line number
#define LOG(x) fprintf(stderr, "%s:%d |-> %s\n", __FILE__, __LINE__, x)

// the current executing fiber in this thread (or NULL of none are executing)
// (TODO: make this TLS)
static fiber *current_fiber = NULL;

/// prepare fiber 'f' to be run
void fiber_init(fiber *f, void(* fiber_func)()) {
// zero out the fiber
memset(f, 0, sizeof(fiber));

f->state = FIBER_PENDING;
f->stack = (char*) malloc(SIGSTKSZ);

// init stack config of the fiber context
ucontext_t *f_context = &(f->context);

getcontext(f_context);
f_context->uc_stack.ss_sp = f->stack;
f_context->uc_stack.ss_size = SIGSTKSZ;
f_context->uc_stack.ss_flags = 0;

// initialize the context
makecontext(f_context, fiber_func, 0);
}

/// Deallocate resources associated with 'f'
void fiber_destroy(fiber *f) {
free(f->stack);
}

/// Start or resume fiber 'f'
void fiber_run(fiber *f) {
// context to switch back to when yielding, or when the fiber returns
ucontext_t return_context;
f->context.uc_link = &return_context;

// save the fiber being swapped away from (or NULL)
fiber *old_fiber = current_fiber;
current_fiber = f;

LOG("Swapping into fiber context");

getcontext(&return_context);
int status = swapcontext(
&return_context,
&(f->context));
assert(status == 0 && "Failed to swap to fiber context");

LOG("Back to parent context from swap");

if(f->state == FIBER_YIELDING) {
f->state = FIBER_EXECUTING;
LOG("Fiber yielded");
}
else {
LOG("Fiber done executing; marking as finished");
current_fiber->state = FIBER_FINISHED;
}

// restore old fiber
current_fiber = old_fiber;
}

/// from witin a fiber, yield control to the caller's context
void fiber_yield() {
assert(current_fiber && "No fiber is currently running!");

current_fiber->state = FIBER_YIELDING;

LOG("Yielding back to caller context");
int status = swapcontext(
&(current_fiber->context),
current_fiber->context.uc_link);
assert(status == 0 && "Failed to swap to parent context");
LOG("Swapped back into fiber context (post-yield)");
}

/// query fiber state
int fiber_is_pending(const fiber *const f) {
return f->state == FIBER_PENDING;
}
int fiber_is_finished(const fiber *const f) {
return f->state == FIBER_FINISHED;
}
int fiber_is_executing(const fiber *const f) {
return f->state == FIBER_EXECUTING;
}

看来,在 Fiber 中调用 Fiber_yield() 并不能正确地将上下文与调用者的上下文交换(其引用存储在 Fiber 上下文的 uc_link 中,请参阅 current_fiber->context.uc_link 中的 fiber_yield )

运行该程序的痕迹:

void my_func() {
LOG(" ------- I'm the fiber function! yielding");
fiber_yield();
LOG(" ------- End of my_func");
}

int main() {
fiber f;
fiber_init(&f, my_func);

while(!fiber_is_finished(&f)) {
fiber_run(&f);
LOG("Back in main run loop");
}

fiber_destroy(&f);
return 0;
}

产生输出:

fibers.c:70 |-> Swapping into fiber context
test_harness.c:5 |-> ------- I'm the fiber function! yielding
fibers.c:99 |-> Yielding back to caller context
Segmentation fault: 11

我读到 OSX 有堆栈对齐限制(到 16 字节边界),但我正在使用 malloc分配堆栈,它返回一个与 16 字节边界对齐的 block (或者我读过的)。也就是说,重新排列声明的顺序似乎有时会导致段错误不发生,但它非常虚假且难以重现。

检查fiber_yield在调用 swapcontext 之前显示 current_fiber->context具有非常大的堆栈大小;比应有的大得多。也许这是腐败的迹象:

(lldb) p current_fiber->context
(ucontext_t) $3 = {
uc_onstack = 0
uc_sigmask = 0
uc_stack = (ss_sp = 0x00007fff5fbff720, ss_size = 140734799804176, ss_flags = 0)
uc_link = 0x00007fff5fbff780
uc_mcsize = 0
uc_mcontext = 0x00007fff5fbff828
}
(lldb) p *(current_fiber->context.uc_link)
(__darwin_ucontext) $4 = {
uc_onstack = -541067328
uc_sigmask = 0
uc_stack = (ss_sp = 0x00007fff5fbff700, ss_size = 8388608, ss_flags = 0)
uc_link = 0x0000000000000000
uc_mcsize = 140734799804400
uc_mcontext = 0x00007fff5fbff7b8
}

知道会发生什么吗?谢谢!

最佳答案

我能够使用您的代码重现相同的问题,该代码是在 OS X 10.6.8 上使用 Apple 的 gcc-4.2.1 编译的。

我注意到您没有包含 ucontext.h。编译-Wall导致编译器对 ucontext 函数的隐式声明发出警告。

添加#include <ucontext.h>导致错误:

In file included from foo.c:6:
/usr/include/ucontext.h:42:2: error: #error ucontext routines are deprecated, and require _XOPEN_SOURCE to be defined

添加#define _XOPEN_SOURCE首先包括修复该问题以及程序的行为。显然,该宏更改了相关结构的布局以匹配这些函数的期望和要求。

关于这些函数已被弃用的事实,我不知道该告诉您什么。据我所知,没有支持的替代品。

关于c - 交换到 ucontext_t 的 uc_link 时发生 swapcontext 段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23852522/

69 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com