Для реализации такого поведения ОС Linux предоставляет системный вызов
fsync
. Эта функция принимает один аргумент — дескриптор записываемого файла — и принудительно переносит на диск все данные этого файла, находящиеся в кэш-буфере. Функция не завершается до тех пор, пока данные не окажутся на диске.
В листинге 8.3 показана функция, использующая данный системный вызов. Она записывает переданную ей строку в журнальный файл.
Листинг 8.3. (write_journal_entry.c) Запись строки в журнальный файл с последующей синхронизацией
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
const char* journal_filename = "journal.log";
void write_journal_entry(char* entry) {
int fd =
open(journal_filename,
O_WRONLY | O_CREAT | O_APPEND, 0660);
write(fd, entry, strlen(entry));
write(fd, "\n", 1);
fsync(fd);
close(fd);
}
Аналогичное
действие выполняет другой системный вызов:
fdatasync
. Но если функция
fsync
гарантирует, что дата модификации файла будет обновлена, то функция
fdatasync
этого не делает, а лишь гарантирует запись данных. В принципе это означает, что функция
fdatasync
способна выполняться быстрее, чем
fsync
, так как ей требуется выполнить одну операцию записи на диск, а не две. Но в настоящее время в Linux обе функции работают одинаково, обновляя дату модификации.
Файл можно также открыть в режиме синхронного ввода-вывода, при котором все операции записи будут немедленно фиксироваться на диске. Для этого в функции
open
следует указать флаг
O_SYNC
.
8.5. Функции getrlimit и setrlimit: лимиты ресурсов
Функции
getrlimit
и
setrlimit
позволяют процессу определять и задавать лимиты использования системных ресурсов. Аналогичные действия выполняет команда
ulimit
, которая ограничивает доступ запускаемых пользователем программ к ресурсам.
У каждого ресурса есть два лимита: жесткий и нежесткий. Второе значение никогда не может быть больше первого, и лишь процессы с привилегиями супер пользователя имеют право менять жесткий лимит. Обычно приложение уменьшает нежесткий лимит, ограничивая потребление системных ресурсов.
Обе функции принимают два аргумента: код, задающий тип ограничения, и указатель на структуру типа
rlimit
. Функция
getrlimit
заполняет поля этой структуры, тогда как функция
setrlimit
проверяет их и соответствующим образом меняет лимит. У структуры
rlimit
два поля: в поле
rlim_cur
содержится значение нежесткого лимита, а в поле
rlim_max
— значение жесткого лимита.
Ниже перечислены коды наиболее полезных лимитов, допускающих возможность изменения.
■
RLIMIT_CPU
. Это максимальный интервал времени центрального процессора (в секундах), занимаемый программой. Именно столько времени отводится программе на доступ к процессору. В случае превышения данного ограничения программа будет завершена по сигналу
SIGXCPU
.
■
RLIMIT_DATA
. Это максимальный объем памяти, который программа может запросить для своих данных. Запросы на дополнительную память будут отвергнуты системой.
■
RLIMIT_NPROC
. Это максимальное число дочерних процессов, которые могут быть запущены пользователем. Если процесс вызывает функцию
fork
, а лимит уже исчерпал, функция завершается ошибкой.
■
RLIMIT_NOFILE
. Это максимальное число файлов, которые могут быть одновременно открыты процессом.
Программа, приведенная в листинге 8.4, задает односекундный лимит использования центрального процессора, после чего переходит в бесконечный цикл. Как только программа превышает установленный ею же лимит, ОС Linux уничтожает ее.
Листинг 8.4. (limit-cpu.c) Задание ограничения на использование нейтрального процессора
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
int main {
struct rlimit rl;
/* Определяем текущие лимиты. */
getrlimit(RLIMIT_CPU, &rl);
/* Ограничиваем время доступа к процессору
одной секундой. */
rl.rlim_cur = 1;
setrlimit(RLIMIT_CPU, &rl);
/* Переходим в бесконечный цикл. */
while(1);
return 0;
}
Когда программа завершается по сигналу
SIGXCPU
, интерпретатор команд выдает поясняющее сообщение:
% ./limit_cpu
CPU time limit exceeded
8.6. Функция getrusage: статистика процессов
Функция
getrusage
запрашивает у ядра статистику работы процессов. Если первый аргумент функции равен
RUSAGE_SELF
, процесс получит информацию о самом себе. Если же первым аргументом является константа
RUSAGE_CHILDREN
, будет выдана информация обо всех его завершившихся дочерних процессах. Второй аргумент — это указатель на структуру типа