Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Файловая система /proc в Linux как инструмент разработчика

Оригинал: The Linux /proc Filesystem as a Programmers' Tool
Автор: Joshua Birnbaum
Дата публикации: 17 июня 2005 г.
Перевод: А.Панин
Дата перевода: 5 декабря 2012 г.

В статье описываются приемы, которые могут применяться для манипуляции различными типами информации, связанной с работой операционной системы, при помощи системных вызовов и команд для работы с файлами.

Одним из лучших типов времяпрепровождения для человека, активно интересующегося принципами работы операционных систем, является их всестороннее изучение. Установка и настройка систем позволяют закрепить знания о них, зачастую получаемые из статей по администрированию систем и работе с сетями. Источники информации в электронном виде, такие, как руководства пользователя (man pages) и информационные руководства (Linux info (1)) также легко доступны для ознакомления. Группы Google (groops.google.com), содержащие архив сообщений Usenet за более чем двадцатилетний срок, могут помочь в поиске мнения экспертов о широком спектре задач, начиная с настройки DNS и заканчивая расслоением памяти. Тем не менее, мне кажется уникальным другой подход к изучению операционных систем - разработка программного обеспечения для них.

Мое знакомство с системным программированием явилось результатом моего желания лучше понять принципы работы операционных систем, с которыми мне приходилось иметь дело ежедневно, работая по контракту на должности администратора UNIX-, а позднее и Linux-систем. Результатом этого знакомства стала утилита ifchk, представляющая из себя детектор сниффера пакетов, разработанная на языке C и выложенная для публичного ознакомления в июне 2003 года. Изначально утилита ifchk была разработана для IRIX, но затем было осуществлено портирование под Linux в основном для версии ядра 2.4. Текущей ревизией ifchk является недавно опубликованная beta 4, а beta 5 находится в разработке.

Моя работа над утилитой ifchk позволила мне ознакомиться с рядом аспектов, связанных с функционированием операционных систем. Примерами этих аспектов являются подсистемы netlink(7) и rtnetlink(7) в Linux, системный вызов ioctl(2), применяемый для управления устройствами и сетевыми интерфейсами, система сигналов и файловая система proc, предоставляющая информацию о процессах. Файловая система proc и ее возможность предоставлять широкий спектр информации о состоянии операционной системы является ключевым вопросом, рассматриваемым в рамках данной статьи.

Подробнее о файловой системе /proc

Перед тем, как начать рассматривать файловую систему proc с точки зрения разработчика, нам необходимо определиться с вопросом о том, что это за файловая система на самом деле. Файловая система proc является виртуальной файловой системой, корневая директория которой монтируется в директорию /proc, содержащей объекты, связанные с состоянием ядра и доступные для пользователей, а также, в качестве расширения, содержащая информацию о процессах, выполняющихся в системе. Слово "виртуальная" применяется по той причине, что данная файловая система существует только с целью отражения внутренних структур данных ядра, хранящихся в оперативной памяти. По этой причине большинство файлов и директорий в /proc имеют размер 0 байт.

В общих чертах, список директорий файловой системы /proc может быть разделен на две основных категории. Каждая директория с именем в форме числа соответствует идентификатору процесса (PID), выполняющегося в системе. Следующая строка из вывода программы ls иллюстрирует это:
dr-xr-xr-x    3 noorg    noorg           0 Apr 16 23:24 19636

Директория 19636 соответствует процессу с идентификатором PID 19636, являющемуся текущей сессией командной оболочки bash. Эти директории содержат в себе как поддиректории, так и обычные файлы, которые в подробностях описывают атрибуты выполнения заданного процесса. Страница руководства proc(5) содержит подробное описание этих атрибутов.

К второй категории директорий и файлов в /proc относятся директории и файлы, имена которых не представлены в числовой форме и которые описывают ряд аспектов функционирования ядра. Примером может служить файл /proc/version, содержащий информацию о версии используемого образа ядра операционной системы.

Файлы, принадлежащие файловой системе proc, могут быть открыты либо только для чтения, либо для чтения и записи. Приведенный в качестве примера файл /proc/version может быть открыт только для чтения. Его содержимое может быть просмотрено при помощи утилиты cat(1) и остается неизменным в течение периода, пока система включена и доступна для пользователей. Тем не менее, файлы, доступные для открытия в режиме чтения и записи, позволяют просматривать и изменять свое содержимое, при этом изменение содержимого отражается на режиме работы ядра операционной системы. Одним из примеров является файл /proc/sys/net/ipv4/ip_forwarding. С помощью утилиты cat(1) можно выяснить, пересылает ли система IP-дейтаграммы между сетевыми интерфейсами - файл содержит 1 в случае включенной пересылки или 0 в противном случае. При помощи утилиты echo(1) можно записать значения 1 или 0 в этот файл, тем самым включая или отключая возможность пересылки пакетов без перекомпиляции ядра и перезагрузки. Этот прием работает для множества других файлов из файловой системы proc, поддерживающих возможность открытия в режиме чтения и записи.

Предлагаю читателям самостоятельно исследовать директорию /proc доступной системы и обратиться к странице руководства proc(5) для лучшего понимания вещей, описанных выше.

Разработка программ, взаимодействующих с файловой системой /proc

Основываясь на вышесказанном, начнем рассматривать вопрос о том, как программным путем при помощи файловой системы proc получить доступ к информации о текущем состоянии ядра. В качестве замечания следует сказать том, что несмотря на то, что в статье содержатся примеры исходного кода, для лучшего понимания вы можете загрузить исходный код ifchk и скомпилировать его, после чего исследовать программу при помощи отладчика gdb(1).

Одна из функций утилиты ifchk заключается в отображении счетчиков принятых и отправленных пакетов для всех сетевых интерфейсов системы. В ifchk эта функция реализована путем исследования файла /proc/net/dev и последующей обработки данных, полученных из него. Хотя этот файл и содержит множество статистических данных, относящихся к сетевым интерфейсам, нас в первую очередь интересует получение данных о количестве пакетов, прошедших через интерфейс.

Функция ifMetric() реализует эту задачу. Код этой функции приведен в статье ниже. Также функция ifMetric() находится в файле исходного кода ~/ifchk-0.95b4/linux.c из комплекта поставки утилиты ifchk.

Объявление функции ifMetric() выглядит следующим образом:
int ifMetric( struct ifList *list );
Параметр *list является указателем на первый элемент связанного списка из структур типа ifList. Каждый элемент этого списка описывает характеристики сетевого интерфейса, присутствующего в системе. Структура ifList объявлена в файле ~/ifchk-0.9.5b4/linux.h следующим образом:
struct ifList
{
    int flags;            /* Флаги интерфейса.                            */
    char data[DATASZ];    /* Имя интерфейса/номер, например, "eth0".      */
    struct ifList *next;  /*Указатель на следующую структуру типа ifList. */
};

После начала работы нашим первым желанием могло бы быть открытие файла /proc/net/dev и поиск данных, относящихся к количеству пакетов в нем. Тем не менее, между этими двумя шагами необходимо провести мероприятия по изучению содержимого файла. Крах программ из-за некорректных исходных данных, сформированных злоумышленниками, в настоящее время является типичной проблемой безопасности. Мы не можем делать никаких предположений о данных, предоставляемых извне только на основании их источника. Как мы увидим, существует еще одна причина, связанная с безопасностью работы приложения, по которой мы выстраиваем последовательность операций по доступу к файлу таким образом, как представлено ниже. Помня обо всем вышесказанном, приступим к работе.

540  int ifMetric( struct ifList *list )
541  {
542      struct stat fileAttrs;         /* Атрибуты файла /proc/net/dev. */
543      struct stat linkAttrs;         /* Атрибуты ссылки /proc/net/dev. */
544      char *filePath = NULL;         /* Путь до файла /proc/net/dev. */
545      FILE *file = NULL;             /* Указатель на файл для вызова fopen(). */
546      int status = 0;                /* Результат закрытия файла /proc/net/dev при помощи close(). */
547      char buf[1024] = "";           /* Буфер символов. */
548      struct ifList *cur = NULL;     /* Текущий элемент связанного списка. */
549      int conv = 0;                  /* Количество совпадений при вызове sscanf(). */
550      long long rxPackets = 0;       /* Количество принятых пакетов. */
551      long long txPackets = 0;       /* Количество отправленных пакетов. */
552      char *delim = NULL;            /* Указатель на символ, найденный в строке. */
553      unsigned int ifIndex = 0;      /* Идентификатор интерфейса. */
554      mode_t mode = 0;               /* Права доступа к файлу /proc/net/dev. */

558      cur = list;

559      if( cur -> data == NULL )
560      {
561          fprintf( stderr, "ifchk: ERROR: interface list is empty\n"
);
562          return (-1);
563       }

564      filePath = "/proc/net/dev";

565      if( ( lstat( filePath, &linkAttrs ) ) != 0 )
566      {
567          perror( "lstat" );
568          return (-1);
569       }

570      if( S_ISLNK( linkAttrs.st_mode ) )
571      {
572          fprintf( stderr, "ifchk: ERROR: /proc/net/dev is a symbolic
link\n" );
573          return (-1);
574       }

Перед тем, как открыть файл /proc/net/dev, мы должны убедиться, что этот файл не является на самом деле символьной ссылкой (symlink). В том случае, если вместо файла программе передается символьная ссылка, которой рассматриваемый файл не должен являться, функция fopen(3) проследует по ней и откроет тот файл, на который она указывает. Следующий вызов fstat(2), использующий файловый дескриптор, возвращенный fopen(3), возвратит информацию, не относящуюся к необходимому нам файлу. Для защиты от таких ситуаций, мы используем вызов lstat(2) в отношении файла /prc/net/dev и затем проверяем, является ли файл символьной ссылкой при помощи макроса POSIX S_ISLINK. Если S_ISLINK определяет ссылку, выводим сообщение об ошибке с помощью вызова fprintf(3) и возвращаем значение -1, указывающее на ошибку. В противном случае выполнение функции продолжается.

578      if( ( file = fopen( "/proc/net/dev", "r" ) ) == NULL )
579      {
580          perror( "fopen" );
581          return (-1);
582       }

Предполагая, что с помощью макроса S_ISLINK не установлено наличие ссылки, мы открываем файл /proc/net/dev при помощи вызова fopen(3). Если вызов завершается успешно, fopen(3) возвращает указатель на структуру типа FILE для доступа открытому файлу /proc/net/dev. Если вызов завершается неудачно, мы вызываем perror(3) и возвращаем значение -1, указывающее на ошибку. Заметьте, что мы проверяем значение, возвращаемое функцией fopen(3). Напоследок отметим, что проверка значений, возвращаемых функциями обязательна.

586      if( ( fstat( fileno( file ), &fileAttrs ) ) != 0 )
587      {
588          perror( "fstat" );
589          return (-1);
590       }

Одной из первых вещей, о которых я думаю во время разработки кода, является то, какие варианты его некорректной работы возможны. Дополнительно я думаю том, как минимизировать ущерб от его некорректной работы путем использования приемов безопасного программирования. Эти действия необходимо предпринимать, особенно ввиду повышенных требований к безопасности системы и возможного ущерба от действий злоумышленников по компрометации системы.

Зная об этом, мы должны продолжить исследовать файл /proc/net/dev при помощи вызова fstat(2) для получения атрибутов файла и установления соответствия их атрибутам обычного файла из файловой системы proc, доступного только для чтения. Эти атрибуты включают в себя тип файла (за исключением символьной ссылки), размер в байтах и дисковых блоках, принадлежность файла пользователю и группе и права доступа. В качестве эталонных атрибутов файла /proc/net/dev, можно привести атрибуты, которые я наблюдал на ряде Linux-систем, с которыми работал.
тип файла:       regular
размер в байтах: 0
размер в блоках: 0
владелец:        root:root
права доступа:   0444 (-r--r--r--)

Если говорить о стандартной сборке ifchk, то файл /proc/net/dev должен соответствовать вышеприведенным критериям атрибутов. Тем не менее, простейшее изменение кода ifchk в случае необходимости позволяет подстроиться под изменившиеся обстоятельства.

В случае успешного завершения вызова fstat(2) производится заполнение структуры fileAttrs атрибутами файла /proc/net/dev. В случае неудачи мы выводим пользователю подробное сообщение об ошибке при помощи вызова perror(3). Затем возвращаем значение -1, указывающее на ошибку. Исследуя структуру fileAttrs в отладчике gdb(1), мы видим, что вызов fstat(2) возвращает следующую информацию о файле /proc/net/dev (некоторые поля структуры были удалены из вывода GDB, так как их значение не принципиально):
(gdb) print fileAttrs 
$1 = {..., st_mode = 33060,
           st_uid = 0,
           st_gid = 0,
           st_size = 0,
           st_blocks = 0, ...}

Выше я упоминал о дополнительной причине, связанной с безопасностью, по которой была сформирована последовательность действий по доступу к файлу /proc/net/dev. Вспомним, что в ходе открытия файла мы использовали вызов lstat(2) для того, чтобы не следовать по символьным ссылкам, затем последовательность состояла из вызова fopen(3) и следующего за ним вызова fstat(2). Мы могли бы получить идентичные результаты просто использовав вызов stat(2) вместо fstat(2), после чего вызвав fopen(3), так как stat(2) возвращает информацию об атрибутах файла, идентичную той, которую возвращает вызов fstat(2). Так для чего же использовать первую последовательность вызовов - fopen(3), fstat(2) вместо второй - stat(2), fopen(3)? Это делается для того, чтобы избежать ситуации гонки (race condition). Последовательность вызовов stat(2), fopen(3) делает возможной ситуацию при которой файл, в отношении которого был использован вызов stat(2) может быть подменен на другой файл, возможно с другими атрибутами или другим содержанием до того, как будет выполнен вызов fopen(3). В этой ситуации нам кажется, что мы используем вызов fopen(3) по отношению к тому же файлу, что и вызов stat(2), но на самом деле это не соответствует действительности. Опасность такой ситуации, я думаю, очевидна.

591      if( ( ( linkAttrs.st_ino ) != ( fileAttrs.st_ino ) ) ||
592          ( ( linkAttrs.st_dev ) != ( fileAttrs.st_dev ) ) )
593      {
594          fprintf( stderr, "ifchk: ERROR: /proc/net/dev file attribute inconsistency\n" );
595          return (-1);
596       }

В качестве дополнительной меры для проверки того, что работа происходит с одним и тем же файлом из файловой системы proc, мы сравниваем номера структур inode на основании поля st_ino и идентификаторы файловых систем на основании поля st_dev, а поля в свою очередь берутся из структур, возвращаемых вызовами lstat(2) и fstat(2), применяемыми для получения атрибутов файла /proc/net/dev. Если файл не подменен, значение linkAttrs.st_ino должно быть равно значению fileAttrs.st_ino и значение linkAttrs.st_dev должно быть равно значению fileAttrs.st_dev. В случае выполнения условия, выполнение функции продолжается. В случае различий атрибутов, выводится сообщение об ошибке при помои вызова fprintf(3) и возвращается значение -1, указывающее на ошибку.

600      if( ! ( S_ISREG( fileAttrs.st_mode ) ) )
601      {
602          fprintf( stderr, "ifchk: ERROR: /proc/net/dev is not a regular file\n" );
603          return (-1);
604       }

Макрос S_ISREG является стандартным макросом POSIX, предназначенным для проверки, является ли аргумент обычным файлом или не является файлом, а, например, является директорией. Если мы имеем дело с обычным файлом, выполнение функции продолжается. В противном случае выводится сообщение об ошибке при помощи вызова fprintf(3) и возвращается значение -1, указывающее на ошибку. На этом этапе может возникнуть вопрос о том, для чего нужно было использовать макрос S_ISLINK после вызова lstat(2) для установления того, что файл не является символьной ссылкой, если сейчас также проверяется тип файла. Обращение к описанию проверки на наличие символьной ссылки выше должно помочь с ответом на этот вопрос.

608      if( ( ( fileAttrs.st_size ) || ( fileAttrs.st_blocks ) ) != 0 )
609      {
610          fprintf( stderr, "ifchk: ERROR: /proc/net/dev file size is greater than 0\n" );
611          return (-1);
612       }

Равен ли размер файла нулю байтам и занимает ли он 0 блоков на диске? Если оба параметра равны нулю, выполнение функции продолжается. В противном случае выводится сообщение об ошибке при помощи вызова fprintf(3) и возвращается значение -1, указывающее на ошибку. Стоит отметить, что только одного невыполненного условия из двух достаточно для завершения программы с ошибкой.

616      if( ( ( fileAttrs.st_uid ) || ( fileAttrs.st_gid ) ) != 0 )
617      {
618          fprintf( stderr, "ifchk: ERROR: /proc/net/dev is not owned by UID 0, GID 0\n" );
619          return (-1);
620       }

Является ли владельцем файла /proc/net/dev пользователь root и принадлежит ли он группе root? В этом случае также достаточно одного невыполненного условия для возвращения значения -1, указывающего на ошибку.

624      if( ( mode = fileAttrs.st_mode & ALLPERMS ) != MODEMASK )
625      {
626          fprintf( stderr, "ifchk: ERROR: /proc/net/dev permissions are not mode 0444\n" );
627          return (-1);
628       }

Соответствуют ли права доступа к файлу /proc/net/dev значению 0444 - доступ только на чтение для владельца, участников группы и остальных пользователей? Макрос ALLPERMS описывается в заголовочном файле /usr/include/sys/stat.h и задает маску всех возможных прав доступа к файлу, или 07777. Макрос MODEMASK описан в файле ~/ifchk-0.95b4/linux.h и задает маску прав доступа только для чтения для владельца файла, участников группы и остальных пользователей, или 0444.

После применения битовой операции "И" к значениям fileAttrs.st_mode и ALLPERMS и последующего сравнения результата с значением MODEMASK, можно установить задаются ли права доступа к файлу /proc/net/dev значением 0444. Если это так, продолжаем выполнение функции. В противном случае, выводим сообщение об ошибке при помощи вызова fprintf(3) и возвращаем значение -1, указывающее на ошибку. На этом проверка атрибутов файла /prc/net/dev завершается. Тем не менее, перед тем, как ifchk сможет работать с содержимым этого файла, необходимо исследовать его содержимое.

Проверка соответствия содержимого файла /proc/net/dev стандартному формату необходима, поэтому сформулируем критерий, по которому ifchk принимает решение том, использовать ли файл или отклонить. В процессе развития Linux-систем, количество полей, таких, как bytes, packets, errs изменялось. Все файлы /proc/net/dev, которые я видел, имеют структуру, идентичную представленной ниже; вывод справа урезан ввиду ограниченного места на странице.

Inter-|   Receive                                                |  Transmit ...
 face |bytes    packets errs drop fifo frame compressed multicast|bytes      ...
    lo:   34230     586    0    0    0     0          0         0    34230   ...
  eth0:22476180  208548    0    0    0     0          0         0 52718375   ...

После двух строк заголовка следуют данные статистики для каждого интерфейса. В более ранних версиях систем нет поля "compressed". Для простоты я решил, что файл /proc/net/dev, не содержащий этого поля, должен быть отклонен утилитой ifchk. Обладая этими знаниями, начнем рассматривать процесс программного извлечения данных из файла /proc/net/dev.

632      if( ! fgets(buf, sizeof( buf ), file) )
633      {
634          perror( "fgets" );
635          return (-1);
636       }

637      if( ! fgets(buf, sizeof( buf ), file) )
638      {
639          perror( "fgets" );
640          return (-1);
641       }

645      if( ( strstr( buf, "compressed" ) ) == NULL )
646      {
647          fprintf( stderr, "ifchk: ERROR: /proc/net/dev header format is not supported\n" );
648          return (-1);
649       }

Мы использовали два идентичных вызова fgets(3) для чтения первой и второй строк заголовка из файла /proc/net/dev. Каждый вызов fgets(3) приводит к перезаписи содержимого буфера buf. В результате buf будет содержать вторую строку заголовка. После этого мы проверяем, содержит ли вторая строка заголовка поле "compressed".

Если поле "compressed" обнаружено, вызов strstr(3) завершается успешно и мы имеем пригодный для получения информации файл. Если же поле "compressed" не обнаружено в буфере buf, содержащем вторую строку заголовка, выводим сообщение об ошибке при помощи вызова fprintf(3) и возвращаем значение -1, указывающее на ошибку. После этого все проверки файла, начатые с проверки атрибутов подходят к своему завершению.

Следующая часть кода представляет собой цикл while, в котором обрабатываются и выводятся данные для каждого из сетевых интерфейсов. Цикл выполняется столько раз, сколько активных интерфейсов в системе.

653      printf( "***   Network Interface Metrics   ***\n" );
654      printf( "Name    Index   RX-OK           TX-OK\n" );

659      while( fgets( buf, sizeof( buf ), file ) )
660      {
664          if( ( strstr( buf, cur -> data ) ) != NULL )
665          {
666              delim = strchr( buf, ':' );

670              if( *( delim + 1 ) == ' ' )
671              {
672                  conv = sscanf( buf,
673                  "%*s %*Lu %Lu %*lu %*lu %*lu %*lu %*lu %*lu %*Lu%Lu %*lu %*lu %*lu %*lu %*lu %*lu",
674                  &rxPackets, &txPackets );
675               }

676              else
677              {
678                  conv = sscanf( buf,
679                  "%*s %Lu %*lu %*lu %*lu %*lu %*lu %*lu %*Lu %Lu%*lu %*lu %*lu %*lu %*lu %*lu",
680                  &rxPackets, &txPackets );
681               }
682           }

Мы вызываем fgets(3) для чтения следующей строки из файла /proc/net/dev в буфер buf. После этого вызывается strstr(3) для проверки того, что имя интерфейса в cur->data совпадает с именем интерфейса в строке только что считанной из файла /proc/net/dev. Строки статистики для интерфейсов в файле /proc/net/dev начинаются с имени интерфейса, за которым следует двоеточие, после которого записано количество байт, принятое данным интерфейсом. В некоторых случаях между двоеточием и числом принятых байт ставится пробел, например, eth0: 6571407, в некоторых случаях пробела нет, например, eth0:12795779.

Для обработки строки в обоих случаях используется действие с указателем, целью которого является установление факта наличия или отсутствия пробела. Если пробел существует, выполняется условие оператора if () в строке 670, при этом параметр, задающий формат строки для вызова sscanf(3) учитывает наличие пробела. Если пробела не обнаружено, выполняется блок кода со строки 676. В этом случае параметр, задающий формат строки учитывает отсутствие пробела.

В обоих случаях количество принятых и отправленных пакетов копируется при помощи вызова sscanf(3) из строки файла /proc/net/dev в переменные rxPackets и txPackets для последующего вывода.

683          else
684          {
685              fprintf( stderr, "ifchk: ERROR: current metrics do not describe current interface %s\n",
686
cur -> data );
687              return (-1);
688           }

Если при сравнении имени интерфейса в переменной cur->data и в строке файла /proc/net/dev обнаруживается несоответствие, выводится сообщение об ошибке при помощи вызова fprintf(3) и возвращается значение -1, указывающее на ошибку.

692          if( conv != 2 )
693          {
694              fprintf( stderr, "ifchk: ERROR: /proc/net/dev parse error\n" );
695              return (-1);
696           }

В случае успешного выполнения, вызов sscanf(3) возвращает количество найденных элементов строки. В результате значение переменной conv должно быть равно двум для rxPackets и txPackets. Если это не так, выводим сообщение с помощью fprintf(3) и возвращаем значение -1, указывающее на ошибку.

697          if( ( ifIndex = if_nametoindex( cur -> data ) ) == 0 )
698          {
699              perror( "if_nametoindex" );
700              return (-1);
701           }

Далее мы используем функцию if_namtoindex(), в качестве аргумента передавая ей имя интерфейса из переменной cur->data и, в случае успеха, сохраняем целочисленный идентификатор интерфейса в переменной ifIndex. Если возникает ошибка, обрабатываем ее как обычно. Идентификатор интерфейса является положительным целым числом, которое ядро ставит в соответствие каждому интерфейсу в системе.

702          printf( "%-7s %-7d %-13Lu   %-13Lu\n", cur -> data, ifIndex, rxPackets, txPackets );
703
704          conv = 0;

705          if( cur -> next != NULL )
706          {
707              cur = cur -> next;
708           }
709       }

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

713      if( ( status = fclose( file ) != 0 ) )
714      {
715          perror( "fclose" );
716          return (-1);
717       }

721      if( ( writeLog( LOGINFO, pw -> pw_name, NULLSTATE ) ) != 0 )
722      {
723          fprintf( stderr, "ifchk: ERROR: could not pass logging message to syslogd\n" );
724          return (-1);
725       }

726      return (0);
727  }

После завершения цикла вызывается fclose(3), этот вызов закрывает файл, открытый с помощью вызова fopen(3) в строке 578. Затем используется функция для записи в системный журнал информации о том, что были получены данные о состоянии сетевых интерфейсов.

После выполнения все этих действий, ifchk выводит данные о количестве переданных пакетов в системе с двумя интерфейсами в следующей форме:

***   Network Interface Metrics   ***
Name    Index   RX-OK           TX-OK
lo      1       104             104          
eth0    3       1280903         1162571<--.
^       ^         ^                        |
|       |         |[из файла/proc/net/dev]-'
|       |
|       |[результат вызова функции if_nametoindex()]
|
|[из переменной cur -> data]

Заключение

Файловая система proc предоставляет огромное количество информации о системе всем ее использующим. Возможность манипулирования всеми типами информации о текущем состоянии системы с использованием системных вызовов и команд для работы с файлами, таких, как cat(1) и echo(1), делает эту файловую систему кандидатом номер один на включение в список инструментов для обслуживания Linux-систем каждого специалиста.