Чтение системных файлов с помощью Perl без выдачи дополнительных поисковых запросов при открытии
Я пытаюсь использовать Perl для анализа некоторых псевдо-файлов из /proc
а также /sys
псевдо файловые системы linux ( procfs и sysfs). Такие файлы отличаются от обычных файлов - они реализуются с помощью пользовательских обработчиков файловых операций. Большинство из них имеют нулевой размер для stat
некоторые не могут быть открыты для чтения, другие не могут быть написаны. Иногда они реализованы неправильно (это ошибка, но она уже есть в ядре), и я все еще хочу читать их напрямую из Perl без запуска некоторых вспомогательных инструментов.
Есть быстрый пример чтения /proc/loadavg
с помощью Perl этот файл реализован правильно:
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
Есть 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
файловые операции и не даст никаких полезных данных read
(или для write
) системный вызов после seek
,
Есть ли способ открытия файлов для чтения в perl5 (без внешних модулей) без вызова extra lseek
? (и без использования system("cat < /proc/loadavg")
)
Есть ли способ открытия файлов для записи в perl5 без вызова extra lseek
?
Существует sysopen, но он также выполняет дополнительные действия: perl -e 'use Fcntl;sysopen(F,"/proc/loadavg",O_RDONLY);sysread(F,$_,2048); print '
2 ответа
Как вы заметили, встроенный Perl open
маскирует немного магии. Если эта магия встает на вашем пути, есть sysopen
а также POSIX::open()
которые предлагают уменьшающие степени магии. POSIX::open()
достаточно немагичен, что возвращает файловые дескрипторы, а не файловые дескрипторы Perl, и вы должны использовать POSIX::read()
вместо обычных операторов Perl, чтобы получить данные из него. Если этого недостаточно для ваших обстоятельств, вам может не повезти.
POSIX
Модуль является частью основного дистрибутива Perl с самого первого выпуска Perl 5, поэтому, если у вас его нет, ваша установка Perl будет повреждена.
Если вы хотите пойти на самом низком уровне и избежать даже mmap
от POSIX::open()
(а также избегайте загрузки огромного 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 =
"\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
РЕДАКТИРОВАТЬ:
Что касается комментария от @osgx,
Есть
\0
специальный символ в конце чтения. Как мне разобрать многострочный из POSIX::read, там нетwhile(<FILE>)
сейчас.
Обратите внимание, что когда выwhile(<FILE>)
"Вы действительно просто звоните read()
один байт за раз и проверка на '\n'
чар - или что там у тебя $/
(разделитель входной записи) установлен на (вы можете подтвердить это с помощью strace
).
Таким образом, простой цикл для проверки $/
может быть достаточно. (Обратите внимание, что read()
в случае успеха возвращает количество прочитанных байтов (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
Возможно, это один из худших вариантов, но это цена специфичности. (POSIX::open/read/close()
не намного лучше в этом смысле.). Чтобы сохранить мобильность, лучше использовать комментарий @ikegami (<: unix) и игнорировать дополнительные вызовы fstat
а также fcntl
;