实现用户级线程库 - 从makecontext返回值

时间:2015-01-16 07:11:48

标签: c multithreading

我在用户线程库上看到了一些问题,但似乎都没有回答我的问题。我能够创建线程,运行它们,取消它们,然后退出它们。我出于某种原因不能做的是获得一个返回数据的线程。

当我启动我的线程库时,我按如下方式设置我的退出线程上下文:

getcontext(&threadEnd);
threadEnd.uc_stack.ss_sp = (char *)malloc(SIGSTKSZ);
threadEnd.uc_stack.ss_size = SIGSTKSZ;
makecontext(&threadEnd, (void *) thread_exit, 1, &ReturnValue);

我创建了一个线程,并按如下方式对其进行分配:

thread->myContext.uc_stack.ss_sp = (char *) malloc(SIGSTKSZ);
thread->myContext.uc_stack.ss_size = SIGSTKSZ;
thread->myContext.uc_link = &threadEnd;

当函数返回并调用thread_exit时:     void thread_exit(void * retval){

int* test;
if (retval != NULL)
{
    test = (int*) retval;
    printf("Returned value: %i\n", *test);
    fflush(stdout);
}

打印输出始终为“返回值:0”

被调用的函数返回一个指向整数的指针。

我做错了什么?

3 个答案:

答案 0 :(得分:1)

如果您在GBD中逐步执行程序,则makecontext不会保存用于创建上下文的函数的返回值。

我的实验示例:(观察rax寄存器):

返回声明:

<div id="axles">
            <?php
                $total_distance = 0;
                foreach ($testingggg as $lol){ 
                $total_distance += $lol['distance'];
                $margin = $total_distance/25000*100;
            ?>
            <div id="axle" style="margin-left:<?= $margin; ?>%">
                    <div id="circle" name="<?= $lol['axle'] ?>"><?= $lol['axle_nr'] ?></div>
            </div>
        <?php } ?>
            </div>
返回后

thread1 (arg=0x1) at test_create_join.c:14
14      return (void *)11;
Value returned is $1 = 19
(gdb) info registers
rax            0x13 19
---

内部上下文切换:

(gdb) step
15  }
(gdb) info registers
rax            0xb  11

你可以看到几条指令保留了返回值,但是在几步之后它变为0.显然它特定于x86_64 arch但我认为它可能与大多数arch相同(即makecontext的行为)

现在,如果你需要线程函数的返回值,你可以采取另一种方式。只需创建一个线程处理程序来运行你的线程并使用处理程序来创建新的上下文。在这里,您可以获得要作为线程运行的函数的返回值,并将其保存在线程控制块结构中以供以后使用。

__start_context () at ../sysdeps/unix/sysv/linux/x86_64/__start_context.S:32
32  ../sysdeps/unix/sysv/linux/x86_64/__start_context.S: No such file or directory.
(gdb) info registers
rax            0xb  11

答案 1 :(得分:0)

在一个线程上等待需要一些简单的同步。工作线程设置变量test和signal。

// Pseudo code
// worker thread
retval = &VALUE; // retval is pointer?
SignalEvent( hEvent );

主线程:

// Pseudo code
// main thread
int* test;

hEvent = CreateEvent();

WaitOnEvent( hEvent );

if (retval != NULL)
{
    test = (int*) retval;
    printf("Returned value: %i\n", *test);
    fflush(stdout);
}

对于许多线程,您需要决定是等待它们中的任何一个还是其他任何线程,直到它们都发出信号或者您想要等到所有信号然后再检查值。此外,我们通常会考虑等待事件的线程是否在等待时可能会执行一些空闲循环,并且可能会执行其他工作(稍微复杂一些)。

如果您使用C ++我可以提供C ++ 11条件变量的示例,但使用C它通常是POSIX或Win32 API但我们可以使用伪代码。 Win32 SetEventWaitForSingleObject。这个POSIX主题几乎涵盖了condition variables

请注意,使用线程完成一个线程也会触发信号,所以在Win32中你可以直接在线程句柄上等待,但这种方法可能不可移植。此外,线程及其同步不是C语言标准的一部分,因此只适用于同时提供这两者的操作系统才有意义。

答案 2 :(得分:0)

我们不应该在 makecontext() 的参数列表中传递指针。它们必须是手册中指定的整数:

<块引用>

函数func被调用,并传递了系列 argc 后面的整数 (int) 参数;来电者必须 在 argc 中指定这些参数的数量。

在某些架构上,整数与指针具有相同的大小(例如 32 位),但在其他架构上,指针是 64 位,而整数是 32 位长。在最近的 GLIBC/Linux x86_64 架构上,参数在上下文中存储为“long long”整数。因此,这可能会起作用,因为这会使参数存储为 64 位值(与指针大小兼容),但这不是可移植的。因此,这可以解释为什么您没有通过“&ReturnValue”指针获得正确的值。