Программирование для Linux. Профессиональный подход
Шрифт:
char* process_info;
/* Переходим к очередному элементу списка. */
proc_entry = readdir(proc_listing);
if (proc_entry == NULL)
/* Достигнут конец списка. */
break;
/* Если имя каталога не состоит из одних цифр, то это не
каталог процесса; пропускаем его. */
name = proc_entry->d_name;
if (strspn(name, "0123456789") != strlen(name))
continue;
/*
Именем каталога является идентификатор процесса. */
pid = (pid_t)atoi(name);
/* генерируем HTML-код для строки таблицы, содержащей
описание данного процесса. */
process_info = format_process_info(pid);
if (process_info == NULL)
/* Произошла какая-то ошибка. Возможно, процесс уже
завершился. Создаем строку-заглушку. */
process_info =
"<tr><td colspan=\"5\">ERROR</td></tr>";
/* Убеждаемся в том, что в массиве iovec достаточно места
для записи буфера (один элемент будет добавлен в массив
по окончании обработки списка процессов). Если места
не хватает, удваиваем размер массива. */
if (vec_length == vec_size - 1) {
vec_size *= 2;
vec = xrealloc(vec, vec_size - sizeof(struct iovec));
}
/* Сохраняем в массиве информацию о процессе. */
vec[vec_length].iov_base = process_info;
vec[vec_length].iov_len = strlen(process_info);
++vec_length;
}
/* Конец обработки списка каталогов */
closedir(proc_listing);
/* Добавляем HTML-код конца страницы. */
vec[vec_length].iov_base = page_end;
vec[vec_length].iov_len = strlen(page_end);
++vec_length;
/* Передаем всю страницу клиенту. */
writev(fd, vec, vec_length);
/* Удаляем выделенные буферы. Первый и последний буферы
являются статическими, поэтому не должны удаляться. */
for (i = 1; i < vec_length - 1; ++i)
free(vec[i].iov_base);
/* Удаляем массив iovec. */
free(vec);
}
Задача сбора информации о процессах и представления ее в виде HTML-таблицы разбивается на ряд более простых операций.
■ Функция
get_uid_gid
возвращает идентификатор пользователя и группы, которым принадлежит процесс. Для этого вызывается функция stat
(описана в приложении Б, "Низкоуровневый ввод-вывод"), берущая информацию из каталога процесса в файловой системе /proc. ■ Функция
get_user_name
возвращает имя пользователя, соответствующее заданному идентификатору. Она просто вызывает библиотечную функцию getpwuid
, которая обращается к файлу /etc/passwd
и возвращает копию строки из него. Функция get_group_name
находит имя группы по заданному идентификатору. Она вызывает функцию getgrgid
. ■ Функция
gеt_program_name
возвращает имя программы, соответствующей заданному процессу. Эта информация извлекается из файла stat
,
находящегося в каталоге процесса в файловой системе /proc
(см. раздел 7.2, "Каталоги процессов"). Мы поступаем так, а не проверяем символические ссылки exe
или cmdline
, поскольку последние недоступны, если серверный процесс не принадлежит тому же пользователю, что и проверяемый процесс. ■ Функция
get_rss
определяет объем резидентной части процесса. Эта информация содержится во втором элементе файла statm
(см. раздел 7.2.6, "Статистика использования процессом памяти"), находящегося в каталоге процесса в файловой системе /proc
. ■ Функция
format_process_info
генерирует набор HTML-тэгов для строки таблицы, представляющей заданный процесс. Здесь вызываются все вышеперечисленные функции. ■ Функция
module_generate
генерирует HTML-страницу с таблицей процессов. Выводная информация включает начальный HTML-блок (переменная page_start
), строки с информацией о процессах (создаются функцией format_process_info
) и конечный HTML-блок (переменная page_end
). Функция
module_generate
определяет идентификаторы процессов, проверяя содержимое файловой системы /proc
. Для получения и анализа списка каталогов вызываются функции opendir
и readdir
(описаны в приложении Б, "Низкоуровневый ввод-вывод''). Из данного списка отбираются элементы, имена которых состоят из одних цифр: это каталоги процессов. Поскольку в таблице может содержаться достаточно большое число строк, последовательная запись их в сокет с помощью функции
write
приведет к ненужному повышению трафика. Для оптимизации числа передаваемых пакетов используется функция writev (описана в приложении Б, "Низкоуровневый ввод-вывод"). Для нее создается массив vec
, состоящий из элементов типа iovec
. Так как число процессов не известно заранее приходится начинать с маленького массива и увеличивать его по мере необходимости. В переменной vec_length
содержится число используемых элементов массива vec
, а в переменной vec_size
— число выделенных элементов. Когда эти переменные становятся почти равными друг другу, размер массива удваивается с помощью функции xrealloc
. По окончании работы с массивом удаляются все адресуемые в нем строки, а также сам массив. 11.4. Работа с сервером
Если бы демонстрационную программу нужно было распространять в виде исходных текстов, сопровождать и переносить на другие платформы, потребовалось бы упаковать ее с помощью GNU-утилит Automake и Autoconf. Но их рассмотрение выходит за рамки нашей книги.
11.4.1. Файл Makefile
Вместо утилиты Autoconf мы воспользуемся простым файлом
Makefile
, совместимым с GNU-утилитой Make. [39] Этот файл упростит компиляцию и компоновку сервера и его модулей. Содержимое файла показано в листинге 11.10.39
Эта утилита входит в состав Linux.
Листинг 11.10. (Makefile) Файл конфигурации сервера
### Конфигурация. ##############################################
# Стандартные параметры компилятора языка С.
CFLAGS = -Wall -g
# Исходные файлы сервера.
SOURCES = server.c module.c common.c main.c
# Соответствующие объектные файлы.
OBJECTS = $(SOURCES:.c=.o)
Поделиться с друзьями: