Программирование для Linux. Профессиональный подход - Марк Митчелл
Шрифт:
Интервал:
Закладка:
7.5.4. Блокировки
В файле /proc/locks перечислены все блокировки файлов, установленные в настоящий момент в системе. Каждая строка соответствует одной блокировке.
Для блокировок, созданных функцией fcntl() (описана в разделе 8.3. "Функция fcntl(): блокировки и другие операции над файлами"), первыми двумя элементами строки будут слова POSIX и ADVISORY. Третьим элементом будет WRITE или READ, в зависимости от типа блокировки. Следующее число — это идентификатор процесса, установившего блокировку. За ним идут три числа, разделенные двоеточиями. Это старший и младший номера устройства, на котором расположен файл, а также номер индексного дескриптора, оказывающий на местоположение файла в файловой системе. Оставшиеся числа используются внутри ядра и не представляют интереса.
Чтобы понять, как работает файл /proc/locks, запустите программу, приведенную в листинге 8.2. и поставьте блокировку записи на файл /tmp/test-file.
% touch /trap/test-file
% ./lock-file /tmp/test-file
file /tmp/test-file
opening /tmp/test-file
locking
locked; hit enter to unlock...
В другом окне просмотрите содержимое файла /proc/ locks:
% cat /proc/locks
ls POSIX ADVISORY WRITE 5467 08:05:181288 0 2147483647 d1b5f740
00000000 dfea7d40 00000000 00000000
В файле могут присутствовать дополнительные строки, если какие-то программы устанавливали свои блокировки. В данном случае идентификатор процесса программы lock-file — 5467. Убедимся в этом с помощью команды ps:
% ps 5467
PID TTY STAT TIME COMMAND
5467 pts/28 S 0:00 ./lock-file /tmp/test-file
Заблокированный файл /tmp/test-file находится на устройстве со старшим и младшим номерами 8 и 5 соответственно. Это номера устройства /dev/sda5:
% df /trap
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/sda5 8459764 5094292 2935736 63% /
% ls -l /dev/sda5
brw-rw---- 1 root disk 8, 5 May 5 1998 /dev/sda5
На этом устройстве с файлом /tmp/test-file связав индексный дескриптор 181288:
% ls --inode /trap/test-file
181288 /tmp/test-file
7.6. Системная статистика
Два элемента файловой системы /proc содержат полезную статистическую информацию. В файле /proc/loadavg находятся данные о загруженности системы. Первые три показателя — это число активных задач (выполняющихся процессов) за последние 1, 5 и 15 минут. Следующая строка отображает число выполняемых задач (процессов, запланированных к выполнению, а не заблокированных в каком-нибудь системном вызове) в данный момент времени и общее число процессов в системе. Последняя строка содержит идентификатор самого недавнего процесса.
В файле /proc/uptime отражено, сколько времени прошло с момента загрузки системы и сколько времени с тех пор система пребывала в неактивном состоянии. Оба показателя выражены в секундах и представлены числами с плавающей запятой:
% cat /proc/uptime
3248936.18 3072330.49
Программа, показанная в листинге 7 7. определяет общее время работы и время простоя системы и отображает эти значения в понятном формате.
Листинг 7.7. (print-uptime.c) Отображение времени работы и времени простоя системы#include <stdio.h>
/* Запись результата в стандартный выходной поток.
Параметр TIME это количество времени, а параметр LABEL --
короткая описательная строка. */
void print_time(char* label, long time) {
/* Константы преобразования. */
const long minute = 60;
const long hour = minute * 60;
const long day = hour * 24; /* Вывод результата. */
printf("%s: %ld days, %ld:%02ld:%02ldn", label, time / day,
(time % day) / hour, (time % hour) / minute, time % minute);
}
int main() {
FILE* fp;
double uptime, idle_time;
/* Чтение показателей времени из файла /proc/uptime. */
fp = fopen("/proc/uptime", "r");
fscanf(fp, "%lf %lfn", &uptime, &idle_time);
fclose(fp);
/* Форматирование и вывод. */
print_time("uptime ", (long)uptime);
print_time("idle time", (long)idle_time);
return 0;
}
Общее время работы системы отображают также команда uptime и функция sysinfo() (описана в разделе 8.14, "Функция sysinfo(): получение системной статистики"). Команда uptime дополнительно выдает показатели средней загруженности, извлекаемые из файла /proc/loadavg.
Глава 8
Системные вызовы Linux
Мы уже познакомились с большим количеством функций, реализующих различные системные задачи, например анализ командной строки, манипулирование процессами и отображение файлов в памяти. Если присмотреться повнимательнее, то окажется, что все они подпадают под две категории в зависимости от способа реализации.
■ Библиотечная функция — это обычная функция, которая находится во внешней библиотеке, подключаемой к программе. Большинство рассмотренных нами функций содержится в стандартной библиотеке языка С, libc. Вызов библиотечной функции реализуется традиционно: ее аргументы помещаются в регистры процессора или в стек и управление передается в начало кода функции (этот код находится в библиотеке, загруженной в память).
■ Системный вызов реализован в ядре Linux. Аргументы вызова упаковываются и передаются ядру, которое берет на себя управление программой, пока вызов не завершится. Системный вызов — это не обычная функция, и для передачи управления ядру требуется специальная подпрограмма. В GNU-библнотеке языка С (реализация стандартной библиотеки, имеющаяся в Linux) для системных вызовов созданы функции-оболочки, упрощающие обращение к ним. В качестве примеров системных вызовов можно привести низкоуровневые функции ввода-вывода, такие как open() и read().
Совокупность системных вызовов Linux формирует основной интерфейс между программами и ядром. Каждому вызову соответствует некая элементарная операция или функция.
Некоторые системные вызовы оказывают очень большое влияние на систему. Например. есть вызовы, позволяющие завершить работу Linux, выделить системные ресурсы или запретить другим пользователям доступ к ресурсам. С такими вызовами связано ограничение: только процессы, выполняющиеся с привилегиями суперпользователя (учетная запись root), имеют право обращаться к ним. В противном случае вызовы завершатся ошибкой.
Внутри себя библиотечная функция может обращаться к другим функциям или системным вызовам.
В настоящее время в Linux есть около 200 системных вызовов. Их список находится в файле /usr/include/asm/unistd.h. Некоторые из них используются только внутри системы, а некоторые предназначены лишь для реализации специализированных библиотечных функций. В этой главе будут рассмотрены те системные вызовы, которые чаще всего используются системными программистами.
8.1. Команда strace
Прежде чем изучать системные вызовы, полезно познакомиться с командой strace, которая отслеживает выполнение заданной программы, выводя список всех запрашиваемых системных вызовов и получаемых сигналов. Эта команда ставится в начале строки вызова программы, например:[26]
% strace hostname
В результате будет получено несколько экранов выходной информации. Каждая строка соответствует одному системному вызову. В строке указываются имя вызова, его аргументы (или их сокращенные обозначения, если аргументы слишком длинные) и возвращаемое значение. По возможности команда strace старается отображать не числовые значения, а символические константы. Показываются также поля структур, переданных по указателю. Вызовы обычных функций не регистрируются.
В случае команды strace hostname первая строка сообщает о системном вызове execve(), загружающем программу hostname:[27]
execve("/bin/hostname", ["hostname"], [/* 49 vars */]) = 0
Первый аргумент — это имя запускаемой программы. За ним идет список аргументов, состоящий из одного элемента. Дальше указан список переменных среды, который команда strace опустила для краткости.
Следующие примерно 30 строк отражают работу механизма загрузки стандартной библиотеки языка С из библиотечного файла. Ближе к концу наконец встречаются системные вызовы, связанные непосредственно с работой программы. Системный вызов uname() запрашивает имя компьютера у ядра:
uname({sys="Linux", node="myhostname", ...}) = 0
Заметьте, что команда strace показала метки полей структуры, в которой хранятся аргументы. Эта структура заполняется в системном вызове: Linux помещает в поле sys имя операционной системы, а в поле node — имя компьютера. Функция uname() будет описана ниже, в разделе 8.15. "Функция uname()".