выяснить, какие файловые дескрипторы имеют одно и то же «описание открытого файла»

17

Если я это сделаю (в Bourne-подобной оболочке):

exec 3> file 4>&3 5> file 6>> file

Файловые дескрипторы 3 и 4, так как 4 был dup()отредактирован от 3, имеют одинаковое описание открытого файла (те же свойства, то же смещение внутри файла ...). В то время как файловые дескрипторы 5 и 6 этого процесса находятся в разных описаниях открытого файла (например, каждый из них имеет свой собственный указатель в файле).

Теперь в lsofрезультате мы видим только:

zsh     21519 stephane    3w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    4w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    5w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    6w   REG  254,2        0 10505865 /home/stephane/file

Это немного лучше с lsof +fg:

zsh     21519 stephane    3w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    4w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    5w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    6w   REG       W,AP,LG  254,2        0 10505865 /home/stephane/file

(здесь, в Linux 3.16), поскольку мы видим, что fd 6 имеет разные флаги, поэтому описание открытого файла должно отличаться от описания на fd 3, 4 или 5, но из этого нельзя сказать, что fd 5 находится на другое описание открытого файла . С помощью -oмы также можем увидеть смещение, но опять же смещение не гарантирует того же описания открытого файла .

Есть ли ненавязчивые 1 способ это выяснить? Внешне или для собственных файловых дескрипторов процесса?


1 . Один эвристический подход может состоять в том, чтобы изменить флаги одного fd fcntl()и посмотреть, как в результате у других файловых дескрипторов обновляются их флаги, но это, очевидно, не идеально и не дурак.

Стефан Шазелас
источник
Этот подход должен работать, в принципе, и не должен быть слишком разрушительным в большинстве сценариев: сначала разветвите ребенка (с помощью ptrace, если делаете это извне). Затем в дочернем элементе сделайте что-нибудь с файловым дескриптором, чтобы это не влияло на другие процессы. В Linux аренда должна работать для этого.
Жиль "ТАК - перестать быть злым"
@ Жиль, спасибо, но это более или менее подход, который я предлагаю уже в вопросе. аренда (предполагается, что вы имеете в виду F_SETLEASE fcntl, спасибо, что сообщили мне о них, кстати) будет работать только для обычных файлов, которыми вы владеете, а не в том случае, если есть другое «записать» описание открытого файла в тот же файл (EBUSY), и это не совсем -intrusive.
Стефан Шазелас
Вы отказались от этого вопроса? Я опубликовал некоторую информацию о том, как SystemTap может делать то, что вы хотите, но вы не пометили ни одного ответа как завершенный ...?
Ажрей

Ответы:

2

Для Linux 3.5 и выше это можно сделать с помощью kcmp (3) :

KCMP_FILE

  • Проверьте , является ли дескриптор файла idx1 в процессе PID1 относится к тому же описанию открытого файла (см открытого (2) ) в качестве дескриптора файл idx2 в процессе PID2 . Наличие двух файловых дескрипторов, которые ссылаются на одно и то же описание открытого файла, может возникать в результате dup (2) (и аналогичного) fork (2) или передачи дескрипторов файлов через сокет домена (см. Unix (7) ).

Страница man предоставляет пример специально для запрашиваемого варианта использования OP. Обратите внимание, что этот системный вызов требует, чтобы ядро ​​было скомпилировано с помощью CONFIG_CHECKPOINT_RESTOREset.

minmaxavg
источник
Благодарю. Именно то, что я искал. Обратите внимание, что если вы не являетесь суперпользователем, это должны быть два ваших процесса (и не быть setuid / setgid ...) (понятно)
Стефан Шазелас
@ StéphaneChazelas Точно. Если по какой-то причине поддержка CPIU не встроена в ваше ядро, и вы не хотите его перестраивать, то я полагаю, что вы всегда можете написать модуль ядра, который экспортирует некоторый пользовательский интерфейс, который позволяет сравнивать struct file *указатели.
minmaxavg
3

То, что вы хотите сравнить, это struct fileуказатели, на которые указывают файловые дескрипторы. (Внутри ядра есть одна task_structструктура данных для каждого потока. Он содержит указатель на другую структуру, называемую files_struct. И эта структура содержит массив указателей, каждый из которых на a struct file. Это то, struct fileчто содержит смещение поиска, флаги открытия и несколько других полей.)

Я не знаю ни одного видимого для пользователя способа увидеть указатели в files_structдругом, кроме как использовать некоторые навязчивые инструменты. Например, SystemTap может быть присвоен PID, и он может найти соответствующие task_structи следовать указателям. Если вы ищете пассивный, хотя, я думаю, что об этом. Dell давно выпустила инструмент под названием KME (Kernel Memory Editor), который предоставил интерфейс в виде электронных таблиц для оперативной памяти ядра, и он мог делать то, что вы хотите, но он никогда не был перенесен на 64-битную. (Я пытался, но так и не заработал, и не знал почему.)

Одна из причин, по которой вы не находите lsofполезными, заключается в том, что он также не видит эти указатели (но посмотрите на +fвариант для не-Linux систем). Вы можете теоретически сравнить все поля в struct fileи думать, что эти две структуры одинаковы, но все же они могут быть из отдельных open(2)вызовов.

Посмотрите на сценарий pfiles SystemTap для идей. Если вы изменили его, чтобы распечатать адрес struct file, у вас будет решение. Вы также можете проверить open_file_by_pid.stp, так как в нем есть функция, которая проходит files_struct, т.е. таблица дескрипторов файлов, глядя на struct fileобъекты ...

Могу я спросить, чего ты пытаешься достичь?

Azhrei
источник
Я должен признать, что не могу вспомнить тот самый случай, когда мне это было нужно. Некоторая отладка или судебная задача, без сомнения.
Стефан Шазелас
Я с нетерпением жду кода системной записи PoC :-)
Стефан Шазелас
Прежде чем опубликовать вопрос, я взглянул на подходы systemtap или / proc / kcore. Сложной задачей было получить информацию для каждого руководителя каждой задачи . Самым многообещающим подходом, который я нашел, было подключение к функциям, которые генерируют содержимое каталога / proc / * / task / fd, но единственные выполнимые вещи, которые я мог придумать, включали в себя перехват определенных номеров строк в исходном файле, поэтому переносим из одной версии ядра в другую. Вы действительно не можете перебрать список задач в системной тапе. Может быть возможно через / proc / kcore, но слишком много усилий и, вероятно, ненадежно.
Стефан Шазелас
Спасибо за лучший ответ до сих пор. Я посмотрю на ваши указатели.
Стефан Шазелас
Что вы можете! Настройте probe beginблок и сделайте так, чтобы он использовал for_each_processмакрос в блоке кода C, встроенного в скрипт (для встраивания кода C вам потребуется использовать SystemTap в режиме «гуру»). Фактически, чтобы сделать это интересным (!), Вы можете использовать один из ассоциативных массивов SystemTap; используйте files_structадрес в качестве ключа, а список PID / TID в качестве значений. Теперь у вас есть список каждого открытого файла и какие задачи делятся ими (они могут быть разделены между родителем / ребенком). Ответьте еще раз, если хотите обсудить SystemTap.
Ажрей
0

Вот решение для Linux: / proc / self / fd - это каталог символических ссылок для дескрипторов открытых файлов в текущем процессе. Вы можете просто сравнить значения ссылок. Это становится более сложным при использовании дочернего процесса, потому что у дочернего процесса будет другая / proc / self, потому что это символическая ссылка, зависящая от pid. Вы можете обойти эту проблему, используя / proc / $$ / fd, где $$ - желаемый pid.

hildred
источник
Благодарю. Но я не об этом. В Linux lsof действительно использует / proc / pid / fd для получения путей для каждого файлового дескриптора и / proc / pid / fdinfo для флагов. Но я хочу, чтобы для двух fds одного и того же файла они указывали на одно и то же описание открытого файла или два дескриптора файла были открыты независимо.
Стефан Шазелас
Хорошо, после того, как вы нашли пары файловых дескрипторов, которые открыты для одного и того же имени файла, сделайте анализ обоих и сравните результаты, если они отличаются, они раздельные. Если они одинаковые, ищите один файловый дескриптор и повторяйте. Если они все еще совпадают, они одинаковы.
hildred
Ну, это более навязчивый вариант эвристического подхода, о котором я говорю в этом вопросе, и он работает только для обычных файлов (не для сокетов, устройств (например, терминалов), каналов ...).
Стефан Шазелас