Книга: Программирование для Linux. Профессиональный подход
Листинг 11.9. (processes.c) Серверный модуль, отображающий таблицу процессов
Листинг 11.9. (processes.c) Серверный модуль, отображающий таблицу процессов
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "server.h"
/* Эта функция записывает в аргументы UID и GID
идентификаторы пользователя и группы, которым
принадлежит процесс с указанным идентификатором,
в случае успешного завершения возвращается нуль,
иначе -- ненулевое значение. */
static int get_uid_gid(pid_t pid, uid_t* uid, gid_t* gid) {
char dir_name[64];
struct stat dir_info;
int rval;
/* Формирование имени каталога процесса
в файловой системе /proc. */
snprintf(dir_name, sizeof(dir_name), "/proc/%d", (int)pid);
/* Получение информации о каталоге. */
rval = stat(dir_name, &dir_info);
if (rval != 0)
/* Каталог не найден. Возможно, процесс больше
не существует. */
return 1;
/* Убеждаемся в том, что это действительно каталог. */
assert(S_ISDIR(dir_info.st_mode));
/* Определяем интересующие нас идентификаторы. */
*uid = dir_info.st_uid;
*gid = dir_info.st_gid;
return 0;
}
/* Эта функция находит имя пользователя,
соответствующее заданному идентификатору.
Возвращаемый буфер должен быть удален
в вызывающей функции. */
static char* get_user_name(uid_t uid) {
struct passwd* entry;
entry = getpwuid(uid);
if (entry == NULL)
system_error("getpwuid");
return xstrdup(entry->pw_name);
}
/* Эта функция находит имя группы, соответствующее
заданному идентификатору, возвращаемый буфер
должен быть удален в вызывающей функции. */
static char* get_group_name(gid_t gid) {
struct group* entry;
entry = getgrgid(gid);
if (entry == NULL)
system_error("getgrgid");
return xstrdup(entry->gr_name);
}
/* Эта функция находит имя программы, которую выполняет
процесс с заданным идентификатором. Возвращаемый буфер
должен быть удален в вызывающей функции. */
static char* get_program_name(pid_t pid) {
char file_name[64];
char status_info[256];
int fd;
int rval;
char* open_paren;
char* close_paren;
char* result;
/* Генерируем имя файла "stat", находящегося в каталоге
данного процесса в файловой системе /proc,
и открываем этот файл. */
snprintf(file_name, sizeof(file_name), "/proc/%d/stat",
(int)pid);
fd = open(file_name, O_RDONLY);
if (fd == 1)
/* Файл не удалось открыть. Возможно, процесс
больше не существует. */
return NULL;
/* Чтение содержимого файла
rval = read(fd, status_info, sizeof(status_info) — 1);
close(fd);
if (rval <= 0)
/* По какой-то причине файл не удалось прочитать, завершаем
работу. */
return NULL;
/* Завершаем прочитанный текст нулевым символом. */
status_info[rval] = '';
/* Имя программы -- это второй элемент файла, заключенный в
круглые скобки. Находим местоположение скобок. */
open_paren = strchr(status_info, '(');
close_paren = strchr(status_info, ')');
if (open_paren == NULL ||
close_paren == NULL || close_paren < open_paren)
/* He удалось найти скобки, завершаем работу. */
return NULL;
/* Выделение памяти для результирующей строки */
result = (char*)xmalloc(close_paren — open_paren);
/* Копирование имени программы в буфер. */
strncpy(result, open_paren + 1, close_paren - open_paren — 1);
/* Функция strncpy() не завершает строку нулевым символом,
приходится это делать самостоятельно. */
result[close_paren - open_paren - 1] = '';
/* Конец работы. */
return result;
}
/* Эта функция определяет размер (в килобайтах) резидентной
части процесса с заданным идентификатором.
В случае ошибки возвращается -1. */
static int get_rss(pid_t pid) {
char file_name[64];
int fd;
char mem_info[128];
int rval;
int rss;
/* Генерируем имя файла "statm", находящегося в каталоге
данного процесса в файловой системе proc. */
snprintf(file_name, sizeof(file_name), "/proc/%d/statm",
(int)pid);
/* Открытие файла. */
fd = open(file_name, O_RDONLY);
if (fd == -1)
/* Файл не удалось открыть. Возможно, процесс больше не
существует. */
return -1;
/* Чтение содержимого файла. */
rval = read(fd, mem_info, sizeof(mem_info) — 1);
close(fd);
if (rval <= 0)
/* Файл не удалось прочитать, завершаем работу. */
return -1;
/* Завершаем прочитанный текст нулевым символом. */
mem_infо[rval] = '';
/* Определяем размер резидентной части процесса. Это второй
элемент файла. */
rval = sscanf(mem_info, "%*d %d", &rss);
if (rval != 1)
/* Содержимое файла statm отформатировано непонятным
образом. */
return -1;
/* Значения в файле statm приведены в единицах, кратных размеру
системной страницы. Преобразуем в килобайты. */
return rss * getpagesize() / 1024;
}
/* Эта функция генерирует строку таблицы для процесса
с заданным идентификатором. Возвращаемый буфер должен
удаляться в вызывающей функции, в случае ошибки
возвращается NULL. */
static char* format_process_info(pid_t pid) {
int rval;
uid_t uid;
gid_t gid;
char* user_name;
char* group_name;
int rss;
char* program_name;
size_t result_length;
char* result;
/* Определяем идентификаторы пользователя и группы, которым
принадлежит процесс. */
rval = get_uid_gid(pid, &uid, &gid);
if (rval != 0)
return NULL;
/* Определяем размер резидентной части процесса. */
rss = get_rss(pid);
if (rss == -1)
return NULL;
/* Определяем имя исполняемого файла процесса. */
program_name = get_program_name(pid);
if (program_name == NULL)
return NULL;
/* Преобразуем идентификаторы пользователя и группы в имена. */
user_name = get_user_name(uid);
group_name = get_group_name(gid);
/* Вычисляем длину строки, в которую будет помещен результат,
и выделяем для нее буфер. */
result_length =
strlen(program_name) + strlen(user_name) +
strlen(group_name) + 128;
result = (char*)xmalloc(result_length);
/* Форматирование результата. */
snprintf(result, result_length,
"<tr><td align=" right">%d</td><td><tt>%s</tt></td><td>%s</td>"
"<td>%s</td><td align= "right">%d</td></tr>n",
(int)pid, program_name, user_name, group_name, rss);
/* Очистка памяти. */
free(program_name);
free(user_name);
free(group_name);
/* Конец работы. */
return result;
}
/* HTML-код начала страницы, содержащей таблицу процессов. */
static char* page_start =
"<html>n"
" <body>n"
" <table cellpadding="4" cellspacing="0" border="1">n"
" <thead>n"
" <tr>n"
" <th>PID</th>n"
" <th>Program</th>n"
" <th>User</th>n"
" <th>Group</th>n"
" <th>RSS (KB)</th>n"
" </tr>n"
" </thead>n"
" <tbody>n";
/* HTML-код конца страницы, содержащей таблицу процессов. */
static char* page_end =
" </tbody>n"
" </table>n"
" </body>n"
"</html>n";
void module_generate(int fd) {
size_t i;
DIR* proc_listing;
/* Создание массива iovec. В этот массив помещается выходная
информации, причем массив может увеличиваться динамически. */
/* Число используемых элементов массива */
size_t vec_length = 0;
/* выделенный размер массива */
size_t vec_size = 16;
/* Массив элементов iovec. */
struct iovec* vec =
(struct iovec*)xmalloc(vec_size *
sizeof(struct iovec));
/* Сначала в массив записывается HTML-код начала страницы. */
vec[vec_length].iov_base = page_start;
vec[vec_length].iov_len = strlen(page_start);
++vec_length;
/* Получаем список каталогов в файловой системе /proc. */
proc_listing = opendir("/proc");
if (proc_listing == NULL)
system_error("opendir");
/* Просматриваем список каталогов. */
while (1) {
struct dirent* proc_entry;
const char* name;
pid_t pid;
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()
. По окончании работы с массивом удаляются все адресуемые в нем строки, а также сам массив.
- Листинг 10.1. (simpleid.c) Отображение идентификаторов пользователя и группы
- Глава 7 Чего нужно опасаться при моделировании бизнес-процессов. Проектные риски моделирования бизнеспроцессов
- Эффективное взаимодействие процессов архитектуры Classic Server
- 1.2. Понятие информации. Общая характеристика процессов сбора, передачи, обработки и накопления информации
- Листинг 15.11. Код для загрузки файла с Web-сервера
- Using the kill Command to Control Processes
- Как работает модуль оперативной памяти
- Листинг 3.1. Конфигурационный файл lilo.conf
- Листинг 3.2. Файл настройки программы inittab
- Листинг 3.3. Файл
- 3.4.2. Остановка процессов
- 3.4.3. Просмотр процессов