我写了 Zsh 模块。在那里,我有一个映射到 Zsh 命令的内置函数。此函数复制其 stdin 文件描述符:
/* 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 ] = ' ';
if ( errno ) {
fprintf( oconf->err, "Read error (descriptor: %d): %sn", 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 *
流传递给辅助线程。也尝试了POSIXopen()
,read()
和close()
,结果是一样的。
您测试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 ] = ' ';
if ( errno ) {
fprintf( oconf->err, "Read error (descriptor: %d): %sn", 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): %sn", fileno(oconf->stream), strerror(errno));
…flow control?…
}
else
buf[index + count] = ' ';
您可能需要在 EOF 和错误路径中提供一些其他控制流详细信息(返回或中断或设置标志);从引用的片段中不清楚什么是合适的。