perlapio - perl 的 IO 抽象接口。
#define PERLIO_NOT_STDIO 0 /* For co-existence with stdio only */
#include <perlio.h> /* Usually via #include <perl.h> */
PerlIO *PerlIO_stdin(void);
PerlIO *PerlIO_stdout(void);
PerlIO *PerlIO_stderr(void);
PerlIO *PerlIO_open(const char *path,const char *mode);
PerlIO *PerlIO_fdopen(int fd, const char *mode);
PerlIO *PerlIO_reopen(const char *path, /* deprecated */
const char *mode, PerlIO *old);
int PerlIO_close(PerlIO *f);
int PerlIO_stdoutf(const char *fmt,...)
int PerlIO_puts(PerlIO *f,const char *string);
int PerlIO_putc(PerlIO *f,int ch);
SSize_t PerlIO_write(PerlIO *f,const void *buf,size_t numbytes);
int PerlIO_printf(PerlIO *f, const char *fmt,...);
int PerlIO_vprintf(PerlIO *f, const char *fmt, va_list args);
int PerlIO_flush(PerlIO *f);
int PerlIO_fill(PerlIO *f);
int PerlIO_eof(PerlIO *f);
int PerlIO_error(PerlIO *f);
void PerlIO_clearerr(PerlIO *f);
int PerlIO_getc(PerlIO *d);
int PerlIO_ungetc(PerlIO *f,int ch);
SSize_t PerlIO_read(PerlIO *f, void *buf, size_t numbytes);
Size_t PerlIO_unread(PerlIO *f,const void *vbuf, size_t count
int PerlIO_fileno(PerlIO *f);
void PerlIO_setlinebuf(PerlIO *f);
Off_t PerlIO_tell(PerlIO *f);
int PerlIO_seek(PerlIO *f, Off_t offset, int whence);
void PerlIO_rewind(PerlIO *f);
int PerlIO_getpos(PerlIO *f, SV *save); /* prototype changed */
int PerlIO_setpos(PerlIO *f, SV *saved); /* prototype changed */
int PerlIO_fast_gets(PerlIO *f);
int PerlIO_has_cntptr(PerlIO *f);
SSize_t PerlIO_get_cnt(PerlIO *f);
char *PerlIO_get_ptr(PerlIO *f);
void PerlIO_set_ptrcnt(PerlIO *f, char *ptr, SSize_t count);
int PerlIO_canset_cnt(PerlIO *f); /* deprecated */
void PerlIO_set_cnt(PerlIO *f, int count); /* deprecated */
int PerlIO_has_base(PerlIO *f);
char *PerlIO_get_base(PerlIO *f);
SSize_t PerlIO_get_bufsiz(PerlIO *f);
PerlIO *PerlIO_importFILE(FILE *stdio, const char *mode);
FILE *PerlIO_exportFILE(PerlIO *f, const char *mode);
FILE *PerlIO_findFILE(PerlIO *f);
void PerlIO_releaseFILE(PerlIO *f,FILE *stdio);
int PerlIO_apply_layers(pTHX_ PerlIO *f, const char *mode,
const char *layers);
int PerlIO_binmode(pTHX_ PerlIO *f, int ptype, int imode,
const char *layers);
void PerlIO_debug(const char *fmt,...);
Perl 的源代码以及需要最大可移植性的扩展应该使用以上函数,而不是 ANSI C 的 stdio.h 中定义的函数。perl 头文件(特别是 "perlio.h")将 #define
它们到在配置时选择的 I/O 机制。
这些函数是根据 stdio.h 中的函数建模的,但参数顺序已经“稍微整理了一下”。
PerlIO *
代替 FILE *。与 FILE * 一样,它应该被视为不透明的(可以安全地假设它是一个指向某个东西的指针)。
目前有两种实现
以上所有内容都 #define 为 stdio 函数,或者是一些调用 stdio 的简单包装函数。在这种情况下,只有 PerlIO * 是一个 FILE *。自 perl5.003_02 中引入抽象以来,这一直是默认实现。
在 perl5.7.0 之后引入,这是对上述抽象的重新实现,它允许 perl 对 IO 的执行方式有更多控制,因为它将 IO 与操作系统和 C 库选择执行方式的方式分离。对于 USE_PERLIO,PerlIO * 具有额外的间接层 - 它是一个指向指针的指针。这允许 PerlIO * 保持已知值,同时在运行时在底层交换实现。在这种情况下,以上所有内容都是真实的(但非常简单)函数,它们调用底层实现。
这是唯一一个 PerlIO_apply_layers()
执行“有趣”操作的实现。
USE_PERLIO 的实现描述在 perliol 中。
由于 "perlio.h" 是一个薄层(为了效率),这些函数的语义在一定程度上依赖于底层实现。在理解这些差异的地方,将在下面进行说明。
除非另有说明,函数在成功时返回 0,或在错误时返回负值(通常为 EOF
,通常为 -1),并设置 errno
。
使用这些而不是 stdin
、stdout
、stderr
。它们被写成看起来像 "函数调用" 而不是变量,因为这使得在平台无法将数据导出到已加载的模块,或者(例如)不同的 "线程" 可能具有不同的值时,使它们成为函数调用变得更容易。
这些对应于 fopen()/fdopen(),参数相同。如果发生错误,则返回 NULL
并设置 errno
。打开句柄的数量可能存在实现限制,这可能低于打开文件的数量限制 - 如果超过此限制,则在返回 NULL
时可能不会设置 errno
。
虽然这目前在两种实现中都存在,但 perl 本身并不使用它。由于 perl 不使用它,因此它没有经过充分测试。
Perl 倾向于将新的低级描述符 dup
到现有 PerlIO 使用的描述符。这可能会成为将来此函数的行为。
这些是 fprintf()/vfprintf() 等价物。
这是 printf() 等价物。printf 被 #defined 为此函数,因此在 perl 源代码中使用 printf(fmt,...)
是(目前)合法的。
这些在功能上对应于 fread() 和 fwrite(),但参数和返回值不同。PerlIO_read() 和 PerlIO_write() 的签名是根据更合理的低级 read() 和 write() 函数建模的:首先传递“文件”参数,只有一个“计数”,返回值可以区分错误和EOF
。
如果成功,则返回字节计数(可能是零或正数),如果出错,则返回负值并设置errno
。根据实现,errno
可能是EINTR
,如果操作被信号中断。
用来自下一层的数据填充与f
关联的缓冲区。PerlIO_read
在其正常操作过程中调用此函数。成功时返回 0;失败时返回 -1。
根据实现,errno
可能是EINTR
,如果操作被信号中断。
这些对应于 fputs() 和 fputc()。请注意,参数已修改为首先具有“文件”。
这对应于 ungetc()。请注意,参数已修改为首先具有“文件”。安排下次读取操作将返回字节c。尽管名称中隐含着“字符”,但仅定义了 0..0xFF 范围内的值。成功时返回字节c,出错时返回 -1 (EOF
)。可以“回推”的字节数可能会有所不同,只有 1 个字符是确定的,并且只有当它是从句柄读取的最后一个字符时。
这允许您回退多个字节。它有效地将count
个字节移到缓冲区buf
的开头,以便下次读取操作将返回它们,然后再返回缓冲区中的任何其他内容。
返回未读字节数。
这对应于 getc()。尽管名称中包含 c,但仅支持字节范围 0..0xFF。返回读取的字符,出错时返回 -1 (EOF
)。
这对应于 feof()。返回一个真/假指示,指示句柄是否位于文件末尾。对于终端设备,这可能是“粘性”的,也可能不是,具体取决于实现。该标志由 PerlIO_seek() 或 PerlIO_rewind() 清除。
这对应于 ferror()。返回一个真/假指示,指示句柄上是否发生了 IO 错误。
这对应于 fileno(),请注意,在某些平台上,“fileno”的含义可能与 Unix 不匹配。如果句柄没有与之关联的打开描述符,则返回 -1。
这对应于 clearerr(),即清除“流”的“错误”和(通常)“eof”标志。不返回值。
这对应于 fflush()。将任何缓冲的写入数据发送到底层文件。如果使用 NULL
调用,这可能会刷新所有打开的流(或在某些 USE_STDIO 实现中进行核心转储)。在以只读方式打开的句柄上调用,或者在上次操作是某种读取操作的句柄上调用,可能会导致某些 USE_STDIO 实现出现未定义的行为。USE_PERLIO(层)实现试图表现得更好:它在传递 NULL
时刷新所有打开的流,并尝试将读取流上的数据保留在缓冲区中或通过将句柄定位到当前逻辑位置来保留数据。
这对应于 fseek()。将缓冲的写入数据发送到底层文件,或丢弃任何缓冲的读取数据,然后根据 offset 和 whence(原文如此)指定的偏移量定位文件描述符。当在同一个句柄上切换读取和写入时,这是正确的方法(参见上面 PerlIO_flush() 的问题)。Offset 的类型为 Off_t
,它是一个 perl Configure 值,可能与 stdio 的 off_t
不相同。
这对应于 ftell()。返回当前文件位置,或在出错时返回 (Off_t) -1。可能会在不进行系统调用或检查底层文件描述符的情况下返回系统“知道”的值(因此在共享文件描述符上使用是不安全的,除非使用 PerlIO_seek())。返回值的类型为 Off_t
,它是一个 perl Configure 值,可能与 stdio 的 off_t
不相同。
这些对应于(松散地)fgetpos() 和 fsetpos()。它们不是 stdio 的 Fpos_t,而是期望传递一个“Perl 标量值”。存储在其中的内容应被视为不透明的。数据的布局可能因句柄而异。当不使用 stdio 或平台没有 stdio 调用时,它们是根据 PerlIO_tell() 和 PerlIO_seek() 实现的。
这对应于 rewind()。它通常被定义为
PerlIO_seek(f,(Off_t)0L, SEEK_SET);
PerlIO_clearerr(f);
此函数对应于 tmpfile(),即在出错时返回一个匿名 PerlIO 或 NULL。系统将在关闭文件时尝试自动删除它。在 Unix 上,文件通常在创建后立即被 unlink
,因此关闭方式无关紧要。在其他系统上,文件可能仅在通过 PerlIO_close() 关闭或程序通过 exit
退出时才会被删除。根据实现的不同,可能存在允许其他进程访问文件的“竞争条件”,尽管总的来说,与临时方案相比,它在这方面更安全。
此函数对应于 setlinebuf()。不返回值。什么是“行”取决于实现,但通常意味着写入“\n”会刷新缓冲区。对于“this\nthat”之类的情况,结果是不确定的。(Perl 核心仅在“转储”时使用它;它与 $| 自动刷新无关。)
PerlIO 与 stdio 共存的概述支持。显然,如果 PerlIO 是用 stdio 实现的,则没有问题。但在其他情况下,必须存在机制来创建 FILE *,该 FILE * 可以传递给将使用 stdio 调用的库代码。
第一步是添加以下行
#define PERLIO_NOT_STDIO 0
在包含任何 perl 头文件之前。(这可能在某个时候成为默认设置)。这将阻止“perlio.h”尝试将 stdio 函数定义为 PerlIO 函数。
如果 XS 代码期望 FILE * 参数,则最好使用“typemap”。标准 typemap 将被调整以理解此领域的任何更改。
用于从 FILE * 获取 PerlIO *。
mode 参数应为字符串,与传递给 fopen/PerlIO_open 的字符串相同。如果它是 NULL,则出于向后兼容性的考虑,代码将(根据平台和实现)尝试经验性地确定 f 打开的模式,或者使用“r+”来表示读写流。
调用后,FILE * 只能通过对返回的 PerlIO * 调用 PerlIO_close()
来关闭。
PerlIO 设置为文本模式。如果这不是所需的模式,请使用 PerlIO_binmode。
这不是 PerlIO_exportFILE() 的反函数。
给定一个 PerlIO *,创建一个适合传递给期望与 ANSI C stdio.h 编译和链接的代码的“本机”FILE *。mode 参数应为字符串,与传递给 fopen/PerlIO_open 的字符串相同。如果它是 NULL,则出于向后兼容性的考虑,FILE * 将以与 PerlIO * 相同的模式打开。
将文件“导出”的事实会被记录下来(通常是通过将新的 :stdio “层”推送到 PerlIO 上),这可能会影响将来对原始 PerlIO 的操作。除非调用 `PerlIO_releaseFILE()` 来将其与 PerlIO 分离,否则不应在该文件上调用 `fclose()`。(不要使用 PerlIO_importFILE() 来进行分离。)
重复调用此函数将在每次调用时创建一个 FILE*(并且每次也会推送一个 :stdio 层)。
调用 PerlIO_releaseFILE 通知 PerlIO 所有对 FILE* 的使用已完成。它将从“导出”的 FILE* 列表中删除,并且关联的 PerlIO* 应恢复其原始行为。
使用此函数将文件与使用 PerlIO_exportFILE() 关联的 PerlIO* 分离。
返回 stdio 层使用的原生 FILE*。如果没有,它将使用 PerlIO_exportFILE 创建一个。无论哪种情况,FILE* 都应被视为属于 PerlIO 子系统,并且只能通过调用 `PerlIO_close()` 来关闭。
除了上面定义的类似标准的 API 之外,还存在一个“实现”接口,它允许 perl 访问 PerlIO 的内部结构。以下调用对应于由 Configure 确定的各种 FILE_xxx 宏 - 或其他实现中的等效项。本节实际上只对那些关心 perl 内核行为细节、实现 PerlIO 映射或编写可以利用 IO 系统以与 perl 相同的方式完成的“预读”的代码的人感兴趣。请注意,任何使用这些接口的代码都必须做好以传统方式执行操作的准备,如果句柄不支持这些接口。
如果实现具有允许 perl 的 `sv_gets` “绕过”正常 IO 机制所需的所有接口,则返回 true。这因句柄而异。
PerlIO_fast_gets(f) = PerlIO_has_cntptr(f) && \
PerlIO_canset_cnt(f) && \
'Can set pointer into buffer'
实现可以返回指向“缓冲区”中当前位置的指针以及缓冲区中可用字节数。不要使用此函数 - 使用 PerlIO_fast_gets。
返回缓冲区中可读字节数。零或负返回值表示没有更多字节可用。
返回缓冲区中下一个可读字节的指针,仅当 PerlIO_get_cnt() 返回正值时,通过指针访问(解引用)才是安全的。仅允许不超过 PerlIO_get_cnt() 返回值的正偏移量。
设置缓冲区中的指针,以及缓冲区中剩余的字节数。仅应用于将指针设置为之前调用 PerlIO_get_ptr
和 PerlIO_get_cnt
所隐含的范围内。这两个值必须彼此一致(实现可能只使用其中一个或两个,或者可能需要两者)。
实现可以调整其对缓冲区中字节数的理解。不要使用此函数 - 使用 PerlIO_fast_gets。
晦涩 - 设置缓冲区中字节的数量。已弃用。仅当 PerlIO_canset_cnt() 返回 true 时才可用。目前仅在 doio.c 中使用,以强制将小于 -1 的计数设置为 -1。也许应该改为 PerlIO_set_empty 或类似的函数。如果“计数”是从指针和“限制”推断出来的,则此调用实际上可能什么也不做。不要使用此函数 - 使用 PerlIO_set_ptrcnt()。
如果实现具有缓冲区,并且可以返回指向整个缓冲区及其大小的指针,则返回 true。perl 用于-T / -B 测试。其他用途将非常晦涩...
返回缓冲区的起始位置。仅访问缓冲区中不超过 PerlIO_get_bufsiz() 返回值的正偏移量。
返回缓冲区中的总字节数,这既不是可以读取的字节数,也不是分配给缓冲区的内存量。而是操作系统和/或实现上次请求 IO 时碰巧read()
(或其他)的值。
这是 USE_PERLIO 实现的新接口。对于其他实现,只允许使用 ":crlf" 和 ":raw" 层,并且会静默忽略它们。(从 perl5.8 开始,":raw" 已被弃用。)对于可移植情况,请使用下面的 PerlIO_binmode()。
perl 的 binmode
运算符使用的钩子。ptype 是 perl 用于表示 IO 种类的字符
imode 是 O_BINARY
或 O_TEXT
。
layers 是要应用的层字符串;在非 USE_PERLIO 情况下,只有 ":crlf" 有意义。(从 perl5.8 开始,":raw" 已被弃用,建议使用 NULL。)
可移植情况是
PerlIO_binmode(aTHX_ f,ptype,O_BINARY,NULL);
and
PerlIO_binmode(aTHX_ f,ptype,O_TEXT,":crlf");
在 Unix 上,这些调用可能根本没有效果。在其他地方,它们会将 "\n" 更改为 CR,LF 转换,并且可能会导致在读取时写入或识别特殊的文本“文件结束”指示符。在对句柄进行任何 IO 操作后进行调用所产生的效果取决于实现。(它可能会被忽略,影响已经缓冲的任何数据,或者只应用于后续数据。)
PerlIO_debug 是一个类似 printf() 的函数,可用于调试。没有返回值。它主要用于 PerlIO 内部,在其中使用真正的 printf、warn() 等会导致递归调用 PerlIO,从而成为问题。
PerlIO_debug 写入由 $ENV{'PERLIO_DEBUG'} 命名的文件,或者如果环境变量未定义,则默认为 stderr。典型用法可能是
Bourne shells (sh, ksh, bash, zsh, ash, ...):
PERLIO_DEBUG=/tmp/perliodebug.log ./perl -Di somescript some args
Csh/Tcsh:
setenv PERLIO_DEBUG /tmp/perliodebug.log
./perl -Di somescript some args
If you have the "env" utility:
env PERLIO_DEBUG=/tmp/perliodebug.log ./perl -Di somescript args
Win32:
set PERLIO_DEBUG=perliodebug.log
perl -Di somescript some args
在没有 -DDEBUGGING
构建的 Perl 上,或者当未指定 -Di
命令行开关时,或者在 taint 下,PerlIO_debug() 是一个空操作。