Чи безпечно розбирати / proc / файл?


152

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

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


29
+1. Це чортово добре питання. Я б тільки хотів, щоб я отримав відповідь, але з нетерпінням чекаю, коли це я зробив досить багато раніше.
paxdiablo

1
Я впевнений, що лише прочитавши його, ви отримаєте список підключень, а також UID, який належить кожному, як вони були, коли ви відкривали його. Я не можу знайти це документально підтвердженим, тому зараз роблю це коментарем.
Тім Пост

3
Проста відповідь, очевидно, так, оскільки це не файл - читання його завжди повинно бути безпечним. Відповіді, можливо, не будуть послідовними наступного разу, коли ви їх прочитаєте, але це буде безпечно.
Rory Alsop

Ось чому ви повинні використовувати sysctl замість цього. (це також менше системних викликів)
Good Person

@GoodPerson - як це може sysctlдопомогти мені розібрати /proc/net/tcp/файл, наприклад?
Кирило Кіров

Відповіді:


111

Загалом, ні. (Тож більшість відповідей тут неправильні.) Це може бути безпечним, залежно від того, яке майно ви хочете. Але помилки у вашому коді легко, якщо ви припускаєте занадто багато про узгодженість файлу в /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ви можете прочитати його та проаналізувати кожен рядок без побоювання. Але якщо спробувати зробити якісь - які висновки з кількох рядків відразу - стережіться, інші процеси і ядро є зміни, поки ви читаєте це, і ви, ймовірно , створює помилку.


1
як щодо readdir атомності? як читання / proc / self / fd? це безпечно?
socketpair

Чи не те, що він відповідає на питання , але додати про те , як перевірити безвідмовну роботу ви можете використовувати clock_gettime(2)з CLOCK_MONOTONIC(хоча , можливо , є формальність я не знаю тут , але особисто я тільки бачив його , так як час завантаження). Для Linux у вас також є можливість sysinfo(2).
Прифтан

44

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

Все, що ядро ​​робить, - це надрукувати його внутрішній стан у власну пам’ять за допомогою sprintfфункції -подібної форми, і ця пам'ять копіюється у користувальний простір щоразу при read(2)системному виклику.

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

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

Найпростішим прикладом може бути реалізація uptimeфайлу proc в Linux. Зверніть увагу, як створюється весь буфер у функції зворотного дзвінка, що постачається single_open.


3
@Ignacio: Я просто вказую ОП у цьому напрямку, тому що в мене залишилося враження, що він вважає, що procфайли - це звичайні файли, відкриті для запису ядром.
Благовест Буюклієв

4
Ваша порада подивитися на реалізацію конкретного файлу - це добре. На жаль, здогадка про те, що все це зроблено, open()є помилковою для багатьох файлів, зокрема, для /proc/net/tcpяких стосується ОП. Це має сенс, якщо ви думаєте про витрати на надання цієї семантики - вам доведеться зробити щось на кшталт блокування внутрішніх структур даних, які записують всі ті з’єднання TCP, що в зайнятій системі - це катастрофа, навіть якщо ви лише довго тримаєте її достатньо для сканування та відформатування даних у буфер. Дивіться мою відповідь для детальної інформації про те, що насправді відбувається.
Грег Ціна

16

/ proc - це віртуальна файлова система: насправді вона просто дає зручне уявлення про внутрішні файли ядра. Однозначно це безпечно читати (саме тому воно є тут), але довгостроково це ризиковано, оскільки внутрішня частина цих віртуальних файлів може розвиватися з новою версією ядра.

EDIT

Більш детальна інформація доступна в документації на програму в Linux kernel doc , розділ 1.4 Мережа Я не можу знайти, чи інформація про те, як інформація розвивається з часом. Я думав, що він заморожений на відкритому повітрі, але не можу отримати однозначної відповіді.

EDIT2

За словами Sco doc (не Linux, але я впевнений, що всі аромати * nix так поводяться)

Хоча стан процесу і, отже, вміст / proc файлів може змінюватися від миттєвого до миттєвого, одне зчитування (2) файлу / proc гарантується, що поверне `` здорове '' представлення стану, тобто прочитане буде атомний знімок стану процесу. Така гарантія не застосовується до послідовних зчитувань, застосованих до файлу / proc для запущеного процесу. Крім того, атомність конкретно не гарантується для будь-якого вводу-виводу, застосованого до файлу як (адресний простір); вміст адресного простору будь-якого процесу може бути одночасно змінено LWP цього процесу або будь-якого іншого процесу в системі.


3
"Я думаю" ? Було б непогано відповісти :)
static_rtti

Враховуючи реалізацію / proc в ядрі, це також справедливо для Linux. Якщо ви читаєте файл procfs в одному дзвінку для читання, це послідовно - звичайно, якщо припустити, що файл proc, який ви прочитали, був реалізований правильно в ядрі.
Ерік

8
Я не думаю, що ви могли б прийти до гіршого джерела інформації, ніж SCO, і намагаєтесь ставитись procтак, ніби вона має подібну поведінку між різними ядрами (або навіть припускаючи, що вона існує - цього не потрібно в системі Unix ) збирається отримати вам світ боляче.
Миколай Лицар

1
@Nicholas: ну, я не зміг знайти якусь остаточну відповідь у документі ядра, не соромтеся вказати його, якщо ви це знаєте.
Брюс

2
Цікаво, що про це говорять документи ШОС. На жаль, це не завжди вірно в Linux, і, зокрема, це не відповідає дійсності /proc/net/tcp, що є головним питанням ОП. Швидше, лише кожен окремий рядок у виводі є атомним. Детальну інформацію див. У моїй відповіді.
Грег Ціна

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

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

Я перевірив це з програмою, що використовує 64K буфер зчитування, але це призводить до появи буфера простору ядра в 3072 байтів у моїй системі для proc_read для повернення даних. Для отримання більшого кількості поверненого тексту потрібно кілька дзвінків із вказівниками вперед. Я не знаю, який правильний спосіб зробити узгоджені дані послідовними, коли потрібно більше одного вводу-виводу. Безумовно, кожен запис у ньому /proc/net/tcpє послідовним. Існує деяка ймовірність того, що рядки поруч є знімком у різний час.


Дійсно вибачте, я не дуже зрозумів. Отже, ви маєте на увазі, що якщо я використовую ifstream, це буде небезпечно, але якщо я використовую, readце буде безпечно? Або ifstreamвикористовує внутрішньо read? А що ви пропонуєте?
Кирило Кіров

@Kiril: Вибачте за плутанину. Це пояснення того, як /proc/net/tcpформатуються дані для і повністю залежить від того, як хтось їх читає.
wallyk

1
Так! І ваша здогадка правильна, що різні рядки (в /proc/net/tcp) не походять з одного і того ж знімка. Дивіться мою відповідь для деяких пояснень.
Грег Ціна

3

За винятком невідомих помилок, немає гоночних умов, /procякі б призвели до зчитування пошкоджених даних або до поєднання старих і нових даних. У цьому сенсі це безпечно. Однак все ж є умова перегонів, що значна частина даних, з яких ви читаєте /proc, потенційно застаріла, як тільки вона генерується, і навіть більше, до того моменту, коли ви отримаєте її читання / обробку. Наприклад, процеси можуть загинути в будь-який час, і новому процесу може бути призначений той самий pid; єдиний ідентифікатор процесу, який ви можете використовувати без перегонових умов, - це ваші власні дочірні процеси '. Те саме стосується мережевої інформації (відкриті порти тощо) та дійсно більшість інформації в /proc. Я вважаю б поганою та небезпечною практикою покладатися на будь-які дані/procбути точним, за винятком даних про власний процес та потенційно його дочірніх процесах. Звичайно, все ж може бути корисно представити іншу інформацію/procкористувачеві / адміністратору для інформативної / реєстрації / тощо. цілей.


Я роблю це, щоб отримати та використовувати певну інформацію для власного процесу (для мого PID, використовуючи getpid()). Отже, це повинно бути безпечно.
Кирило Кіров

1
Так, я вважав би це цілком безпечним.
R .. GitHub ЗАСТАНОВИТИ ДІЙ

Я не згоден, що дочірні процеси будуть вести себе краще, ніж будь-який інший процес. Що стосується /procінтерфейсу, то всі вони мають однакові слабкі та сильні сторони. У будь-якому разі ОП запитує інформацію про драйвери пристрою, а не про процеси.
wallyk

1
Якщо pid Nє вашим дочірнім процесом, ви можете переконатися, що pid Nяк і раніше посилається на той самий (можливо, припинений) процес, поки ви не викликаєте waitна нього функцію -сімейки. Це забезпечує відсутність перегонів.
R .. GitHub СТОП ДОПОМОГА ЛІС

Що з потопом -1-х років і пояснень немає?
R .. GitHub СТОП ДОПОМОГАЙТЕ

2

Коли ви читаєте з файлу / proc, ядро ​​викликає функцію, яка була зареєстрована заздалегідь, функцією "read" для цього файлу proc. Дивіться __proc_file_readфункцію в fs / proc / generic.c.

Отже, безпека proc read є настільки ж безпечною, як і функція, яку ядро ​​викликає для задоволення запиту читання. Якщо ця функція належним чином блокує всі дані, до яких вона торкається, і повертається вам у буфер, читати за допомогою цієї функції цілком безпечно. Оскільки файли 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 відповідно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.