Linux 使用perl读取系统文件而不在open上发出额外的seek系统调用

Linux 使用perl读取系统文件而不在open上发出额外的seek系统调用,linux,perl,file-io,seek,procfs,Linux,Perl,File Io,Seek,Procfs,我正在尝试使用perl解析来自/proc和/syslinux伪文件系统(和)的一些伪文件。这类文件不同于常规文件-它们由自定义文件操作处理程序实现。它们中的大多数对stat的大小为零,一些无法打开以供读取,另一些无法写入。有时它们的实现不正确(这是错误的,但它已经在内核中),我仍然希望直接从perl读取它们,而不启动一些帮助工具 有一个使用perl读取/proc/loadavg的快速示例,该文件已正确实现: perl -e 'open F,"</proc/loadavg"; $_=<

我正在尝试使用perl解析来自
/proc
/sys
linux伪文件系统(和)的一些伪文件。这类文件不同于常规文件-它们由自定义文件操作处理程序实现。它们中的大多数对stat的大小为零,一些无法打开以供读取,另一些无法写入。有时它们的实现不正确(这是错误的,但它已经在内核中),我仍然希望直接从perl读取它们,而不启动一些帮助工具

有一个使用perl读取
/proc/loadavg
的快速示例,该文件已正确实现:

perl -e 'open F,"</proc/loadavg"; $_=<F>; print '
lseek
系统调用由
open
perl函数使用

使用
cat/proc/loadavg
策略时,没有额外的
seek
类型的系统调用:

$ strace cat /proc/loadavg 2>&1 | egrep -A2 ^open.*loadavg
open("/proc/loadavg", O_RDONLY)         = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
我要读取(或写入)的特殊文件错误地执行了
seek
文件操作,并且在
seek
之后不会向
read
(或
write
)系统调用提供任何有用的数据

有没有办法在不调用额外的
lseek
的情况下打开文件以读取perl5(无外部模块)?(并且不使用
系统(“cat

有没有办法在不调用额外的
lseek
的情况下打开文件以在perl5中写入


有sysopen,但它也有额外的lseek:
perl-e'使用Fcntl;sysopen(F,“/proc/loadavg”,仅限ordu);sysread(F,$\uu2048);打印'

正如您所注意到的,Perl的内置
打开
屏蔽了相当多的魔力。如果这种魔力妨碍了你,那么会有
sysopen
POSIX::open()
提供降低的魔力
POSIX::open()
非常不神奇,它返回的是文件描述符而不是Perl文件句柄,您必须使用
POSIX::read()
而不是普通的Perl操作符从中获取数据。如果这还不适合你的情况,你可能就不走运了


自从perl 5第一次发布以来,
POSIX
模块是核心perl发行版的一部分,因此如果您没有它,您的perl安装将受到严重影响。

如果您想真正做到低级别,甚至避免使用
POSIX::open()
中的
mmap
(同时避免加载庞大的
POSIX
模块),自己执行
syscall()
s。可能希望
需要syscall.ph
来获取
SYS\u open
SYS\u read
的值(如果您不知道)(对我来说,我知道
read
write
open
分别是
0
1
2
,这对于下面的
syscall
函数很重要)

以下代码:

strace perl -mPOSIX -e'$fd=POSIX::open("/proc/loadavg");POSIX::read($fd, 
$_, 9999);' 2>&1 | egrep -A2 '^open.*loadavg'
给出如下内容(对于我来说,
open()
openat()

试着这样做:

strace perl -MFcntl -E'$p="/proc/loadavg"; $fd=syscall 2, $p, O_RDONLY; $bf = 
"\0"x50; syscall 0, $fd, $bf, 50' 2>&1 | egrep -A1 '^open.*loadavg'
并获得:

open("/proc/loadavg", O_RDONLY) = 3
read(3, "0.45 0.18 0.20 2/241 12349\n", 50) = 27
编辑
关于

读取的末尾有
\0
特殊字符。如何从POSIX::read解析多行,现在没有
while()

请注意,当您“
while()
”时,您实际上只是一次调用
read()
一个字节,并检查
'\n'
字符——或者设置为
$/
(输入记录分隔符)的任何内容(您可以通过
strace
确认)

因此,检查
$/
的简单循环就足够了。(请注意,
read()
成功返回读取的字节数(0表示EOF)。下面是单个“
readline
”的粗略示例:


请注意,如果您打算实现任何类型的可移植性,
syscall
-ing可能是最糟糕的选择之一,但这是专用性的代价。(
POSIX::open/read/close()
)在这个意义上也没有多大改善。要保持可移植性,最好使用@ikegami的注释(在我试用过的linux设备上,
open F,“@ikegami,什么样的黑魔法(
,如文档中所述,PerlIO层在中有文档记录。请查看
perl-MPerlIO-E'open F,“@ikegame,还有
fstat
带有
my$SET,”>:unix“../driver\u命令;print$SET$command。”\n";close$SET
;我可以禁用它吗?我的脚本仍然无法与驱动程序通信;但是简单的
回显相同的命令>../driver\u命令
可以工作,我在这里只看到额外的fstat。Calle,请添加一些文档链接。ikegami推荐
Calle,谢谢,POSIX::open+read可以工作,没有任何额外的系统调用:
perl-e'usePOSIX();$fd=POSIX::open(“/proc/loadavg”);$bytes=POSIX::read($fd,$buf,30);POSIX::close($fd);print$bytes.“read:“$buf;”结果为
27 read:0.16 0.09 0.02…”读取的末尾有“\0”特殊字符。如何从POSIX::read解析多行,现在没有
while()
。“read、write和open是0,1,2”对于任何linux平台,它们总是0、1和2吗?不;直接使用系统调用不是最方便的。请尝试
require syscall.ph
,以了解
系统读取的内容
系统写入的内容
系统打开的
常量适合您。旁注:我没有关闭文件描述符的任何方法。您可以使用
PO六::close()
,但您也可以使用
syscall(SYS\u close,$fd)
,(对我来说这是
syscall(3,$fd)
)。
strace perl -MFcntl -E'$p="/proc/loadavg"; $fd=syscall 2, $p, O_RDONLY; $bf = 
"\0"x50; syscall 0, $fd, $bf, 50' 2>&1 | egrep -A1 '^open.*loadavg'
open("/proc/loadavg", O_RDONLY) = 3
read(3, "0.45 0.18 0.20 2/241 12349\n", 50) = 27
require 'syscall.ph';
require Fcntl;
my($path, $fd, $buf, $res);
$path = '/proc/meminfo';
$fd = syscall SYS_open(), $path, O_RDONLY;
$buf = ' ';
$res = '';
$res .= $buf while syscall SYS_read(), $fd, $buf, 1 and $buf ne $/;
syscall SYS_close(), $fd; # optional in this case