Согласно стандарту POSIX,
Всемирным называют
Московское
Московское
За стандартную единицу измерения tm_sec ), tm_min ), tm_hour ), а также номером tm_yday ) и номером tm_year ). Тогда число
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 + (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 - ((tm_year-1)/100)* 86400 + ((tm_year+299)/400)*86400
(три последних слагаемых предназначены для учета високосных лет).
Учет разного рода поправок к
Для представления данных о
int tm_sec; /* Секунды [0,60] */
int tm_min; /* Минуты [0,59] */
int tm_hour; /* Часы [0,23] */
int tm_mday; /* Номер дня в месяце [1,31] */
int tm_mon; /* Номер месяца в году [0,11] */
int tm_year; /* Номер года минус 1900 */
int tm_wday; /* Номер дня недели [0,6] */
/* (воскресенью соответствует ноль) */
int tm_yday; /* Номер дня в году [0,365] */
int tm_isdst;
/* Признак учета летнего времени */
Диапазон [0, 60] для поля tm_sec позволяет справляться с производимой время от времени вставкой дополнительной
Значение поля tm_year задается со знаком, что позволяет представлять
Значение признака tm_isdst должно быть положительным, если действует
Реальным (или астрономическим) называется время, измеренное по системным
Одноразовые
Периодические
Под
Все, что касается
Простейшим средством опроса и/или изменения текущих даты и
date [-u] [+формат] date [-u] ммддччмм[[вв]гг]
В первой форме утилита date выдает на стандартный вывод дату и date позволяет установить системные дату и время.
При наличии опции -u работа ведется без учета TZ была цепочка "UTC0" ). Без этой опции учитывается значение TZ или, если таковое не установлено или пусто, принимается во внимание системное подразумеваемое значение.
Если в командной строке date задан аргумент, начинающийся со знака +, форматом вывода даты и % и замещаются при выводе соответствующим значением.
| Спецификатор | Описание |
|---|---|
| %a | Сокращенное название |
| %A | Полное название |
| %b | |
| %B | Полное название |
| %c | Принятое в данной |
| %C | Две первые цифры четырехзначного номера |
| %d | Номер |
| %D | Дата в формате мм/дд/гг. |
| %e | Номер |
| %h | То же, что %b. |
| %H | Номер |
| %I | Номер |
| %j | Номер |
| %m | Номер |
| %M | |
| %n | Перевод строки |
| %S | |
| %t | Табуляция |
| %T | Время в формате чч:мм:сс. |
| %u | Номер |
| %U | Номер недели в |
| %V | Номер недели в |
| %w | Номер |
| %W | Номер недели в |
| %x | Принятое в данной |
| %X | Принятое в данной |
| %y | Две младшие цифры номера |
| %Y | Четырехзначный номер |
| %Z | Имя |
| %% | Знак процента. |
Чтобы установить текущие дату и время, необходимо, чтобы система допускала данное действие, а у процесса были соответствующие привилегии.
Во второй форме вызова утилиты date первая пара букв м обозначает номер
Приведем несколько примеров употребления служебной программы date.
Вывод в подразумеваемом формате, используемом командой date
без аргументов, может выглядеть так:
Fri Dec 26 17:48:02 MSK 2003
Опция -u повлияет на вывод следующим образом:
Fri Dec 26 14:48:05 UTC 2003
Команда
date '+Дата: %d.%m.%Y%nВремя: %H:%M:%S'
выдаст примерно следующее:
Дата: 26.12.2003 Время: 17:49:15
Для сдвига показаний системных
moda=`date +%m%d`
ho=$((`date +%H` + 1))
mi=`date +%M`
date ${moda}${ho}${mi}
На уровне языка C для опроса time() (см. листинг 12.2).
#include <time.h> time_t time (time_t *tloc);
Если значение аргумента tloc отлично от пустого указателя, то
Отметим, что если тип time_t реализован как 32-разрядное целое со знаком, то в 2038-м time_t вместо какой-либо конкретной разновидности int. Вероятно, в последующие версии стандарта POSIX будут включены требования к способности значений типа time_t представлять
С функцией time() ассоциирована функция difftime(), вычисляющая (в виде значения типа double ) разность в секундах между двумя time1 - time0, см. листинг 12.3).
#include <time.h> double difftime (time_t time1, time_t time0);
Узнать gettimeofday() (см. листинг 12.4).
#include <sys/time.h>
int gettimeofday (struct timeval *restrict tp,
void *restrict tzp);
Согласно стандарту POSIX-2001, описанная в заголовочном файле <sys/time.h> структура timeval содержит по крайней мере следующие поля.
time_t tv_sec; /* Секунды */ suseconds_t tv_usec; /* Микросекунды */
Функция gettimeofday() записывает timeval, на которую указывает аргумент tp. Указатель tzp должен быть пустым.
Впрочем, микросекунды - это еще на предел точности, а опрос системных
#include <time.h>
int clock_getres (clockid_t clock_id,
struct timespec *res);
int clock_gettime (clockid_t clock_id,
struct timespec *tp);
int clock_settime (clockid_t clock_id,
const struct timespec *tp);
Структура timespec отличается от timeval тем, что вместо микросекунд хранит наносекунды:
time_t tv_sec; /* Секунды */ long tv_nsec; /* Наносекунды */
Функция clock_getres() позволяет опросить res.
Функция clock_gettime() записывает в аналогичную структуру текущие показания заданных
Наконец, функция clock_settime() устанавливает заданные timespec, на которую указывает аргумент tp.
CLOCK_REALTIME. Интерпретация показаний других
Установка новых показаний для CLOCK_REALTIME влияет на срабатывание
Если в системе поддерживаются CLOCK_MONOTONIC ), они измеряют clock_settime().
Применение описанных выше функций иллюстрируется программой, показанной в листинге 12.6. Возможно, при редактировании ее внешних связей придется указать дополнительные библиотеки (например, -lrt ), что является следствием использования необязательных компонентов стандарта POSIX.
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main (void) {
struct timespec tmsp;
struct timeval tmvl;
time_t st;
double s = 0;
double d = 1;
int i;
if (clock_getres (CLOCK_REALTIME, tmsp) == -1) {
perror ("CLOCK_GETRES");
return (1);
}
printf ("Разрешающая способность общесистемных часов: %ld нсек.\n",
tmsp.tv_nsec);
if (clock_gettime (CLOCK_REALTIME, tmsp) == -1) {
perror ("CLOCK_GETTIME");
return (2);
}
printf ("Текущее время по общесистемным часам: %ld сек. %ld нсек.\n",
tmsp.tv_sec, tmsp.tv_nsec);
(void) gettimeofday (tmvl, NULL);
printf ("Текущее время, функция gettimeofday(): %ld сек. %ld мксек.\n",
tmvl.tv_sec, tmvl.tv_usec);
(void) time (st);
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
printf ("Время выполнения цикла: %g сек.\n", difftime (time (NULL), st));
return (0);
}
Результаты работы этой программы могут выглядеть так, как показано в листинге 12.7.
Разрешающая способность общесистемных часов: 10000000 нсек. Текущее время по общесистемным часам: 1072534678 сек. 296598000 нсек. Текущее время, функция gettimeofday(): 1072534678 сек. 296637 мксек. Время выполнения цикла: 15 сек.
Стандартом POSIX-2001 предусмотрено несколько способов time_t. Кроме того, timeval и timespec, но они в данном контексте играют подчиненную роль, лишь уточняя значения типа time_t.)
Для выполнения преобразований между разными
(рис 12.1) Функции для выполнения преобразований между разными представлениями данных о времени.Функции gmtime() и localtime() (см. листинг 12.8) преобразуют значения типа time_t в структуру типа tm. Соотношение между tm дается в приведенной выше формуле. Кроме того, функция localtime() учитывает данные о
#include <time.h> struct tm *gmtime (const time_t *tloc); struct tm *localtime (const time_t *tloc)
Для учета данных о tzname, timezone, daylight, значения которых функция tzset() устанавливает по переменной окружения TZ (см. листинг 12.9).
#include <time.h> extern char *tzname[2]; extern long timezone; extern int daylight; void tzset (void);
Элементам массива tzname присваиваются имена местного tzname [0] ) и "летнем" ( tzname [1] ) вариантах. Значение переменной timezone устанавливается равным разности в daylight присваивается отличное от нуля значение, если для местного
Отметим, что в общем случае значение переменой окружения TZ устроено довольно сложным образом:
Станд_поясСмещение[Лет_пояс[Смещение]
[,Нач_лет[/Время],Кон_лет[/Время]]]
Здесь Станд_пояс и Лет_пояс - имена, присваиваемые элементам массива tzname [], Смещение - разность между всемирным и местным Нач_лет и Кон_лет, соответственно, даты начала и окончания действия
Функцию mktime() (см. листинг 12.10) можно считать обратной по отношению к localtime(). Она преобразует местное время, заданное в виде структуры типа tm, в значение типа time_t, т. е. в
#include <time.h> time_t mktime (struct tm *tmptr);
При входе в функцию mktime() значения полей tm_wday и tm_yday структуры tm, на которую указывает аргумент tmptr, игнорируются; при выходе они устанавливаются должным образом. Значения других полей также приводятся к стандартным для них диапазонам (при входе это условие может не выполняться).
Другим весьма мощным средством преобразования местного strftime() (см. листинг 12.11). Как и служебная программа date, она преобразует дату и tm, на которую указывает аргумент tmptr, и результат направляется не на стандартный вывод, а в буфер, заданный указателем s и длиной maxsize.
#include <time.h> size_t strftime (char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr);
По сравнению с date, функция strftime() содержит несколько дополнительных спецификаторов форматных преобразований. Перечислим наиболее употребительные из них.
| Спецификатор | Описание |
|---|---|
| %F | Представление даты в соответствии со |
| %R | |
| %z | Смещение относительно |
Функция strftime() возвращает число байт, помещенное в буфер (без учета завершающего нулевого байта). Если буфер оказался мал, возвращается ноль.
На роль обратной по отношению к strftime() могут претендовать сразу две функции: strptime() и getdate() (см. листинг 12.12).
#include <time.h>
char *strptime (const char *restrict s,
const char *restrict format,
struct tm *restrict tmptr);
struct tm *getdate (const char *s);
Функция strptime() напоминает sscanf(): она сканирует цепочку символов, на которую указывает аргумент s, в соответствии с заданным форматом, включающим описанные выше tm по указателю tmptr. В качестве результата возвращается указатель на первый несканированный символ или NULL в случае неудачи.
Функция getdate(), по сути аналогичная strptime(), использует для разбора входной цепочки s форматы, содержащиеся в файле, чье полное DATEMSK (для интерпретации выбирается первый подходящий формат). Если дата и %Z, выходная структура инициализируется
С помощью внешней переменной (или макроса) getdate_err функция getdate() возвращает коды ошибок.
Приведем пример использования описанных функций преобразования данных о
#include <stdio.h>
#include <time.h>
#include <limits.h>
int main (void) {
char dtbuf [LINE_MAX]; /* Буфер для данных о времени */
time_t st;
struct tm stm;
(void) time (st);
(void) strftime (dtbuf, sizeof (dtbuf), "%c", gmtime (st));
printf ("Текущее всемирное время: %s\n", dtbuf);
(void) strftime (dtbuf, sizeof (dtbuf), "%c", localtime (st));
printf ("Текущее местное время: %s\n", dtbuf);
/* Узнаем, каким днем недели будет 01 января 2038 года */
stm.tm_year = 2038 - 1900;
stm.tm_mon = 1 - 1;
stm.tm_mday = 1;
stm.tm_hour = 0;
stm.tm_min = 0;
stm.tm_sec = 0;
stm.tm_isdst = -1;
if ((st = mktime (stm)) == (time_t) (-1)) {
perror ("MKTIME");
} else {
(void) strftime (dtbuf, sizeof (dtbuf), "%A", stm);
printf ("День недели 01 января 2038 года: %s\n", dtbuf);
printf ("Число секунд от начала отсчета в начале 2038 года (шест.): %x\n",
(unsigned int) st);
}
/* Узнаем, когда наступит переполнение значений типа time_t, */
/* представленных как 32-разрядное целое со знаком */
st = (time_t) 0x7fffffff;
(void) strftime (dtbuf, sizeof (dtbuf), "%c", gmtime (st));
printf ("Всемирное время конца 32-разрядного отсчета: %s\n", dtbuf);
/* Преобразуем эту дату в формат ISO 8601:2000 */
if (strptime (dtbuf, "%c", stm) == NULL) {
perror ("STRPTIME");
} else {
(void) strftime (dtbuf, sizeof (dtbuf), "%F", stm);
printf ("Дата конца 32-разрядного отсчета в формате ISO 8601:2000: %s\n",
dtbuf);
}
return (0);
}
Возможные результаты выполнения этой программы показаны в листинге 12.14.
Текущее всемирное время: Sat Jan 3 13:54:02 2004 Текущее местное время: Sat Jan 3 16:54:02 2004 День недели 01 января 2038 года: Friday Число секунд от начала отсчета в начале 2038 года (шест.): 7fe7ed50 Всемирное время конца 32-разрядного отсчета: Tue Jan 19 03:14:07 2038 Дата конца 32-разрядного отсчета в формате ISO 8601:2000: 2038-01-19
Последовательность инструкций языка C
char dtbuf [];
time_t st;
(void) strftime (dtbuf, sizeof (dtbuf), "%c",
localtime (st));
является стандартной для получения %c " допустим другой gmtime() и localtime() возвращают указатели на статические структуры, содержимое которых, возможно, перезаписывается при каждом вызове, поэтому, если оно понадобится в дальнейшем, его следует скопировать в собственные объекты.
Базовым средством для работы с clock() (см. листинг 12.15). Она возвращает в качестве результата clock_t ) (-1).
#include <time.h> clock_t clock (void);
Чтобы перевести время, возвращаемое функцией clock(), в CLOCKS_PER_SEC, которая определена в заголовочном файле <time.h> равной одному миллиону, хотя разрешающая способность
Отметим, что если значения типа clock_t представлены как 32-разрядные целые со знаком, то менее чем через 36
В листинге 12.16 показан пример использования функции clock(). Поскольку
#include <stdio.h>
#include <time.h>
int main (void) {
time_t st;
clock_t ct;
double s = 0;
double d = 1;
int i;
fprintf (stderr, "Начало выполнения цикла\n");
(void) time (st);
ct = clock;
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
fprintf (stderr, "Процессорное время выполнения цикла: %g сек.\n",
(double) (clock - ct) / CLOCKS_PER_SEC);
fprintf (stderr, "Астрономическое время выполнения цикла: %g сек.\n",
difftime (time (NULL), st));
return (0);
}
Возможные результаты работы этой программы показаны в листинге 12.17.
Начало выполнения цикла Процессорное время выполнения цикла: 15.19 сек. Астрономическое время выполнения цикла: 15 сек.
Для измерения time (не входящей, правда, в обязательную часть стандарта POSIX-2001 ):
time [-p] команда [аргумент ...]
Она выдает в стандартный протокол астрономическое и -p, my_tcex, то командная строка
time -p my_tcex
выдаст в стандартный протокол примерно следующее (см. листинг 12.18):
Начало выполнения цикла Процессорное время выполнения цикла: 15.2 сек. Астрономическое время выполнения цикла: 15 сек. real 15.20 user 15.20 sys 0.00
Если нужно измерить
time sh -c 'составная команда'
Оружием более крупного калибра является специальная встроенная в shell команда
times
Она выдает на стандартный вывод
my_tcex my_tcex times на стандартный вывод может быть выдано: 0m0.010s 0m0.000s 0m30.410s 0m0.000s
Можно видеть, что сам командный интерпретатор практически не занимал процессор, а два порожденных им процесса в сумме потребили чуть меньше тридцати с половиной
Реализация описанных выше утилит time и times опирается на функцию times() (см. листинг 12.19). Она опрашивает данные о wait() или waitpid(). В отличие от clock(), функция times() измеряет clock_t происходит не через полчаса, а примерно в течение times() в sysconf ( _SC_CLK_TCK ), а не на CLOCKS_PER_SEC.
#include <sys/times.h> clock_t times (struct tms *buffer);
В качестве результата функция times() возвращает , которая описана в заголовочном файле <sys/times.h> и, согласно стандарту POSIX-2001, должна содержать по крайней мере следующие поля:
clock_t tms_utime; /* Процессорное время, затраченное */ /* вызывающим процессом */ clock_t tms_stime; /* Процессорное время, затраченное системой */ /* на обслуживание вызывающего процесса */ clock_t tms_cutime; /* Процессорное время, затраченное /*завершившимися порожденными процессами */ clock_t tms_cstime; /* Процессорное время, затраченное системой */ /* на обслуживание завершившихся */ /* порожденных процессов */
Значения tms_utime и tms_cutime, а также tms_stime и tms_cstime ) добавляются, соответственно, к элементам tms_cutime и tms_cstime структуры родительского процесса при возврате из функций wait() или waitpid(). Это происходит рекурсивно: если порожденный процесс ожидал завершения своих потомков, то в упомянутых элементах структуры накопятся данные о
В качестве примера использования функции times() измерим
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа копирует строки со стандартного ввода на стандартный вывод, */
/* "прокачивая" их через пару сокетов. */
/* Используются функции ввода/вывода нижнего уровня. */
/* Измеряется астрономическое и процессорное время */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/times.h>
#define MY_PROMPT "Вводите строки\n"
#define MY_MSG "Вы ввели: "
int main (void) {
int sds [2];
char buf [1];
int new_line = 1; /* Признак того, что надо выдать сообщение MY_MSG */
/* перед отображением очередной строки */
clock_t st_ct; /* Данные об астрономическом и процессорном времени */
struct tms st_cpt; /* в начале работы программы */
clock_t en_ct; /* Данные об астрономическом и процессорном времени */
struct tms en_cpt; /* в конце работы программы */
long tck_p_sec; /* Количество тактов часов в секунде */
/* Опросим данные о времени начала выполнения */
if ((st_ct = times (st_cpt)) == (clock_t) (-1)) {
perror ("TIMES-1");
return (1);
}
/* Создадим пару соединенных безымянных сокетов */
if (socketpair (AF_UNIX, SOCK_STREAM, 0, sds) < 0) {
perror ("SOCKETPAIR");
return (2);
}
switch (fork ()) {
case -1:
perror ("FORK");
return (3);
case 0:
/* Чтение из сокета sds [0] и выдачу на стандартный вывод */
/* реализуем в порожденном процессе */
while (read (sds [0], buf, 1) == 1) {
if (write (STDOUT_FILENO, buf, 1) != 1) {
perror ("WRITE TO STDOUT");
break;
}
}
exit (0);
}
/* Чтение со стандартного ввода и запись в сокет sds [1] */
/* возложим на родительский процесс */
if (write (sds [1], MY_PROMPT, sizeof (MY_PROMPT) - 1) !=
sizeof (MY_PROMPT) - 1) {
perror ("WRITE TO SOCKET-1");
}
while (read (STDIN_FILENO, buf, 1) == 1) {
/* Перед отображением очередной строки */
/* нужно выдать сообщение MY_MSG */
if (new_line) {
if (write (sds [1], MY_MSG, sizeof (MY_MSG) - 1) != sizeof (MY_MSG) - 1) {
perror ("WRITE TO SOCKET-2");
break;
}
}
if (write (sds [1], buf, 1) != 1) {
perror ("WRITE TO SOCKET-3");
break;
}
new_line = (buf [0] == '\n');
}
shutdown (sds [1], SHUT_WR);
(void) wait (NULL);
/* Опросим данные о времени конца выполнения, */
/* вычислим и выдадим результаты измерений */
if ((en_ct = times (en_cpt)) == (clock_t) (-1)) {
perror ("TIMES-2");
return (4);
}
tck_p_sec = sysconf (_SC_CLK_TCK);
fprintf (stderr, "Тактов в секунде: %ld\n", tck_p_sec);
fprintf (stderr, "Астрономическое время работы программы: %g сек.\n",
(double) (en_ct - st_ct) / tck_p_sec);
fprintf (stderr, "Процессорное время, затраченное процессом: %g сек.\n",
(double) (en_cpt.tms_utime - st_cpt.tms_utime) / tck_p_sec);
fprintf (stderr, "Процессорное время, затраченное системой: %g сек.\n",
(double) (en_cpt.tms_stime - st_cpt.tms_stime) / tck_p_sec);
fprintf (stderr, "Аналогичные данные для порожденных процессов:\n");
fprintf (stderr, "%g сек.\n%g сек.\n",
(double) (en_cpt.tms_cutime - st_cpt.tms_cutime) / tck_p_sec,
(double) (en_cpt.tms_cstime - st_cpt.tms_cstime) / tck_p_sec);
return (0);
}
Если перенаправить стандартный ввод в какой-либо текстовый файл заметных размеров, а стандартный вывод - в другой файл, можно получить результаты, подобные тем, что показаны в листинге 12.21.
Тактов в секунде: 100 Астрономическое время работы программы: 1.19 сек. Процессорное время, затраченное процессом: 0.02 сек. Процессорное время, затраченное системой: 0.08 сек. Аналогичные данные для порожденных процессов: 0.09 сек. 1 сек.
Прямое clock_getcpuclockid() (см. листинг 12.22), позволяющей выяснить их идентификатор. Оговоримся, однако, что эта функция не входит в обязательную часть стандарта POSIX-2001 (она относится к продвинутым средствам реального времени).
#include <time.h>
int clock_getcpuclockid
(pid_t pid, clockid_t *clock_id);
pid процесс, возвращается по адресу, специфицированному аргументом clock_id. Если значение pid равно нулю, имеется в виду вызывающий процесс. Признаком успешного завершения является нулевой результат; в противном случае возвращается код ошибки.
В листинге 12.23 показан пример употребления функции clock_getcpuclockid() и
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <errno.h>
int main (void) {
clockid_t clk_id; /* Идентификатор часов процессорного времени */
struct timespec tmsp;
double s = 0;
double d = 1;
int i;
if ((errno = clock_getcpuclockid ((pid_t) 0, clk_id)) != 0) {
perror ("CLOCK_GETCPUCLOCKID");
return (1);
}
if (clock_getres (clk_id, tmsp) == -1) {
perror ("CLOCK_GETRES");
return (2);
}
printf ("Разрешающая способность часов процессорного времени: %ld нсек.\n",
tmsp.tv_nsec);
/* Измерим процессорное время выполнения цикла. */
fprintf (stderr, "Начало выполнения цикла\n");
tmsp.tv_sec = tmsp.tv_nsec = 0;
if (clock_settime (clk_id, tmsp) == -1) {
perror ("CLOCK_SETTIME");
return (3);
}
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
if (clock_gettime (clk_id, tmsp) == -1) {
perror ("CLOCK_GETTIME");
return (4);
}
fprintf (stderr, "Процессорное время выполнения цикла: %ld сек. %ld нсек.\n",
tmsp.tv_sec, tmsp.tv_nsec);
return (0);
}
Разрешающая способность часов процессорного времени: 2 нсек. Начало выполнения цикла Процессорное время выполнения цикла: 15 сек. 350313054 нсек.
Обратим внимание на два момента. Перед началом измеряемого участка программы на clock_settime(). Второй заслуживающий отдельного упоминания момент состоит в отсутствии необходимости выяснять clock_getcpuclockid(): можно воспользоваться именованной константой CLOCK_PROCESS_CPUTIME_ID.
Информацию о файле, в том числе данные о stat().
Для изменения ассоциированных с файлами данных о utime() (см. листинг 12.25), оперирующая в терминах
#include <utime.h>
int utime (const char *path,
const struct utimbuf *times);
Если значение аргумента times равно пустому указателю, используется
Если значение times отлично от NULL, оно трактуется как указатель на структуру типа utimbuf, которая, согласно стандарту POSIX-2001, содержит по крайней мере следующие поля:
time_t actime; /* Время последнего доступа */ time_t modtime; /* Время последнего изменения */
При этом данные о
В случае успешного завершения функция utime() возвращает нулевой результат и, кроме того, переустанавливает
Приведем пример программы, изменяющей
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа устанавливает время последнего доступа к файлу - */
/* аргументу командной строки, равное текущему времени */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#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);
}
Чтобы изменить только одно из двух ассоциированных с файлом времен, необходимо с помощью функции stat() получить информацию о файле, перенести неизменяемое значение из структуры типа stat в utimbuf, установить требуемым образом второй элемент структуры типа utimbuf и только после этого обратиться к функции utime().
Читателю предлагается убедиться, что приведенная программа работает должным образом и что опция -u служебной программы ls предписывает оперировать со
Обычным средством ожидания наступления каких-либо событий, не прерывающих выполнения процесса (потока управления), является циклическое чередование
Функция sleep() (см. листинг 12.27) позволяет приостановить выполнение процесса (потока управления) на заданное число sleep() завершится досрочно, а результат будет равен "недоспанному" числу
#include <unistd.h> unsigned sleep (unsigned seconds);
Обратим внимание на то, что аргумент и результат функции 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);
Аргумент 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.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 . . .
Функция 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;
}
}
}
. . .
Подобные действия характерны для функций аккуратного завершения многопроцессных приложений, когда
Описываемые далее средства для работы с
Реализация должна предоставлять каждому процессу по крайней мере три
ITIMER_REAL
CLOCK_REALTIME и, следовательно, его показания уменьшаются в реальном масштабе времени. Когда он срабатывает, процессу доставляется сигнал SIGALRM.
ITIMER_VIRTUAL
SIGVTALRM.
ITIMER_PROF
SIGPROF.
Согласно стандарту POSIX-2001, getitimer() и setitimer() (см. листинг 12.32).
#include <sys/time.h>
int getitimer (int timer_id,
struct itimerval *cvalue);
int setitimer (int timer_id,
const struct itimerval *restrict nvalue,
struct itimerval *restrict ovalue);
Поддерживаемые стандартом значения аргумента timer_id перечислены выше. Другие характеристики itimerval, которая должна содержать по крайней мере следующие поля:
struct timeval it_interval; /* Интервал таймера */ struct timeval it_value; /* Текущие показания таймера */ /* (ведется обратный отсчет) */
(Напомним, что структура типа timeval была описана выше. Она содержит по крайней мере два поля, задающие время, соответственно, в
Значение поля it_value (если оно отлично от нуля) показывает время, оставшееся до срабатывания it_value, равным it_interval (если последнее отлично от нуля).
Установка нулевого значения в поле it_value, независимо от величины it_interval, it_interval,
Функция getitimer() запоминает текущие характеристики cvalue. Функция setitimer() - ее можно назвать многоцелевой - взводит или nvalue, и сохраняет старые по указателю ovalue (если он отличен от NULL ).
Признаком успешного завершения обеих функций является нулевой результат.
Здесь уместно акцентировать внимание на одной тонкости, которую до сих пор мы старательно замалчивали. Поскольку разрешающая способность любых clock_settime(), nanosleep() или setitimer() не удовлетворяют данному условию, производится округление. Для
Последнее обстоятельство позволяет выяснить размер
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа выясняет размер такта часов реального времени */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <sys/time.h>
int main (void) {
struct itimerval itvl;
itvl.it_interval.tv_sec = 0;
itvl.it_interval.tv_usec = 1;
itvl.it_value.tv_sec = 0;
itvl.it_value.tv_usec = 0;
if (setitimer (ITIMER_REAL, itvl, NULL) < 0) {
perror ("SETITIMER");
return (1);
}
if (getitimer (ITIMER_REAL, itvl) < 0) {
perror ("GETITIMER");
return (2);
}
if (itvl.it_interval.tv_usec < 2) {
printf ("Не удалось выяснить размер такта часов реального времени\n");
} else {
printf ("Размер такта часов реального времени: %ld мксек\n",
itvl.it_interval.tv_usec);
}
return 0;
}
Здесь it_value - нулевое) и, соответственно, не срабатывает. Он нужен лишь для того, чтобы операционная система подкорректировала должным образом (с учетом it_interval.tv_usec. В результате корректировки первоначально присвоенная этому полю единица может превратиться, например, в 10000 (микросекунд), что дает тактовую частоту
Следующая программа (см. листинг 12.34) сравнивает ход реального и
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа сравнивает ход реального и виртуального */
/* времени процесса */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#define TM_BUF_SIZE 128
/* Массив для хранения показаний часов */
/* реального времени в момент срабатывания */
/* таймера виртуального времени процесса */
static struct timeval rt_vls [TM_BUF_SIZE];
/* Указатель на текущий элемент */
/* массива rt_vls */
static struct timeval *prt_vls = rt_vls;
/* Функция обработки срабатывания таймеров */
/* (сигналы SIGALRM и SIGVTALRM) */
static void proc_itexprtn (int signo) {
struct itimerval itmvl = {{0, 0}, {0, 0}};
/* Запомним текущее астрономическое время */
(void) gettimeofday (prt_vls++, NULL);
if (signo == SIGALRM) {
/* Сработал таймер реального времени. */
/* Разрядим таймер виртуального времени */
/* и завершимся (с выдачей результатов) */
(void) setitimer (ITIMER_VIRTUAL, itmvl, NULL);
exit (0);
}
}
/* Функция выдачи данных о ходе реального времени */
/* на фоне равномерного течения виртуального времени процесса */
static void print_rt_data (void) {
struct timeval *tprt_vls = rt_vls;
int i = 0;
printf ("Прошедшее реальное время за один квант виртуального\n");
while (++tprt_vls != prt_vls) {
printf (" %3d %10ld мксек\n", ++i,
(tprt_vls->tv_sec - (tprt_vls - 1)->tv_sec) * 1000000 +
(tprt_vls->tv_usec - (tprt_vls - 1)->tv_usec));
}
}
int main (void) {
struct itimerval itvl;
struct sigaction sact;
sigset_t sset;
/* Установим реакцию на сигналы SIGALRM и SIGVTALRM. */
/* Позаботимся, чтобы функция обработки не могла быть прервана */
/* срабатыванием другого таймера */
if ((sigemptyset (sset) < 0) || (sigaddset (sset, SIGALRM) < 0) ||
(sigaddset (sset, SIGVTALRM) < 0)) {
perror ("SIGEMPTYSET or SIGADDSET");
return (1);
}
sact.sa_handler = proc_itexprtn;
sact.sa_flags = 0;
sact.sa_mask = sset;
if (sigaction (SIGALRM, sact, NULL) < 0) {
perror ("SIGACTION-SIGALRM");
return (2);
}
if (sigaction (SIGVTALRM, sact, NULL) < 0) {
perror ("SIGACTION-SIGVTALRM");
return (3);
}
/* Зарегистрируем функцию print_rt_data() в atexit() */
if (atexit (print_rt_data) != 0) {
perror ("ATEXIT");
return (4);
}
/* Взведем таймер реального времени как одноразовый */
itvl.it_interval.tv_sec = 0;
itvl.it_interval.tv_usec = 0;
itvl.it_value.tv_sec = 10;
itvl.it_value.tv_usec = 0;
if (setitimer (ITIMER_REAL, itvl, NULL) < 0) {
perror ("SETITIMER-REAL");
return (5);
}
/* Установим начало отсчета для данных о реальном времени */
(void) gettimeofday (prt_vls++, NULL);
/* Таймер виртуального времени сделаем периодическим */
itvl.it_interval.tv_sec = 0;
itvl.it_interval.tv_usec = 100000; /* 0.1 сек */
itvl.it_value.tv_sec = 0;
itvl.it_value.tv_usec = 100000;
if (setitimer (ITIMER_VIRTUAL, itvl, NULL) < 0) {
perror ("SETITIMER-VIRTUAL");
return (6);
}
/* Убедимся, что пока процесс приостановлен, */
/* его виртуальное время также стоит */
sleep (5);
/* Нагрузим процессор вычислениями, которые не должны завершиться */
/* до срабатывания таймера реального времени */
{
double s = 0;
double d = 1;
int i;
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
}
return 0;
}
Результаты работы программы могут выглядеть так, как показано в листинге 12.35. Можно видеть, что, во-первых, доля sleep (5) в листинге 12.34, отсчет 1 в результатах).
Прошедшее реальное время за один квант виртуального 1 5255378 мксек 2 249993 мксек 3 250003 мксек 4 100000 мксек 5 250177 мксек 6 859831 мксек 7 100054 мксек 8 729943 мксек 9 580003 мксек 10 99989 мксек 11 580129 мксек 12 429881 мксек 13 99990 мксек
IT_PERIOD числа
/* * * * * * * * * * * * * * * * * * * * */
/* Программа вызывает функции обработки */
/* и контролирует время их выполнения */
/* с помощью интервального таймера */
/* * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <setjmp.h>
/* Период интервального таймера (в секундах) */
#define IT_PERIOD 1
static jmp_buf buf_env; /* Буфер для функций setjmp и longjmp */
static double s; /* Результат функций обработки данных */
/* Функция обработки срабатывания таймера реального времени */
/* (сигнал SIGALRM) */
static void proc_sigalrm (int dummy) {
printf ("Текущий результат текущей функции обработки данных: %g\n", s);
longjmp (buf_env, 1);
}
/* Первая функция обработки данных */
/* (вычисляет ln (2)) */
static void proc_data_1 (void) {
double d = 1;
int i;
s = 0;
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
}
/* Вторая функция обработки данных */
/* (вычисляет sqrt (2)) */
static void proc_data_2 (void) {
s = 1;
do {
s = (s + 2 / s) * 0.5;
} while ((s * s - 2) > 0.000000001);
}
int main (void) {
/* Массив указателей на функции обработки данных */
void (*fptrs []) (void) = {proc_data_1, proc_data_2, NULL};
/* Указатель на указатель */
/* на текущую функцию обработки данных */
void (**tfptr) (void);
/* Вспомогательная временная переменная */
void (*tmpfptr) (void);
struct itimerval itvl;
struct sigaction sact;
sigset_t sset;
int i;
/* Установим реакцию на сигнал SIGALRM */
if (sigemptyset (sset) < 0) {
perror ("SIGEMPTYSET");
return (1);
}
sact.sa_handler = proc_sigalrm;
sact.sa_flags = SA_NODEFER; /* Не блокировать SIGALRM */
/* в функции обработки этого сигнала */
sact.sa_mask = sset;
if (sigaction (SIGALRM, sact, NULL) < 0) {
perror ("SIGACTION");
return (2);
}
/* На всякий случай сделаем таймер реального времени периодическим */
itvl.it_interval.tv_sec = IT_PERIOD;
itvl.it_interval.tv_usec = 0;
/* Цикл вызова функций обработки данных. */
/* Выполним его дважды */
for (i = 0; i < 2; i++) {
tfptr = fptrs;
(void) setjmp (buf_env);
/* Сюда вернется управление после срабатывания таймера */
while ((tmpfptr = *tfptr++) != NULL) {
/* Даже если предыдущая функция обработки данных */
/* закончилась до того, как сработал таймер, */
/* обеспечим текущей функции полный интервал */
itvl.it_value.tv_sec = IT_PERIOD;
itvl.it_value.tv_usec = 0;
if (setitimer (ITIMER_REAL, itvl, NULL) < 0) {
perror ("SETITIMER");
return (3);
}
(*tmpfptr) ();
printf ("Результат текущей функции обработки данных: %g\n", s);
}
}
return 0;
}
Результаты работы приведенной программы могут выглядеть так, как показано в листинге 12.37. Видно, что первая функция обработки данных не уложилась в отведенное время, поэтому ее выполнение было прервано срабатыванием
Текущий результат текущей функции обработки данных: 0.693147 Результат текущей функции обработки данных: 1.41421 Текущий результат текущей функции обработки данных: 0.693147 Результат текущей функции обработки данных: 1.41421
Комбинация
Во-первых, обратим внимание на использование флага SA_NODEFER, предписывающего не блокировать сигнал при входе в функцию его (сигнала) обработки. Этот флаг необходим, поскольку не происходит нормального выхода из функции proc_sigalrm(), а значит, не происходит и восстановления маски сигналов процесса. В результате, после первого входа в SIGALRM он рискует остаться заблокированным, вследствие чего процесс не будет извещен о последующих срабатываниях
Во-вторых, следует исходить из возможности (**tfptr++) (), то нет гарантии, что компилятор не запланирует увеличение указателя tfptr после возврата из функции, выполнение которой может быть прервано срабатыванием tmpfptr.
В-третьих, процессу может выделяться сколь угодно малая доля setjmp() после buf_env, после чего нелокальный переход из функции обработки сигнала приведет к полному хаосу.
if (setitimer (ITIMER_REAL,
itvl, NULL) < 0) {
. . .
}
. . .
(void) setjmp (buf_env);
Проще (и, вероятно, лучше) всего бороться с подобными проблемами, манипулируя SIGALRM. Мы, однако, попытались обойтись без этого и будем признательны, если читатель обнаружит ошибки в нашем варианте программы и сообщит о них.
Еще один любопытный момент технического характера состоит в том, что обычно проверяют значение, возвращаемое setjmp(), чтобы понять, как управление оказалось в данной точке - после естественного возврата или нелокального перехода. Но поскольку в приведенной программе это ни на что не влияет, проверку можно опустить.
К теме alarm() (см. листинг 12.39), позволяющая вызывающему процессу заказать SIGALRM через заданное число alarm() предыдущий заказ существовал и еще не был выполнен, он отменяется, а если значение аргумента seconds отлично от нуля, его место занимает новый.
#include <unistd.h> unsigned alarm (unsigned seconds);
При наличии предыдущего заказа функция alarm() возвращает число
Примеры использования функции alarm() были приведены при рассмотрении средств межпроцессного взаимодействия.
Согласно стандарту POSIX,
Всемирным называют
Московское
Московское
За стандартную единицу измерения tm_sec ), tm_min ), tm_hour ), а также номером tm_yday ) и номером tm_year ). Тогда число
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 + (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 - ((tm_year-1)/100)* 86400 + ((tm_year+299)/400)*86400
(три последних слагаемых предназначены для учета високосных лет).
Учет разного рода поправок к
Для представления данных о
int tm_sec; /* Секунды [0,60] */
int tm_min; /* Минуты [0,59] */
int tm_hour; /* Часы [0,23] */
int tm_mday; /* Номер дня в месяце [1,31] */
int tm_mon; /* Номер месяца в году [0,11] */
int tm_year; /* Номер года минус 1900 */
int tm_wday; /* Номер дня недели [0,6] */
/* (воскресенью соответствует ноль) */
int tm_yday; /* Номер дня в году [0,365] */
int tm_isdst;
/* Признак учета летнего времени */
Диапазон [0, 60] для поля tm_sec позволяет справляться с производимой время от времени вставкой дополнительной
Значение поля tm_year задается со знаком, что позволяет представлять
Значение признака tm_isdst должно быть положительным, если действует
Реальным (или астрономическим) называется время, измеренное по системным
Одноразовые
Периодические
Под
Все, что касается
Простейшим средством опроса и/или изменения текущих даты и
date [-u] [+формат] date [-u] ммддччмм[[вв]гг]
В первой форме утилита date выдает на стандартный вывод дату и date позволяет установить системные дату и время.
При наличии опции -u работа ведется без учета TZ была цепочка "UTC0" ). Без этой опции учитывается значение TZ или, если таковое не установлено или пусто, принимается во внимание системное подразумеваемое значение.
Если в командной строке date задан аргумент, начинающийся со знака +, форматом вывода даты и % и замещаются при выводе соответствующим значением.
| Спецификатор | Описание |
|---|---|
| %a | Сокращенное название |
| %A | Полное название |
| %b | |
| %B | Полное название |
| %c | Принятое в данной |
| %C | Две первые цифры четырехзначного номера |
| %d | Номер |
| %D | Дата в формате мм/дд/гг. |
| %e | Номер |
| %h | То же, что %b. |
| %H | Номер |
| %I | Номер |
| %j | Номер |
| %m | Номер |
| %M | |
| %n | Перевод строки |
| %S | |
| %t | Табуляция |
| %T | Время в формате чч:мм:сс. |
| %u | Номер |
| %U | Номер недели в |
| %V | Номер недели в |
| %w | Номер |
| %W | Номер недели в |
| %x | Принятое в данной |
| %X | Принятое в данной |
| %y | Две младшие цифры номера |
| %Y | Четырехзначный номер |
| %Z | Имя |
| %% | Знак процента. |
Чтобы установить текущие дату и время, необходимо, чтобы система допускала данное действие, а у процесса были соответствующие привилегии.
Во второй форме вызова утилиты date первая пара букв м обозначает номер
Приведем несколько примеров употребления служебной программы date.
Вывод в подразумеваемом формате, используемом командой date
без аргументов, может выглядеть так:
Fri Dec 26 17:48:02 MSK 2003
Опция -u повлияет на вывод следующим образом:
Fri Dec 26 14:48:05 UTC 2003
Команда
date '+Дата: %d.%m.%Y%nВремя: %H:%M:%S'
выдаст примерно следующее:
Дата: 26.12.2003 Время: 17:49:15
Для сдвига показаний системных
moda=`date +%m%d`
ho=$((`date +%H` + 1))
mi=`date +%M`
date ${moda}${ho}${mi}
На уровне языка C для опроса time() (см. листинг 12.2).
#include <time.h> time_t time (time_t *tloc);
Если значение аргумента tloc отлично от пустого указателя, то
Отметим, что если тип time_t реализован как 32-разрядное целое со знаком, то в 2038-м time_t вместо какой-либо конкретной разновидности int. Вероятно, в последующие версии стандарта POSIX будут включены требования к способности значений типа time_t представлять
С функцией time() ассоциирована функция difftime(), вычисляющая (в виде значения типа double ) разность в секундах между двумя time1 - time0, см. листинг 12.3).
#include <time.h> double difftime (time_t time1, time_t time0);
Узнать gettimeofday() (см. листинг 12.4).
#include <sys/time.h>
int gettimeofday (struct timeval *restrict tp,
void *restrict tzp);
Согласно стандарту POSIX-2001, описанная в заголовочном файле <sys/time.h> структура timeval содержит по крайней мере следующие поля.
time_t tv_sec; /* Секунды */ suseconds_t tv_usec; /* Микросекунды */
Функция gettimeofday() записывает timeval, на которую указывает аргумент tp. Указатель tzp должен быть пустым.
Впрочем, микросекунды - это еще на предел точности, а опрос системных
#include <time.h>
int clock_getres (clockid_t clock_id,
struct timespec *res);
int clock_gettime (clockid_t clock_id,
struct timespec *tp);
int clock_settime (clockid_t clock_id,
const struct timespec *tp);
Структура timespec отличается от timeval тем, что вместо микросекунд хранит наносекунды:
time_t tv_sec; /* Секунды */ long tv_nsec; /* Наносекунды */
Функция clock_getres() позволяет опросить res.
Функция clock_gettime() записывает в аналогичную структуру текущие показания заданных
Наконец, функция clock_settime() устанавливает заданные timespec, на которую указывает аргумент tp.
CLOCK_REALTIME. Интерпретация показаний других
Установка новых показаний для CLOCK_REALTIME влияет на срабатывание
Если в системе поддерживаются CLOCK_MONOTONIC ), они измеряют clock_settime().
Применение описанных выше функций иллюстрируется программой, показанной в листинге 12.6. Возможно, при редактировании ее внешних связей придется указать дополнительные библиотеки (например, -lrt ), что является следствием использования необязательных компонентов стандарта POSIX.
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main (void) {
struct timespec tmsp;
struct timeval tmvl;
time_t st;
double s = 0;
double d = 1;
int i;
if (clock_getres (CLOCK_REALTIME, tmsp) == -1) {
perror ("CLOCK_GETRES");
return (1);
}
printf ("Разрешающая способность общесистемных часов: %ld нсек.\n",
tmsp.tv_nsec);
if (clock_gettime (CLOCK_REALTIME, tmsp) == -1) {
perror ("CLOCK_GETTIME");
return (2);
}
printf ("Текущее время по общесистемным часам: %ld сек. %ld нсек.\n",
tmsp.tv_sec, tmsp.tv_nsec);
(void) gettimeofday (tmvl, NULL);
printf ("Текущее время, функция gettimeofday(): %ld сек. %ld мксек.\n",
tmvl.tv_sec, tmvl.tv_usec);
(void) time (st);
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
printf ("Время выполнения цикла: %g сек.\n", difftime (time (NULL), st));
return (0);
}
Результаты работы этой программы могут выглядеть так, как показано в листинге 12.7.
Разрешающая способность общесистемных часов: 10000000 нсек. Текущее время по общесистемным часам: 1072534678 сек. 296598000 нсек. Текущее время, функция gettimeofday(): 1072534678 сек. 296637 мксек. Время выполнения цикла: 15 сек.
Стандартом POSIX-2001 предусмотрено несколько способов time_t. Кроме того, timeval и timespec, но они в данном контексте играют подчиненную роль, лишь уточняя значения типа time_t.)
Для выполнения преобразований между разными
(рис 12.1) Функции для выполнения преобразований между разными представлениями данных о времени.Функции gmtime() и localtime() (см. листинг 12.8) преобразуют значения типа time_t в структуру типа tm. Соотношение между tm дается в приведенной выше формуле. Кроме того, функция localtime() учитывает данные о
#include <time.h> struct tm *gmtime (const time_t *tloc); struct tm *localtime (const time_t *tloc)
Для учета данных о tzname, timezone, daylight, значения которых функция tzset() устанавливает по переменной окружения TZ (см. листинг 12.9).
#include <time.h> extern char *tzname[2]; extern long timezone; extern int daylight; void tzset (void);
Элементам массива tzname присваиваются имена местного tzname [0] ) и "летнем" ( tzname [1] ) вариантах. Значение переменной timezone устанавливается равным разности в daylight присваивается отличное от нуля значение, если для местного
Отметим, что в общем случае значение переменой окружения TZ устроено довольно сложным образом:
Станд_поясСмещение[Лет_пояс[Смещение]
[,Нач_лет[/Время],Кон_лет[/Время]]]
Здесь Станд_пояс и Лет_пояс - имена, присваиваемые элементам массива tzname [], Смещение - разность между всемирным и местным Нач_лет и Кон_лет, соответственно, даты начала и окончания действия
Функцию mktime() (см. листинг 12.10) можно считать обратной по отношению к localtime(). Она преобразует местное время, заданное в виде структуры типа tm, в значение типа time_t, т. е. в
#include <time.h> time_t mktime (struct tm *tmptr);
При входе в функцию mktime() значения полей tm_wday и tm_yday структуры tm, на которую указывает аргумент tmptr, игнорируются; при выходе они устанавливаются должным образом. Значения других полей также приводятся к стандартным для них диапазонам (при входе это условие может не выполняться).
Другим весьма мощным средством преобразования местного strftime() (см. листинг 12.11). Как и служебная программа date, она преобразует дату и tm, на которую указывает аргумент tmptr, и результат направляется не на стандартный вывод, а в буфер, заданный указателем s и длиной maxsize.
#include <time.h> size_t strftime (char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr);
По сравнению с date, функция strftime() содержит несколько дополнительных спецификаторов форматных преобразований. Перечислим наиболее употребительные из них.
| Спецификатор | Описание |
|---|---|
| %F | Представление даты в соответствии со |
| %R | |
| %z | Смещение относительно |
Функция strftime() возвращает число байт, помещенное в буфер (без учета завершающего нулевого байта). Если буфер оказался мал, возвращается ноль.
На роль обратной по отношению к strftime() могут претендовать сразу две функции: strptime() и getdate() (см. листинг 12.12).
#include <time.h>
char *strptime (const char *restrict s,
const char *restrict format,
struct tm *restrict tmptr);
struct tm *getdate (const char *s);
Функция strptime() напоминает sscanf(): она сканирует цепочку символов, на которую указывает аргумент s, в соответствии с заданным форматом, включающим описанные выше tm по указателю tmptr. В качестве результата возвращается указатель на первый несканированный символ или NULL в случае неудачи.
Функция getdate(), по сути аналогичная strptime(), использует для разбора входной цепочки s форматы, содержащиеся в файле, чье полное DATEMSK (для интерпретации выбирается первый подходящий формат). Если дата и %Z, выходная структура инициализируется
С помощью внешней переменной (или макроса) getdate_err функция getdate() возвращает коды ошибок.
Приведем пример использования описанных функций преобразования данных о
#include <stdio.h>
#include <time.h>
#include <limits.h>
int main (void) {
char dtbuf [LINE_MAX]; /* Буфер для данных о времени */
time_t st;
struct tm stm;
(void) time (st);
(void) strftime (dtbuf, sizeof (dtbuf), "%c", gmtime (st));
printf ("Текущее всемирное время: %s\n", dtbuf);
(void) strftime (dtbuf, sizeof (dtbuf), "%c", localtime (st));
printf ("Текущее местное время: %s\n", dtbuf);
/* Узнаем, каким днем недели будет 01 января 2038 года */
stm.tm_year = 2038 - 1900;
stm.tm_mon = 1 - 1;
stm.tm_mday = 1;
stm.tm_hour = 0;
stm.tm_min = 0;
stm.tm_sec = 0;
stm.tm_isdst = -1;
if ((st = mktime (stm)) == (time_t) (-1)) {
perror ("MKTIME");
} else {
(void) strftime (dtbuf, sizeof (dtbuf), "%A", stm);
printf ("День недели 01 января 2038 года: %s\n", dtbuf);
printf ("Число секунд от начала отсчета в начале 2038 года (шест.): %x\n",
(unsigned int) st);
}
/* Узнаем, когда наступит переполнение значений типа time_t, */
/* представленных как 32-разрядное целое со знаком */
st = (time_t) 0x7fffffff;
(void) strftime (dtbuf, sizeof (dtbuf), "%c", gmtime (st));
printf ("Всемирное время конца 32-разрядного отсчета: %s\n", dtbuf);
/* Преобразуем эту дату в формат ISO 8601:2000 */
if (strptime (dtbuf, "%c", stm) == NULL) {
perror ("STRPTIME");
} else {
(void) strftime (dtbuf, sizeof (dtbuf), "%F", stm);
printf ("Дата конца 32-разрядного отсчета в формате ISO 8601:2000: %s\n",
dtbuf);
}
return (0);
}
Возможные результаты выполнения этой программы показаны в листинге 12.14.
Текущее всемирное время: Sat Jan 3 13:54:02 2004 Текущее местное время: Sat Jan 3 16:54:02 2004 День недели 01 января 2038 года: Friday Число секунд от начала отсчета в начале 2038 года (шест.): 7fe7ed50 Всемирное время конца 32-разрядного отсчета: Tue Jan 19 03:14:07 2038 Дата конца 32-разрядного отсчета в формате ISO 8601:2000: 2038-01-19
Последовательность инструкций языка C
char dtbuf [];
time_t st;
(void) strftime (dtbuf, sizeof (dtbuf), "%c",
localtime (st));
является стандартной для получения %c " допустим другой gmtime() и localtime() возвращают указатели на статические структуры, содержимое которых, возможно, перезаписывается при каждом вызове, поэтому, если оно понадобится в дальнейшем, его следует скопировать в собственные объекты.
Базовым средством для работы с clock() (см. листинг 12.15). Она возвращает в качестве результата clock_t ) (-1).
#include <time.h> clock_t clock (void);
Чтобы перевести время, возвращаемое функцией clock(), в CLOCKS_PER_SEC, которая определена в заголовочном файле <time.h> равной одному миллиону, хотя разрешающая способность
Отметим, что если значения типа clock_t представлены как 32-разрядные целые со знаком, то менее чем через 36
В листинге 12.16 показан пример использования функции clock(). Поскольку
#include <stdio.h>
#include <time.h>
int main (void) {
time_t st;
clock_t ct;
double s = 0;
double d = 1;
int i;
fprintf (stderr, "Начало выполнения цикла\n");
(void) time (st);
ct = clock;
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
fprintf (stderr, "Процессорное время выполнения цикла: %g сек.\n",
(double) (clock - ct) / CLOCKS_PER_SEC);
fprintf (stderr, "Астрономическое время выполнения цикла: %g сек.\n",
difftime (time (NULL), st));
return (0);
}
Возможные результаты работы этой программы показаны в листинге 12.17.
Начало выполнения цикла Процессорное время выполнения цикла: 15.19 сек. Астрономическое время выполнения цикла: 15 сек.
Для измерения time (не входящей, правда, в обязательную часть стандарта POSIX-2001 ):
time [-p] команда [аргумент ...]
Она выдает в стандартный протокол астрономическое и -p, my_tcex, то командная строка
time -p my_tcex
выдаст в стандартный протокол примерно следующее (см. листинг 12.18):
Начало выполнения цикла Процессорное время выполнения цикла: 15.2 сек. Астрономическое время выполнения цикла: 15 сек. real 15.20 user 15.20 sys 0.00
Если нужно измерить
time sh -c 'составная команда'
Оружием более крупного калибра является специальная встроенная в shell команда
times
Она выдает на стандартный вывод
my_tcex my_tcex times на стандартный вывод может быть выдано: 0m0.010s 0m0.000s 0m30.410s 0m0.000s
Можно видеть, что сам командный интерпретатор практически не занимал процессор, а два порожденных им процесса в сумме потребили чуть меньше тридцати с половиной
Реализация описанных выше утилит time и times опирается на функцию times() (см. листинг 12.19). Она опрашивает данные о wait() или waitpid(). В отличие от clock(), функция times() измеряет clock_t происходит не через полчаса, а примерно в течение times() в sysconf ( _SC_CLK_TCK ), а не на CLOCKS_PER_SEC.
#include <sys/times.h> clock_t times (struct tms *buffer);
В качестве результата функция times() возвращает , которая описана в заголовочном файле <sys/times.h> и, согласно стандарту POSIX-2001, должна содержать по крайней мере следующие поля:
clock_t tms_utime; /* Процессорное время, затраченное */ /* вызывающим процессом */ clock_t tms_stime; /* Процессорное время, затраченное системой */ /* на обслуживание вызывающего процесса */ clock_t tms_cutime; /* Процессорное время, затраченное /*завершившимися порожденными процессами */ clock_t tms_cstime; /* Процессорное время, затраченное системой */ /* на обслуживание завершившихся */ /* порожденных процессов */
Значения tms_utime и tms_cutime, а также tms_stime и tms_cstime ) добавляются, соответственно, к элементам tms_cutime и tms_cstime структуры родительского процесса при возврате из функций wait() или waitpid(). Это происходит рекурсивно: если порожденный процесс ожидал завершения своих потомков, то в упомянутых элементах структуры накопятся данные о
В качестве примера использования функции times() измерим
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа копирует строки со стандартного ввода на стандартный вывод, */
/* "прокачивая" их через пару сокетов. */
/* Используются функции ввода/вывода нижнего уровня. */
/* Измеряется астрономическое и процессорное время */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/times.h>
#define MY_PROMPT "Вводите строки\n"
#define MY_MSG "Вы ввели: "
int main (void) {
int sds [2];
char buf [1];
int new_line = 1; /* Признак того, что надо выдать сообщение MY_MSG */
/* перед отображением очередной строки */
clock_t st_ct; /* Данные об астрономическом и процессорном времени */
struct tms st_cpt; /* в начале работы программы */
clock_t en_ct; /* Данные об астрономическом и процессорном времени */
struct tms en_cpt; /* в конце работы программы */
long tck_p_sec; /* Количество тактов часов в секунде */
/* Опросим данные о времени начала выполнения */
if ((st_ct = times (st_cpt)) == (clock_t) (-1)) {
perror ("TIMES-1");
return (1);
}
/* Создадим пару соединенных безымянных сокетов */
if (socketpair (AF_UNIX, SOCK_STREAM, 0, sds) < 0) {
perror ("SOCKETPAIR");
return (2);
}
switch (fork ()) {
case -1:
perror ("FORK");
return (3);
case 0:
/* Чтение из сокета sds [0] и выдачу на стандартный вывод */
/* реализуем в порожденном процессе */
while (read (sds [0], buf, 1) == 1) {
if (write (STDOUT_FILENO, buf, 1) != 1) {
perror ("WRITE TO STDOUT");
break;
}
}
exit (0);
}
/* Чтение со стандартного ввода и запись в сокет sds [1] */
/* возложим на родительский процесс */
if (write (sds [1], MY_PROMPT, sizeof (MY_PROMPT) - 1) !=
sizeof (MY_PROMPT) - 1) {
perror ("WRITE TO SOCKET-1");
}
while (read (STDIN_FILENO, buf, 1) == 1) {
/* Перед отображением очередной строки */
/* нужно выдать сообщение MY_MSG */
if (new_line) {
if (write (sds [1], MY_MSG, sizeof (MY_MSG) - 1) != sizeof (MY_MSG) - 1) {
perror ("WRITE TO SOCKET-2");
break;
}
}
if (write (sds [1], buf, 1) != 1) {
perror ("WRITE TO SOCKET-3");
break;
}
new_line = (buf [0] == '\n');
}
shutdown (sds [1], SHUT_WR);
(void) wait (NULL);
/* Опросим данные о времени конца выполнения, */
/* вычислим и выдадим результаты измерений */
if ((en_ct = times (en_cpt)) == (clock_t) (-1)) {
perror ("TIMES-2");
return (4);
}
tck_p_sec = sysconf (_SC_CLK_TCK);
fprintf (stderr, "Тактов в секунде: %ld\n", tck_p_sec);
fprintf (stderr, "Астрономическое время работы программы: %g сек.\n",
(double) (en_ct - st_ct) / tck_p_sec);
fprintf (stderr, "Процессорное время, затраченное процессом: %g сек.\n",
(double) (en_cpt.tms_utime - st_cpt.tms_utime) / tck_p_sec);
fprintf (stderr, "Процессорное время, затраченное системой: %g сек.\n",
(double) (en_cpt.tms_stime - st_cpt.tms_stime) / tck_p_sec);
fprintf (stderr, "Аналогичные данные для порожденных процессов:\n");
fprintf (stderr, "%g сек.\n%g сек.\n",
(double) (en_cpt.tms_cutime - st_cpt.tms_cutime) / tck_p_sec,
(double) (en_cpt.tms_cstime - st_cpt.tms_cstime) / tck_p_sec);
return (0);
}
Если перенаправить стандартный ввод в какой-либо текстовый файл заметных размеров, а стандартный вывод - в другой файл, можно получить результаты, подобные тем, что показаны в листинге 12.21.
Тактов в секунде: 100 Астрономическое время работы программы: 1.19 сек. Процессорное время, затраченное процессом: 0.02 сек. Процессорное время, затраченное системой: 0.08 сек. Аналогичные данные для порожденных процессов: 0.09 сек. 1 сек.
Прямое clock_getcpuclockid() (см. листинг 12.22), позволяющей выяснить их идентификатор. Оговоримся, однако, что эта функция не входит в обязательную часть стандарта POSIX-2001 (она относится к продвинутым средствам реального времени).
#include <time.h>
int clock_getcpuclockid
(pid_t pid, clockid_t *clock_id);
pid процесс, возвращается по адресу, специфицированному аргументом clock_id. Если значение pid равно нулю, имеется в виду вызывающий процесс. Признаком успешного завершения является нулевой результат; в противном случае возвращается код ошибки.
В листинге 12.23 показан пример употребления функции clock_getcpuclockid() и
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <errno.h>
int main (void) {
clockid_t clk_id; /* Идентификатор часов процессорного времени */
struct timespec tmsp;
double s = 0;
double d = 1;
int i;
if ((errno = clock_getcpuclockid ((pid_t) 0, clk_id)) != 0) {
perror ("CLOCK_GETCPUCLOCKID");
return (1);
}
if (clock_getres (clk_id, tmsp) == -1) {
perror ("CLOCK_GETRES");
return (2);
}
printf ("Разрешающая способность часов процессорного времени: %ld нсек.\n",
tmsp.tv_nsec);
/* Измерим процессорное время выполнения цикла. */
fprintf (stderr, "Начало выполнения цикла\n");
tmsp.tv_sec = tmsp.tv_nsec = 0;
if (clock_settime (clk_id, tmsp) == -1) {
perror ("CLOCK_SETTIME");
return (3);
}
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
if (clock_gettime (clk_id, tmsp) == -1) {
perror ("CLOCK_GETTIME");
return (4);
}
fprintf (stderr, "Процессорное время выполнения цикла: %ld сек. %ld нсек.\n",
tmsp.tv_sec, tmsp.tv_nsec);
return (0);
}
Разрешающая способность часов процессорного времени: 2 нсек. Начало выполнения цикла Процессорное время выполнения цикла: 15 сек. 350313054 нсек.
Обратим внимание на два момента. Перед началом измеряемого участка программы на clock_settime(). Второй заслуживающий отдельного упоминания момент состоит в отсутствии необходимости выяснять clock_getcpuclockid(): можно воспользоваться именованной константой CLOCK_PROCESS_CPUTIME_ID.
Информацию о файле, в том числе данные о stat().
Для изменения ассоциированных с файлами данных о utime() (см. листинг 12.25), оперирующая в терминах
#include <utime.h>
int utime (const char *path,
const struct utimbuf *times);
Если значение аргумента times равно пустому указателю, используется
Если значение times отлично от NULL, оно трактуется как указатель на структуру типа utimbuf, которая, согласно стандарту POSIX-2001, содержит по крайней мере следующие поля:
time_t actime; /* Время последнего доступа */ time_t modtime; /* Время последнего изменения */
При этом данные о
В случае успешного завершения функция utime() возвращает нулевой результат и, кроме того, переустанавливает
Приведем пример программы, изменяющей
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа устанавливает время последнего доступа к файлу - */
/* аргументу командной строки, равное текущему времени */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#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);
}
Чтобы изменить только одно из двух ассоциированных с файлом времен, необходимо с помощью функции stat() получить информацию о файле, перенести неизменяемое значение из структуры типа stat в utimbuf, установить требуемым образом второй элемент структуры типа utimbuf и только после этого обратиться к функции utime().
Читателю предлагается убедиться, что приведенная программа работает должным образом и что опция -u служебной программы ls предписывает оперировать со
Обычным средством ожидания наступления каких-либо событий, не прерывающих выполнения процесса (потока управления), является циклическое чередование
Функция sleep() (см. листинг 12.27) позволяет приостановить выполнение процесса (потока управления) на заданное число sleep() завершится досрочно, а результат будет равен "недоспанному" числу
#include <unistd.h> unsigned sleep (unsigned seconds);
Обратим внимание на то, что аргумент и результат функции 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);
Аргумент 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.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 . . .
Функция 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;
}
}
}
. . .
Подобные действия характерны для функций аккуратного завершения многопроцессных приложений, когда
Описываемые далее средства для работы с
Реализация должна предоставлять каждому процессу по крайней мере три
ITIMER_REAL
CLOCK_REALTIME и, следовательно, его показания уменьшаются в реальном масштабе времени. Когда он срабатывает, процессу доставляется сигнал SIGALRM.
ITIMER_VIRTUAL
SIGVTALRM.
ITIMER_PROF
SIGPROF.
Согласно стандарту POSIX-2001, getitimer() и setitimer() (см. листинг 12.32).
#include <sys/time.h>
int getitimer (int timer_id,
struct itimerval *cvalue);
int setitimer (int timer_id,
const struct itimerval *restrict nvalue,
struct itimerval *restrict ovalue);
Поддерживаемые стандартом значения аргумента timer_id перечислены выше. Другие характеристики itimerval, которая должна содержать по крайней мере следующие поля:
struct timeval it_interval; /* Интервал таймера */ struct timeval it_value; /* Текущие показания таймера */ /* (ведется обратный отсчет) */
(Напомним, что структура типа timeval была описана выше. Она содержит по крайней мере два поля, задающие время, соответственно, в
Значение поля it_value (если оно отлично от нуля) показывает время, оставшееся до срабатывания it_value, равным it_interval (если последнее отлично от нуля).
Установка нулевого значения в поле it_value, независимо от величины it_interval, it_interval,
Функция getitimer() запоминает текущие характеристики cvalue. Функция setitimer() - ее можно назвать многоцелевой - взводит или nvalue, и сохраняет старые по указателю ovalue (если он отличен от NULL ).
Признаком успешного завершения обеих функций является нулевой результат.
Здесь уместно акцентировать внимание на одной тонкости, которую до сих пор мы старательно замалчивали. Поскольку разрешающая способность любых clock_settime(), nanosleep() или setitimer() не удовлетворяют данному условию, производится округление. Для
Последнее обстоятельство позволяет выяснить размер
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа выясняет размер такта часов реального времени */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <sys/time.h>
int main (void) {
struct itimerval itvl;
itvl.it_interval.tv_sec = 0;
itvl.it_interval.tv_usec = 1;
itvl.it_value.tv_sec = 0;
itvl.it_value.tv_usec = 0;
if (setitimer (ITIMER_REAL, itvl, NULL) < 0) {
perror ("SETITIMER");
return (1);
}
if (getitimer (ITIMER_REAL, itvl) < 0) {
perror ("GETITIMER");
return (2);
}
if (itvl.it_interval.tv_usec < 2) {
printf ("Не удалось выяснить размер такта часов реального времени\n");
} else {
printf ("Размер такта часов реального времени: %ld мксек\n",
itvl.it_interval.tv_usec);
}
return 0;
}
Здесь it_value - нулевое) и, соответственно, не срабатывает. Он нужен лишь для того, чтобы операционная система подкорректировала должным образом (с учетом it_interval.tv_usec. В результате корректировки первоначально присвоенная этому полю единица может превратиться, например, в 10000 (микросекунд), что дает тактовую частоту
Следующая программа (см. листинг 12.34) сравнивает ход реального и
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа сравнивает ход реального и виртуального */
/* времени процесса */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#define TM_BUF_SIZE 128
/* Массив для хранения показаний часов */
/* реального времени в момент срабатывания */
/* таймера виртуального времени процесса */
static struct timeval rt_vls [TM_BUF_SIZE];
/* Указатель на текущий элемент */
/* массива rt_vls */
static struct timeval *prt_vls = rt_vls;
/* Функция обработки срабатывания таймеров */
/* (сигналы SIGALRM и SIGVTALRM) */
static void proc_itexprtn (int signo) {
struct itimerval itmvl = {{0, 0}, {0, 0}};
/* Запомним текущее астрономическое время */
(void) gettimeofday (prt_vls++, NULL);
if (signo == SIGALRM) {
/* Сработал таймер реального времени. */
/* Разрядим таймер виртуального времени */
/* и завершимся (с выдачей результатов) */
(void) setitimer (ITIMER_VIRTUAL, itmvl, NULL);
exit (0);
}
}
/* Функция выдачи данных о ходе реального времени */
/* на фоне равномерного течения виртуального времени процесса */
static void print_rt_data (void) {
struct timeval *tprt_vls = rt_vls;
int i = 0;
printf ("Прошедшее реальное время за один квант виртуального\n");
while (++tprt_vls != prt_vls) {
printf (" %3d %10ld мксек\n", ++i,
(tprt_vls->tv_sec - (tprt_vls - 1)->tv_sec) * 1000000 +
(tprt_vls->tv_usec - (tprt_vls - 1)->tv_usec));
}
}
int main (void) {
struct itimerval itvl;
struct sigaction sact;
sigset_t sset;
/* Установим реакцию на сигналы SIGALRM и SIGVTALRM. */
/* Позаботимся, чтобы функция обработки не могла быть прервана */
/* срабатыванием другого таймера */
if ((sigemptyset (sset) < 0) || (sigaddset (sset, SIGALRM) < 0) ||
(sigaddset (sset, SIGVTALRM) < 0)) {
perror ("SIGEMPTYSET or SIGADDSET");
return (1);
}
sact.sa_handler = proc_itexprtn;
sact.sa_flags = 0;
sact.sa_mask = sset;
if (sigaction (SIGALRM, sact, NULL) < 0) {
perror ("SIGACTION-SIGALRM");
return (2);
}
if (sigaction (SIGVTALRM, sact, NULL) < 0) {
perror ("SIGACTION-SIGVTALRM");
return (3);
}
/* Зарегистрируем функцию print_rt_data() в atexit() */
if (atexit (print_rt_data) != 0) {
perror ("ATEXIT");
return (4);
}
/* Взведем таймер реального времени как одноразовый */
itvl.it_interval.tv_sec = 0;
itvl.it_interval.tv_usec = 0;
itvl.it_value.tv_sec = 10;
itvl.it_value.tv_usec = 0;
if (setitimer (ITIMER_REAL, itvl, NULL) < 0) {
perror ("SETITIMER-REAL");
return (5);
}
/* Установим начало отсчета для данных о реальном времени */
(void) gettimeofday (prt_vls++, NULL);
/* Таймер виртуального времени сделаем периодическим */
itvl.it_interval.tv_sec = 0;
itvl.it_interval.tv_usec = 100000; /* 0.1 сек */
itvl.it_value.tv_sec = 0;
itvl.it_value.tv_usec = 100000;
if (setitimer (ITIMER_VIRTUAL, itvl, NULL) < 0) {
perror ("SETITIMER-VIRTUAL");
return (6);
}
/* Убедимся, что пока процесс приостановлен, */
/* его виртуальное время также стоит */
sleep (5);
/* Нагрузим процессор вычислениями, которые не должны завершиться */
/* до срабатывания таймера реального времени */
{
double s = 0;
double d = 1;
int i;
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
}
return 0;
}
Результаты работы программы могут выглядеть так, как показано в листинге 12.35. Можно видеть, что, во-первых, доля sleep (5) в листинге 12.34, отсчет 1 в результатах).
Прошедшее реальное время за один квант виртуального 1 5255378 мксек 2 249993 мксек 3 250003 мксек 4 100000 мксек 5 250177 мксек 6 859831 мксек 7 100054 мксек 8 729943 мксек 9 580003 мксек 10 99989 мксек 11 580129 мксек 12 429881 мксек 13 99990 мксек
IT_PERIOD числа
/* * * * * * * * * * * * * * * * * * * * */
/* Программа вызывает функции обработки */
/* и контролирует время их выполнения */
/* с помощью интервального таймера */
/* * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <setjmp.h>
/* Период интервального таймера (в секундах) */
#define IT_PERIOD 1
static jmp_buf buf_env; /* Буфер для функций setjmp и longjmp */
static double s; /* Результат функций обработки данных */
/* Функция обработки срабатывания таймера реального времени */
/* (сигнал SIGALRM) */
static void proc_sigalrm (int dummy) {
printf ("Текущий результат текущей функции обработки данных: %g\n", s);
longjmp (buf_env, 1);
}
/* Первая функция обработки данных */
/* (вычисляет ln (2)) */
static void proc_data_1 (void) {
double d = 1;
int i;
s = 0;
for (i = 1; i <= 100000000; i++) {
s += d / i;
d = -d;
}
}
/* Вторая функция обработки данных */
/* (вычисляет sqrt (2)) */
static void proc_data_2 (void) {
s = 1;
do {
s = (s + 2 / s) * 0.5;
} while ((s * s - 2) > 0.000000001);
}
int main (void) {
/* Массив указателей на функции обработки данных */
void (*fptrs []) (void) = {proc_data_1, proc_data_2, NULL};
/* Указатель на указатель */
/* на текущую функцию обработки данных */
void (**tfptr) (void);
/* Вспомогательная временная переменная */
void (*tmpfptr) (void);
struct itimerval itvl;
struct sigaction sact;
sigset_t sset;
int i;
/* Установим реакцию на сигнал SIGALRM */
if (sigemptyset (sset) < 0) {
perror ("SIGEMPTYSET");
return (1);
}
sact.sa_handler = proc_sigalrm;
sact.sa_flags = SA_NODEFER; /* Не блокировать SIGALRM */
/* в функции обработки этого сигнала */
sact.sa_mask = sset;
if (sigaction (SIGALRM, sact, NULL) < 0) {
perror ("SIGACTION");
return (2);
}
/* На всякий случай сделаем таймер реального времени периодическим */
itvl.it_interval.tv_sec = IT_PERIOD;
itvl.it_interval.tv_usec = 0;
/* Цикл вызова функций обработки данных. */
/* Выполним его дважды */
for (i = 0; i < 2; i++) {
tfptr = fptrs;
(void) setjmp (buf_env);
/* Сюда вернется управление после срабатывания таймера */
while ((tmpfptr = *tfptr++) != NULL) {
/* Даже если предыдущая функция обработки данных */
/* закончилась до того, как сработал таймер, */
/* обеспечим текущей функции полный интервал */
itvl.it_value.tv_sec = IT_PERIOD;
itvl.it_value.tv_usec = 0;
if (setitimer (ITIMER_REAL, itvl, NULL) < 0) {
perror ("SETITIMER");
return (3);
}
(*tmpfptr) ();
printf ("Результат текущей функции обработки данных: %g\n", s);
}
}
return 0;
}
Результаты работы приведенной программы могут выглядеть так, как показано в листинге 12.37. Видно, что первая функция обработки данных не уложилась в отведенное время, поэтому ее выполнение было прервано срабатыванием
Текущий результат текущей функции обработки данных: 0.693147 Результат текущей функции обработки данных: 1.41421 Текущий результат текущей функции обработки данных: 0.693147 Результат текущей функции обработки данных: 1.41421
Комбинация
Во-первых, обратим внимание на использование флага SA_NODEFER, предписывающего не блокировать сигнал при входе в функцию его (сигнала) обработки. Этот флаг необходим, поскольку не происходит нормального выхода из функции proc_sigalrm(), а значит, не происходит и восстановления маски сигналов процесса. В результате, после первого входа в SIGALRM он рискует остаться заблокированным, вследствие чего процесс не будет извещен о последующих срабатываниях
Во-вторых, следует исходить из возможности (**tfptr++) (), то нет гарантии, что компилятор не запланирует увеличение указателя tfptr после возврата из функции, выполнение которой может быть прервано срабатыванием tmpfptr.
В-третьих, процессу может выделяться сколь угодно малая доля setjmp() после buf_env, после чего нелокальный переход из функции обработки сигнала приведет к полному хаосу.
if (setitimer (ITIMER_REAL,
itvl, NULL) < 0) {
. . .
}
. . .
(void) setjmp (buf_env);
Проще (и, вероятно, лучше) всего бороться с подобными проблемами, манипулируя SIGALRM. Мы, однако, попытались обойтись без этого и будем признательны, если читатель обнаружит ошибки в нашем варианте программы и сообщит о них.
Еще один любопытный момент технического характера состоит в том, что обычно проверяют значение, возвращаемое setjmp(), чтобы понять, как управление оказалось в данной точке - после естественного возврата или нелокального перехода. Но поскольку в приведенной программе это ни на что не влияет, проверку можно опустить.
К теме alarm() (см. листинг 12.39), позволяющая вызывающему процессу заказать SIGALRM через заданное число alarm() предыдущий заказ существовал и еще не был выполнен, он отменяется, а если значение аргумента seconds отлично от нуля, его место занимает новый.
#include <unistd.h> unsigned alarm (unsigned seconds);
При наличии предыдущего заказа функция alarm() возвращает число
Примеры использования функции alarm() были приведены при рассмотрении средств межпроцессного взаимодействия.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.