/var/log/messages ),
Для работы с syslog() ), установки фильтра ( setlogmask() ) и других параметров openlog() ) и, наконец, завершения работы с closelog() ) (см. листинг 9.1). Средства чтения
#include <syslog.h> void syslog (int priority, const char *message, ... /* аргументы */); int setlogmask (int maskpri); void openlog (const char *ident, int logopt, int facility); void closelog (void);
syslog(), включает
message, играющего роль формата, и следующих за ним необязательных printf(), только допускается один дополнительный %m, не требующий errno
Значение priority формируется как побитное ИЛИ флагов двух видов, задающих, соответственно,
LOG_EMERG
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG
Из LOG_USER ). Зарезервированы флаги для системных LOG_KERN, LOG_MAIL, LOG_NEWS, LOG_UUCP, LOG_DAEMON, LOG_AUTH, LOG_CRON, LOG_LPR ) и для абстрактных локальных сущностей ( LOG_LOCAL0 – LOG_LOCAL7 ).
Функция setlogmask() в качестве результата возвращает предыдущую и устанавливает новую maskpri. Для формирования этого LOG_MASK ( ). Для задания маски, включающей несколько уровней, нужно взять побитное ИЛИ подобных выражений.
Если значение maskpri равно нулю, текущая маска остается неизменной. Подразумевая маска является "полной", она специфицирует
Функция openlog() устанавливает значения syslog(). ident задает logopt специфицирует
LOG_PID
Записывать вместе с
LOG_CONS
Выдавать
LOG_NDELAY
Немедленно открыть
LOG_ODELAY
Отложить syslog() (выбор между немедленным или отложенным
LOG_NOWAIT
Не ждать завершения процессов, которые могли быть порождены в ходе SIGCHLD.
facility устанавливает подразумеваемое значение для
Функции openlog() и syslog() могут открывать (расходовать) closelog() закроет их.
Вообще говоря, вызывать openlog() до syslog() и setlogmask() не обязательно.
К рассматриваемой прикладной области можно отнести служебную программу :
logger цепочка_символов ...
которая неким неспецифицированным образом сохраняет
Подобная возможность полезна для выдачи диагностических
Рассмотрим пример применения функций для работы с
/* * * * * * * * * * * * * * * * * */
/* Пример использования функций */
/* для работы с системным журналом */
/* * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <syslog.h>
int main (void) {
int logmask; /* Прежняя маска журналирования */
/* Будем включать в журналируемые сообщения */
/* идентификатор процесса и выдавать их при */
/* возникновении проблем на системную консоль */
openlog ("Intuit syslog test", LOG_PID | LOG_CONS, LOG_USER);
/* Пренебрежем предупреждениями и менее серьезными сообщениями */
logmask = setlogmask (LOG_MASK (LOG_EMERG) | LOG_MASK
(LOG_ALERT) | LOG_MASK (LOG_CRIT) | LOG_MASK (LOG_ERR));
printf ("Подразумеваемая маска журналирования: %x\n", logmask);
/* Поместим сообщение в журнал */
syslog (LOG_ALERT | LOG_USER, "Как читать системный журнал?");
/* Восстановим прежнюю маску журналирования */
(void) setlogmask (logmask);
closelog ();
return 0;
}
Результатом работы этой программы может быть строка, показанная на листинге 9.3.
Подразумеваемая маска журналирования: ff
Привлечь внимание fmtmsg() (см. листинг 9.4).
#include <fmtmsg.h>
int fmtmsg (long classification,
const char *label,
int severity, const char *text,
const char *action,
const char *tag);
Функция fmtmsg() конструирует отформатированное
определяет MM_HARD (аппаратура), MM_SOFT (программное обеспечение), MM_FIRM (программно-аппаратные средства). MM_APPL (приложение), MM_UTIL (служебная программа), MM_OPSYS (операционная система). Проблемы классифицируются также по признаку нейтрализуемости – соответственно, MM_RECOVER и MM_NRECOV.
Если следует прибавить константу MM_PRINT ; вывод на MM_CONSOLE. Возможно одновременное указание обеих констант.
Константа MM_NULLMC означает отсутствие классификационного компонента (естественно, ее значение равно нулю).
label специфицирует первый из пяти компонентов выдаваемого , определяет
severity характеризует серьезность проблемы. Стандартом POSIX-2001 предусмотрены следующие
MM_HALT
В приложении встретилась серьезная ошибка, его работа остановлена. В ".
MM_ERROR
В работе приложения обнаружена ошибка. В ERROR ".
MM_WARNING
При работе приложения возникла необычная ситуация, возможно, являющаяся ошибочной и требующая внимания. В WARNING ".
MM_INFO
Информация о ситуации, не являющейся ошибочной. В INFO ".
MM_NOSEV
Данная константа обозначает отсутствие у
text в свободной форме описывает ситуацию, приведшую к генерации
action также в свободной форме описывает первый шаг по нейтрализации ошибки. Перед цепочкой, на которую указывает action, в TO FIX:".
tag служит ссылкой на документацию по выявленной проблеме.
На работу функции fmtmsg() влияет MSGVERB, которая определяет, какие из пяти возможных компонентов label, severity, text, action, tag – разделенных двоеточиями. Если какие-то ключевые слова отсутствуют, соответствующие компоненты MSGVERB отсутствует в окружении, имеет пустое или некорректное значение,
Возможные результаты функции fmtmsg() устроены необычным образом. Константа MM_OK обозначает полный успех, MM_NOTOK – полную неудачу, MM_NOMSG – невозможность выдать MM_NOCON – невозможность вывода на консоль.
Приведем не очень серьезный пример применения функции fmtmsg() (см. листинг 9.5).
#include <stdio.h>
#include <fmtmsg.h>
int main (void) {
if (fmtmsg (MM_SOFT + MM_OPSYS + MM_RECOVER
+ MM_PRINT + MM_CONSOLE, "POSIX:fmtmsg",
MM_INFO, "Отсутствует функция fmtmsg()",
"Установите функцию fmtmsg()
или не пользуйтесь ею\n",
"См. functions/fmtmsg.html") != MM_OK) {
perror ("FMTMSG");
return (1);
}
return 0;
}
В результате выполнения приведенной программы в
POSIX:fmtmsg: INFO: Отсутствует функция fmtmsg() TO FIX: Установите функцию fmtmsg() или не пользуйтесь ею См. functions/fmtmsg.html
Читателю предлагается самостоятельно поэкспериментировать с этой программой, варьируя значение MSGVERB.
Мы продолжаем рассматривать функции, находящиеся на стыке пользовательских и административных средств.
Стандартом POSIX-2001 предусмотрен набор функций для работы с getutxent() ), getutxid(), getutxline() ), pututxline() ), setutxent() ) и endutxent() ) (см. листинг 9.7).
#include <utmpx.h>
struct utmpx *getutxent (void);
struct utmpx *getutxid (
const struct utmpx *id);
struct utmpx *getutxline (
const struct utmpx *line);
struct utmpx *pututxline (
const struct utmpx *utmpx);
void setutxent (void);
void endutxent (void);
Центральную роль для описываемого набора функций играет структура типа utmpx, которая, согласно стандарту, должна содержать по крайней мере следующие поля.
char ut_user [];
/* Входное имя пользователя */
char ut_id [];
/* Неспецифицированный */
/* инициализационный идентификатор */
/* процесса (например, первое поле */
/* в строке файла inittab) */
char ut_line [];
/* Имя устройства */
pid_t ut_pid;
/* Идентификатор процесса */
short ut_type;
/* Тип записи */
struct timeval ut_tv;
/* Время создания записи */
В зависимости от ut_type ) определяется подмножество полей, содержащих осмысленные значения. Для пустых EMPTY ) таких полей нет вообще. Для BOOT_TIME (идентифицирует время загрузки системы), OLD_TIME (время изменения показаний системных часов), NEW_TIME (показания системных часов после изменения) имеет смысл только ut_tv ). USER_PROCESS идентифицируют ut_user ( ut_id, ut_line, ut_pid и ut_tv. Почти такое же подмножество полей имеет смысл для LOGIN_PROCESS (по стандарту он идентифицирует ut_user (зависящее от реализации имя входного процесса), ut_id, ut_pid, ut_tv. Наконец, для INIT_PROCESS (идентифицирует процесс, порожденный системным процессом init ) и DEAD_PROCESS (по стандарту он идентифицирует ut_id, ut_pid и ut_tv.
Разумеется, реальные размеры массивов, являющихся элементами структуры, можно узнать, применяя к ним на sizeof().
Функция getutxent() читает очередную utmpx.
Функция getutxid(), начиная с текущей позиции, разыскивает ut_type соответствует значению id->ut_type. Если элемент id->ut_type равен BOOT_TIME, OLD_TIME, или NEW_TIME, то требуется точное равенство типов. Если же id->ut_type равняется INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS или DEAD_PROCESS, то функция getutxid() вернет указатель на копию первой ut_id соответствует значению id->ut_id.
Функция getutxline() аналогичным образом разыскивает LOGIN_PROCESS или USER_PROCESS, а поле ut_line соответствует значению line->ut_line.
Доступ к
Функция pututxline() записывает указанную utmpx -структуру в getutxid(), если обнаруживается, что текущая позиция не является подходящей. В случае неудачи
Отметим, что getutxid() или getutxline() реализация имеет право сначала проанализировать упомянутую статическую структуру и, если та окажется подходящей, не производить
В свою очередь, согласно стандарту, приложение имеет право передать функции pututxline() указатель на статическую структуру, заполненную в результате обращения к getutxent(), getutxid() или getutxline(), предварительно изменив ее требуемым образом. Неявное чтение, осуществляемое функцией pututxline() для определения замещаемой
Функция setutxent() устанавливает
Функция endutxent() закрывает
Приведем пример использования описанных функций (см. листинг 9.8). Прочитаем и выведем все
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Пример использования функций для работы */
/* с базой данных учетной информации о пользователях */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <limits.h>
#include <time.h>
#include <utmpx.h>
#include <string.h>
int main (void) {
struct utmpx *utmpx_ptr; /* Указатель на текущую запись */
char dtbuf [LINE_MAX]; /* Буфер для данных о времени */
struct utmpx spat; /* Шаблон для поиска в базе */
/* Прочитаем и распечатаем все записи в базе */
printf ("Содержимое базы данных учетной информации "
"о пользователях\n");
while ((utmpx_ptr = getutxent ()) != NULL) {
(void) strftime (dtbuf, sizeof (dtbuf), "%c",
localtime ((utmpx_ptr->ut_tv.tv_sec)));
switch (utmpx_ptr->ut_type) {
case EMPTY:
printf ("Пустая запись\n");
break;
case BOOT_TIME:
printf ("Время загрузки системы: %s\n", dtbuf);
break;
case OLD_TIME:
printf ("Время изменения показаний системных "
"часов: %s\n", dtbuf);
break;
case NEW_TIME:
printf ("Показания системных часов после "
"изменения: %s\n", dtbuf);
break;
case USER_PROCESS:
printf ("Процесс пользователя: %s, идентификатор: "
"%d,\n", utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Имя устройства: %s,\n",
utmpx_ptr->ut_line);
printf ("Время создания записи: %s\n", dtbuf);
break;
case LOGIN_PROCESS:
printf ("Входной процесс: %s, идентификатор: %d,\n",
utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Время создания записи: %s\n", dtbuf);
break;
case INIT_PROCESS:
printf ("Процесс, порожденный системным процессом "
"init:\n");
printf ("Идентификатор: %d,\n",
utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Время создания записи: %s\n", dtbuf);
break;
case DEAD_PROCESS:
printf ("Лидер сеанса, завершивший выполнение:\n");
printf ("Идентификатор: %d,\n", utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Время создания записи: %s\n", dtbuf);
break;
default:
printf ("Нестандартный тип записи: %x\n",
utmpx_ptr->ut_type);
break;
}
}
/* Найдем и распечатаем записи, */
/* инициализационный идентификатор которых */
/* равняется S4 */
spat.ut_type = INIT_PROCESS;
(void) strncpy (spat.ut_id, "S4", sizeof (spat.ut_id));
/* Позиционируемся на начало базы */
setutxent ();
printf ("Записи, инициализационный идентификатор "
"которых равняется S4:\n");
while ((utmpx_ptr = getutxid (spat)) != NULL) {
switch (utmpx_ptr->ut_type) {
case USER_PROCESS:
printf ("Процесс пользователя: %s, "
"идентификатор: %d\n", utmpx_ptr->ut_user,
utmpx_ptr->ut_pid);
break;
case LOGIN_PROCESS:
printf ("Входной процесс: %s, идентификатор: "
"%d\n",utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
break;
case INIT_PROCESS:
printf ("Процесс, порожденный системным процессом "
"init:\n");
printf ("Идентификатор: %d\n", utmpx_ptr->ut_pid);
break;
case DEAD_PROCESS:
printf ("Лидер сеанса, завершивший "
"выполнение:\n");
printf ("Идентификатор: %d\n", utmpx_ptr->ut_pid);
break;
default:
printf ("Нестандартный тип результата поиска: "
"%x\n", utmpx_ptr->ut_type);
break;
}
/* Обеспечим сдвиг поиска с текущей записи */
utmpx_ptr->ut_id [0] = 0;
}
endutxent ();
return 0;
}
Фрагмент возможных результатов выполнения приведенной программы на платформе ОС Linux показан на листинге 9.9. Его изучение позволяет лучше уяснить смысл элементов структуры utmpx для разных
Содержимое базы данных учетной информации о пользователях
Лидер сеанса, завершивший выполнение:
Идентификатор: 17,
Инициализационный идентификатор процесса: si,
Время создания записи: Tue Apr 27 10:08:42 2004
Время загрузки системы: Tue Apr 27 10:08:42 2004
Нестандартный тип записи: 1
Лидер сеанса, завершивший выполнение:
Идентификатор: 284,
Инициализационный идентификатор процесса: l5,
Время создания записи: Tue Apr 27 10:09:15 2004
Лидер сеанса, завершивший выполнение:
Идентификатор: 1115,
Инициализационный идентификатор процесса: ud,
Время создания записи: Tue Apr 27 10:09:15 2004
. . .
Входной процесс: LOGIN, идентификатор: 1123,
Инициализационный идентификатор процесса: S3,
Время создания записи: Tue Apr 27 10:09:15 2004
Процесс пользователя: galat, идентификатор: 1124,
Инициализационный идентификатор процесса: S4,
Имя устройства: ttyS4,
Время создания записи: Tue Apr 27 12:52:51 2004
Процесс пользователя: sambor, идентификатор: 1125,
Инициализационный идентификатор процесса: S5,
Имя устройства: ttyS5,
Время создания записи: Tue Apr 27 13:57:31 2004
Процесс пользователя: kost, идентификатор: 1126,
Инициализационный идентификатор процесса: S6,
Имя устройства: ttyS6,
Время создания записи: Tue Apr 27 10:09:30 2004
. . .
Процесс, порожденный системным процессом init:
Идентификатор: 1128,
Инициализационный идентификатор процесса: x,
Время создания записи: Tue Apr 27 10:09:15 2004
. . .
Лидер сеанса, завершивший выполнение:
Идентификатор: 11708,
Инициализационный идентификатор процесса: /1,
Время создания записи: Tue Apr 27 11:19:33 2004
. . .
Записи, инициализационный идентификатор которых равняется S4:
Процесс пользователя: galat, идентификатор: 1124
Отметим, что нестандартный 1 соответствует смене уровня выполнения.
Обратим также внимание на то, что в шаблоне getutxid(), значение поля ut_type задано как INIT_PROCESS, а в результате USER_PROCESS (в полном соответствии со стандартом).
Описываемые ниже функции полезны для реализации "игрушечных"
Набор стандартизованных функций "традиционно минимален" (см. листинг 9.10). dbm_open() ) и закрыть ( dbm_close() ), выбрать ( dbm_fetch() ), сохранить ( dbm_store() ) и удалить ( dbm_delete() ) dbm_firstkey(), dbm_nextkey() ), опросить dbm_error() ) и очистить его ( dbm_clearerr() ).
#include <ndbm.h>
DBM *dbm_open (const char *file,
int open_flags, mode_t file_mode);
void dbm_close (DBM *db);
datum dbm_fetch (DBM *db, datum key);
int dbm_store (DBM *db, datum key,
datum content, int store_mode);
int dbm_delete (DBM *db, datum key);
datum dbm_firstkey (DBM *db);
datum dbm_nextkey (DBM *db);
int dbm_error (DBM *db);
int dbm_clearerr (DBM *db);
Функцию dbm_open() можно считать аналогом open(), только в роли возвращаемого в качестве результата дескриптора (структура последнего скрыта от приложения), база хранится в двух файлах – file.dir и file.pag, ее нельзя открыть только на O_APPEND ведет к неспецифицированным последствиям. В случае ошибки результат вызова dbm_open() равняется (.
При работе с datum, которая по стандарту должна содержать по крайней мере два поля.
void *dptr;
/* Указатель на прикладные данные */
size_t dsize;
/* Размер прикладных данных */
Функция dbm_fetch() служит для key. Если таковая отсутствует или обнаруживается ошибка, то в возвращаемом объекте datum элемент dptr равняется пустому указателю.
Функция dbm_store() позволяет поместить данные, заданные content, в базу. store_mode определяет способ key, то в режиме DBM_REPLACE новая DBM_INSERT она (новая key в базе нет, новая
Функция dbm_delete() предназначена для key.
Нормальным результатом функций dbm_store() и dbm_delete() является нуль; в случае ошибки возвращается отрицательное значение.
Функция dbm_nextkey() является итератором по dbm_firstkey() инициализирует этот итератор, возвращая первый datum с пустым указателем в качестве значения элемента dptr. Если по ходу итераций содержимое базы менялось (вызывались функции dbm_store() и/или dbm_delete() ), перебор
Функция dbm_error() возвращает ненулевое значение при установленном dbm_clearerr() делает статус "безошибочным". Таким образом, вся диагностика по сути сводится к одному биту, интерпретировать который должно приложение.
Несмотря на "игрушечность" описанного интерфейса, он находит определенное применение на Unix-системах. В качестве примера рассмотрим программу, которая распечатывает содержимое aliases, расположенной в /etc/mail на SPARC -станции (см. листинг 9.11).
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа перебирает все ключи в базе данных */
/* и выдает ассоциированную с ними информацию. */
/* Предполагается, что ключи и данные – текстовые*/
/* * * * * * * * * * * * * * * * * * * * * * * * */
#include <ndbm.h>
#include <stdio.h>
#include <fcntl.h>
int main (int argc, char *argv []) {
DBM *dbdes; /* Дескриптор открытой базы */
datum ckey; /* Текущий ключ */
datum cdat; /* Текущие данные */
int nkeys = 0; /* Число ключей */
if (argc != 2) {
fprintf (stderr, "Использование: %s имя_базы\n",
argv [0]);
return (1);
}
if ((dbdes = dbm_open (argv [1],
O_RDONLY, 0777)) == (DBM *) NULL) {
fprintf (stderr, "Не удалось открыть базу данных %s\n",
argv [1]);
return (2);
}
for (ckey = dbm_firstkey (dbdes); ckey.dptr != NULL;
ckey = dbm_nextkey (dbdes)) {
nkeys++;
printf ("Длина ключа номер %d: %d\n", nkeys,
ckey.dsize);
printf ("Ключ номер %d: %s\n", nkeys, ckey.dptr);
if (cdat = dbm_fetch (dbdes, ckey),
cdat.dptr != NULL) {
printf ("Длина данных для ключа номер %d: %d\n",
nkeys, cdat.dsize);
printf ("Данные для ключа номер %d: %s\n", nkeys,
cdat.dptr);
} else {
fprintf (stderr, "Отсутствуют данные для "
"ключа номер %d\n", nkeys);
}
}
printf ("Число ключей в базе: %d\n", nkeys);
dbm_close (dbdes);
return 0;
}
Результаты работы этой программы могут выглядеть так, как показано на листинге 9.12.
Длина ключа номер 1: 16 Ключ номер 1: YP_LAST_MODIFIED Длина данных для ключа номер 1: 10 Данные для ключа номер 1: 0898782331 Длина ключа номер 2: 14 Ключ номер 2: mailer-daemon Длина данных для ключа номер 2: 11 Данные для ключа номер 2: postmaster Длина ключа номер 3: 14 Ключ номер 3: YP_MASTER_NAME Длина данных для ключа номер 3: 3 Данные для ключа номер 3: t41 Длина ключа номер 4: 11 Ключ номер 4: postmaster Длина данных для ключа номер 4: 5 Данные для ключа номер 4: root Длина ключа номер 5: 7 Ключ номер 5: nobody Длина данных для ключа номер 5: 10 Данные для ключа номер 5: /dev/null Длина ключа номер 6: 2 Ключ номер 6: @ Длина данных для ключа номер 6: 2 Данные для ключа номер 6: @ Число ключей в базе: 6
Стандарт POSIX-2001 предлагает несколько способов
#include <stdlib.h>
void *bsearch (const void *key,
const void *base,
size_t nel, size_t width,
int (*compar) (const void *,
const void *));
Функция в дополнительной литературе, пункт 6.2.1, алгоритм B).
Функция bsearch() возвращает указатель внутрь массива на искомые данные или NULL в случае неудачи compar().
key указывает на объект данных, разыскиваемый в массиве ( base указывает на начало (первый элемент) массива; nel задает количество элементов в массиве; width специфицирует размер элемента в массиве.
compar() – это
Для bsearch().
#include <stdlib.h>
void qsort (void *base, size_t nel,
size_t width,
int (*compar) (const void *,
const void *));
Рассмотрим пример последовательного применения функций qsort() и bsearch() (см. листинг 9.15). Здесь в роли элементов массива выступают указатели на StringSpace ; тот же тип имеет и
/* * * * * * * * * * * * * * * * * * * * * */
/* Программа сортирует массив указателей */
/* на случайные цепочки символов, а затем */
/* выполняет в этом массиве бинарный поиск */
/* * * * * * * * * * * * * * * * * * * * * */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 10000000
/* Число элементов в таблице указателей на цепочки символов */
#define TAB_SIZE 1000000
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 10
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* Массив указателей на цепочки символов */
static char *PtsTable [TAB_SIZE];
/* Число занятых элементов в массиве указателей */
static size_t nelst;
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * */
/* Заполнение массива указателями */
/* на случайные цепочки символов */
/* * * * * * * * * * * * * * * * * */
static void tabl_fill (void) {
char *pss; /* Указатель на свободное место */
/* в области StringSpace */
int i;
for (pss = StringSpace, i = 0; i < TAB_SIZE;
pss += STRING_SIZE, i++) {
if (((pss + STRING_SIZE) –
(StringSpace + SPACE_SIZE)) > 0) {
fprintf (stderr, "tabl_fill: исчерпано "
"пространство цепочек\n");
nelst = i;
return;
}
str_rnd (pss, STRING_SIZE);
PtsTable [i] = pss;
}
nelst = TAB_SIZE;
}
/* * * * * * * * * * */
/* Функция сравнения */
/* * * * * * * * * * */
static int str_compar (const void *pkey,
const void *pelem) {
return strcoll (*((char **) pkey), *((char **) pelem));
}
/* * * * * * * * * * * */
/* Сортировка и поиск */
/* * * * * * * * * * * */
int main (void) {
char *skey; /* Указатель на искомую цепочку символов */
char **res; /* Результат бинарного поиска */
/* Буфер для формирования случайных цепочек */
char sbuf [STRING_SIZE];
double ntr; /* Номер найденной случайной цепочки */
/* Заполнение массивов */
tabl_fill ();
/* Сортировка массива указателей */
qsort (PtsTable, nelst, sizeof (PtsTable [0]),
str_compar);
/* Формирование ключа поиска */
/* (будем искать первую из случайных цепочек) */
skey = StringSpace;
if ((res = (char **) bsearch (skey, PtsTable,
nelst, sizeof (PtsTable [0]), str_compar)) != NULL) {
printf ("Указатель на первую цепочку %s\n"
"после сортировки стал %d-м элементом массива\n",
skey, (res – PtsTable) / sizeof (PtsTable [0]));
} else {
printf ("Не удалось найти цепочку %s\n", skey);
}
/* Будем формировать и искать новые случайные цепочки */
skey = sbuf;
ntr = 0;
do {
str_rnd (skey, STRING_SIZE);
ntr++;
} while (bsearch (skey, PtsTable, nelst,
sizeof (PtsTable [0]), str_compar) == NULL);
printf ("Удалось найти %g-ю по счету случайную цепочку"
" %s\n", ntr, skey);
return 0;
}
Отметим, что при PtsTable [] – указатели на
Если на компьютере, которым в данный момент пользуется автор, измерить время выполнения приведенной программы посредством time с -p, результаты будут выглядеть следующим образом (см. листинг 9.16).
Указатель на первую цепочку NWLRBBMQB после сортировки стал 133253-м элементом массива Удалось найти 168221-ю по счету случайную цепочку VBBDZTNMZ real 15.67 user 15.57 sys 0.10
Читателю предлагается сравнить эти результаты с экспериментально полученными собственными (и с гордостью убедиться, что его компьютер гораздо мощнее), а также оценить зависимость длительности
Стандартом POSIX-2001, помимо <search.h>.
В идейном плане самым простым является lsearch() ) или без таковой ( lfind() ) (см. листинг 9.17).
#include <search.h>
void *lsearch (const void *key,
void *base, size_t *nelp,
size_t width,
int (*compar) (const void *,
const void *));
void *lfind (const void *key,
const void *base, size_t *nelp,
size_t width, int (*compar) (const void *,
const void *));
Функции, реализующие bsearch(), только nelp является указателем на lsearch() может увеличить на единицу (если искомого элемента в массиве не было, его добавляют в конец). Разумеется, для compar(): в случае неравенства ее результат должен быть отличен от нуля.
В качестве иллюстрации применения функций
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа генерирует случайные цепочки символов до первого */
/* повторения (или до исчерпания отведенного пространства). */
/* Для выявления повторения применяется */
/* последовательный поиск с вставкой */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <search.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 200000
/* Число элементов в таблице указателей на цепочки символов */
#define TAB_SIZE 20000
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 7
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* Массив указателей на цепочки символов */
static char *PtsTable [TAB_SIZE];
/* * * * * * * * * * */
/* Функция сравнения */
/* * * * * * * * * * */
static int str_compar (const void *pkey,
const void *pelem) {
return strcoll (*((char **) pkey), *((char **) pelem));
}
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Поиск первого повтора в последовательности */
/* случайных цепочек символов */
/* * * * * * * * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
char *pss; /* Указатель на свободное место */
/* в области StringSpace */
char **res; /* Результат поиска с вставкой */
size_t nelst; /* Число занятых элементов */
/* в массиве указателей */
size_t onelst; /* Число элементов в массиве */
/* до поиска с вставкой */
for (pss = StringSpace, nelst = 0; nelst < TAB_SIZE;
pss += STRING_SIZE) {
if (((pss + STRING_SIZE) – (StringSpace +
SPACE_SIZE)) > 0) {
fprintf (stderr, "%s: Исчерпано пространство "
"цепочек\n", argv [0]);
return (1);
}
str_rnd (pss, STRING_SIZE);
onelst = nelst;
res = (char **) lsearch (pss, PtsTable, nelst,
sizeof (PtsTable [0]), str_compar);
if (onelst == nelst) {
/* Искомая цепочка уже была порождена ранее */
printf ("Для случайных цепочек длины %d\n"
"первое совпадение получено на цепочке "
"%s\n", STRING_SIZE, pss);
printf ("Первый раз цепочка была порождена "
"под номером %d,\n" "второй – под номером "
"%d\n", (res – PtsTable) / sizeof
(PtsTable [0]) + 1, nelst + 1);
return 0;
}
} /* for */
printf ("Из %d случайных цепочек длины %d все "
"оказались уникальными\n", TAB_SIZE, STRING_SIZE);
return 0;
}
Указатели на порождаемые случайные цепочки помещаются в массив PtsTable [] функцией lsearch(). В этой связи обратим внимание на нескольку вычурную организацию цикла for в функции main(). По сути здесь две переменные цикла – pss и nelst. Первая продвигается стандартным образом, в заголовке цикла, но проверяется на выход за допустимые границы в его теле; вторая, напротив, стандартно проверяется, но нестандартно продвигается (в результате вызова lsearch() ).
Возможные результаты выполнения этой программы показаны на листинге 9.19.
Для случайных цепочек длины 7 первое совпадение получено на цепочке GLPCSX Первый раз цепочка была порождена под номером 2548, второй - под номером 12530 real 34.80 user 13.70 sys 0.03
При экспериментах с приведенной программой следует соблюдать определенную осторожность, поскольку время ее работы квадратично зависит от величины TAB_SIZE.
Управление hcreate() ) и ликвидации ( hdestroy() ) hsearch() ), быть может,
#include <search.h> int hcreate (size_t nel); void hdestroy (void); ENTRY *hsearch (ENTRY item, ACTION action);
Предполагается, что элементы таблицы ENTRY, определенный так, как показано на листинге 9.21.
typedef struct entry {
char *key; /* Ключ поиска */
void *data;
/* Дополнительные данные, */
/* ассоциированные с ключом */
} ENTRY;
Функция hcreate() резервирует достаточное количество памяти для таблицы и должна вызываться перед обращением к hsearch(). Значением nel является ожидаемое максимальное количество элементов в таблице. Это число можно взять с запасом, чтобы уменьшить среднее время
Нормальный для hcreate() результат отличен от нуля.
Функция hdestroy() ликвидирует таблицу hcreate().
Функция hsearch() возвращает указатель внутрь таблицы на искомые данные. item – это структура типа ENTRY, содержащая два указателя: item.key указывает на сравниваемый ), а item.data – на любые дополнительные данные, ассоциированные с этим
action имеет тип ACTION, определенный так, как показано на листинге 9.22. Он задает способ действий в случае неудачного ENTER предписывает производить FIND предписывает в случае неудачи вернуть пустой указатель NULL. Пустой указатель возвращается и тогда, когда значение action равно ENTER, и таблица заполнена.
enum {
FIND,
ENTER
} ACTION;
В качестве примера применения функций, управляющих
/* * * * * * * * * * * * * * * * * * * * */
/* Программа помещает в хэш-таблицу */
/* заданное число элементов с указателями*/
/* на случайные цепочки символов, */
/* а затем выполняет в этой таблице */
/* поиск новых случайных цепочек, */
/* пока он не окажется успешным */
/* * * * * * * * * * * * * * * * * * * * */
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 10000000
/* Число элементов, помещаемых в хэш-таблицу */
#define TAB_NEL 1000000
/* Размер хэш-таблицы */
#define TAB_SIZE (2 * TAB_NEL)
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 10
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Заполнение хэш-таблицы, поиск повтора в */
/* последовательности случайных цепочек символов */
/* * * * * * * * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
ENTRY item; /* Искомый элемент */
char sbuf [STRING_SIZE]; /* Буфер для формирования */
/* случайных цепочек */
double ntr; /* Номер найденной */
/* случайной цепочки */
size_t i;
if (hcreate (TAB_SIZE) == 0) {
fprintf (stderr, "%s: Не удалось создать хэш-таблицу"
" размера %d\n", argv [0], TAB_SIZE);
return (1);
}
item.data = NULL; /* Нет ассоциированных данных */
/* Заполним таблицу */
for (item.key = StringSpace, i = 0;
i < TAB_NEL;
item.key += STRING_SIZE, i++) {
if (((item.key + STRING_SIZE) – (StringSpace +
SPACE_SIZE)) > 0) {
fprintf (stderr, "%s: Исчерпано пространство "
"цепочек\n", argv [0]);
return (2);
}
str_rnd (item.key, STRING_SIZE);
if (hsearch (item, ENTER) == NULL) {
fprintf (stderr, "%s: Переполнена хэш-таблица\n",
argv [0]);
return (3);
}
} /* for */
/* Будем формировать и искать новые случайные цепочки */
item.key = sbuf;
ntr = 0;
do {
str_rnd (item.key, STRING_SIZE);
ntr++;
} while (hsearch (item, FIND) == NULL);
printf ("Удалось найти %g-ю по счету случайную цепочку %s\n",
ntr, item.key);
hdestroy ();
return 0;
}
Обратим внимание на то, что размер
Удалось найти 168221-ю по счету случайную цепочку VBBDZTNMZ real 9.61 user 9.36 sys 0.25
Читателю предлагается измерить время работы этой программы на своем компьютере, сравнить его с аналогичным временем для
#include <search.h>
void *tsearch (const void *key, void **rootp,
int (*compar) (const void *,
const void *));
void *tfind (const void *key,
void *const *rootp,
int (*compar) (const void *,
const void *));
void *tdelete (const void *restrict key,
void **restrict rootp,
int (*compar) (const void *,
const void *));
void twalk (const void *root,
void (*action) (const void *,
VISIT, int));
Функция tsearch() используется для построения key является указателем на искомые данные (
rootp указывает на переменную, которая является указателем на NULL, специфицирует tsearch() переменная устанавливается равной указателю на единственный узел –
Подобно функции tsearch(), функция tfind() осуществляет tfind() возвращает пустой указатель NULL.
Функция tdelete(), как и tfind(), сначала производит tdelete() служит указатель на вышележащий по сравнению с удаляемым узел или NULL, если
Функция twalk() осуществляет tsearch() так, что, в соответствии с compar(), все узлы root указывает на
Очевидно, в процессе VISIT с исключительно неудачными именами (см. листинг 9.26).
enum {
preorder,
postorder,
endorder,
leaf
} VISIT;
Имена неудачны, потому что они совпадают с названиями разных способов ). Остается надеяться, что читатель не даст себя запутать и уверенно скажет, что в данном контексте – это второе посещение
action – это функция, которую twalk() вызывает при попадании в узел во время обхода. Она, в свою очередь, имеет три VISIT. Напомним еще раз, что оно показывает, который раз (первый, второй или третий) осуществляется доступ к
Читатель наверняка уже догадался, что далее последует пример программы, строящей
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа осуществляет поиск с вставкой в бинарном */
/* дереве, помещая в него заданное число элементов с */
/* указателями на случайные цепочки символов. */
/* Затем подсчитывается число узлов и высота дерева. */
/* Следующим действием является распечатка */
/* нескольких первых цепочек. */
/* После этого выполняется поиск новых случайных цепочек,*/
/* пока он не окажется успешным. */
/* Найденный элемент удаляется из дерева */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 10000000
/* Число элементов, помещаемых в дерево */
#define TREE_NEL 1000000
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 10
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* Число узлов в бинарном дереве поиска */
static size_t node_count;
/* Максимальный уровень узла дерева */
static int max_level;
/* Буфер для функций setjmp и longjmp */
static jmp_buf buf_env;
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция, которая вызывается при обходе дерева */
/* с целью подсчета числа узлов и высоты */
/* * * * * * * * * * * * * * * * * * * * * * * * */
static void tw_nnh (const void *pnode, VISIT nv, int level) {
if (nv == preorder) {
node_count++;
} else if (nv == leaf) {
node_count++;
if (level > max_level) {
max_level = level;
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция, которая вызывается при обходе дерева */
/* с целью распечатки нескольких первых */
/* по алфавиту цепочек символов */
/* * * * * * * * * * * * * * * * * * * * * * * * */
static void tw_pfs (const void *pnode, VISIT nv, int level) {
if (node_count <= 0) {
/* Нужное число цепочек выведено,*/
/* прерываем обход дерева */
longjmp (buf_env, 1);
}
if ((nv == postorder) || (nv == leaf)) {
printf ("%s\n", *((char **) pnode));
node_count--;
}
}
/* * * * * * * * * * * * * * * * * * */
/* Создание бинарного дерева поиска, */
/* определение его характеристик, */
/* поиск повтора в последовательности*/
/* случайных цепочек символов */
/* * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
void *root; /* Указатель на корень дерева */
char *key; /* Указатель на искомую */
/* цепочку символов */
char sbuf [STRING_SIZE]; /* Буфер для формирования */
/* случайных цепочек */
double ntr; /* Номер найденной случайной */
/* цепочки */
size_t i;
/* Создадим бинарное дерево поиска */
root = NULL;
for (key = StringSpace, i = 0; i < TREE_NEL; key +=
STRING_SIZE, i++) {
if (((key + STRING_SIZE) – (StringSpace +
SPACE_SIZE)) > 0) {
fprintf (stderr, "%s: Исчерпано пространство "
"цепочек\n", argv [0]);
return (1);
}
str_rnd (key, STRING_SIZE);
if (tsearch (key, root, (int (*) (const void *,
const void *)) strcoll) == NULL) {
fprintf (stderr, "%s: Поиск с вставкой в бинарное"
" дерево " "завершился неудачей\n", argv [0]);
return (2);
}
} /* for */
/* Подсчитаем число узлов и высоту созданного дерева */
node_count = 0;
max_level = 0;
twalk (root, tw_nnh);
printf ("В дереве оказалось %d узлов\n", node_count);
printf ("Его высота равна %d\n", max_level);
/* Распечатаем несколько первых (по алфавиту) цепочек, */
/* помещенных в созданное дерево */
node_count = 10;
printf ("Первые %d по алфавиту цепочек в дереве:\n",
node_count);
if (setjmp (buf_env) == 0) {
twalk (root, tw_pfs);
}
/* Будем формировать и искать новые случайные цепочки */
ntr = 0;
do {
str_rnd (sbuf, STRING_SIZE);
ntr++;
} while (tdelete (sbuf, root, (int (*) (const void *,
const void *)) strcoll) == NULL);
printf ("Удалось найти и удалить из дерева %g-ю по счету "
"случайную цепочку %s\n", ntr, sbuf);
return 0;
}
Отметим гибкость
Обратим внимание на частичный twalk() ориентирована, разумеется, на полный
Возможные результаты выполнения приведенной программы показаны на листинге 9.28.
В дереве оказалось 1000000 узлов Его высота равна 25 Первые 10 по алфавиту цепочек в дереве: AAAATNRAS AAACHCCLB AAACSJQBP AAADLHFAZ AAAFWLRXM AAAFXGQEC AAAGBMHHA AAAGFAXFI AAAHKLCWW AAAHLOSVQ Удалось найти и удалить из дерева 168221-ю по счету случайную цепочку VBBDZTNMZ real 20.24 user 20.25 sys 0.15
Отметим, что среди первых 1000000 случайных
Полноты ради упомянем еще о двух функциях, описанных в заголовочном файле <search.h>: insque() и remque() (см. листинг 9.29). Они предназначены для выполнения операций над
#include <search.h> void insque (void *element, void *pred); void remque (void *element);
Функция insque() осуществляет element, после элемента pred. В качестве элемента должна выступать структура, первые два поля которой являются указателями на структуры того же типа – соответственно, следующий и предыдущий элементы
Функция remque() удаляет заданный элемент из
insque (element, NULL) ; при инициализации
#include <search.h>
. . .
struct qelem {
struct qelem *q_forw;
struct qelem *q_back;
char *data;
. . .
};
struct qelem element1;
struct qelem element2;
. . .
element1.q_forw = element1;
element1.q_back = element1;
insque (element2, element1);
. . .
Трудно сказать, есть ли смысл в стандартизации функций, исходный текст которых занимает пару строк...
Из тех же соображений полноты вернемся к теме tsort:
tsort [файл]
выполняющую
Исходными данными для tsort служат содержащиеся в файле пары элементов (непустых
Например, если применить tsort к файлу, содержащему строки, показанные на листинге 9.31, то можно получить результат, приведенный на листинге 9.32.
a b c d d e f g e f h h
a c h b d e f g
ucontext_t, содержащей по крайней мере следующие поля.
ucontext_t *uc_link;
/* Указатель на контекст, */
/* в котором будет возобновлено */
/* выполнение при выходе из */
/* данного контекста */
sigset_t uc_sigmask;
/* Набор сигналов, блокированных */
/* в данном контексте */
stack_t uc_stack;
/* Стек, используемый в данном */
/* контексте */
mcontext_t uc_mcontext;
/* Машинно-зависимое представление */
/* сохраненного контекста */
Стандарт POSIX-2001 предоставляет функции для getcontext() ), makecontext() ) и смены ( setcontext() и swapcontext() )
#include <ucontext.h>
int getcontext (ucontext_t *ucp);
void makecontext (ucontext_t *ucp,
void (*func) (void), int argc, ...);
int setcontext (const ucontext_t *ucp);
int swapcontext (ucontext_t *restrict oucp,
const ucontext_t *restrict ucp);
Функция getcontext() – штатное средство получения исходного материала для манипулирования контекстами. Она запоминает текущий контекст вызывающего ucp. Ее нормальный результат равен нулю.
Функция makecontext() модифицирует контекст, заданный ucp. Когда (после вызовов setcontext() или swapcontext() ) выполнение будет возобновлено в этом контексте, оно продолжится обращением к функции func() с передачей ей int в количестве argc, помещенных после argc при вызове makecontext(). Приложение должно позаботиться о том, чтобы модифицируемый контекст включал стек достаточного размера.
Элемент uc_link структуры типа ucontext_t определяет контекст, в котором будет возобновлено выполнение после makecontext() контекста. Приложение должно позаботиться об инициализации этого элемента до обращения к makecontext().
Функция setcontext() устанавливает ucp. После успешного вызова setcontext() возврата не происходит – выполнение возобновляется с точки, специфицированной новым контекстом, а именно: если этот контекст был сформирован в результате обращения к getcontext(), выполнение возобновляется возвратом из getcontext() ; если контекст получен после makecontext(), вызывается функция func(), после возврата из которой makecontext() контексте.
Если значением элемента uc_link структуры, на которую указывает ucp, служит пустой указатель, то данный контекст соответствует функции main(), после выхода из которой выполнение
Функция swapcontext() производит oucp, а новый контекст формируется по значению ucp.
Обратим внимание на следующую тонкость. Когда вызывается функция longjmp() будет аккуратно восстановлен контекст соответствующего вызова setjmp(). В подобных ситуациях рекомендуется применять функции siglongjmp() или setcontext().
В качестве примера применения функций, манипулирующих
/* * * * * * * * * * * * * * * * * * * * * * * */
/* Программа демонстрирует применение функций, */
/* манипулирующих пользовательскими контекстами*/
/* * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <ucontext.h>
/* Размер стеков в формируемых пользовательских контекстах */
#define STACK_SIZE 4096
/* Пространство для стеков */
static char st1 [STACK_SIZE];
static char st2 [STACK_SIZE];
static ucontext_t ctx [3];
static void f1 (int arg) {
printf ("Вызвана функция %s с аргументом %d\n", "f1", arg);
if (swapcontext (ctx [1], ctx [2]) != 0) {
perror ("SWAPCONTEXT-1");
}
printf ("Выход из функции %s\n", "f1");
}
static void f2 (int arg1, int arg2) {
printf ("Вызвана функция %s с аргументами %d, %d\n",
"f2", arg1, arg2);
if (swapcontext (ctx [2], ctx [1]) != 0) {
perror ("SWAPCONTEXT-2");
}
printf ("Выход из функции %s\n", "f2");
}
int main (void) {
(void) getcontext (ctx [1]);
printf ("Параметры первоначального контекста:\n"
"адрес стека %p, размер стека %d\n",
ctx[1].uc_stack.ss_sp,
ctx[1].uc_stack.ss_size);
/* В соответствии с общими рекомендациями */
/* позаботимся о стеке для модифицируемых контекстов */
ctx[1].uc_stack.ss_sp = st1;
ctx[1].uc_stack.ss_size = sizeof (st1);
ctx[1].uc_link = ctx [0];
makecontext (ctx [1], (void (*) (void)) f1, 1, 2);
(void) getcontext (ctx [2]);
ctx[2].uc_stack.ss_sp = st2;
ctx[2].uc_stack.ss_size = sizeof (st2);
ctx[2].uc_link = ctx [1];
makecontext (ctx [2], (void (*) (void)) f2, 2, 3, 4);
if (swapcontext (ctx [0], ctx [2]) != 0) {
perror ("SWAPCONTEXT-3");
return (1);
}
return 0;
}
Обратим внимание на резервирование пространства под стек перед обращением к функции makecontext(), а также на связывание нескольких контекстов в список посредством поля uc_link.
Результаты выполнения приведенной программы показаны на листинге 9.35.
Параметры первоначального контекста:
адрес стека (nil), размер стека 0
Вызвана функция f2 с аргументами 3, 4
Вызвана функция f1 с аргументом 2
Выход из функции f2
Выход из функции f1
Средства управления
Под
Тип данных fenv_t, определенный в заголовочном файле <fenv.h>, представляет всю среду, тип fexcept_t – совокупность
Применительно к FE_DIVBYZERO ( FE_INEXACT ( FE_INVALID ( FE_OVERFLOW ( FE_UNDERFLOW ( FE_ALL_EXCEPT.
Стандартом специфицированы четыре FE_DOWNWARD (вниз, то есть к минус бесконечности), FE_TONEAREST (к ближайшему представимому), FE_TOWARDZERO (к нулю), FE_UPWARD (вверх, то есть к плюс бесконечности).
Подразумеваемая FE_DFL_ENV, имеющей тип указателя на константный объект fenv_t.
Если приложение проверяет #pragma ) FENV_ACCESS:
#pragma STDC FENV_ACCESS ON
Опросить и установить текущую fegetenv() и fesetenv() (см. листинг 9.36).
#include <fenv.h> int fegetenv (fenv_t *fenvp); int fesetenv (const fenv_t *fenvp);
Отметим, что функция fesetenv() не возбуждает
feholdexcept() (см. листинг 9.37) не только запоминает текущую среду по указателю fenvp, но также очищает feholdexcept() имеет смысл, если, помимо безостановочного, реализация предоставляет другие режимы обработки
#include <fenv.h> int feholdexcept (fenv_t *fenvp);
Функция feupdateenv() (см. листинг 9.38) выполняет еще более сложные действия. Она сохраняет в своей локальной памяти информацию о текущей fenvp и затем пытается возбудить в ней сохраненное
#include <fenv.h> int feupdateenv (const fenv_t *fenvp);
Для опроса и установки fegetexceptflag() и fesetexceptflag() (см. листинг 9.39).
#include <fenv.h>
int fegetexceptflag (fexcept_t *flagp,
int excepts);
int fesetexceptflag (const fexcept_t *flagp,
int excepts);
Функция fegetexceptflag() помещает по указателю flagp зависящее от excepts, и ассоциированной с ними информации. Функция fesetexceptflag() выполняет обратную операцию. Как и в случае функции fesetenv(),
Функции fetestexcept(), feclearexcept() и feraiseexcept() (см. листинг 9.40) служат, соответственно, для проверки, сброса и
#include <fenv.h> int fetestexcept (int excepts); int feclearexcept (int excepts); int feraiseexcept (int excepts);
Функция fetestexcept() проверяет, какие из флагов, заданные excepts, в данный момент установлены; результатом служит их побитное ИЛИ.
Функция feclearexcept() пытается сбросить, а feraiseexcept() – возбудить заданные FE_OVERFLOW ) и FE_UNDERFLOW ) может стать FE_INEXACT ).
Опросить и установить fegetround() и fesetround() (см. листинг 9.41).
#include <fenv.h> int fegetround (void); int fesetround (int round);
Свидетельством неудачного завершения функции fegetround() служит отрицательный результат.
Продемонстрируем применение некоторых функций управления
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа демонстрирует применение некоторых функций */
/* управления средой вещественной арифметики */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
int main (void) {
double d1, d2, d3, s;
int res;
printf ("Представление флагов состояния вещественной "
"арифметики\n");
printf (" FE_DIVBYZERO: %x\n", FE_DIVBYZERO);
printf (" FE_INEXACT: %x\n", FE_INEXACT);
printf (" FE_INVALID: %x\n", FE_INVALID);
printf (" FE_OVERFLOW: %x\n", FE_OVERFLOW);
printf (" FE_UNDERFLOW: %x\n", FE_UNDERFLOW);
printf ("Представление режимов округления\n");
printf (" FE_DOWNWARD: %x\n", FE_DOWNWARD);
printf (" FE_TONEAREST: %x\n", FE_TONEAREST);
printf (" FE_TOWARDZERO: %x\n", FE_TOWARDZERO);
printf (" FE_UPWARD: %x\n", FE_UPWARD);
printf ("Текущие исключительные ситуации: %x\n",
fetestexcept (FE_ALL_EXCEPT));
printf ("Текущий режим округления: %x\n", fegetround ());
feclearexcept (FE_ALL_EXCEPT);
/* Вызовем ситуацию исчезновения порядка */
d1 = 1;
do {
d1 /= 2;
} while ((res = fetestexcept (FE_ALL_EXCEPT)) == 0);
printf ("Исключительные ситуации: %x\n", res);
printf ("2^-inf: %g\n", d1);
feclearexcept (res);
/* Вызовем ситуацию переполнения */
d2 = 1;
do {
d2 *= 2;
} while ((res = fetestexcept (FE_ALL_EXCEPT)) == 0);
printf ("Исключительные ситуации: %x\n", res);
printf ("2^+inf: %g\n", d2);
feclearexcept (res);
/* Вызовем ситуацию деления на нуль */
d3 = 1 / d1;
res = fetestexcept (FE_ALL_EXCEPT);
printf ("Исключительные ситуации: %x\n", res);
printf ("1/0: %g\n", d3);
feclearexcept (res);
/* Пример того, как может возникать потеря точности */
s = 1;
do {
s = (s + 2 / s) * 0.5;
} while ((s * s – 2) > 0);
printf ("Исключительные ситуации: %x\n",
fetestexcept (FE_ALL_EXCEPT));
printf ("sqrt (2): %g\n", s);
return 0;
}
Возможные результаты выполнения приведенной программы показаны на листинге 9.43.
Представление флагов состояния вещественной арифметики
FE_DIVBYZERO: 4
FE_INEXACT: 20
FE_INVALID: 1
FE_OVERFLOW: 8
FE_UNDERFLOW: 10
Представление режимов округления
FE_DOWNWARD: 400
FE_TONEAREST: 0
FE_TOWARDZERO: c00
FE_UPWARD: 800
Текущие исключительные ситуации: 0
Текущий режим округления: 0
Исключительные ситуации: 30
2^-inf: 0
Исключительные ситуации: 28
2^+inf: inf
Исключительные ситуации: 4
1/0: inf
Исключительные ситуации: 20
sqrt (2): 1.41421
Отметим, что и для
Обратим внимание также на то, что вместе с
В качестве второго примера рассмотрим программу, реализующую некоторые операции
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа реализует некоторые операции */
/* интервальной арифметики */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
/* Интервальное представление числа */
typedef struct ditvl {
double lb;
double ub;
} ditvl_t;
/* * * * * * * * * * * * * * * * * * * * */
/* Сложение интервалов. */
/* Сумма помещается в выходной аргумент. */
/* Нормальный результат равен нулю */
/* * * * * * * * * * * * * * * * * * * * */
int ditvl_add (const ditvl_t *a1, const ditvl_t *a2,
ditvl_t *res) {
fenv_t cfenv;
/* Сохраним текущую среду вещественной арифметики */
if (fegetenv (cfenv) != 0) {
perror ("FEGETENV");
return (-1);
}
/* Нижние границы нужно складывать с округлением вниз */
if (fesetround (FE_DOWNWARD) != 0) {
perror ("FESETROUND");
return (-1);
}
res->lb = a1->lb + a2->lb;
/* Верхние границы складываются с округлением вверх */
if (fesetround (FE_UPWARD) != 0) {
perror ("FESETROUND");
return (-1);
}
res->ub = a1->ub + a2->ub;
/* Восстановим среду вещественной арифметики */
if (fesetenv (cfenv) != 0) {
perror ("FESETENV");
return (-1);
}
return 0;
}
/* * * * * * * * */
/* Унарный минус */
/* * * * * * * * */
int ditvl_uminus (const ditvl_t *a, ditvl_t *res) {
res->lb = -(a->ub);
res->ub = -(a->lb);
return 0;
}
/* * * * * * * * */
/* Вызов функций */
/* * * * * * * * */
int main (void) {
ditvl_t pi = {3.141592, 3.141593};
ditvl_t e = {2.718281, 2.718282};
ditvl_t res;
ditvl_t tmp;
printf ("Представление числа pi: (%f, %f)\n", pi.lb, pi.ub);
printf ("Представление числа e: (%f, %f)\n", e.lb, e.ub);
/* Вычислим сумму pi и e */
(void) ditvl_add (pi, e, res);
printf ("Сумма pi и e: (%f, %f)\n", res.lb, res.ub);
/* Вычислим разность pi и e */
(void) ditvl_uminus (e, tmp);
(void) ditvl_add (pi, tmp, res);
printf ("Разность pi и e: (%f, %f)\n", res.lb, res.ub);
printf ("Текущие исключительные ситуации: %x\n",
fetestexcept (FE_ALL_EXCEPT));
printf ("Текущие режимы округления: %x\n", fegetround ());
return 0;
}
Программа переустанавливает ditvl_add() для fegetround() / fesetround(), а не fegetenv() / fesetenv().
На листинге 9.45 показаны возможные результаты выполнения приведенной программы.
Представление числа pi: (3.141592, 3.141593) Представление числа e: (2.718281, 2.718282) Сумма pi и e: (5.859873, 5.859875) Разность pi и e: (0.423310, 0.423312) Текущие исключительные ситуации: 0 Текущие режимы округления: 0
Отметим, что в завершающей части программы подразумеваемая
и nftw() (см. листинг 9.46).
#include <ftw.h>
int ftw (const char *path,
int (*fn) (const char *,
const struct stat *, int),
int depth);
int nftw (const char *path,
int (*fn) (const char *,
const struct stat *, int, struct FTW *),
int depth, int flags);
Функция path. Для каждого объекта иерархии вызывает функцию fn(), передавая ей три stat, содержащую информацию об объекте;
Возможны следующие значения
FTW_D
FTW_DNR
FTW_F
FTW_SL
FTW_NS
Объект, отличный от stat() не может выполниться успешно.
Если FTW_DNR, элементы этого FTW_NS, то структура типа stat будет содержать неопределенные значения. Примером объекта, который вызовет передачу функции fn() типа FTW_NS, является файл в
Функция обрабатывает
Функция использует не более одного depth ограничивает количество используемых таким образом дескрипторов; его значение должно принадлежать диапазону [1, OPEN_MAX].
Обход завершится тогда, когда будет обойдена вся иерархия, или функция fn() вернет ненулевое значение, или возникнет ошибка, отличная от EACCES, при работе самой функции (например, ошибка ввода/вывода). Если в качестве результата возвращает нуль. Если fn() вернет ненулевое значение, то прекратит обход и выдаст это значение. Если будет обнаружена ошибка при работе самой функции , то она вернет -1 и соответствующим образом установит значение переменной errno.
Обратим внимание на следующее (впрочем, довольно очевидное) обстоятельство. Функция во время своей работы временно fn() или обработчика сигнала), эти ресурсы останутся неосвобожденными. Рекомендуемый способ обработки прерываний заключается в том, чтобы зафиксировать факт получения прерывания и при очередном вызове fn() заставить ее вернуть ненулевое значение.
Функция nftw() аналогична , однако и у нее самой, и у вызываемой ею функции fn() имеется по одному дополнительному flags управляет работой функции nftw(). Его значение формируется как побитное ИЛИ следующих флагов.
FTW_CHDIR
Делать текущим просматриваемый nftw() не изменит текущий
FTW_DEPTH
FTW_MOUNT
Обрабатывать только файлы из той же файловой системы, что и path.
FTW_PHYS
Осуществлять
Третий fn(), по сравнению с может принимать следующие дополнительные значения.
FTW_DP
FTW_DEPTH ).
FTW_SLN
Дополнительный, четвертый fn() является указателем на структуру типа . Согласно стандарту, она должна содержать по крайней мере следующие поля.
int base;
/* Смещение простого имени файла */
/* от начала маршрутного имени, */
/* переданного fn() в качестве */
/* первого аргумента */
int level;
/* Уровень текущего объекта */
/* относительно корня иерархии */
/* (у самого корня нулевой уровень) */
В качестве примера
/* * * * * * * * * * * * * * * * * * * * */
/* Программа определяет суммарный размер */
/* и высоту файловой иерархии */
/* * * * * * * * * * * * * * * * * * * * */
#define _XOPEN_SOURCE 600
#include <ftw.h>
#include <stdio.h>
/* Суммарный размер файлов в иерархии */
static off_t fsize = 0;
/* Максимальный уровень файлов в иерархии */
static int flevel = 0;
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция, вызываемая для каждого файла в иерархии */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
static int nftwfunc (const char *filename,
const struct stat *statptr, int filetype,
struct FTW *pfwt) {
/* Если установлен флаг FTW_NS, завершим обход */
if (filetype == FTW_NS) {
perror ("STAT");
fprintf (stderr, "Отсутствуют данные о файле %s\n",
filename);
return 1;
}
fsize += statptr->st_size;
if (pfwt->level > flevel) {
flevel = pfwt->level;
}
return 0;
}
/* * * * * * * * * * * */
/* Организация обхода */
/* * * * * * * * * * * */
int main (int argc, char *argv []) {
if (argc != 2) {
fprintf (stderr, "Использование: %s корень_иерархии\n",
argv [0]);
return (1);
}
if (nftw (argv [1], nftwfunc, 16,
FTW_MOUNT | FTW_PHYS) == -1) {
perror ("NFTW");
}
printf ("Суммарный размер обработанных файлов: %ld\n",
fsize);
printf ("Высота иерархии файлов: %d\n", flevel);
return 0;
}
Обратим внимание на возможность досрочного завершения обхода за счет возврата ненулевого результата функцией обработки, если последней передан файл, данные о котором получить не удалось, а также на использование флагов
Пусть в /tmp существует непустой подкаталог gaga со следующим режимом доступа:
drw-r--r-- 2 galat sys 4096 May 7 17:26 gaga
Тогда результаты запуска приведенной программы с /tmp могут выглядеть так, как показано на листинге 9.48.
STAT: Permission denied Отсутствуют данные о файле /tmp/gaga/gugu Суммарный размер обработанных файлов: 2645778 Высота иерархии файлов: 2
Служебная программа xargs позволяет формировать и выполнять
xargs [-E логич_конец_файла]
[-I заменяемая_цепочка]
[-L число] [-n число] [-p] [-s размер]
[-t] [-x]
[утилита [начальный_аргумент ...]]
Программа xargs объединяет зафиксированный набор заданных начальных_аргументов с echo ) в рамках сформированной
xargs приписан следующий смысл.
-E логич_конец_файла
логич_конец_файла считается признаком
-I заменяемая_цепочка
-I включает -x.
-L число
Выполнять
-n число
Выполнить -s, или если для последнего вызова их осталось меньше, чем заданное число.
-p
Режим с xargs перед каждым вызовом -t ), за счет чего в ?...". Положительный ответ, прочитанный с устройства /dev/tty, приводит к выполнению
-s размер
Максимальный общий размер (в символах) каждого списка -n или -L, или если встретится
-t
Режим
-x
Завершить работу служебной программы xargs, если очередная сформированная -n число -L число строк, оказалась длиннее, чем специфицированный -s размер.
Приведем несколько примеров применения служебной программы xargs. На всякий случай подчеркнем, что никто не утверждал, что Linux или какая-либо иная операционная система соответствует стандарту POSIX-2001. Весьма вероятно, что перед исполнением примеров их придется немного подправить.
Следующая однострочная shell-процедура пересылает все файлы из $1 в $2 и сообщает о каждой пересылке, перед тем как ее выполнить:
ls $1 | xargs -I {} -t mv $1/{} $2/{}
Еще одна однострочная shell-процедура применяет служебную программу diff к последовательным парам своих
echo $* | xargs -n 2 diff
Пользователя спрашивают, какие mylib.a. При выполнении первого конвейера (см. ниже) файлы заносятся по одному; при выполнении второго заносится сразу много файлов.
ls *.o | xargs -p -L 1 ar -r mylib.a ls *.o | xargs -p -L 1 | xargs ar -r mylib.a
Отметим, что во втором звене второго конвейера в качестве подразумеваемой echo.
В курсе мы уже останавливались на понятии
Первым действием при работе с posix_openpt() (см. листинг 9.49).
#include <stdlib.h> #include <fcntl.h> int posix_openpt (int oflag);
В значении oflag есть смысл устанавливать флаги O_RDWR и O_NOCTTY (последний означает, что терминал не будет управляющим для процесса).
Разумеется, нормальным результатом функции posix_openpt()> служит -1.
Считается, что после unlockpt() (см. листинг 9.50).
#include <stdlib.h> int unlockpt (int masterfd);
Здесь masterfd – posix_openpt().
Нормальным для unlockpt() является нулевой результат.
Функция grantpt() (см. листинг 9.51) позволяет
#include <stdlib.h> int grantpt (int masterfd);
Применительно к функции grantpt() в стандарте POSIX-2001 оговаривается одна тонкость: вызывающий процесс не должен обрабатывать сигнал SIGCHLD. Это можно понять (и оправдать), поскольку grantpt() выполняет суперпользовательское действие, реализация которого в некоторых ОС может быть сопряжена с
Чтобы узнать ptsname() (см. листинг 9.52).
#include <stdlib.h> char *ptsname (int masterfd);
Разумеется, результирующий указатель может ссылаться на статическую область, перезаписываемую при каждом вызове ptsname(), поэтому рекомендуется полученное имя скопировать или поскорее использовать. Использование очевидно – посредством обычной функции open()
Таким образом, в стандарте POSIX-2001 выстроена пятиэтапная модель получения доступа к
posix_openpt() );unlockpt() );grantpt() );ptsname() );open() ).Применение описанных функций иллюстрируется переработанным вариантом программы из курса , запускающей командный интерпретатор на
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа запускает shell на псевдотерминале */
/* * * * * * * * * * * * * * * * * * * * * * * * */
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <signal.h>
#include <poll.h>
#include <sys/resource.h>
#include <curses.h>
/* * * * * * * * * * * * * * * * * * */
/* Действия при завершении процесса */
/* * * * * * * * * * * * * * * * * * */
static void termination (int errcode) {
endwin ();
exit (errcode);
}
/* * * * * * * * * * * * * * * * * * */
/* Функция обработки сигнала SIGCHLD */
/* * * * * * * * * * * * * * * * * * */
static void chldied (int dummy) {
/* Просто кончимся */
termination (34);
}
/* * * * * * * * * * * * * * * * * * * * */
/* Организация работы с псевдотерминалом */
/* * * * * * * * * * * * * * * * * * * * */
int main (void) {
WINDOW *win1, *win2;
/* win1 – окно только для рамки */
/* win2 – окно для shell'а */
int pty, tty; /* Дескрипторы обеих сторон */
/* псевдотерминала */
int fr; /* Результат fork'а */
unsigned char ch; /* Прочитанный символ */
struct termios pt; /* Структура характеристик */
/* псевдотерминала */
struct pollfd fds [2]; /* Массив параметров для */
/* вызова poll */
int w2lines, w2cols; /* Размер создаваемого окна */
int x, y; /* Координаты в окне */
struct sigaction sact;
int i;
initscr ();
cbreak ();
noecho ();
win1 = newwin (LINES, COLS, 0, 0);
box (win1, 0, 0);
wrefresh (win1);
w2lines = LINES – 2;
w2cols = COLS – 4;
win2 = newwin (w2lines, w2cols, 1, 2);
scrollok (win2, TRUE);
/* Откроем псевдотерминал */
if (((pty = posix_openpt (O_RDWR | O_NOCTTY)) < 0) ||
(unlockpt (pty) == -1) ||
(grantpt (pty) == -1) ||
((tty = open (ptsname (pty), O_RDWR)) < 0)) {
fprintf (stderr, "Не удалось открыть псевдотерминал\n");
perror ("POSIX_OPENPT");
return (1);
}
/* Установим подходящие характеристики псевдотерминала */
if (tcgetattr (pty, pt) < 0) {
perror ("PTY TERMIOS GET ERROR");
return (2);
}
pt.c_iflag = 0;
pt.c_oflag = ONLCR;
pt.c_cflag = CS8 | HUPCL;
pt.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
pt.c_cc [VINTR] = 3; /* CTRL+C */
pt.c_cc [VEOF] = 4; /* CTRL+D */
if (tcsetattr (pty, TCSADRAIN, pt) < 0) {
perror ("PTY TERMIOS SET ERROR");
return (3);
}
/* То же – для стандартного ввода */
(void) tcgetattr (0, pt);
pt.c_lflag = ~ISIG;
(void) tcsetattr (0, TCSADRAIN, pt);
/* Установим обработку сигнала о завершении потомка */
sact.sa_handler = chldied;
(void) sigemptyset (sact.sa_mask);
sact.sa_flags = 0;
(void) sigaction (SIGCHLD, sact, (struct sigaction *) NULL);
/* Раздвоимся на процесс чтения с клавиатуры */
/* и вывода на экран и на процесс, */
/* в рамках которого запустим shell */
if ((fr = fork ()) < 0) {
perror ("FORK1 ERROR");
termination (-1);
} else if (fr) {
/* Это процесс, читающий с клавиатуры */
/* и выводящий на экран */
close (tty);
/* Будем ждать ввода с клавиатуры или псевдотерминала */
fds [0].fd = 0;
fds [0].events = POLLIN;
fds [1].fd = pty;
fds [1].events = POLLIN;
while (1) {
if (poll (fds, 2, -1) < 0) {
perror ("POLL ERROR");
termination (0);
}
if (fds [0].revents POLLIN) {
/* Пришел символ со стандартного ввода */
read (0, ch, 1);
write (pty, ch, 1);
}
if (fds [1].revents POLLIN) {
/* Пришел символ с псевдотерминала */
read (pty, ch, 1);
switch (ch) {
case '\n': {
/* Проинтерпретируем перевод строки */
getyx (win2, y, x);
if (y == (w2lines – 1)) {
wmove (win2, y, w2cols – 1);
waddch (win2, (chtype) ch);
} else {
wmove (win2, y + 1, 0);
}
break;
}
default: {
/* Символ не интерпретируется */
waddch (win2, (chtype) ch);
break;
}
}
wrefresh (win2);
}
}
/* Просто кончимся */
termination (0);
} else { /* Порожденный процесс – запустим в нем shell */
/* Закроем все файлы, кроме псевдотерминала */
for (i = 0; i < RLIMIT_NOFILE; i++) {
if (i != tty) {
(void) close (i);
}
}
/* Сделаем процесс лидером сеанса */
(void) setsid ();
/* Свяжем стандартные ввод, вывод и протокол */
/* с псевдотерминалом */
(void) fcntl (tty, F_DUPFD, 0);
(void) fcntl (tty, F_DUPFD, 0);
(void) fcntl (tty, F_DUPFD, 0);
close (tty);
/* Поместим в окружение параметры псевдотерминала */
{
char lnbuf [20];
char clbuf [20];
sprintf (lnbuf, "LINES=%2d", w2lines);
sprintf (clbuf, "COLUMNS=%2d", w2cols);
putenv (lnbuf);
putenv (clbuf);
}
if (execl ("/bin/sh", "sh", (char *) NULL) < 0) {
perror ("EXECL ERROR");
exit (-1);
}
}
return 0;
}
Здесь все пять упоминавшихся подготовительных этапов вошли в состав одного условного оператора. Обратим внимание на применение флага O_NOCTTY при вызове posix_openpt(), а также на то, что обращение к grantpt() выполнено до установки функции SIGCHLD.
/var/log/messages ),
Для работы с syslog() ), установки фильтра ( setlogmask() ) и других параметров openlog() ) и, наконец, завершения работы с closelog() ) (см. листинг 9.1). Средства чтения
#include <syslog.h> void syslog (int priority, const char *message, ... /* аргументы */); int setlogmask (int maskpri); void openlog (const char *ident, int logopt, int facility); void closelog (void);
syslog(), включает
message, играющего роль формата, и следующих за ним необязательных printf(), только допускается один дополнительный %m, не требующий errno
Значение priority формируется как побитное ИЛИ флагов двух видов, задающих, соответственно,
LOG_EMERG
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG
Из LOG_USER ). Зарезервированы флаги для системных LOG_KERN, LOG_MAIL, LOG_NEWS, LOG_UUCP, LOG_DAEMON, LOG_AUTH, LOG_CRON, LOG_LPR ) и для абстрактных локальных сущностей ( LOG_LOCAL0 – LOG_LOCAL7 ).
Функция setlogmask() в качестве результата возвращает предыдущую и устанавливает новую maskpri. Для формирования этого LOG_MASK ( ). Для задания маски, включающей несколько уровней, нужно взять побитное ИЛИ подобных выражений.
Если значение maskpri равно нулю, текущая маска остается неизменной. Подразумевая маска является "полной", она специфицирует
Функция openlog() устанавливает значения syslog(). ident задает logopt специфицирует
LOG_PID
Записывать вместе с
LOG_CONS
Выдавать
LOG_NDELAY
Немедленно открыть
LOG_ODELAY
Отложить syslog() (выбор между немедленным или отложенным
LOG_NOWAIT
Не ждать завершения процессов, которые могли быть порождены в ходе SIGCHLD.
facility устанавливает подразумеваемое значение для
Функции openlog() и syslog() могут открывать (расходовать) closelog() закроет их.
Вообще говоря, вызывать openlog() до syslog() и setlogmask() не обязательно.
К рассматриваемой прикладной области можно отнести служебную программу :
logger цепочка_символов ...
которая неким неспецифицированным образом сохраняет
Подобная возможность полезна для выдачи диагностических
Рассмотрим пример применения функций для работы с
/* * * * * * * * * * * * * * * * * */
/* Пример использования функций */
/* для работы с системным журналом */
/* * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <syslog.h>
int main (void) {
int logmask; /* Прежняя маска журналирования */
/* Будем включать в журналируемые сообщения */
/* идентификатор процесса и выдавать их при */
/* возникновении проблем на системную консоль */
openlog ("Intuit syslog test", LOG_PID | LOG_CONS, LOG_USER);
/* Пренебрежем предупреждениями и менее серьезными сообщениями */
logmask = setlogmask (LOG_MASK (LOG_EMERG) | LOG_MASK
(LOG_ALERT) | LOG_MASK (LOG_CRIT) | LOG_MASK (LOG_ERR));
printf ("Подразумеваемая маска журналирования: %x\n", logmask);
/* Поместим сообщение в журнал */
syslog (LOG_ALERT | LOG_USER, "Как читать системный журнал?");
/* Восстановим прежнюю маску журналирования */
(void) setlogmask (logmask);
closelog ();
return 0;
}
Результатом работы этой программы может быть строка, показанная на листинге 9.3.
Подразумеваемая маска журналирования: ff
Привлечь внимание fmtmsg() (см. листинг 9.4).
#include <fmtmsg.h>
int fmtmsg (long classification,
const char *label,
int severity, const char *text,
const char *action,
const char *tag);
Функция fmtmsg() конструирует отформатированное
определяет MM_HARD (аппаратура), MM_SOFT (программное обеспечение), MM_FIRM (программно-аппаратные средства). MM_APPL (приложение), MM_UTIL (служебная программа), MM_OPSYS (операционная система). Проблемы классифицируются также по признаку нейтрализуемости – соответственно, MM_RECOVER и MM_NRECOV.
Если следует прибавить константу MM_PRINT ; вывод на MM_CONSOLE. Возможно одновременное указание обеих констант.
Константа MM_NULLMC означает отсутствие классификационного компонента (естественно, ее значение равно нулю).
label специфицирует первый из пяти компонентов выдаваемого , определяет
severity характеризует серьезность проблемы. Стандартом POSIX-2001 предусмотрены следующие
MM_HALT
В приложении встретилась серьезная ошибка, его работа остановлена. В ".
MM_ERROR
В работе приложения обнаружена ошибка. В ERROR ".
MM_WARNING
При работе приложения возникла необычная ситуация, возможно, являющаяся ошибочной и требующая внимания. В WARNING ".
MM_INFO
Информация о ситуации, не являющейся ошибочной. В INFO ".
MM_NOSEV
Данная константа обозначает отсутствие у
text в свободной форме описывает ситуацию, приведшую к генерации
action также в свободной форме описывает первый шаг по нейтрализации ошибки. Перед цепочкой, на которую указывает action, в TO FIX:".
tag служит ссылкой на документацию по выявленной проблеме.
На работу функции fmtmsg() влияет MSGVERB, которая определяет, какие из пяти возможных компонентов label, severity, text, action, tag – разделенных двоеточиями. Если какие-то ключевые слова отсутствуют, соответствующие компоненты MSGVERB отсутствует в окружении, имеет пустое или некорректное значение,
Возможные результаты функции fmtmsg() устроены необычным образом. Константа MM_OK обозначает полный успех, MM_NOTOK – полную неудачу, MM_NOMSG – невозможность выдать MM_NOCON – невозможность вывода на консоль.
Приведем не очень серьезный пример применения функции fmtmsg() (см. листинг 9.5).
#include <stdio.h>
#include <fmtmsg.h>
int main (void) {
if (fmtmsg (MM_SOFT + MM_OPSYS + MM_RECOVER
+ MM_PRINT + MM_CONSOLE, "POSIX:fmtmsg",
MM_INFO, "Отсутствует функция fmtmsg()",
"Установите функцию fmtmsg()
или не пользуйтесь ею\n",
"См. functions/fmtmsg.html") != MM_OK) {
perror ("FMTMSG");
return (1);
}
return 0;
}
В результате выполнения приведенной программы в
POSIX:fmtmsg: INFO: Отсутствует функция fmtmsg() TO FIX: Установите функцию fmtmsg() или не пользуйтесь ею См. functions/fmtmsg.html
Читателю предлагается самостоятельно поэкспериментировать с этой программой, варьируя значение MSGVERB.
Мы продолжаем рассматривать функции, находящиеся на стыке пользовательских и административных средств.
Стандартом POSIX-2001 предусмотрен набор функций для работы с getutxent() ), getutxid(), getutxline() ), pututxline() ), setutxent() ) и endutxent() ) (см. листинг 9.7).
#include <utmpx.h>
struct utmpx *getutxent (void);
struct utmpx *getutxid (
const struct utmpx *id);
struct utmpx *getutxline (
const struct utmpx *line);
struct utmpx *pututxline (
const struct utmpx *utmpx);
void setutxent (void);
void endutxent (void);
Центральную роль для описываемого набора функций играет структура типа utmpx, которая, согласно стандарту, должна содержать по крайней мере следующие поля.
char ut_user [];
/* Входное имя пользователя */
char ut_id [];
/* Неспецифицированный */
/* инициализационный идентификатор */
/* процесса (например, первое поле */
/* в строке файла inittab) */
char ut_line [];
/* Имя устройства */
pid_t ut_pid;
/* Идентификатор процесса */
short ut_type;
/* Тип записи */
struct timeval ut_tv;
/* Время создания записи */
В зависимости от ut_type ) определяется подмножество полей, содержащих осмысленные значения. Для пустых EMPTY ) таких полей нет вообще. Для BOOT_TIME (идентифицирует время загрузки системы), OLD_TIME (время изменения показаний системных часов), NEW_TIME (показания системных часов после изменения) имеет смысл только ut_tv ). USER_PROCESS идентифицируют ut_user ( ut_id, ut_line, ut_pid и ut_tv. Почти такое же подмножество полей имеет смысл для LOGIN_PROCESS (по стандарту он идентифицирует ut_user (зависящее от реализации имя входного процесса), ut_id, ut_pid, ut_tv. Наконец, для INIT_PROCESS (идентифицирует процесс, порожденный системным процессом init ) и DEAD_PROCESS (по стандарту он идентифицирует ut_id, ut_pid и ut_tv.
Разумеется, реальные размеры массивов, являющихся элементами структуры, можно узнать, применяя к ним на sizeof().
Функция getutxent() читает очередную utmpx.
Функция getutxid(), начиная с текущей позиции, разыскивает ut_type соответствует значению id->ut_type. Если элемент id->ut_type равен BOOT_TIME, OLD_TIME, или NEW_TIME, то требуется точное равенство типов. Если же id->ut_type равняется INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS или DEAD_PROCESS, то функция getutxid() вернет указатель на копию первой ut_id соответствует значению id->ut_id.
Функция getutxline() аналогичным образом разыскивает LOGIN_PROCESS или USER_PROCESS, а поле ut_line соответствует значению line->ut_line.
Доступ к
Функция pututxline() записывает указанную utmpx -структуру в getutxid(), если обнаруживается, что текущая позиция не является подходящей. В случае неудачи
Отметим, что getutxid() или getutxline() реализация имеет право сначала проанализировать упомянутую статическую структуру и, если та окажется подходящей, не производить
В свою очередь, согласно стандарту, приложение имеет право передать функции pututxline() указатель на статическую структуру, заполненную в результате обращения к getutxent(), getutxid() или getutxline(), предварительно изменив ее требуемым образом. Неявное чтение, осуществляемое функцией pututxline() для определения замещаемой
Функция setutxent() устанавливает
Функция endutxent() закрывает
Приведем пример использования описанных функций (см. листинг 9.8). Прочитаем и выведем все
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Пример использования функций для работы */
/* с базой данных учетной информации о пользователях */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <limits.h>
#include <time.h>
#include <utmpx.h>
#include <string.h>
int main (void) {
struct utmpx *utmpx_ptr; /* Указатель на текущую запись */
char dtbuf [LINE_MAX]; /* Буфер для данных о времени */
struct utmpx spat; /* Шаблон для поиска в базе */
/* Прочитаем и распечатаем все записи в базе */
printf ("Содержимое базы данных учетной информации "
"о пользователях\n");
while ((utmpx_ptr = getutxent ()) != NULL) {
(void) strftime (dtbuf, sizeof (dtbuf), "%c",
localtime ((utmpx_ptr->ut_tv.tv_sec)));
switch (utmpx_ptr->ut_type) {
case EMPTY:
printf ("Пустая запись\n");
break;
case BOOT_TIME:
printf ("Время загрузки системы: %s\n", dtbuf);
break;
case OLD_TIME:
printf ("Время изменения показаний системных "
"часов: %s\n", dtbuf);
break;
case NEW_TIME:
printf ("Показания системных часов после "
"изменения: %s\n", dtbuf);
break;
case USER_PROCESS:
printf ("Процесс пользователя: %s, идентификатор: "
"%d,\n", utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Имя устройства: %s,\n",
utmpx_ptr->ut_line);
printf ("Время создания записи: %s\n", dtbuf);
break;
case LOGIN_PROCESS:
printf ("Входной процесс: %s, идентификатор: %d,\n",
utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Время создания записи: %s\n", dtbuf);
break;
case INIT_PROCESS:
printf ("Процесс, порожденный системным процессом "
"init:\n");
printf ("Идентификатор: %d,\n",
utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Время создания записи: %s\n", dtbuf);
break;
case DEAD_PROCESS:
printf ("Лидер сеанса, завершивший выполнение:\n");
printf ("Идентификатор: %d,\n", utmpx_ptr->ut_pid);
printf ("Инициализационный идентификатор процесса: "
"%s,\n", utmpx_ptr->ut_id);
printf ("Время создания записи: %s\n", dtbuf);
break;
default:
printf ("Нестандартный тип записи: %x\n",
utmpx_ptr->ut_type);
break;
}
}
/* Найдем и распечатаем записи, */
/* инициализационный идентификатор которых */
/* равняется S4 */
spat.ut_type = INIT_PROCESS;
(void) strncpy (spat.ut_id, "S4", sizeof (spat.ut_id));
/* Позиционируемся на начало базы */
setutxent ();
printf ("Записи, инициализационный идентификатор "
"которых равняется S4:\n");
while ((utmpx_ptr = getutxid (spat)) != NULL) {
switch (utmpx_ptr->ut_type) {
case USER_PROCESS:
printf ("Процесс пользователя: %s, "
"идентификатор: %d\n", utmpx_ptr->ut_user,
utmpx_ptr->ut_pid);
break;
case LOGIN_PROCESS:
printf ("Входной процесс: %s, идентификатор: "
"%d\n",utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
break;
case INIT_PROCESS:
printf ("Процесс, порожденный системным процессом "
"init:\n");
printf ("Идентификатор: %d\n", utmpx_ptr->ut_pid);
break;
case DEAD_PROCESS:
printf ("Лидер сеанса, завершивший "
"выполнение:\n");
printf ("Идентификатор: %d\n", utmpx_ptr->ut_pid);
break;
default:
printf ("Нестандартный тип результата поиска: "
"%x\n", utmpx_ptr->ut_type);
break;
}
/* Обеспечим сдвиг поиска с текущей записи */
utmpx_ptr->ut_id [0] = 0;
}
endutxent ();
return 0;
}
Фрагмент возможных результатов выполнения приведенной программы на платформе ОС Linux показан на листинге 9.9. Его изучение позволяет лучше уяснить смысл элементов структуры utmpx для разных
Содержимое базы данных учетной информации о пользователях
Лидер сеанса, завершивший выполнение:
Идентификатор: 17,
Инициализационный идентификатор процесса: si,
Время создания записи: Tue Apr 27 10:08:42 2004
Время загрузки системы: Tue Apr 27 10:08:42 2004
Нестандартный тип записи: 1
Лидер сеанса, завершивший выполнение:
Идентификатор: 284,
Инициализационный идентификатор процесса: l5,
Время создания записи: Tue Apr 27 10:09:15 2004
Лидер сеанса, завершивший выполнение:
Идентификатор: 1115,
Инициализационный идентификатор процесса: ud,
Время создания записи: Tue Apr 27 10:09:15 2004
. . .
Входной процесс: LOGIN, идентификатор: 1123,
Инициализационный идентификатор процесса: S3,
Время создания записи: Tue Apr 27 10:09:15 2004
Процесс пользователя: galat, идентификатор: 1124,
Инициализационный идентификатор процесса: S4,
Имя устройства: ttyS4,
Время создания записи: Tue Apr 27 12:52:51 2004
Процесс пользователя: sambor, идентификатор: 1125,
Инициализационный идентификатор процесса: S5,
Имя устройства: ttyS5,
Время создания записи: Tue Apr 27 13:57:31 2004
Процесс пользователя: kost, идентификатор: 1126,
Инициализационный идентификатор процесса: S6,
Имя устройства: ttyS6,
Время создания записи: Tue Apr 27 10:09:30 2004
. . .
Процесс, порожденный системным процессом init:
Идентификатор: 1128,
Инициализационный идентификатор процесса: x,
Время создания записи: Tue Apr 27 10:09:15 2004
. . .
Лидер сеанса, завершивший выполнение:
Идентификатор: 11708,
Инициализационный идентификатор процесса: /1,
Время создания записи: Tue Apr 27 11:19:33 2004
. . .
Записи, инициализационный идентификатор которых равняется S4:
Процесс пользователя: galat, идентификатор: 1124
Отметим, что нестандартный 1 соответствует смене уровня выполнения.
Обратим также внимание на то, что в шаблоне getutxid(), значение поля ut_type задано как INIT_PROCESS, а в результате USER_PROCESS (в полном соответствии со стандартом).
Описываемые ниже функции полезны для реализации "игрушечных"
Набор стандартизованных функций "традиционно минимален" (см. листинг 9.10). dbm_open() ) и закрыть ( dbm_close() ), выбрать ( dbm_fetch() ), сохранить ( dbm_store() ) и удалить ( dbm_delete() ) dbm_firstkey(), dbm_nextkey() ), опросить dbm_error() ) и очистить его ( dbm_clearerr() ).
#include <ndbm.h>
DBM *dbm_open (const char *file,
int open_flags, mode_t file_mode);
void dbm_close (DBM *db);
datum dbm_fetch (DBM *db, datum key);
int dbm_store (DBM *db, datum key,
datum content, int store_mode);
int dbm_delete (DBM *db, datum key);
datum dbm_firstkey (DBM *db);
datum dbm_nextkey (DBM *db);
int dbm_error (DBM *db);
int dbm_clearerr (DBM *db);
Функцию dbm_open() можно считать аналогом open(), только в роли возвращаемого в качестве результата дескриптора (структура последнего скрыта от приложения), база хранится в двух файлах – file.dir и file.pag, ее нельзя открыть только на O_APPEND ведет к неспецифицированным последствиям. В случае ошибки результат вызова dbm_open() равняется (.
При работе с datum, которая по стандарту должна содержать по крайней мере два поля.
void *dptr;
/* Указатель на прикладные данные */
size_t dsize;
/* Размер прикладных данных */
Функция dbm_fetch() служит для key. Если таковая отсутствует или обнаруживается ошибка, то в возвращаемом объекте datum элемент dptr равняется пустому указателю.
Функция dbm_store() позволяет поместить данные, заданные content, в базу. store_mode определяет способ key, то в режиме DBM_REPLACE новая DBM_INSERT она (новая key в базе нет, новая
Функция dbm_delete() предназначена для key.
Нормальным результатом функций dbm_store() и dbm_delete() является нуль; в случае ошибки возвращается отрицательное значение.
Функция dbm_nextkey() является итератором по dbm_firstkey() инициализирует этот итератор, возвращая первый datum с пустым указателем в качестве значения элемента dptr. Если по ходу итераций содержимое базы менялось (вызывались функции dbm_store() и/или dbm_delete() ), перебор
Функция dbm_error() возвращает ненулевое значение при установленном dbm_clearerr() делает статус "безошибочным". Таким образом, вся диагностика по сути сводится к одному биту, интерпретировать который должно приложение.
Несмотря на "игрушечность" описанного интерфейса, он находит определенное применение на Unix-системах. В качестве примера рассмотрим программу, которая распечатывает содержимое aliases, расположенной в /etc/mail на SPARC -станции (см. листинг 9.11).
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа перебирает все ключи в базе данных */
/* и выдает ассоциированную с ними информацию. */
/* Предполагается, что ключи и данные – текстовые*/
/* * * * * * * * * * * * * * * * * * * * * * * * */
#include <ndbm.h>
#include <stdio.h>
#include <fcntl.h>
int main (int argc, char *argv []) {
DBM *dbdes; /* Дескриптор открытой базы */
datum ckey; /* Текущий ключ */
datum cdat; /* Текущие данные */
int nkeys = 0; /* Число ключей */
if (argc != 2) {
fprintf (stderr, "Использование: %s имя_базы\n",
argv [0]);
return (1);
}
if ((dbdes = dbm_open (argv [1],
O_RDONLY, 0777)) == (DBM *) NULL) {
fprintf (stderr, "Не удалось открыть базу данных %s\n",
argv [1]);
return (2);
}
for (ckey = dbm_firstkey (dbdes); ckey.dptr != NULL;
ckey = dbm_nextkey (dbdes)) {
nkeys++;
printf ("Длина ключа номер %d: %d\n", nkeys,
ckey.dsize);
printf ("Ключ номер %d: %s\n", nkeys, ckey.dptr);
if (cdat = dbm_fetch (dbdes, ckey),
cdat.dptr != NULL) {
printf ("Длина данных для ключа номер %d: %d\n",
nkeys, cdat.dsize);
printf ("Данные для ключа номер %d: %s\n", nkeys,
cdat.dptr);
} else {
fprintf (stderr, "Отсутствуют данные для "
"ключа номер %d\n", nkeys);
}
}
printf ("Число ключей в базе: %d\n", nkeys);
dbm_close (dbdes);
return 0;
}
Результаты работы этой программы могут выглядеть так, как показано на листинге 9.12.
Длина ключа номер 1: 16 Ключ номер 1: YP_LAST_MODIFIED Длина данных для ключа номер 1: 10 Данные для ключа номер 1: 0898782331 Длина ключа номер 2: 14 Ключ номер 2: mailer-daemon Длина данных для ключа номер 2: 11 Данные для ключа номер 2: postmaster Длина ключа номер 3: 14 Ключ номер 3: YP_MASTER_NAME Длина данных для ключа номер 3: 3 Данные для ключа номер 3: t41 Длина ключа номер 4: 11 Ключ номер 4: postmaster Длина данных для ключа номер 4: 5 Данные для ключа номер 4: root Длина ключа номер 5: 7 Ключ номер 5: nobody Длина данных для ключа номер 5: 10 Данные для ключа номер 5: /dev/null Длина ключа номер 6: 2 Ключ номер 6: @ Длина данных для ключа номер 6: 2 Данные для ключа номер 6: @ Число ключей в базе: 6
Стандарт POSIX-2001 предлагает несколько способов
#include <stdlib.h>
void *bsearch (const void *key,
const void *base,
size_t nel, size_t width,
int (*compar) (const void *,
const void *));
Функция в дополнительной литературе, пункт 6.2.1, алгоритм B).
Функция bsearch() возвращает указатель внутрь массива на искомые данные или NULL в случае неудачи compar().
key указывает на объект данных, разыскиваемый в массиве ( base указывает на начало (первый элемент) массива; nel задает количество элементов в массиве; width специфицирует размер элемента в массиве.
compar() – это
Для bsearch().
#include <stdlib.h>
void qsort (void *base, size_t nel,
size_t width,
int (*compar) (const void *,
const void *));
Рассмотрим пример последовательного применения функций qsort() и bsearch() (см. листинг 9.15). Здесь в роли элементов массива выступают указатели на StringSpace ; тот же тип имеет и
/* * * * * * * * * * * * * * * * * * * * * */
/* Программа сортирует массив указателей */
/* на случайные цепочки символов, а затем */
/* выполняет в этом массиве бинарный поиск */
/* * * * * * * * * * * * * * * * * * * * * */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 10000000
/* Число элементов в таблице указателей на цепочки символов */
#define TAB_SIZE 1000000
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 10
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* Массив указателей на цепочки символов */
static char *PtsTable [TAB_SIZE];
/* Число занятых элементов в массиве указателей */
static size_t nelst;
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * */
/* Заполнение массива указателями */
/* на случайные цепочки символов */
/* * * * * * * * * * * * * * * * * */
static void tabl_fill (void) {
char *pss; /* Указатель на свободное место */
/* в области StringSpace */
int i;
for (pss = StringSpace, i = 0; i < TAB_SIZE;
pss += STRING_SIZE, i++) {
if (((pss + STRING_SIZE) –
(StringSpace + SPACE_SIZE)) > 0) {
fprintf (stderr, "tabl_fill: исчерпано "
"пространство цепочек\n");
nelst = i;
return;
}
str_rnd (pss, STRING_SIZE);
PtsTable [i] = pss;
}
nelst = TAB_SIZE;
}
/* * * * * * * * * * */
/* Функция сравнения */
/* * * * * * * * * * */
static int str_compar (const void *pkey,
const void *pelem) {
return strcoll (*((char **) pkey), *((char **) pelem));
}
/* * * * * * * * * * * */
/* Сортировка и поиск */
/* * * * * * * * * * * */
int main (void) {
char *skey; /* Указатель на искомую цепочку символов */
char **res; /* Результат бинарного поиска */
/* Буфер для формирования случайных цепочек */
char sbuf [STRING_SIZE];
double ntr; /* Номер найденной случайной цепочки */
/* Заполнение массивов */
tabl_fill ();
/* Сортировка массива указателей */
qsort (PtsTable, nelst, sizeof (PtsTable [0]),
str_compar);
/* Формирование ключа поиска */
/* (будем искать первую из случайных цепочек) */
skey = StringSpace;
if ((res = (char **) bsearch (skey, PtsTable,
nelst, sizeof (PtsTable [0]), str_compar)) != NULL) {
printf ("Указатель на первую цепочку %s\n"
"после сортировки стал %d-м элементом массива\n",
skey, (res – PtsTable) / sizeof (PtsTable [0]));
} else {
printf ("Не удалось найти цепочку %s\n", skey);
}
/* Будем формировать и искать новые случайные цепочки */
skey = sbuf;
ntr = 0;
do {
str_rnd (skey, STRING_SIZE);
ntr++;
} while (bsearch (skey, PtsTable, nelst,
sizeof (PtsTable [0]), str_compar) == NULL);
printf ("Удалось найти %g-ю по счету случайную цепочку"
" %s\n", ntr, skey);
return 0;
}
Отметим, что при PtsTable [] – указатели на
Если на компьютере, которым в данный момент пользуется автор, измерить время выполнения приведенной программы посредством time с -p, результаты будут выглядеть следующим образом (см. листинг 9.16).
Указатель на первую цепочку NWLRBBMQB после сортировки стал 133253-м элементом массива Удалось найти 168221-ю по счету случайную цепочку VBBDZTNMZ real 15.67 user 15.57 sys 0.10
Читателю предлагается сравнить эти результаты с экспериментально полученными собственными (и с гордостью убедиться, что его компьютер гораздо мощнее), а также оценить зависимость длительности
Стандартом POSIX-2001, помимо <search.h>.
В идейном плане самым простым является lsearch() ) или без таковой ( lfind() ) (см. листинг 9.17).
#include <search.h>
void *lsearch (const void *key,
void *base, size_t *nelp,
size_t width,
int (*compar) (const void *,
const void *));
void *lfind (const void *key,
const void *base, size_t *nelp,
size_t width, int (*compar) (const void *,
const void *));
Функции, реализующие bsearch(), только nelp является указателем на lsearch() может увеличить на единицу (если искомого элемента в массиве не было, его добавляют в конец). Разумеется, для compar(): в случае неравенства ее результат должен быть отличен от нуля.
В качестве иллюстрации применения функций
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа генерирует случайные цепочки символов до первого */
/* повторения (или до исчерпания отведенного пространства). */
/* Для выявления повторения применяется */
/* последовательный поиск с вставкой */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <search.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 200000
/* Число элементов в таблице указателей на цепочки символов */
#define TAB_SIZE 20000
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 7
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* Массив указателей на цепочки символов */
static char *PtsTable [TAB_SIZE];
/* * * * * * * * * * */
/* Функция сравнения */
/* * * * * * * * * * */
static int str_compar (const void *pkey,
const void *pelem) {
return strcoll (*((char **) pkey), *((char **) pelem));
}
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Поиск первого повтора в последовательности */
/* случайных цепочек символов */
/* * * * * * * * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
char *pss; /* Указатель на свободное место */
/* в области StringSpace */
char **res; /* Результат поиска с вставкой */
size_t nelst; /* Число занятых элементов */
/* в массиве указателей */
size_t onelst; /* Число элементов в массиве */
/* до поиска с вставкой */
for (pss = StringSpace, nelst = 0; nelst < TAB_SIZE;
pss += STRING_SIZE) {
if (((pss + STRING_SIZE) – (StringSpace +
SPACE_SIZE)) > 0) {
fprintf (stderr, "%s: Исчерпано пространство "
"цепочек\n", argv [0]);
return (1);
}
str_rnd (pss, STRING_SIZE);
onelst = nelst;
res = (char **) lsearch (pss, PtsTable, nelst,
sizeof (PtsTable [0]), str_compar);
if (onelst == nelst) {
/* Искомая цепочка уже была порождена ранее */
printf ("Для случайных цепочек длины %d\n"
"первое совпадение получено на цепочке "
"%s\n", STRING_SIZE, pss);
printf ("Первый раз цепочка была порождена "
"под номером %d,\n" "второй – под номером "
"%d\n", (res – PtsTable) / sizeof
(PtsTable [0]) + 1, nelst + 1);
return 0;
}
} /* for */
printf ("Из %d случайных цепочек длины %d все "
"оказались уникальными\n", TAB_SIZE, STRING_SIZE);
return 0;
}
Указатели на порождаемые случайные цепочки помещаются в массив PtsTable [] функцией lsearch(). В этой связи обратим внимание на нескольку вычурную организацию цикла for в функции main(). По сути здесь две переменные цикла – pss и nelst. Первая продвигается стандартным образом, в заголовке цикла, но проверяется на выход за допустимые границы в его теле; вторая, напротив, стандартно проверяется, но нестандартно продвигается (в результате вызова lsearch() ).
Возможные результаты выполнения этой программы показаны на листинге 9.19.
Для случайных цепочек длины 7 первое совпадение получено на цепочке GLPCSX Первый раз цепочка была порождена под номером 2548, второй - под номером 12530 real 34.80 user 13.70 sys 0.03
При экспериментах с приведенной программой следует соблюдать определенную осторожность, поскольку время ее работы квадратично зависит от величины TAB_SIZE.
Управление hcreate() ) и ликвидации ( hdestroy() ) hsearch() ), быть может,
#include <search.h> int hcreate (size_t nel); void hdestroy (void); ENTRY *hsearch (ENTRY item, ACTION action);
Предполагается, что элементы таблицы ENTRY, определенный так, как показано на листинге 9.21.
typedef struct entry {
char *key; /* Ключ поиска */
void *data;
/* Дополнительные данные, */
/* ассоциированные с ключом */
} ENTRY;
Функция hcreate() резервирует достаточное количество памяти для таблицы и должна вызываться перед обращением к hsearch(). Значением nel является ожидаемое максимальное количество элементов в таблице. Это число можно взять с запасом, чтобы уменьшить среднее время
Нормальный для hcreate() результат отличен от нуля.
Функция hdestroy() ликвидирует таблицу hcreate().
Функция hsearch() возвращает указатель внутрь таблицы на искомые данные. item – это структура типа ENTRY, содержащая два указателя: item.key указывает на сравниваемый ), а item.data – на любые дополнительные данные, ассоциированные с этим
action имеет тип ACTION, определенный так, как показано на листинге 9.22. Он задает способ действий в случае неудачного ENTER предписывает производить FIND предписывает в случае неудачи вернуть пустой указатель NULL. Пустой указатель возвращается и тогда, когда значение action равно ENTER, и таблица заполнена.
enum {
FIND,
ENTER
} ACTION;
В качестве примера применения функций, управляющих
/* * * * * * * * * * * * * * * * * * * * */
/* Программа помещает в хэш-таблицу */
/* заданное число элементов с указателями*/
/* на случайные цепочки символов, */
/* а затем выполняет в этой таблице */
/* поиск новых случайных цепочек, */
/* пока он не окажется успешным */
/* * * * * * * * * * * * * * * * * * * * */
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 10000000
/* Число элементов, помещаемых в хэш-таблицу */
#define TAB_NEL 1000000
/* Размер хэш-таблицы */
#define TAB_SIZE (2 * TAB_NEL)
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 10
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Заполнение хэш-таблицы, поиск повтора в */
/* последовательности случайных цепочек символов */
/* * * * * * * * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
ENTRY item; /* Искомый элемент */
char sbuf [STRING_SIZE]; /* Буфер для формирования */
/* случайных цепочек */
double ntr; /* Номер найденной */
/* случайной цепочки */
size_t i;
if (hcreate (TAB_SIZE) == 0) {
fprintf (stderr, "%s: Не удалось создать хэш-таблицу"
" размера %d\n", argv [0], TAB_SIZE);
return (1);
}
item.data = NULL; /* Нет ассоциированных данных */
/* Заполним таблицу */
for (item.key = StringSpace, i = 0;
i < TAB_NEL;
item.key += STRING_SIZE, i++) {
if (((item.key + STRING_SIZE) – (StringSpace +
SPACE_SIZE)) > 0) {
fprintf (stderr, "%s: Исчерпано пространство "
"цепочек\n", argv [0]);
return (2);
}
str_rnd (item.key, STRING_SIZE);
if (hsearch (item, ENTER) == NULL) {
fprintf (stderr, "%s: Переполнена хэш-таблица\n",
argv [0]);
return (3);
}
} /* for */
/* Будем формировать и искать новые случайные цепочки */
item.key = sbuf;
ntr = 0;
do {
str_rnd (item.key, STRING_SIZE);
ntr++;
} while (hsearch (item, FIND) == NULL);
printf ("Удалось найти %g-ю по счету случайную цепочку %s\n",
ntr, item.key);
hdestroy ();
return 0;
}
Обратим внимание на то, что размер
Удалось найти 168221-ю по счету случайную цепочку VBBDZTNMZ real 9.61 user 9.36 sys 0.25
Читателю предлагается измерить время работы этой программы на своем компьютере, сравнить его с аналогичным временем для
#include <search.h>
void *tsearch (const void *key, void **rootp,
int (*compar) (const void *,
const void *));
void *tfind (const void *key,
void *const *rootp,
int (*compar) (const void *,
const void *));
void *tdelete (const void *restrict key,
void **restrict rootp,
int (*compar) (const void *,
const void *));
void twalk (const void *root,
void (*action) (const void *,
VISIT, int));
Функция tsearch() используется для построения key является указателем на искомые данные (
rootp указывает на переменную, которая является указателем на NULL, специфицирует tsearch() переменная устанавливается равной указателю на единственный узел –
Подобно функции tsearch(), функция tfind() осуществляет tfind() возвращает пустой указатель NULL.
Функция tdelete(), как и tfind(), сначала производит tdelete() служит указатель на вышележащий по сравнению с удаляемым узел или NULL, если
Функция twalk() осуществляет tsearch() так, что, в соответствии с compar(), все узлы root указывает на
Очевидно, в процессе VISIT с исключительно неудачными именами (см. листинг 9.26).
enum {
preorder,
postorder,
endorder,
leaf
} VISIT;
Имена неудачны, потому что они совпадают с названиями разных способов ). Остается надеяться, что читатель не даст себя запутать и уверенно скажет, что в данном контексте – это второе посещение
action – это функция, которую twalk() вызывает при попадании в узел во время обхода. Она, в свою очередь, имеет три VISIT. Напомним еще раз, что оно показывает, который раз (первый, второй или третий) осуществляется доступ к
Читатель наверняка уже догадался, что далее последует пример программы, строящей
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа осуществляет поиск с вставкой в бинарном */
/* дереве, помещая в него заданное число элементов с */
/* указателями на случайные цепочки символов. */
/* Затем подсчитывается число узлов и высота дерева. */
/* Следующим действием является распечатка */
/* нескольких первых цепочек. */
/* После этого выполняется поиск новых случайных цепочек,*/
/* пока он не окажется успешным. */
/* Найденный элемент удаляется из дерева */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
/* Размер области для хранения цепочек символов */
#define SPACE_SIZE 10000000
/* Число элементов, помещаемых в дерево */
#define TREE_NEL 1000000
/* Длина одной цепочки символов */
/* (включая завершающий нулевой байт) */
#define STRING_SIZE 10
/* Область для хранения цепочек символов */
static char StringSpace [SPACE_SIZE];
/* Число узлов в бинарном дереве поиска */
static size_t node_count;
/* Максимальный уровень узла дерева */
static int max_level;
/* Буфер для функций setjmp и longjmp */
static jmp_buf buf_env;
/* * * * * * * * * * * * * * * * * * * * * */
/* Формирование случайной цепочки символов */
/* * * * * * * * * * * * * * * * * * * * * */
static void str_rnd (char *buf, size_t str_siz) {
for ( ; str_siz > 1; str_siz--) {
*buf++ = 'A' + rand () % 26;
}
if (str_siz > 0) {
*buf = 0;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция, которая вызывается при обходе дерева */
/* с целью подсчета числа узлов и высоты */
/* * * * * * * * * * * * * * * * * * * * * * * * */
static void tw_nnh (const void *pnode, VISIT nv, int level) {
if (nv == preorder) {
node_count++;
} else if (nv == leaf) {
node_count++;
if (level > max_level) {
max_level = level;
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция, которая вызывается при обходе дерева */
/* с целью распечатки нескольких первых */
/* по алфавиту цепочек символов */
/* * * * * * * * * * * * * * * * * * * * * * * * */
static void tw_pfs (const void *pnode, VISIT nv, int level) {
if (node_count <= 0) {
/* Нужное число цепочек выведено,*/
/* прерываем обход дерева */
longjmp (buf_env, 1);
}
if ((nv == postorder) || (nv == leaf)) {
printf ("%s\n", *((char **) pnode));
node_count--;
}
}
/* * * * * * * * * * * * * * * * * * */
/* Создание бинарного дерева поиска, */
/* определение его характеристик, */
/* поиск повтора в последовательности*/
/* случайных цепочек символов */
/* * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
void *root; /* Указатель на корень дерева */
char *key; /* Указатель на искомую */
/* цепочку символов */
char sbuf [STRING_SIZE]; /* Буфер для формирования */
/* случайных цепочек */
double ntr; /* Номер найденной случайной */
/* цепочки */
size_t i;
/* Создадим бинарное дерево поиска */
root = NULL;
for (key = StringSpace, i = 0; i < TREE_NEL; key +=
STRING_SIZE, i++) {
if (((key + STRING_SIZE) – (StringSpace +
SPACE_SIZE)) > 0) {
fprintf (stderr, "%s: Исчерпано пространство "
"цепочек\n", argv [0]);
return (1);
}
str_rnd (key, STRING_SIZE);
if (tsearch (key, root, (int (*) (const void *,
const void *)) strcoll) == NULL) {
fprintf (stderr, "%s: Поиск с вставкой в бинарное"
" дерево " "завершился неудачей\n", argv [0]);
return (2);
}
} /* for */
/* Подсчитаем число узлов и высоту созданного дерева */
node_count = 0;
max_level = 0;
twalk (root, tw_nnh);
printf ("В дереве оказалось %d узлов\n", node_count);
printf ("Его высота равна %d\n", max_level);
/* Распечатаем несколько первых (по алфавиту) цепочек, */
/* помещенных в созданное дерево */
node_count = 10;
printf ("Первые %d по алфавиту цепочек в дереве:\n",
node_count);
if (setjmp (buf_env) == 0) {
twalk (root, tw_pfs);
}
/* Будем формировать и искать новые случайные цепочки */
ntr = 0;
do {
str_rnd (sbuf, STRING_SIZE);
ntr++;
} while (tdelete (sbuf, root, (int (*) (const void *,
const void *)) strcoll) == NULL);
printf ("Удалось найти и удалить из дерева %g-ю по счету "
"случайную цепочку %s\n", ntr, sbuf);
return 0;
}
Отметим гибкость
Обратим внимание на частичный twalk() ориентирована, разумеется, на полный
Возможные результаты выполнения приведенной программы показаны на листинге 9.28.
В дереве оказалось 1000000 узлов Его высота равна 25 Первые 10 по алфавиту цепочек в дереве: AAAATNRAS AAACHCCLB AAACSJQBP AAADLHFAZ AAAFWLRXM AAAFXGQEC AAAGBMHHA AAAGFAXFI AAAHKLCWW AAAHLOSVQ Удалось найти и удалить из дерева 168221-ю по счету случайную цепочку VBBDZTNMZ real 20.24 user 20.25 sys 0.15
Отметим, что среди первых 1000000 случайных
Полноты ради упомянем еще о двух функциях, описанных в заголовочном файле <search.h>: insque() и remque() (см. листинг 9.29). Они предназначены для выполнения операций над
#include <search.h> void insque (void *element, void *pred); void remque (void *element);
Функция insque() осуществляет element, после элемента pred. В качестве элемента должна выступать структура, первые два поля которой являются указателями на структуры того же типа – соответственно, следующий и предыдущий элементы
Функция remque() удаляет заданный элемент из
insque (element, NULL) ; при инициализации
#include <search.h>
. . .
struct qelem {
struct qelem *q_forw;
struct qelem *q_back;
char *data;
. . .
};
struct qelem element1;
struct qelem element2;
. . .
element1.q_forw = element1;
element1.q_back = element1;
insque (element2, element1);
. . .
Трудно сказать, есть ли смысл в стандартизации функций, исходный текст которых занимает пару строк...
Из тех же соображений полноты вернемся к теме tsort:
tsort [файл]
выполняющую
Исходными данными для tsort служат содержащиеся в файле пары элементов (непустых
Например, если применить tsort к файлу, содержащему строки, показанные на листинге 9.31, то можно получить результат, приведенный на листинге 9.32.
a b c d d e f g e f h h
a c h b d e f g
ucontext_t, содержащей по крайней мере следующие поля.
ucontext_t *uc_link;
/* Указатель на контекст, */
/* в котором будет возобновлено */
/* выполнение при выходе из */
/* данного контекста */
sigset_t uc_sigmask;
/* Набор сигналов, блокированных */
/* в данном контексте */
stack_t uc_stack;
/* Стек, используемый в данном */
/* контексте */
mcontext_t uc_mcontext;
/* Машинно-зависимое представление */
/* сохраненного контекста */
Стандарт POSIX-2001 предоставляет функции для getcontext() ), makecontext() ) и смены ( setcontext() и swapcontext() )
#include <ucontext.h>
int getcontext (ucontext_t *ucp);
void makecontext (ucontext_t *ucp,
void (*func) (void), int argc, ...);
int setcontext (const ucontext_t *ucp);
int swapcontext (ucontext_t *restrict oucp,
const ucontext_t *restrict ucp);
Функция getcontext() – штатное средство получения исходного материала для манипулирования контекстами. Она запоминает текущий контекст вызывающего ucp. Ее нормальный результат равен нулю.
Функция makecontext() модифицирует контекст, заданный ucp. Когда (после вызовов setcontext() или swapcontext() ) выполнение будет возобновлено в этом контексте, оно продолжится обращением к функции func() с передачей ей int в количестве argc, помещенных после argc при вызове makecontext(). Приложение должно позаботиться о том, чтобы модифицируемый контекст включал стек достаточного размера.
Элемент uc_link структуры типа ucontext_t определяет контекст, в котором будет возобновлено выполнение после makecontext() контекста. Приложение должно позаботиться об инициализации этого элемента до обращения к makecontext().
Функция setcontext() устанавливает ucp. После успешного вызова setcontext() возврата не происходит – выполнение возобновляется с точки, специфицированной новым контекстом, а именно: если этот контекст был сформирован в результате обращения к getcontext(), выполнение возобновляется возвратом из getcontext() ; если контекст получен после makecontext(), вызывается функция func(), после возврата из которой makecontext() контексте.
Если значением элемента uc_link структуры, на которую указывает ucp, служит пустой указатель, то данный контекст соответствует функции main(), после выхода из которой выполнение
Функция swapcontext() производит oucp, а новый контекст формируется по значению ucp.
Обратим внимание на следующую тонкость. Когда вызывается функция longjmp() будет аккуратно восстановлен контекст соответствующего вызова setjmp(). В подобных ситуациях рекомендуется применять функции siglongjmp() или setcontext().
В качестве примера применения функций, манипулирующих
/* * * * * * * * * * * * * * * * * * * * * * * */
/* Программа демонстрирует применение функций, */
/* манипулирующих пользовательскими контекстами*/
/* * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <ucontext.h>
/* Размер стеков в формируемых пользовательских контекстах */
#define STACK_SIZE 4096
/* Пространство для стеков */
static char st1 [STACK_SIZE];
static char st2 [STACK_SIZE];
static ucontext_t ctx [3];
static void f1 (int arg) {
printf ("Вызвана функция %s с аргументом %d\n", "f1", arg);
if (swapcontext (ctx [1], ctx [2]) != 0) {
perror ("SWAPCONTEXT-1");
}
printf ("Выход из функции %s\n", "f1");
}
static void f2 (int arg1, int arg2) {
printf ("Вызвана функция %s с аргументами %d, %d\n",
"f2", arg1, arg2);
if (swapcontext (ctx [2], ctx [1]) != 0) {
perror ("SWAPCONTEXT-2");
}
printf ("Выход из функции %s\n", "f2");
}
int main (void) {
(void) getcontext (ctx [1]);
printf ("Параметры первоначального контекста:\n"
"адрес стека %p, размер стека %d\n",
ctx[1].uc_stack.ss_sp,
ctx[1].uc_stack.ss_size);
/* В соответствии с общими рекомендациями */
/* позаботимся о стеке для модифицируемых контекстов */
ctx[1].uc_stack.ss_sp = st1;
ctx[1].uc_stack.ss_size = sizeof (st1);
ctx[1].uc_link = ctx [0];
makecontext (ctx [1], (void (*) (void)) f1, 1, 2);
(void) getcontext (ctx [2]);
ctx[2].uc_stack.ss_sp = st2;
ctx[2].uc_stack.ss_size = sizeof (st2);
ctx[2].uc_link = ctx [1];
makecontext (ctx [2], (void (*) (void)) f2, 2, 3, 4);
if (swapcontext (ctx [0], ctx [2]) != 0) {
perror ("SWAPCONTEXT-3");
return (1);
}
return 0;
}
Обратим внимание на резервирование пространства под стек перед обращением к функции makecontext(), а также на связывание нескольких контекстов в список посредством поля uc_link.
Результаты выполнения приведенной программы показаны на листинге 9.35.
Параметры первоначального контекста:
адрес стека (nil), размер стека 0
Вызвана функция f2 с аргументами 3, 4
Вызвана функция f1 с аргументом 2
Выход из функции f2
Выход из функции f1
Средства управления
Под
Тип данных fenv_t, определенный в заголовочном файле <fenv.h>, представляет всю среду, тип fexcept_t – совокупность
Применительно к FE_DIVBYZERO ( FE_INEXACT ( FE_INVALID ( FE_OVERFLOW ( FE_UNDERFLOW ( FE_ALL_EXCEPT.
Стандартом специфицированы четыре FE_DOWNWARD (вниз, то есть к минус бесконечности), FE_TONEAREST (к ближайшему представимому), FE_TOWARDZERO (к нулю), FE_UPWARD (вверх, то есть к плюс бесконечности).
Подразумеваемая FE_DFL_ENV, имеющей тип указателя на константный объект fenv_t.
Если приложение проверяет #pragma ) FENV_ACCESS:
#pragma STDC FENV_ACCESS ON
Опросить и установить текущую fegetenv() и fesetenv() (см. листинг 9.36).
#include <fenv.h> int fegetenv (fenv_t *fenvp); int fesetenv (const fenv_t *fenvp);
Отметим, что функция fesetenv() не возбуждает
feholdexcept() (см. листинг 9.37) не только запоминает текущую среду по указателю fenvp, но также очищает feholdexcept() имеет смысл, если, помимо безостановочного, реализация предоставляет другие режимы обработки
#include <fenv.h> int feholdexcept (fenv_t *fenvp);
Функция feupdateenv() (см. листинг 9.38) выполняет еще более сложные действия. Она сохраняет в своей локальной памяти информацию о текущей fenvp и затем пытается возбудить в ней сохраненное
#include <fenv.h> int feupdateenv (const fenv_t *fenvp);
Для опроса и установки fegetexceptflag() и fesetexceptflag() (см. листинг 9.39).
#include <fenv.h>
int fegetexceptflag (fexcept_t *flagp,
int excepts);
int fesetexceptflag (const fexcept_t *flagp,
int excepts);
Функция fegetexceptflag() помещает по указателю flagp зависящее от excepts, и ассоциированной с ними информации. Функция fesetexceptflag() выполняет обратную операцию. Как и в случае функции fesetenv(),
Функции fetestexcept(), feclearexcept() и feraiseexcept() (см. листинг 9.40) служат, соответственно, для проверки, сброса и
#include <fenv.h> int fetestexcept (int excepts); int feclearexcept (int excepts); int feraiseexcept (int excepts);
Функция fetestexcept() проверяет, какие из флагов, заданные excepts, в данный момент установлены; результатом служит их побитное ИЛИ.
Функция feclearexcept() пытается сбросить, а feraiseexcept() – возбудить заданные FE_OVERFLOW ) и FE_UNDERFLOW ) может стать FE_INEXACT ).
Опросить и установить fegetround() и fesetround() (см. листинг 9.41).
#include <fenv.h> int fegetround (void); int fesetround (int round);
Свидетельством неудачного завершения функции fegetround() служит отрицательный результат.
Продемонстрируем применение некоторых функций управления
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа демонстрирует применение некоторых функций */
/* управления средой вещественной арифметики */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
int main (void) {
double d1, d2, d3, s;
int res;
printf ("Представление флагов состояния вещественной "
"арифметики\n");
printf (" FE_DIVBYZERO: %x\n", FE_DIVBYZERO);
printf (" FE_INEXACT: %x\n", FE_INEXACT);
printf (" FE_INVALID: %x\n", FE_INVALID);
printf (" FE_OVERFLOW: %x\n", FE_OVERFLOW);
printf (" FE_UNDERFLOW: %x\n", FE_UNDERFLOW);
printf ("Представление режимов округления\n");
printf (" FE_DOWNWARD: %x\n", FE_DOWNWARD);
printf (" FE_TONEAREST: %x\n", FE_TONEAREST);
printf (" FE_TOWARDZERO: %x\n", FE_TOWARDZERO);
printf (" FE_UPWARD: %x\n", FE_UPWARD);
printf ("Текущие исключительные ситуации: %x\n",
fetestexcept (FE_ALL_EXCEPT));
printf ("Текущий режим округления: %x\n", fegetround ());
feclearexcept (FE_ALL_EXCEPT);
/* Вызовем ситуацию исчезновения порядка */
d1 = 1;
do {
d1 /= 2;
} while ((res = fetestexcept (FE_ALL_EXCEPT)) == 0);
printf ("Исключительные ситуации: %x\n", res);
printf ("2^-inf: %g\n", d1);
feclearexcept (res);
/* Вызовем ситуацию переполнения */
d2 = 1;
do {
d2 *= 2;
} while ((res = fetestexcept (FE_ALL_EXCEPT)) == 0);
printf ("Исключительные ситуации: %x\n", res);
printf ("2^+inf: %g\n", d2);
feclearexcept (res);
/* Вызовем ситуацию деления на нуль */
d3 = 1 / d1;
res = fetestexcept (FE_ALL_EXCEPT);
printf ("Исключительные ситуации: %x\n", res);
printf ("1/0: %g\n", d3);
feclearexcept (res);
/* Пример того, как может возникать потеря точности */
s = 1;
do {
s = (s + 2 / s) * 0.5;
} while ((s * s – 2) > 0);
printf ("Исключительные ситуации: %x\n",
fetestexcept (FE_ALL_EXCEPT));
printf ("sqrt (2): %g\n", s);
return 0;
}
Возможные результаты выполнения приведенной программы показаны на листинге 9.43.
Представление флагов состояния вещественной арифметики
FE_DIVBYZERO: 4
FE_INEXACT: 20
FE_INVALID: 1
FE_OVERFLOW: 8
FE_UNDERFLOW: 10
Представление режимов округления
FE_DOWNWARD: 400
FE_TONEAREST: 0
FE_TOWARDZERO: c00
FE_UPWARD: 800
Текущие исключительные ситуации: 0
Текущий режим округления: 0
Исключительные ситуации: 30
2^-inf: 0
Исключительные ситуации: 28
2^+inf: inf
Исключительные ситуации: 4
1/0: inf
Исключительные ситуации: 20
sqrt (2): 1.41421
Отметим, что и для
Обратим внимание также на то, что вместе с
В качестве второго примера рассмотрим программу, реализующую некоторые операции
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа реализует некоторые операции */
/* интервальной арифметики */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
/* Интервальное представление числа */
typedef struct ditvl {
double lb;
double ub;
} ditvl_t;
/* * * * * * * * * * * * * * * * * * * * */
/* Сложение интервалов. */
/* Сумма помещается в выходной аргумент. */
/* Нормальный результат равен нулю */
/* * * * * * * * * * * * * * * * * * * * */
int ditvl_add (const ditvl_t *a1, const ditvl_t *a2,
ditvl_t *res) {
fenv_t cfenv;
/* Сохраним текущую среду вещественной арифметики */
if (fegetenv (cfenv) != 0) {
perror ("FEGETENV");
return (-1);
}
/* Нижние границы нужно складывать с округлением вниз */
if (fesetround (FE_DOWNWARD) != 0) {
perror ("FESETROUND");
return (-1);
}
res->lb = a1->lb + a2->lb;
/* Верхние границы складываются с округлением вверх */
if (fesetround (FE_UPWARD) != 0) {
perror ("FESETROUND");
return (-1);
}
res->ub = a1->ub + a2->ub;
/* Восстановим среду вещественной арифметики */
if (fesetenv (cfenv) != 0) {
perror ("FESETENV");
return (-1);
}
return 0;
}
/* * * * * * * * */
/* Унарный минус */
/* * * * * * * * */
int ditvl_uminus (const ditvl_t *a, ditvl_t *res) {
res->lb = -(a->ub);
res->ub = -(a->lb);
return 0;
}
/* * * * * * * * */
/* Вызов функций */
/* * * * * * * * */
int main (void) {
ditvl_t pi = {3.141592, 3.141593};
ditvl_t e = {2.718281, 2.718282};
ditvl_t res;
ditvl_t tmp;
printf ("Представление числа pi: (%f, %f)\n", pi.lb, pi.ub);
printf ("Представление числа e: (%f, %f)\n", e.lb, e.ub);
/* Вычислим сумму pi и e */
(void) ditvl_add (pi, e, res);
printf ("Сумма pi и e: (%f, %f)\n", res.lb, res.ub);
/* Вычислим разность pi и e */
(void) ditvl_uminus (e, tmp);
(void) ditvl_add (pi, tmp, res);
printf ("Разность pi и e: (%f, %f)\n", res.lb, res.ub);
printf ("Текущие исключительные ситуации: %x\n",
fetestexcept (FE_ALL_EXCEPT));
printf ("Текущие режимы округления: %x\n", fegetround ());
return 0;
}
Программа переустанавливает ditvl_add() для fegetround() / fesetround(), а не fegetenv() / fesetenv().
На листинге 9.45 показаны возможные результаты выполнения приведенной программы.
Представление числа pi: (3.141592, 3.141593) Представление числа e: (2.718281, 2.718282) Сумма pi и e: (5.859873, 5.859875) Разность pi и e: (0.423310, 0.423312) Текущие исключительные ситуации: 0 Текущие режимы округления: 0
Отметим, что в завершающей части программы подразумеваемая
и nftw() (см. листинг 9.46).
#include <ftw.h>
int ftw (const char *path,
int (*fn) (const char *,
const struct stat *, int),
int depth);
int nftw (const char *path,
int (*fn) (const char *,
const struct stat *, int, struct FTW *),
int depth, int flags);
Функция path. Для каждого объекта иерархии вызывает функцию fn(), передавая ей три stat, содержащую информацию об объекте;
Возможны следующие значения
FTW_D
FTW_DNR
FTW_F
FTW_SL
FTW_NS
Объект, отличный от stat() не может выполниться успешно.
Если FTW_DNR, элементы этого FTW_NS, то структура типа stat будет содержать неопределенные значения. Примером объекта, который вызовет передачу функции fn() типа FTW_NS, является файл в
Функция обрабатывает
Функция использует не более одного depth ограничивает количество используемых таким образом дескрипторов; его значение должно принадлежать диапазону [1, OPEN_MAX].
Обход завершится тогда, когда будет обойдена вся иерархия, или функция fn() вернет ненулевое значение, или возникнет ошибка, отличная от EACCES, при работе самой функции (например, ошибка ввода/вывода). Если в качестве результата возвращает нуль. Если fn() вернет ненулевое значение, то прекратит обход и выдаст это значение. Если будет обнаружена ошибка при работе самой функции , то она вернет -1 и соответствующим образом установит значение переменной errno.
Обратим внимание на следующее (впрочем, довольно очевидное) обстоятельство. Функция во время своей работы временно fn() или обработчика сигнала), эти ресурсы останутся неосвобожденными. Рекомендуемый способ обработки прерываний заключается в том, чтобы зафиксировать факт получения прерывания и при очередном вызове fn() заставить ее вернуть ненулевое значение.
Функция nftw() аналогична , однако и у нее самой, и у вызываемой ею функции fn() имеется по одному дополнительному flags управляет работой функции nftw(). Его значение формируется как побитное ИЛИ следующих флагов.
FTW_CHDIR
Делать текущим просматриваемый nftw() не изменит текущий
FTW_DEPTH
FTW_MOUNT
Обрабатывать только файлы из той же файловой системы, что и path.
FTW_PHYS
Осуществлять
Третий fn(), по сравнению с может принимать следующие дополнительные значения.
FTW_DP
FTW_DEPTH ).
FTW_SLN
Дополнительный, четвертый fn() является указателем на структуру типа . Согласно стандарту, она должна содержать по крайней мере следующие поля.
int base;
/* Смещение простого имени файла */
/* от начала маршрутного имени, */
/* переданного fn() в качестве */
/* первого аргумента */
int level;
/* Уровень текущего объекта */
/* относительно корня иерархии */
/* (у самого корня нулевой уровень) */
В качестве примера
/* * * * * * * * * * * * * * * * * * * * */
/* Программа определяет суммарный размер */
/* и высоту файловой иерархии */
/* * * * * * * * * * * * * * * * * * * * */
#define _XOPEN_SOURCE 600
#include <ftw.h>
#include <stdio.h>
/* Суммарный размер файлов в иерархии */
static off_t fsize = 0;
/* Максимальный уровень файлов в иерархии */
static int flevel = 0;
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция, вызываемая для каждого файла в иерархии */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
static int nftwfunc (const char *filename,
const struct stat *statptr, int filetype,
struct FTW *pfwt) {
/* Если установлен флаг FTW_NS, завершим обход */
if (filetype == FTW_NS) {
perror ("STAT");
fprintf (stderr, "Отсутствуют данные о файле %s\n",
filename);
return 1;
}
fsize += statptr->st_size;
if (pfwt->level > flevel) {
flevel = pfwt->level;
}
return 0;
}
/* * * * * * * * * * * */
/* Организация обхода */
/* * * * * * * * * * * */
int main (int argc, char *argv []) {
if (argc != 2) {
fprintf (stderr, "Использование: %s корень_иерархии\n",
argv [0]);
return (1);
}
if (nftw (argv [1], nftwfunc, 16,
FTW_MOUNT | FTW_PHYS) == -1) {
perror ("NFTW");
}
printf ("Суммарный размер обработанных файлов: %ld\n",
fsize);
printf ("Высота иерархии файлов: %d\n", flevel);
return 0;
}
Обратим внимание на возможность досрочного завершения обхода за счет возврата ненулевого результата функцией обработки, если последней передан файл, данные о котором получить не удалось, а также на использование флагов
Пусть в /tmp существует непустой подкаталог gaga со следующим режимом доступа:
drw-r--r-- 2 galat sys 4096 May 7 17:26 gaga
Тогда результаты запуска приведенной программы с /tmp могут выглядеть так, как показано на листинге 9.48.
STAT: Permission denied Отсутствуют данные о файле /tmp/gaga/gugu Суммарный размер обработанных файлов: 2645778 Высота иерархии файлов: 2
Служебная программа xargs позволяет формировать и выполнять
xargs [-E логич_конец_файла]
[-I заменяемая_цепочка]
[-L число] [-n число] [-p] [-s размер]
[-t] [-x]
[утилита [начальный_аргумент ...]]
Программа xargs объединяет зафиксированный набор заданных начальных_аргументов с echo ) в рамках сформированной
xargs приписан следующий смысл.
-E логич_конец_файла
логич_конец_файла считается признаком
-I заменяемая_цепочка
-I включает -x.
-L число
Выполнять
-n число
Выполнить -s, или если для последнего вызова их осталось меньше, чем заданное число.
-p
Режим с xargs перед каждым вызовом -t ), за счет чего в ?...". Положительный ответ, прочитанный с устройства /dev/tty, приводит к выполнению
-s размер
Максимальный общий размер (в символах) каждого списка -n или -L, или если встретится
-t
Режим
-x
Завершить работу служебной программы xargs, если очередная сформированная -n число -L число строк, оказалась длиннее, чем специфицированный -s размер.
Приведем несколько примеров применения служебной программы xargs. На всякий случай подчеркнем, что никто не утверждал, что Linux или какая-либо иная операционная система соответствует стандарту POSIX-2001. Весьма вероятно, что перед исполнением примеров их придется немного подправить.
Следующая однострочная shell-процедура пересылает все файлы из $1 в $2 и сообщает о каждой пересылке, перед тем как ее выполнить:
ls $1 | xargs -I {} -t mv $1/{} $2/{}
Еще одна однострочная shell-процедура применяет служебную программу diff к последовательным парам своих
echo $* | xargs -n 2 diff
Пользователя спрашивают, какие mylib.a. При выполнении первого конвейера (см. ниже) файлы заносятся по одному; при выполнении второго заносится сразу много файлов.
ls *.o | xargs -p -L 1 ar -r mylib.a ls *.o | xargs -p -L 1 | xargs ar -r mylib.a
Отметим, что во втором звене второго конвейера в качестве подразумеваемой echo.
В курсе мы уже останавливались на понятии
Первым действием при работе с posix_openpt() (см. листинг 9.49).
#include <stdlib.h> #include <fcntl.h> int posix_openpt (int oflag);
В значении oflag есть смысл устанавливать флаги O_RDWR и O_NOCTTY (последний означает, что терминал не будет управляющим для процесса).
Разумеется, нормальным результатом функции posix_openpt()> служит -1.
Считается, что после unlockpt() (см. листинг 9.50).
#include <stdlib.h> int unlockpt (int masterfd);
Здесь masterfd – posix_openpt().
Нормальным для unlockpt() является нулевой результат.
Функция grantpt() (см. листинг 9.51) позволяет
#include <stdlib.h> int grantpt (int masterfd);
Применительно к функции grantpt() в стандарте POSIX-2001 оговаривается одна тонкость: вызывающий процесс не должен обрабатывать сигнал SIGCHLD. Это можно понять (и оправдать), поскольку grantpt() выполняет суперпользовательское действие, реализация которого в некоторых ОС может быть сопряжена с
Чтобы узнать ptsname() (см. листинг 9.52).
#include <stdlib.h> char *ptsname (int masterfd);
Разумеется, результирующий указатель может ссылаться на статическую область, перезаписываемую при каждом вызове ptsname(), поэтому рекомендуется полученное имя скопировать или поскорее использовать. Использование очевидно – посредством обычной функции open()
Таким образом, в стандарте POSIX-2001 выстроена пятиэтапная модель получения доступа к
posix_openpt() );unlockpt() );grantpt() );ptsname() );open() ).Применение описанных функций иллюстрируется переработанным вариантом программы из курса , запускающей командный интерпретатор на
/* * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа запускает shell на псевдотерминале */
/* * * * * * * * * * * * * * * * * * * * * * * * */
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <signal.h>
#include <poll.h>
#include <sys/resource.h>
#include <curses.h>
/* * * * * * * * * * * * * * * * * * */
/* Действия при завершении процесса */
/* * * * * * * * * * * * * * * * * * */
static void termination (int errcode) {
endwin ();
exit (errcode);
}
/* * * * * * * * * * * * * * * * * * */
/* Функция обработки сигнала SIGCHLD */
/* * * * * * * * * * * * * * * * * * */
static void chldied (int dummy) {
/* Просто кончимся */
termination (34);
}
/* * * * * * * * * * * * * * * * * * * * */
/* Организация работы с псевдотерминалом */
/* * * * * * * * * * * * * * * * * * * * */
int main (void) {
WINDOW *win1, *win2;
/* win1 – окно только для рамки */
/* win2 – окно для shell'а */
int pty, tty; /* Дескрипторы обеих сторон */
/* псевдотерминала */
int fr; /* Результат fork'а */
unsigned char ch; /* Прочитанный символ */
struct termios pt; /* Структура характеристик */
/* псевдотерминала */
struct pollfd fds [2]; /* Массив параметров для */
/* вызова poll */
int w2lines, w2cols; /* Размер создаваемого окна */
int x, y; /* Координаты в окне */
struct sigaction sact;
int i;
initscr ();
cbreak ();
noecho ();
win1 = newwin (LINES, COLS, 0, 0);
box (win1, 0, 0);
wrefresh (win1);
w2lines = LINES – 2;
w2cols = COLS – 4;
win2 = newwin (w2lines, w2cols, 1, 2);
scrollok (win2, TRUE);
/* Откроем псевдотерминал */
if (((pty = posix_openpt (O_RDWR | O_NOCTTY)) < 0) ||
(unlockpt (pty) == -1) ||
(grantpt (pty) == -1) ||
((tty = open (ptsname (pty), O_RDWR)) < 0)) {
fprintf (stderr, "Не удалось открыть псевдотерминал\n");
perror ("POSIX_OPENPT");
return (1);
}
/* Установим подходящие характеристики псевдотерминала */
if (tcgetattr (pty, pt) < 0) {
perror ("PTY TERMIOS GET ERROR");
return (2);
}
pt.c_iflag = 0;
pt.c_oflag = ONLCR;
pt.c_cflag = CS8 | HUPCL;
pt.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
pt.c_cc [VINTR] = 3; /* CTRL+C */
pt.c_cc [VEOF] = 4; /* CTRL+D */
if (tcsetattr (pty, TCSADRAIN, pt) < 0) {
perror ("PTY TERMIOS SET ERROR");
return (3);
}
/* То же – для стандартного ввода */
(void) tcgetattr (0, pt);
pt.c_lflag = ~ISIG;
(void) tcsetattr (0, TCSADRAIN, pt);
/* Установим обработку сигнала о завершении потомка */
sact.sa_handler = chldied;
(void) sigemptyset (sact.sa_mask);
sact.sa_flags = 0;
(void) sigaction (SIGCHLD, sact, (struct sigaction *) NULL);
/* Раздвоимся на процесс чтения с клавиатуры */
/* и вывода на экран и на процесс, */
/* в рамках которого запустим shell */
if ((fr = fork ()) < 0) {
perror ("FORK1 ERROR");
termination (-1);
} else if (fr) {
/* Это процесс, читающий с клавиатуры */
/* и выводящий на экран */
close (tty);
/* Будем ждать ввода с клавиатуры или псевдотерминала */
fds [0].fd = 0;
fds [0].events = POLLIN;
fds [1].fd = pty;
fds [1].events = POLLIN;
while (1) {
if (poll (fds, 2, -1) < 0) {
perror ("POLL ERROR");
termination (0);
}
if (fds [0].revents POLLIN) {
/* Пришел символ со стандартного ввода */
read (0, ch, 1);
write (pty, ch, 1);
}
if (fds [1].revents POLLIN) {
/* Пришел символ с псевдотерминала */
read (pty, ch, 1);
switch (ch) {
case '\n': {
/* Проинтерпретируем перевод строки */
getyx (win2, y, x);
if (y == (w2lines – 1)) {
wmove (win2, y, w2cols – 1);
waddch (win2, (chtype) ch);
} else {
wmove (win2, y + 1, 0);
}
break;
}
default: {
/* Символ не интерпретируется */
waddch (win2, (chtype) ch);
break;
}
}
wrefresh (win2);
}
}
/* Просто кончимся */
termination (0);
} else { /* Порожденный процесс – запустим в нем shell */
/* Закроем все файлы, кроме псевдотерминала */
for (i = 0; i < RLIMIT_NOFILE; i++) {
if (i != tty) {
(void) close (i);
}
}
/* Сделаем процесс лидером сеанса */
(void) setsid ();
/* Свяжем стандартные ввод, вывод и протокол */
/* с псевдотерминалом */
(void) fcntl (tty, F_DUPFD, 0);
(void) fcntl (tty, F_DUPFD, 0);
(void) fcntl (tty, F_DUPFD, 0);
close (tty);
/* Поместим в окружение параметры псевдотерминала */
{
char lnbuf [20];
char clbuf [20];
sprintf (lnbuf, "LINES=%2d", w2lines);
sprintf (clbuf, "COLUMNS=%2d", w2cols);
putenv (lnbuf);
putenv (clbuf);
}
if (execl ("/bin/sh", "sh", (char *) NULL) < 0) {
perror ("EXECL ERROR");
exit (-1);
}
}
return 0;
}
Здесь все пять упоминавшихся подготовительных этапов вошли в состав одного условного оператора. Обратим внимание на применение флага O_NOCTTY при вызове posix_openpt(), а также на то, что обращение к grantpt() выполнено до установки функции SIGCHLD.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.