作为作业的一部分,我正在尝试创建一个像 pthreads 这样的用户级线程库。
为了处理线程之间的上下文切换,我使用了“swapcontext”函数。在使用它之前,我必须使用“makecontext”函数创建一个上下文。'makecontext' 需要一个具有返回类型void
和参数类型的函数指针(void)
。
而线程函数必须是类型void* thread_func (void*)
有没有办法进行类型转换?还是有其他方法可以在用户级别进行上下文切换?
作为作业的一部分,我正在尝试创建一个像 pthreads 这样的用户级线程库。
为了处理线程之间的上下文切换,我使用了“swapcontext”函数。在使用它之前,我必须使用“makecontext”函数创建一个上下文。'makecontext' 需要一个具有返回类型void
和参数类型的函数指针(void)
。
而线程函数必须是类型void* thread_func (void*)
有没有办法进行类型转换?还是有其他方法可以在用户级别进行上下文切换?
通过将函数的地址转换为不同的原型并通过结果指针调用它来调用具有不兼容原型的函数是非法的:
void *my_callback(void *arg) { ... }
void (*broken)(void *) = (void (*)(void *)) my_callback;
broken(some_arg); // incorrect, my_callback returns a `void *`
您可以做的是传递给makecontext
您自己的回调,该回调将调用thread_func
并忽略其返回值。仅用于调用另一个函数的小函数有时称为蹦床。
/* return type is compatible with the prototype of the callback received
by makecontext; simply calls the real callback */
static void trampoline(int cb, int arg)
{
void *(*real_cb)(void *) = (void *(*)(void *)) cb;
void *real_arg = arg;
real_cb(real_arg);
}
int my_pthread_create(void *(*cb)(void *), void *arg)
{
ucontext_t *ucp;
...
/* For brevity treating `void *` as the same size as `int` -
DO NOT USE AS-IS.
makecontext exposes an annoyingly inconvenient API that only
accepts int arguments; correct code would deconstruct each
pointer into two ints (on architectures where pointer is
larger than int) and reconstruct them in the trampoline. */
makecontext(ucp, trampoline, 2, (int) cb, (int) arg);
...
}
对于奖励积分,您可以修改蹦床以将void *
回调函数返回的值存储在堆栈上,并让您等效于pthread_join()
检索它。
原则上,您始终可以将任何类型的指针转换为任何其他类型的指针,但对于函数指针,我强烈建议不要使用.
您thread_func
会期望堆栈上有一个参数,如果在您的错误转换后调用该参数,则不会提供该参数。更糟糕的是,thread_func
会在不应该的地方写入返回值,从而破坏你的堆栈。
一种解决方案是将调用包装在它自己的适当类型的函数中。
您可以像变量一样对函数指针进行类型转换。语法更尴尬,但它肯定是可能的(这是否是一个好主意完全是另一个讨论)。
但是,在这种情况下,这可能不是您想要做的。从手册页swapcontext
:
在调用 makecontext() 之前,调用者必须为此上下文分配一个新的堆栈并将其地址分配给 ucp->uc_stack,
您的线程函数需要一个参数。通过您创建的堆栈将该参数传递给新上下文。您传递给的函数makecontext()
可以是一个包装函数,它从堆栈中检索值并将其作为参数传递给线程函数。单独的类型转换不会提供将参数中的数据向下传递到新上下文中的函数的方法。