使用 perl 读取系统文件而不在打开时发出额外的搜索系统调用
Reading system files with perl without issuing extra seek syscalls on open
我正在尝试使用 perl 从 /proc
和 /sys
linux 伪文件系统 (procfs and sysfs) 中解析一些伪文件。此类文件不同于常规文件 - 它们由自定义文件操作处理程序实现。其中大多数 stat
的大小为零,有些无法打开以供读取,有些则无法写入。有时它们实现不正确(这是错误,但它已经在内核中),我仍然想直接从 perl 读取它们而不启动一些辅助工具。
有使用 perl 读取 /proc/loadavg
的快速示例,此文件已正确实现:
perl -e 'open F,"</proc/loadavg"; $_=<F>; print '
通过命令的 strace
我可以检查 perl 如何实现 open
函数:
$ strace perl -e 'open F,"</proc/loadavg"; $_=<F>; print ' 2>&1 | egrep -A5 ^open.*loadavg
open("/proc/loadavg", O_RDONLY) = 3
ioctl(...something strange...) = -1 ENOTTY
lseek(3, 0, SEEK_CUR) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
open
perl 函数使用了 lseek
个系统调用。
使用 cat /proc/loadavg
的 strace 没有额外的 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
文件操作,不会向 read
(或 write
)系统调用提供任何有用的数据 seek
.
是否可以在不调用额外 lseek
的情况下打开文件以在 perl5(无外部模块)中读取? (并且不使用 system("cat < /proc/loadavg")
)
有没有办法在不调用额外的情况下打开文件以在 perl5 中写入 lseek
?
有 sysopen,但它也有额外的 lseek:perl -e 'use Fcntl;sysopen(F,"/proc/loadavg",O_RDONLY);sysread(F,$_,2048); print '
如您所见,Perl 的内置 open
掩盖了相当多的魔法。如果魔法挡住了你的路,sysopen
和 POSIX::open()
会提供递减的魔法等级。 POSIX::open()
非常不神奇,它 returns 文件描述符而不是 Perl 文件句柄,你必须使用 POSIX::read()
而不是普通的 Perl 运算符来从中获取数据。如果这对你的情况来说不够原始,你可能运气不好。
POSIX
模块是核心 perl 发行版的一部分,因为它是 Perl 5 的第一个版本,所以如果你没有它,你的 Perl 安装就会瘫痪。
如果你想要真正的低级并避免 POSIX::open()
中的 mmap
(并且避免加载巨大的 POSIX
模块),请执行 syscall()
是你自己。可能想 require syscall.ph
获取 SYS_open
和 SYS_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()
)
openat(AT_FDCWD, "/proc/loadavg", O_RDONLY) = 3
mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7f2389f0d000
read(3, "1.22 2.51 1.54 3/206 18145\n", 9999) = 27
尝试这样的事情:
strace perl -MFcntl -E'$p="/proc/loadavg"; $fd=syscall 2, $p, O_RDONLY; $bf =
"[=12=]"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
编辑:
关于,
There is [=31=]
special char in end of the read. How can I parse multiline from POSIX::read, there is no while(<FILE>)
now.
请注意,当您“while(<FILE>)
”时,您实际上只是一次调用 read()
一个字节并检查 '\n'
字符——或任何您的 $/
(输入记录分隔符)设置为(您可以通过 strace
确认)。
因此,检查 $/
的简单循环就足够了。 (注意 read()
returns,成功时,读取的字节数(0 表示 EOF)。这是单个“readline
”的粗略示例:
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
请注意,如果您想要便携性,syscall
-ing 可能是其中之一最糟糕的选择,但这是特异性的代价。 (从这个意义上说,POSIX::open/read/close()
也好不了多少。)。为了保持可移植性,您最好使用 ,并忽略对 fstat
和 fcntl
;
的额外调用
我正在尝试使用 perl 从 /proc
和 /sys
linux 伪文件系统 (procfs and sysfs) 中解析一些伪文件。此类文件不同于常规文件 - 它们由自定义文件操作处理程序实现。其中大多数 stat
的大小为零,有些无法打开以供读取,有些则无法写入。有时它们实现不正确(这是错误,但它已经在内核中),我仍然想直接从 perl 读取它们而不启动一些辅助工具。
有使用 perl 读取 /proc/loadavg
的快速示例,此文件已正确实现:
perl -e 'open F,"</proc/loadavg"; $_=<F>; print '
通过命令的 strace
我可以检查 perl 如何实现 open
函数:
$ strace perl -e 'open F,"</proc/loadavg"; $_=<F>; print ' 2>&1 | egrep -A5 ^open.*loadavg
open("/proc/loadavg", O_RDONLY) = 3
ioctl(...something strange...) = -1 ENOTTY
lseek(3, 0, SEEK_CUR) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
open
perl 函数使用了 lseek
个系统调用。
使用 cat /proc/loadavg
的 strace 没有额外的 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
文件操作,不会向 read
(或 write
)系统调用提供任何有用的数据 seek
.
是否可以在不调用额外 lseek
的情况下打开文件以在 perl5(无外部模块)中读取? (并且不使用 system("cat < /proc/loadavg")
)
有没有办法在不调用额外的情况下打开文件以在 perl5 中写入 lseek
?
有 sysopen,但它也有额外的 lseek:perl -e 'use Fcntl;sysopen(F,"/proc/loadavg",O_RDONLY);sysread(F,$_,2048); print '
如您所见,Perl 的内置 open
掩盖了相当多的魔法。如果魔法挡住了你的路,sysopen
和 POSIX::open()
会提供递减的魔法等级。 POSIX::open()
非常不神奇,它 returns 文件描述符而不是 Perl 文件句柄,你必须使用 POSIX::read()
而不是普通的 Perl 运算符来从中获取数据。如果这对你的情况来说不够原始,你可能运气不好。
POSIX
模块是核心 perl 发行版的一部分,因为它是 Perl 5 的第一个版本,所以如果你没有它,你的 Perl 安装就会瘫痪。
如果你想要真正的低级并避免 POSIX::open()
中的 mmap
(并且避免加载巨大的 POSIX
模块),请执行 syscall()
是你自己。可能想 require syscall.ph
获取 SYS_open
和 SYS_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()
)
openat(AT_FDCWD, "/proc/loadavg", O_RDONLY) = 3
mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7f2389f0d000
read(3, "1.22 2.51 1.54 3/206 18145\n", 9999) = 27
尝试这样的事情:
strace perl -MFcntl -E'$p="/proc/loadavg"; $fd=syscall 2, $p, O_RDONLY; $bf =
"[=12=]"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
编辑:
关于
There is
[=31=]
special char in end of the read. How can I parse multiline from POSIX::read, there is nowhile(<FILE>)
now.
请注意,当您“while(<FILE>)
”时,您实际上只是一次调用 read()
一个字节并检查 '\n'
字符——或任何您的 $/
(输入记录分隔符)设置为(您可以通过 strace
确认)。
因此,检查 $/
的简单循环就足够了。 (注意 read()
returns,成功时,读取的字节数(0 表示 EOF)。这是单个“readline
”的粗略示例:
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
请注意,如果您想要便携性,syscall
-ing 可能是其中之一最糟糕的选择,但这是特异性的代价。 (从这个意义上说,POSIX::open/read/close()
也好不了多少。)。为了保持可移植性,您最好使用 fstat
和 fcntl
;