Безопасно ли анализировать файл / proc /?

145

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

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

  • 29
    +1. Это чертовски хороший вопрос. Я только хотел бы, чтобы у меня был ответ, но я с нетерпением жду, чтобы узнать, так как я сделал это довольно много раньше.
  • 1
    Я почти уверен, что просто прочитав его, вы получите список подключений, а также UID, который владеет каждым из них, как они были, когда вы открывали его. Однако я не могу найти это документированным, поэтому пока делаю это комментарием.
Показать ещё 4 комментария
Теги:
procfs

7 ответов

107
Лучший ответ

В общем, нет. (так что большинство ответов здесь неверны.) Это может быть безопасно, в зависимости от того, какое свойство вы хотите. Но легко получить ошибки в коде, если вы слишком много думаете о согласованности файла в /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 и посмотрите, как он захватывает семафор, который защищает список mountpoints, который сохраняется до m_stop(), который вызывается, когда выполняется read(). Чтобы узнать, что может пойти не так, см. эту ошибку с прошлого года (то же самое, что я связал выше) в другом качественном программном обеспечении, которое blithely читает /proc/mounts.

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

Другая часть, которая содержит /proc/net/tcp atomic в каждой строке, является буферизацией в 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 вы можете прочитать его и проанализировать каждую строку без страха. Но если вы попытаетесь сделать какие-либо выводы из нескольких строк сразу - будьте осторожны, другие процессы и ядро ​​меняют его во время чтения, и вы, вероятно, создаете ошибку.

  • 0
    как насчет атомности readdir? нравится читать / proc / self / fd? это безопасно?
  • 0
    Не то, чтобы это отвечало на вопрос, но чтобы добавить о том, как проверить время безотказной работы, вы можете использовать clock_gettime(2) с CLOCK_MONOTONIC (хотя, возможно, здесь есть CLOCK_MONOTONIC техническая информация, о которой я не знаю, но я лично видел ее только со времени загрузки). Для Linux у вас также есть опция sysinfo(2) .
43

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

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

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

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

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

  • 3
    Конечно мог. Но так ли это?
  • 3
    @Ignacio: Я просто указываю ОП в этом направлении, потому что у меня сложилось впечатление, что он думает, что файлы proc - это обычные файлы, открытые для записи ядром.
Показать ещё 1 комментарий
16

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

ИЗМЕНИТЬ

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

EDIT2

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

Хотя состояние процесса и следовательно, содержание /proc файлы могут меняться от момента до мгновенное, одно чтение (2) of/proc файл гарантированно вернет "разумное" представление состояния, которое есть, чтение будет атомарным моментальный снимок состояния процесса. Никакая такая гарантия не распространяется на последовательные чтения применяются к /proc файл для текущего процесса. В добавление, атомарность конкретно не гарантируется для любого ввода-вывода, применяемого к файл as (address-space); содержимое любого адреса процесса пространство может быть одновременно изменено LWP этого процесса или любой другой процесса в системе.

  • 3
    "Я думаю" ? Было бы неплохо получить окончательный ответ :)
  • 0
    Учитывая реализацию / proc в ядре, это также относится и к linux. Если вы читаете файл procfs за один вызов read, это согласуется - конечно, при условии, что прочитанный вами файл proc был правильно реализован на стороне ядра.
Показать ещё 5 комментариев
14

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

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

  • 4
    К сожалению, многие файлы в /proc фактически не обеспечивают согласованность. Смотрите мой ответ для деталей.
  • 3
    Кроме того, __proc_file_read() устарела в пользу seq_file . Посмотрите довольно раздраженно звучащий комментарий (от Линуса) чуть выше длинного блочного комментария.
6

У меня есть источник для 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. Текст не генерируется до тех пор, пока запрос read() не будет сделан из файла /proc/net/tcp, разумный механизм, поскольку чтение procfs, безусловно, намного реже, чем обновление информации.

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

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

  • 0
    Очень жаль, я не получил это много. Итак, вы имеете в виду, что если я буду использовать ifstream , это будет небезопасно, но если я буду использовать read это будет безопасно? Или если ifstream использует внутреннее read ? И что ты предлагаешь?
  • 0
    @ Кирилл: Извините за путаницу. Это объяснение того, как данные для /proc/net/tcp отформатированы и полностью не зависит от того, как их читают.
Показать ещё 2 комментария
3

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

  • 0
    Я делаю это, чтобы получить и использовать некоторую информацию для моего собственного процесса (для моего PID, используя getpid() ). Так что это должно быть безопасно.
  • 1
    Да, я бы посчитал это совершенно безопасным.
Показать ещё 4 комментария
2

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

Следовательно, безопасность чтения proc только настолько же безопасна, как функция, которую ядро ​​вызывает для удовлетворения запроса на чтение. Если эта функция должным образом блокирует все данные, которые она затрагивает и возвращает вам в буфер, то ее можно безопасно читать с использованием этой функции. Поскольку файлы proc, подобные тем, которые используются для удовлетворения запросов на чтение /proc/net/tcp, уже давно существуют и прошли тщательный обзор, они настолько безопасны, насколько вы могли бы попросить. На самом деле многие распространенные утилиты Linux полагаются на чтение из файловой системы proc и форматирование вывода по-другому. (Сверху моей головы я думаю, что "ps" и "netstat" делают это).

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню