克隆后如何访问errno(或:如何设置errno位置)

时间:2013-11-10 17:34:35

标签: linux clone errno

根据传统的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。我不想要pthreads的任何不明智的功能,我不想处理它的任何怪癖,我也不想要开销来实现这些怪癖。我认识到pthreads中的大部分内容来自于它必须工作(并且令人惊讶地,成功地工作)以及其他一些已经将近三十年历史的完全破坏的系统。并不意味着它对每个人和每种情况都是好事。在这种情况下,便携性无关紧要 在这种特殊情况下我想要的只是启动另一个与父节点在同一地址空间中运行的进程,通过一个简单的锁(例如,一个futex)同步,write正常工作(这意味着我必须能够正确阅读errno。尽可能少的开销,不需要甚至不需要其他功能或特殊行为。

2 个答案:

答案 0 :(得分:4)

根据glibc source codeerrno被定义为线程局部变量。不幸的是,这需要大量的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子集,它只包含一个小的汇编程序和一些常量。这非常简单,有很多可用的参考代码,虽然它可能过于雄心勃勃。

答案 1 :(得分: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