【发布时间】: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