1

根据传统的 POSIX,errno它只是一个整数左值,它可以很好地与. 一起fork工作,但显然不能与线程一起工作。根据 pthreads,errno是一个线程局部整数左值。在 Linux/NTPL 下,作为实现细节,errno 是一些“扩展为返回整数左值的函数的宏”。

在我的 Debian 系统上,这似乎是*__errno_location (),在我见过的其他一些系统上,例如&(gettib()->errnum.

TL;DR
假设我曾经clone创建过一个线程,我可以直接调用errno并期望它会起作用,还是我必须做一些特殊的雨舞?例如,我是否需要读取线程信息块中的一些特殊字段,或者一些特殊的 TLS 值,或者,我是否需要设置 glibc 以某种方式存储错误值的线程局部变量的地址?可能是什么__set_errno_location()

或者,它会“正常工作”吗?

不可避免地,有人会试图回答“只使用 phtreads”——请不要。我不想使用 pthreads。我想要clone。我不想要 pthread 的任何不明智的功能,我不想处理它的任何怪癖,也不想要实现这些怪癖的开销。我认识到 pthreads 中的大部分问题来自于它必须工作(而且,令人惊讶的是,它成功地工作)在一些已经有近 30 年历史的完全损坏的系统中,但这并不意味着它是对每个人和每种情况都必然是一件好事。在这种情况下,可移植性无关紧要。
在这种特殊情况下,我想要的只是启动另一个与父进程在同一地址空间中运行的进程,通过一个简单的锁(比如 futex)进行同步,并write正常工作(这意味着我必须能够errno正确读取) . 尽可能少的开销,不需要甚至不需要其他功能或特殊行为。

4

2 回答 2

4

根据glibc源代码errno定义为线程局部变量。不幸的是,这需要大量的 C 库支持。使用创建的任何线程pthread_create()都将知道线程局部变量。我什至不会费心试图让 glibc 接受你的外来线程。

另一种方法是使用不同的 libc 实现,它可能允许您提取其一些内部结构并手动设置线程控制块(如果errno它是其中的一部分)。这将是令人难以置信的hacky和不可靠的。我怀疑你会找到类似的东西__set_errno_location(),而是类似的东西__set_tcb()

#include <bits/some_hidden_file.h>

void init_errno(void)
{
    struct __tcb* tcb;

    /* allocate a dummy thread control block (malloc may set errno
     * so might have to store the tcb on stack or allocate it in the
     * parent) */
    tcb = malloc(sizeof(struct __tcb));

    /* initialize errno */
    tcb->errno = 0;

    /* set pointer to thread control block (x86) */
    arch_prctl(ARCH_SET_FS, tcb);
}

这假设errno宏扩展为:((struct __tcb*)__read_fs())->errno.

当然,总是可以选择自己实现一个非常小的 libc 子集。或者,您可以编写自己的write()系统调用实现,并使用自定义存根进行处理errno,并使其与所选的 libc 实现共存。

#define my_errno /* errno variable stored at some known location */

ssize_t my_write(int fd, const void* buf, size_t len)
{
    ssize_t ret;

    __asm__ (
        /* set system call number */
        /* set up parameters */
        /* make the call */
        /* retrieve return value in c variable */
    );

    if (ret >= -4096 && ret < 0) {
        my_errno = -ret;
        return -1;
    }

    return ret;
}

我不记得 GCC 内联汇编的确切细节,系统调用调用细节因平台而异。

就个人而言,我只是实现了一个非常小的 libc 子集,它只包含一个小汇编程序和一些常量。这非常简单,有很多可用的参考代码,尽管它可能过于雄心勃勃。

于 2013-11-11T00:10:02.017 回答
0

如果 errno 是线程局部变量,那么 clone() 会将其复制到新进程的地址空间中吗?我已经像 2001 年左右那样覆盖了 errno_location() 函数,以使用基于 pid 的 errno。

http://tamtrajnana.blogspot.com/2012/03/thread-safety-of-errno-variable.html

因为 errno 现在被定义为“__thread int errno;” (见上面的评论)这解释了如何处理 __thread 类型:Linux's thread local storage implementation

于 2013-11-18T04:59:20.157 回答