perlapio - PerlIO_findFILE() 有效但将 errno 设置为 "illegal seek" (ESPIPE 29)
perlapio - PerlIO_findFILE() works but sets errno to "illegal seek" (ESPIPE 29)
这个问题或多或少与 embedded perl in C, perlapio - interoperability with STDIO 有关,我认为我已经为 Windows 环境解决了这个问题。如果这个新问题也解决了,我将post一个完整的解决方案。
在链接的问题中,
讲故事的人 gave me the hint
使用 PerlIO_findFILE()
解决了眼前的问题,但是 Linux 上的相同代码表现得很奇怪。
Perl 的 dup2()
在 Win32 上似乎有不同的行为,其中 dup2()
是 win32_dup2()
的宏,据我所知它只是使用 dup2()
来自 io.h
.
在 Win32 上,Perl 的版本 returns 成功时为零,错误时为非零,但在 Linux 上将使用默认的 ANSI dup2()
,而不是 returns 新的文件描述符。然后,我必须检查 errno
是否一切正常。
如果对 PerlIO_findFILE()
的调用将 errno
设置为 "illegal seek"(错误号 29 - ESPIPE
),则在 dup
、dup2
之后、pipe
等。errno
仍设置为 "illegal seek",对 errno
的任何进一步检查仍会看到相同的错误。
(实际上一切都对我有用,因为没有实际错误。另外,通过检查 errno
的解决方案不是线程安全的,因为在系统调用和检查之间,另一个胎面可能会重置 errno。)
注意我有
#define PERLIO_NOT_STDIO 0
有效并且我使用的是 Perl5.14.1。
我真的做错了什么吗?
这是一个简化的代码片段:
stdOutFILE = PerlIO_findFILE(PerlIO_stderr()); // convert Perl's stdout to stdio FILE handle
fdStdOutOriginal = fileno(stdOutFILE); // get descriptor
if ( fdStdOutOriginal >= 0 ) {
relocatedStdOut = dup(fdStdOutOriginal); // relocate stdOut for external writing
if ( relocatedStdOut >= 0 )
{
if ( pipe(fdPipeStdOut) == 0 ) // create pipe for forwarding to stderr
{
// this has to be done on win32:
// if ( dup2(fdPipeStdOut[1], fdStdOutOriginal) == 0 ) // hang pipe on stdOut
dup2(fdPipeStdOut[1], fdStdOutOriginal);
if( errno == 0 ) {
// do some funny stuff
} else {
// report error
}
}
}
}
errno
是没有意义的,除非C库调用或者系统调用报错,所以不能用来判断是否有错误发生。值得注意的是,这些调用不需要(通常也不需要)在成功时重置 errno
。在调用之前清除 errno
甚至是不安全的,因为即使没有发生错误,调用也可能设置 errno
。
据我所知,Perl 的 emulation of dup2
returns 与 POSIX 的值相同(-1
出错,newfd
成功)。
#ifndef HAS_DUP2
int
dup2(int oldfd, int newfd)
{
#if defined(HAS_FCNTL) && defined(F_DUPFD)
if (oldfd == newfd)
return oldfd;
PerlLIO_close(newfd);
return fcntl(oldfd, F_DUPFD, newfd);
#else
#define DUP2_MAX_FDS 256
int fdtmp[DUP2_MAX_FDS];
I32 fdx = 0;
int fd;
if (oldfd == newfd)
return oldfd;
PerlLIO_close(newfd);
/* good enough for low fd's... */
while ((fd = PerlLIO_dup(oldfd)) != newfd && fd >= 0) {
if (fdx >= DUP2_MAX_FDS) {
PerlLIO_close(fd);
fd = -1;
break;
}
fdtmp[fdx++] = fd;
}
while (fdx > 0)
PerlLIO_close(fdtmp[--fdx]);
return fd;
#endif
}
#endif
(从 5.24.1 开始)
这意味着我们可以 platform-independent 方式检测错误,尽管您的说法与此相反。因此,正确的用法是
if ( dup2(fdPipeStdOut[1], fdStdOutOriginal) >= 0 ) {
//do some funny stuff
} else {
//report error
}
这个问题或多或少与 embedded perl in C, perlapio - interoperability with STDIO 有关,我认为我已经为 Windows 环境解决了这个问题。如果这个新问题也解决了,我将post一个完整的解决方案。
在链接的问题中,
讲故事的人 gave me the hint
使用 PerlIO_findFILE()
解决了眼前的问题,但是 Linux 上的相同代码表现得很奇怪。
Perl 的 dup2()
在 Win32 上似乎有不同的行为,其中 dup2()
是 win32_dup2()
的宏,据我所知它只是使用 dup2()
来自 io.h
.
在 Win32 上,Perl 的版本 returns 成功时为零,错误时为非零,但在 Linux 上将使用默认的 ANSI dup2()
,而不是 returns 新的文件描述符。然后,我必须检查 errno
是否一切正常。
如果对 PerlIO_findFILE()
的调用将 errno
设置为 "illegal seek"(错误号 29 - ESPIPE
),则在 dup
、dup2
之后、pipe
等。errno
仍设置为 "illegal seek",对 errno
的任何进一步检查仍会看到相同的错误。
(实际上一切都对我有用,因为没有实际错误。另外,通过检查 errno
的解决方案不是线程安全的,因为在系统调用和检查之间,另一个胎面可能会重置 errno。)
注意我有
#define PERLIO_NOT_STDIO 0
有效并且我使用的是 Perl5.14.1。
我真的做错了什么吗?
这是一个简化的代码片段:
stdOutFILE = PerlIO_findFILE(PerlIO_stderr()); // convert Perl's stdout to stdio FILE handle
fdStdOutOriginal = fileno(stdOutFILE); // get descriptor
if ( fdStdOutOriginal >= 0 ) {
relocatedStdOut = dup(fdStdOutOriginal); // relocate stdOut for external writing
if ( relocatedStdOut >= 0 )
{
if ( pipe(fdPipeStdOut) == 0 ) // create pipe for forwarding to stderr
{
// this has to be done on win32:
// if ( dup2(fdPipeStdOut[1], fdStdOutOriginal) == 0 ) // hang pipe on stdOut
dup2(fdPipeStdOut[1], fdStdOutOriginal);
if( errno == 0 ) {
// do some funny stuff
} else {
// report error
}
}
}
}
errno
是没有意义的,除非C库调用或者系统调用报错,所以不能用来判断是否有错误发生。值得注意的是,这些调用不需要(通常也不需要)在成功时重置 errno
。在调用之前清除 errno
甚至是不安全的,因为即使没有发生错误,调用也可能设置 errno
。
据我所知,Perl 的 emulation of dup2
returns 与 POSIX 的值相同(-1
出错,newfd
成功)。
#ifndef HAS_DUP2
int
dup2(int oldfd, int newfd)
{
#if defined(HAS_FCNTL) && defined(F_DUPFD)
if (oldfd == newfd)
return oldfd;
PerlLIO_close(newfd);
return fcntl(oldfd, F_DUPFD, newfd);
#else
#define DUP2_MAX_FDS 256
int fdtmp[DUP2_MAX_FDS];
I32 fdx = 0;
int fd;
if (oldfd == newfd)
return oldfd;
PerlLIO_close(newfd);
/* good enough for low fd's... */
while ((fd = PerlLIO_dup(oldfd)) != newfd && fd >= 0) {
if (fdx >= DUP2_MAX_FDS) {
PerlLIO_close(fd);
fd = -1;
break;
}
fdtmp[fdx++] = fd;
}
while (fdx > 0)
PerlLIO_close(fdtmp[--fdx]);
return fd;
#endif
}
#endif
(从 5.24.1 开始)
这意味着我们可以 platform-independent 方式检测错误,尽管您的说法与此相反。因此,正确的用法是
if ( dup2(fdPipeStdOut[1], fdStdOutOriginal) >= 0 ) {
//do some funny stuff
} else {
//report error
}