Безопасно ли анализировать файл / proc /? - PullRequest
148 голосов
/ 19 апреля 2011

Я хочу разобрать /proc/net/tcp/, но безопасно ли это?

Как мне открывать и читать файлы из /proc/ и не бояться, что какой-то другой процесс (или сама ОС) изменит его одновременно?

Ответы [ 7 ]

105 голосов
/ 04 мая 2011

Как правило, нет. (поэтому большинство ответов здесь неправильные.) Это может быть безопасным, в зависимости от того, какое свойство вы хотите. Но это может привести к ошибкам в вашем коде, если вы слишком много думаете о согласованности файла в /proc. Например, см. эту ошибку, возникшую из предположения, что /proc/mounts был непротиворечивым снимком .

Например:

  • /proc/uptime равен полностью атомарному , как кто-то упоминал в другом ответе - но только начиная с Linux 2.6.30 , что меньше чем два года. Таким образом, даже этот крошечный, тривиальный файл до тех пор находился в состоянии состязания и все еще находится в большинстве корпоративных ядер. См. fs/proc/uptime.c для текущего источника или коммит, который сделал его атомарным . В ядре до 2.6.30 вы можете open файл, read его часть, тогда, если вы позже вернетесь и read снова, полученный вами фрагмент будет несовместим с первым фрагментом. (Я только что продемонстрировал это - попробуйте сами для удовольствия.)

  • /proc/mounts является атомарным в одном системном вызове read. Так что, если вы read весь файл сразу, вы получите единый согласованный снимок точек монтирования в системе. Однако, если вы используете несколько системных вызовов read - и если файл большой, это именно то, что произойдет, если вы используете обычные библиотеки ввода-вывода и не обращаете особого внимания на эту проблему - вы будете подвержены в состоянии гонки. Вы не только не получите непротиворечивый снимок, но и точки монтирования, которые присутствовали до того, как вы начали и никогда не прекращали присутствовать, могут пропасть в том, что вы видите. Чтобы увидеть, что она атомарна для одного read(), посмотрите на m_start() в fs/namespace.c и посмотрите, как он захватывает семафор, который защищает список точек монтирования, который он хранит до m_stop(), который называется когда read() сделано. Чтобы увидеть, что может пойти не так, посмотрите эту ошибку прошлого года (ту же, что я упоминал выше) в другом высококачественном программном обеспечении, которое беспечно читает /proc/mounts.

  • /proc/net/tcp, о котором вы на самом деле спрашиваете, даже менее последовательно, чем это. Он атомный только в каждой строке таблицы . Чтобы увидеть это, посмотрите на listening_get_next() в net/ipv4/tcp_ipv4.c и established_get_next() чуть ниже в том же файле, и посмотрите блокировки, которые они снимают для каждой записи по очереди. У меня нет удобного кода воспроизведения, чтобы продемонстрировать отсутствие согласованности от строки к строке, но там нет блокировок (или чего-либо еще), которые бы делали его согласованным. Что имеет смысл, если подумать - сетевые решения часто являются очень загруженной частью системы, поэтому не стоит тратить время на представление единого представления в этом диагностическом инструменте.

Другая часть, которая сохраняет /proc/net/tcp атомарность в каждой строке, - это буферизация в seq_read(), которую вы можете прочитать в fs/seq_file.c. Это гарантирует, что после того, как вы read() часть одной строки, текст всей строки будет сохранен в буфере, так что следующий read() получит оставшуюся часть этой строки перед началом новой. Тот же самый механизм используется в /proc/mounts для сохранения атомарности каждой строки, даже если вы делаете несколько вызовов read(), и это также механизм, который /proc/uptime использует в более новых ядрах, чтобы оставаться атомарным. Этот механизм не буферизирует весь файл, потому что ядро ​​осторожно относится к использованию памяти.

Большинство файлов в /proc будут, по крайней мере, такими же непротиворечивыми, как /proc/net/tcp, с каждой строкой непротиворечивым изображением одной записи в любой информации, которую они предоставляют, потому что большинство из них используют одну и ту же seq_file абстракцию. Как показывает пример /proc/uptime, некоторые файлы все еще переносились на использование seq_file совсем недавно, в 2009 году; Бьюсь об заклад, есть некоторые, которые используют старые механизмы и даже не имеют такого уровня атомарности. Эти предостережения редко документируются. Для данного файла ваша единственная гарантия - прочитать исходный код.

В случае /proc/net/tcp, вы можете прочитать и проанализировать каждую строку без страха.Но если вы попытаетесь сделать какие-либо выводы из нескольких строк одновременно - будьте осторожны, другие процессы и ядро ​​ меняют его во время чтения, и вы, вероятно, создаете ошибку.

43 голосов
/ 19 апреля 2011

Хотя файлы в /proc отображаются как обычные файлы в пользовательском пространстве, на самом деле это не файлы, а сущности, которые поддерживают стандартные файловые операции из пользовательского пространства (open, read, close). Обратите внимание, что это сильно отличается от наличия обычного файла на диске, который изменяется ядром.

Все, что делает ядро, это печатает свое внутреннее состояние в своей собственной памяти, используя sprintf -подобная функция, и эта память копируется в пространство пользователя всякий раз, когда вы выполняете системный вызов read(2).

Ядро обрабатывает эти вызовы совершенно иначе, чем для обычных файлов, что может означать, что весь снимокданных, которые вы будете читать, могут быть готовы к тому моменту, когда вы их open(2), в то время как ядро ​​обеспечивает согласованность и атомарность одновременных вызовов.Я нигде этого не читал, но на самом деле иначе не имеет смысла.

Мой совет - взглянуть на реализацию файла proc в вашем конкретном Unix-стиле.Это действительно проблема реализации (так же как формат и содержимое выходных данных), которая не регулируется стандартом.

Простейшим примером будет реализация uptimeфайл proc в Linux.Обратите внимание, как весь буфер создается в функции обратного вызова, переданной в single_open.

16 голосов
/ 19 апреля 2011

/ proc - это виртуальная файловая система: фактически она просто предоставляет удобный обзор внутренних компонентов ядра. Это определенно безопасно читать (вот почему это здесь), но это рискованно в долгосрочной перспективе, так как внутренняя часть этих виртуальных файлов может развиваться с более новой версией ядра.

EDIT

Более подробная информация доступна в документации proc в ядре Linux doc , глава 1.4 Сеть Я не могу найти информацию, как информация эволюционирует с течением времени. Я думал, что это было заморожено на открытом воздухе, но не могу дать однозначного ответа.

EDIT2

Согласно Sco doc (не Linux, но я уверен, что все разновидности * nix ведут себя так)

Хотя состояние процесса и следовательно, содержимое / proc файлы могут меняться от мгновенного к мгновенное, одно чтение (2) из ​​/ proc файл гарантированно вернет «нормальное» представление о том, что есть, чтение будет атомным снимок состояния процесса. Такая гарантия не распространяется на последовательные операции чтения применяются к / proc файл для запущенного процесса. В Кроме того, атомарность конкретно не гарантируется для любого ввода / вывода, примененного к файл as (адресное пространство); содержимое адреса любого процесса пространство может быть одновременно изменено LWP этого процесса или любого другого процесс в системе.

14 голосов
/ 19 апреля 2011

API procfs в ядре Linux предоставляет интерфейс, который гарантирует, что чтения возвращают согласованные данные. Прочитайте комментарии в __proc_file_read. Пункт 1) в большом блоке комментариев объясняет этот интерфейс.

Это, как говорится, конечно, до реализации конкретного файла proc, чтобы правильно использовать этот интерфейс, чтобы убедиться, что его возвращаемые данные согласованы. Итак, чтобы ответить на ваш вопрос: нет, ядро ​​не гарантирует согласованность файлов proc во время чтения, но предоставляет средства для реализации этих файлов для обеспечения согласованности.

6 голосов
/ 20 апреля 2011

У меня есть источник для Linux 2.6.27.8, так как я сейчас занимаюсь разработкой драйвера для встроенной цели ARM.

Файл ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.c в строке 934 содержит, например,

    seq_printf(seq, "%4d: %08X:%04X %08X:%04X"
            " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n",
            i, src, srcp, dest, destp, sp->sk_state,
            atomic_read(&sp->sk_wmem_alloc),
            atomic_read(&sp->sk_rmem_alloc),
            0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp),
            atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));

, который выводит

[wally@zenetfedora ~]$ cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 15160 1 f552de00 299
   1: 00000000:C775 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13237 1 f552ca00 299
...

в функции raw_sock_seq_show(), которая является частью иерархии procfs функций обработки.Текст не генерируется до тех пор, пока из файла /proc/net/tcp не будет сделан запрос read(), что является разумным механизмом, поскольку операции чтения procfs , безусловно, встречаются гораздо реже, чем обновление информации.

Некоторые драйверы(например, мой) реализовать функцию proc_read с одним sprintf().Дополнительным усложнением в реализации драйверов ядра является обработка потенциально очень длинных выходных данных, которые могут не помещаться в промежуточный буфер пространства ядра во время одного чтения.

Я протестировал это с программой, использующей буфер чтения 64K, ноэто приводит к тому, что в моей системе буфер пространства ядра составляет 3072 байта для proc_read для возврата данных.Многократные вызовы с продвигающимися указателями необходимы, чтобы получить больше, чем столько возвращаемого текста.Я не знаю, как правильно сделать согласованные возвращаемые данные, когда требуется более одного ввода-вывода.Конечно, каждая запись в /proc/net/tcp является самосогласованной.Существует некоторая вероятность того, что линии рядом друг с другом являются снимками в разное время.

3 голосов
/ 19 апреля 2011

Если не считать неизвестных ошибок, в /proc нет условий гонки, которые могли бы привести к чтению поврежденных данных или сочетанию старых и новых данных.В этом смысле это безопасно.Однако все еще существует условие, что большая часть данных, которые вы читаете из /proc, потенциально устарела, как только она сгенерирована, и тем более к тому времени, когда вы приступите к чтению / обработке.Например, процессы могут умереть в любое время, и новому процессу может быть назначен тот же pid;единственные идентификаторы процессов, которые вы можете использовать без условий гонки, - это ваши собственные дочерние процессы ».То же самое касается информации о сети (открытые порты и т. Д.) И действительно большей части информации в /proc.Я бы посчитал плохой и опасной практикой полагаться на точность любых данных в /proc, кроме данных о вашем собственном процессе и, возможно, его дочерних процессах.Конечно, все еще может быть полезно представить другую информацию из /proc пользователю / администратору для информативности / регистрации / и т.д.цели.

2 голосов
/ 22 апреля 2011

Когда вы читаете из файла / proc, ядро ​​вызывает функцию, которая была заранее зарегистрирована, чтобы быть функцией «чтения» для этого файла proc. Смотрите функцию __proc_file_read в fs / proc / generic.c.

Таким образом, безопасность чтения процесса является такой же безопасной, как и функция, вызываемая ядром для удовлетворения запроса чтения. Если эта функция правильно блокирует все данные, к которым она прикасается, и возвращает их в буфер, тогда ее использование полностью безопасно для чтения. Поскольку файлы proc, подобные тем, которые использовались для удовлетворения запросов на чтение в / proc / net / tcp, существуют уже некоторое время и проходят тщательную проверку, они настолько безопасны, насколько вы можете просить. Фактически, многие распространенные утилиты Linux полагаются на чтение из файловой системы proc и форматирование вывода другим способом. (Мне кажется, что «ps» и «netstat» делают это).

Как всегда, вы не должны верить мне на слово; Вы можете посмотреть на источник, чтобы успокоить свои страхи. Следующая документация из proc_net_tcp.txt сообщает вам, где находятся функции «чтения» для / proc / net / tcp, так что вы можете посмотреть фактический код, который запускается при чтении из этого файла proc, и убедиться, что нет опасность блокировки.

Этот документ описывает интерфейсы / proc / net / tcp и /proc/net/tcp6.
Обратите внимание, что эти интерфейсы не рекомендуется в пользу tcp_diag. Эти интерфейсы / proc предоставляют информацию о текущем активном TCP соединения и реализуются tcp4_seq_show () в net / ipv4 / tcp_ipv4.c и tcp6_seq_show () в net / ipv6 / tcp_ipv6.c соответственно.

...