Опубликован: 15.06.2004 | Доступ: свободный | Студентов: 2290 / 579 | Оценка: 4.35 / 3.96 | Длительность: 27:47:00
ISBN: 978-5-9556-0011-6
Лекция 12:

Время и работа с ним

Опрос и изменение данных о времени, ассоциированных с файлами

Информацию о файле, в том числе данные о времени последнего доступа и изменения, можно получить с помощью описанных ранее функций семейства 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 - не нужно                     */
/* &lt; 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 (&lt_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 &lt; 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 &lt; 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().

Подобные действия характерны для функций аккуратного завершения многопроцессных приложений, когда значение семафора играет роль счетчика активных порожденных процессов, от которых через канал поступают данные.

Антон Коновалов
Антон Коновалов

В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13
Планируется ли актуализация материалов данного очень полезного курса?

Павел Храмцов
Павел Храмцов
Россия
Денис Комаров
Денис Комаров
Россия, Москва