【问题标题】:dup( fileno( stdin ) ) and then spawn of 32 threads -> I/O errorsdup(fileno(stdin)),然后产生 32 个线程 -> I/O 错误
【发布时间】:2017-02-12 14:07:21
【问题描述】:

我已经编写了 Zsh 模块。我有一个映射到 Zsh 命令的内置函数。这个函数复制了它的标准输入文件描述符:

/* Duplicate standard input */
oconf->stream = fdopen( dup( fileno( stdin ) ), "r" );

然后产生一个获得oconf结构的线程。在那个线程中,我这样做:

errno = 0;

/* Read e.g. 5 characters, putting them after previous portion */
int count = fread( buf + index, 1, read_size, oconf->stream );
/* Ensure that our whole data is a string - null terminated */
buf[ index + count ] = '\0';

if ( errno ) {
    fprintf( oconf->err, "Read error (descriptor: %d): %s\n", fileno( oconf->stream ), strerror( errno ) >
}

如果我在 zsh 中生成 32 个线程:

for (( i=1; i<=32; i ++ )); do
    ls -R /Users/myuser/Documents | mybuiltin -A myhash_$i $i
done

然后有2-3个线程出现上述fprintf()报的I/O错误,例如:

读取错误(描述符:7):输入/输出错误

读取错误(描述符:5):设备的 ioctl 不合适

读取错误(描述符:14):设备的 ioctl 不合适

调试器表示,这些线程在多次 (5-20) fread() 重复后,会在内核的 __read_nocancel() 中被阻塞。所以文件描述符发生了一些非常糟糕的事情。

否则这有效。管道正确地传递来自ls -R 的数据,它被自定义内置函数读取。那么危险在哪里呢?为什么dup() 在主线程中执行会导致fread() 无法读取?我可能会怀疑我是否会在辅助线程中执行dup()。但我只把它保存在安全的地方——主线程,然后将准备好的FILE *流传递给辅助线程。还尝试了POSIX open()read()close(),结果是一样的。

【问题讨论】:

    标签: c thread-safety fread file-descriptor dup


    【解决方案1】:

    您正在错误地测试errno。如果设置它的函数报告了错误,您应该只检查errno。标准 C 或 POSIX 库中的任何函数都不会将 errno 设置为零。并且函数可以将errno 设置为非零而不报告错误。

    例如,在 Solaris 上,在写入操作之后,如果文件流不是终端(例如重定向到文件或管道),则过去是(并且可能仍然是)errno == ENOTTY。没有问题;输出设备不是终端,因此仅终端操作失败,将 errno 设置为 ENOTTY

    您目前拥有:

    /* Read e.g. 5 characters, putting them after previous portion */
    int count = fread( buf + index, 1, read_size, oconf->stream );
    /* Ensure that our whole data is a string - null terminated */
    buf[ index + count ] = '\0';
    
    if ( errno ) {
        fprintf( oconf->err, "Read error (descriptor: %d): %s\n", fileno( oconf->stream ), strerror( errno ));
    }
    

    你需要使用类似的东西:

    int count = fread(buf + index, 1, read_size, oconf->stream);
    if (count == 0)
    {
        /* EOF or error — this might be a time to use feof() or ferror() */
        fprintf(oconf->err, "Read error (descriptor: %d): %s\n", fileno(oconf->stream), strerror(errno));
        …flow control?…
    }
    else
        buf[index + count] = '\0';
    

    您可能需要在 EOF 和错误路径中添加一些其他的控制流细节(返回或中断或设置标志);从您引用的片段中不清楚什么可能是合适的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-18
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-24
      相关资源
      最近更新 更多