|
В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13 |
Время и работа с ним
Опрос и изменение данных о времени, ассоциированных с файлами
Информацию о файле, в том числе данные о времени последнего доступа и изменения, можно получить с помощью описанных ранее функций семейства stat().
Для изменения ассоциированных с файлами данных о времени служит функция utime() (см. листинг 12.25), оперирующая в терминах секунд от начала отсчета.
#include <utime.h>
int utime (const char *path,
const struct utimbuf *times);
Листинг
12.25.
Описание функции utime().
Если значение аргумента times равно пустому указателю, используется текущее время. В этом случае для успешного изменения данных о времени действующий идентификатор процесса должен соответствовать владельцу файла либо процесс должен иметь права на запись в файл или соответствующие привилегии.
Если значение times отлично от NULL, оно трактуется как указатель на структуру типа utimbuf, которая, согласно стандарту POSIX-2001, содержит по крайней мере следующие поля:
time_t actime; /* Время последнего доступа */ time_t modtime; /* Время последнего изменения */
При этом данные о времени устанавливаются по указуемой структуре, а вызывающий процесс должен обладать соответствующими привилегиями или действующим идентификатором, совпадающим с идентификатором владельца файла (наличия права на запись недостаточно).
В случае успешного завершения функция utime() возвращает нулевой результат и, кроме того, переустанавливает время последнего изменения статуса файла.
Приведем пример программы, изменяющей время последнего доступа к файлу (см. листинг 12.26).
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа устанавливает время последнего доступа к файлу - */
/* аргументу командной строки, равное текущему времени */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <utime.h>
int main (int argc, char *argv []) {
struct stat st_buf; /* Буфер для опроса данных о файле */
struct utimbuf ut_buf; /* Буфер для формирования изменяемых данных о файле */
if (argc != 2) {
fprintf (stderr, "Использование: %s файл\n", argv [0]);
return (1);
}
if (stat (argv [1], &st_buf) != 0) {
perror ("STAT");
return (2);
}
ut_buf.actime = time (NULL);
ut_buf.modtime = st_buf.st_mtime;
if (utime (argv [1], &ut_buf) != 0) {
perror ("UTIME");
return (3);
}
return (0);
}
Листинг
12.26.
Пример программы, изменяющей время последнего доступа к файлу.
Чтобы изменить только одно из двух ассоциированных с файлом времен, необходимо с помощью функции stat() получить информацию о файле, перенести неизменяемое значение из структуры типа stat в utimbuf, установить требуемым образом второй элемент структуры типа utimbuf и только после этого обратиться к функции utime().
Читателю предлагается убедиться, что приведенная программа работает должным образом и что опция -u служебной программы ls предписывает оперировать со временем последнего доступа вместо времени последнего изменения.
Приостановка выполнения на заданное время
Обычным средством ожидания наступления каких-либо событий, не прерывающих выполнения процесса (потока управления), является циклическое чередование приостановки выполнения на заданное время и опроса статуса события.
Функция sleep() (см. листинг 12.27) позволяет приостановить выполнение процесса (потока управления) на заданное число секунд. Если в это время будет доставлен сигнал, который не игнорируется, вызов sleep() завершится досрочно, а результат будет равен "недоспанному" числу секунд. (В случае нормального завершения результат равен нулю, что формально можно считать частным случаем предыдущего.)
#include <unistd.h> unsigned sleep (unsigned seconds);Листинг 12.27. Описание функции sleep().
Обратим внимание на то, что аргумент и результат функции sleep() описаны как unsigned. Это значит, что приложения, строго соответствующие стандарту POSIX-2001, не должны передавать sleep() величины, превышающие минимально допустимое значение конфигурационной константы UINT_MAX, которое в стандарте ISO C [5] полагается равным 65535. Использование больших величин, вообще говоря, ограничивает мобильность приложений.
Если вместо функции sleep() воспользоваться ее более современным аналогом nanosleep() (см. листинг 12.28), можно убить сразу двух зайцев: избавиться от обременительного ограничения на максимальную длительность приостановки выполнения и получить возможность задавать эту длительность с существенно более высокой точностью, ограниченной только разрешающей способностью часов реального времени.
#include <time.h>
int nanosleep (const struct timespec *rqtp,
struct timespec *rmtp);
Листинг
12.28.
Описание функции nanosleep().
Аргумент rqtp функции nanosleep() задает запрашиваемую длительность приостановки. По указателю rmtp, если он не пуст, возвращается "недоспанное" время. В отличие от sleep(), результат nanosleep() равен -1 - как в результате ошибки, так и при "недосыпании", вызванном доставкой сигнала.
В общем случае ожидание наступления какого-либо события может быть активным или пассивным. Активное ожидание предполагает постоянный, повторяющийся в цикле опрос ассоциированных с событием значений и, следовательно, вызывает расход процессорного времени. При пассивном ожидании процесс приостанавливается (например, на семафоре) до наступления события, не занимая процессор. Функции sleep() и nanosleep() позволяют выбрать компромиссный способ ожидания, когда процессорного времени расходуется немного и оперативность реагирования на события оказывается удовлетворительной.
В листинге 12.29 показан пример использования функции sleep() для "полупассивного" ожидания изменения файла. Разумеется, обработка файла может быть более содержательной, однако представленная схема действий носит весьма общий характер и используется, например, служебными программами cron, tail и др.
/* * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа отслеживает изменение размера файла, */
/* заданного как аргумент командной строки */
/* * * * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <limits.h>
static time_t lt_mod = 0; /* Время последнего изменения файла */
static struct stat st_buf; /* Данные о файле */
/* Функция начальной обработки файла */
static void proc_init (const char *path) {
fprintf (stderr, "Данные о размере файла %s\n", path);
fprintf (stderr, "Время изменения Размер\n");
}
/* Функция проверки, нужно ли обрабатывать файл */
/* Результат > 0 - нужно */
/* 0 - не нужно */
/* < 0 - ошибка */
static int check_mod (const char *path) {
if (stat (path, &st_buf) != 0) {
perror ("STAT");
return (-1);
}
if (st_buf.st_mtime != lt_mod) {
/* Файл изменился и, следовательно, нуждается в обработке */
lt_mod = st_buf.st_mtime;
return (1);
}
return 0;
}
/* Функция обработки файла. */
/* Выведем время последнего изменения и текущий размер */
static void proc_mod (void) {
char dtbuf [LINE_MAX]; /* Буфер для данных о времени */
(void) strftime (dtbuf, sizeof (dtbuf), "%c", localtime (<_mod));
fprintf (stderr, "%s %ld\n", dtbuf, st_buf.st_size);
}
int main (int argc, char *argv []) {
int res;
if (argc != 2) {
fprintf (stderr, "Использование: %s файл\n", argv [0]);
return (1);
}
proc_init (argv [1]);
while (1) {
if ((res = check_mod (argv [1])) > 0) {
proc_mod ();
} else if (res < 0) {
return (res);
}
sleep (1);
}
return 0;
}
Листинг
12.29.
Пример программы, использующей функцию sleep().
Возможные результаты работы этой программы показаны в листинге 12.30.
Данные о размере файла /var/log/cron Время изменения Размер Tue Jan 6 12:50:00 2004 11191 Tue Jan 6 13:01:00 2004 11263 Tue Jan 6 13:10:00 2004 11409 Tue Jan 6 13:20:00 2004 11481 . . . Tue Jan 6 13:40:00 2004 11624 . . .Листинг 12.30. Возможные результаты работы программы, использующей функцию sleep().
Функция nanosleep() позволяет до некоторой степени промоделировать работу функций poll() и select(), реализующих пассивное ожидание готовности данных, в тех ситуациях, когда данные поступают из разнородных источников, которые poll() и select() не обслуживают (не только из файлов, но и из очередей сообщений, как результат опроса значений семафоров и т.п.). В листинге 12.31 показан цикл "полуактивного" (с короткими приостановками) ожидания поступления данных из канала и обнуления значения семафора.
#define N ...
#define MY_BUFSIZ ...
struct timespec tmsp = {0, 20000000};
int fds [2]; /* Файловые дескрипторы канала */
int semid; /* Идентификатор набора семафоров */
struct sembuf sembuf [1]; /* Массив для задания операций над семафором */
char buf [MY_BUFSIZ]; /* Буфер для чтения данных из канала */
int brd; /* Число прочитанных байт */
int i;
. . .
fcntl (fds [0], F_SETFL, O_NONBLOCK);
sembuf [0].sem_num = 0;
sembuf [0].sem_flg = IPC_NOWAIT;
sembuf [0].sem_op = 0;
for (i = 0; i < N; i++) {
nanosleep (&tmsp, NULL);
if ((brd = read (fds [0], buf, MY_BUFSIZ) > 0) {
/* Обработка прочитанных данных. */
/* Возможно, поступление данных на этом не закончилось */
. . .
continue;
}
if (semop (semid, sembuf, 1) == 0) {
/* Значение семафора обнулено, ждать больше нечего */
. . .
if (brd <= 0) {
close (fds [0]);
break;
}
}
}
. . .
Листинг
12.31.
Пример реализации полуактивного ожидания наступления нескольких событий с помощью функции nanosleep().
Подобные действия характерны для функций аккуратного завершения многопроцессных приложений, когда значение семафора играет роль счетчика активных порожденных процессов, от которых через канал поступают данные.