В настоящее время актуальный стандарт - это 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().
Подобные действия характерны для функций аккуратного завершения многопроцессных приложений, когда значение семафора играет роль счетчика активных порожденных процессов, от которых через канал поступают данные.