it-swarm.com.ru

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

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

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

148
Kiril Kirov

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

105
Greg Price

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

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

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

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

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

43
Blagovest Buyukliev

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

РЕДАКТИРОВАТЬ

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

EDIT2

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

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

16
Bruce

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

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

14
Job

У меня есть удобный источник для 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));

какие выводы

[[email protected] ~]$ 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 является самосогласованной. Существует некоторая вероятность того, что линии рядом друг с другом являются снимками в разное время.

6
wallyk

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

3
R..

Когда вы читаете из файла/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 соответственно.

2
heath