Средства
Вообще говоря,
Операция
За исключением параллельной работы с инициировавшим их приложением и интерфейсных отличий, read(), write(), lseek() и fsync(). Выдать
Параллельные асинхронные и синхронные операции обмена с одним и тем же файлом изменяют его так, как если бы они выполнялись последовательно.
После завершения
"Жизненный цикл"
После завершения каждого из этапов приложение получает
Пока операция не завершена, ее EINPROGRESS. Теоретически, приложение может дожидаться завершения асинхронной операции, периодически опрашивая EINPROGRESS.
Из общих соображений следует, что если есть возможность
Полезно иметь возможность за один вызов
Отметим, что потенциальное
Наиболее важным объектом, обслуживающим aiocb со следующими полями.
int aio_fildes; // Файловый дескриптор
off_t aio_offset; // Позиция в файле
volatile void *aio_buf; // Адрес буфера
size_t aio_nbytes; // Число передаваемых
// байт
int aio_reqprio; // Величина понижения
// приоритета
struct sigevent aio_sigevent; // Номер и
// значение
// сигнала
int aio_lio_opcode; // Запрошенная
// операция
Значением элемента aio_fildes является
Элемент aio_offset задает (абсолютную) O_APPEND или заданное дескриптором устройство не поддерживает позиционирования, операции записи
Поля aio_buf и aio_nbytes имеют тот же смысл, что и соответствующие аргументы функций read() и write().
Если реализация поддерживает aio_reqprio позволяет понизить (но не повысить) AIO_PRIO_DELTA_MAX включительно; оно вычитается из
Элемент aio_sigevent, имеющий структурный тип sigevent, определяет aio_sigevent.sigev_notify равно SIGEV_NONE, никаких
Элемент aio_lio_opcode используется только в
EINPROGRESS. Разумеется, приложение не должно модифицировать в это время структуру aiocb.
Основными
#include <aio.h> int aio_read (struct aiocb *aiocbp); int aio_write (struct aiocb *aiocbp);
Функция aio_read() инициирует aiocbp->aio_nbytes байт из файла, ассоциированного с дескриптором aiocbp->aio_fildes, начиная с абсолютной позиции aiocbp->aio_offset, в aiocbp->aio_buf. Возврат из функции произойдет тогда, когда -1 (в совокупности с errno ) свидетельствует об ошибке (например, исчерпаны ресурсы и
Функция aio_write() осуществляет соответствующие действия для записи данных. Если для aiocbp->aio_fildes установлен флаг O_APPEND,
Функция lio_listio() (см. листинг 7.2) позволяет за один вызов инициировать (
#include <aio.h>
int lio_listio (
int mode, struct aiocb *restrict const
listio [restrict], int nent,
struct sigevent *restrict sigev);
Элементы массива listio [] специфицируют отдельные aio_lio_opcode указуемых структур типа aiocb могут располагаться значения LIO_READ ( LIO_WRITE ( LIO_NOP (пустой aio_read() или aio_write() ). listio [] задается аргументом nent.
mode определяет LIO_WAIT ) или LIO_NOWAIT ). В первом случае возврат из вызова lio_listio() произойдет только после завершения всех заказанных sigev игнорируется. Во втором случае возврат из lio_listio() произойдет немедленно, а по завершении всех sigev будет сгенерировано асинхронное
Нормальный результат выполнения функции lio_listio() равен нулю, но для двух описанных случаев он имеет разный смысл – успешное завершение всех aio_return() и aio_error() (см. листинг 7.3).
#include <aio.h>
ssize_t aio_return (
struct aiocb *aiocbp);
int aio_error (
const struct aiocb *aiocbp);
Аргументом этих функций служит aio_return() выдает значение, которое вернула бы соответствующая функция обычного ввода/вывода (если aio_return() можно применить ровно один раз; последующие вызовы aio_return() и aio_error() могут завершиться неудачей. (Конечно, если в той же указуемой структуре типа aiocb сформировать новый aio_return() и aio_error() появляется снова.)
Результатом вызова aio_error() служит значение, которое установила бы в переменную errno соответствующая функция обычного ввода/вывода (или, если операция еще не завершена, результат равен EINPROGRESS ). Если aio_error() вернет нулевой результат. Функцию aio_error() к
Ожидающие обработки aio_cancel() (см. листинг 7.4).
#include <aio.h>
int aio_cancel (int fildes,
struct aiocb *aiocbp);
Функция aio_cancel() пытается аннулировать один (если значение аргумента aiocbp отлично от NULL ) или все ожидающие обработки fildes. После ECANCELED, а в качестве -1.
Если
Результат вызова aio_cancel() равен AIO_CANCELED, если все указанные AIO_NOTCANCELED (а со статусом lio_listio(), нужно разбираться индивидуально, пользуясь функцией aio_error() ). Результат AIO_ALLDONE свидетельствует о том, что выполнение всех указанных aio_cancel() равен -1.
Согласно стандарту POSIX-2001, списком можно не только инициировать aio_suspend() (см. листинг 7.5).
#include <aio.h>
int aio_suspend (
const struct aiocb *const listio [],
int nent,
const struct timespec *timeout);
Поток управления, вызвавший aio_suspend(), приостанавливает выполнение на время, не большее, чем задано аргументом timeout, до тех пор пока не завершится по крайней мере одна из listio ( nent – число его элементов), или
Нулевой результат свидетельствует о завершении по крайней мере одной из aio_error() и aio_return().
#include <unistd.h>
void sync (void);
#include <unistd.h>
int fsync (int fildes);
#include <unistd.h>
int fdatasync (int fildes);
#include <aio.h>
int aio_fsync (int op,
struct aiocb *aiocbp);
Функция имеет глобальный характер – она планирует запись в
Функция fsync() обеспечивает fildes. Более того, после успешного возврата из функции (с нулевым результатом) окажутся выполненными все стоявшие в
Функция fdatasync() аналогична fsync(), но гарантируется лишь
aio_fsync() осуществляет op, равном O_DSYNC, эквивалентно вызову fdatasync (aiocbp->aio_fildes), а при op, равном O_SYNC, – fsync (aiocbp->aio_fildes). Как и для других aio_error() и aio_return(), а после завершения генерируется асинхронное aiocbp->aio_sigevent. Все остальные поля указуемой структуры типа aiocb игнорируются
Проиллюстрируем использование функций
/* * * * * * * * * * * * * * * * * * */
/* Программа подсчитывает суммарное */
/* число строк – в файлах аргументах */
/* командной строки. */
/* Если аргументы отсутствуют или в */
/* качестве имени задан минус, */
/* читается стандартный ввод. */
/* * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <aio.h>
#include <assert.h>
#include <errno.h>
int main (int argc, char *argv []) {
/* Указатель на массив */
/* указателей на управляющие */
/* блоки запросов операций */
/* асинхронного чтения */
struct aiocb **lio_ptr;
// Общее число строк в файлах
long nlines = 0;
// Признак повторного указания
// стандартного ввода
int dup_stdin = 0;
int i;
/* Сведем случай с отсутствием */
/* аргументов к общему, */
/* воспользовавшись одним из */
/* немногих стандартизованных */
/* файлов */
if (argc == 1) {
argv [0] = "-";
} else {
argv [0] = "/dev/null";
}
/* Зарезервируем память, */
/* откроем заданные файлы */
/* и сформируем начальный список */
/* запросов на чтение */
assert ((lio_ptr =
(struct aiocb **) malloc
(sizeof (struct aiocb *) * argc))
!= NULL);
for (i = 0; i < argc; i++) {
assert ((lio_ptr [i] =
(struct aiocb *) malloc
(sizeof (struct aiocb)))
!= NULL);
if (strcmp (argv [i], "-") == 0) {
if (dup_stdin == 0) {
lio_ptr [i]->aio_fildes =
STDIN_FILENO;
dup_stdin = 1;
} else {
lio_ptr [i]->aio_fildes =
fcntl (STDIN_FILENO,
F_DUPFD, 0);
}
} else
if ((lio_ptr [i]->aio_fildes =
open (argv [i],
O_RDONLY)) == -1) {
perror ("OPEN");
free (lio_ptr [i]);
lio_ptr [i] = NULL;
continue;
}
lio_ptr [i]->aio_offset = 0;
assert ((lio_ptr [i]->aio_buf =
malloc(BUFSIZ)) != NULL);
lio_ptr [i]->aio_nbytes = BUFSIZ;
lio_ptr [i]->aio_reqprio = 0;
lio_ptr
[i]->aio_sigevent.sigev_notify
= SIGEV_NONE;
lio_ptr [i]->aio_lio_opcode =
LIO_READ;
} /* for (i < argc) */
/* Поехали ... */
assert (lio_listio (LIO_NOWAIT,
lio_ptr, argc,
lio_ptr [0]->aio_sigevent)
== 0);
/* Будем ждать завершения */
/* операций ввода/вывода, */
/* обрабатывать прочитанные */
/* данные и */
/* инициировать новые запросы */
/* на чтение */
while (aio_suspend ((
const struct aiocb **)
lio_ptr,
argc, NULL) == 0) {
/* Выясним, какой запрос */
/* и как выполнен */
/* Число недочитанных файлов */
int nreqs = 0;
for (i = 0; i < argc; i++) {
if (lio_ptr [i] == NULL) {
continue;
}
/* Есть обслуживаемые */
/* запросы */
nreqs++;
if (aio_error (lio_ptr [i])
== EINPROGRESS) {
continue;
}
{
// Запрос выполнен
// Число прочитанных байт
ssize_t nb;
if ((nb = aio_return
(lio_ptr [i])) <= 0) {
/* Дошли до конца файла*/
/* или чтение */
/* завершилось ошибкой */
(void) close(lio_ptr
[i]->aio_fildes);
free ((void *) lio_ptr
[i]->aio_buf);
free (lio_ptr [i]);
lio_ptr [i] = NULL;
nreqs--;
} else {
/* Обработаем прочитанные */
/* данные */
/* Текущее начало поиска */
/* перевода строки */
char *p;
/* Позиция, где нашли */
/* перевод строки */
char *p1;
p = (char *) lio_ptr [i]->aio_buf;
while ((p1 =
(char *) memchr
(p, '\n',
nb - (p – (char *)
lio_ptr
[i]->aio_buf)))
!= NULL) {
nlines++;
p = p1 + 1;
}
/* Инициируем новый */
/* запрос на чтение */
lio_ptr [i]->aio_offset
+= nb;
(void) aio_read
(lio_ptr [i]);
}
}
} /* for (i < argc) */
/* Остались недочитанные */
/* файлы? */
if (nreqs == 0) {
break;
}
} /* while */
printf ("%ld\n", nlines);
return 0;
}
Отметим списочную форму начального представления и aiocb (функция lio_listio() игнорирует такие элементы) и в качестве аргумента timeout функции aio_suspend() (означает бесконечное
Второй вариант решения той же задачи (см. листинг 7.8) демонстрирует другой
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа подсчитывает суммарное число строк */
/* в файлах – аргументах командной строки. */
/* Если аргументы отсутствуют или в качестве имени */
/* задан минус, читается стандартный ввод. */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <aio.h>
#include <pthread.h>
#include <assert.h>
/* Указатель на массив указателей на управляющие */
/* блоки запросов операций асинхронного чтения */
static struct aiocb **lio_ptr;
/* Общее число строк в файлах */
static long nlines_total = 0;
/* Переменная условия, на которой ждут */
/* окончания обработки всех файлов */
static pthread_cond_t open_files_cond =
PTHREAD_COND_INITIALIZER;
/* Мьютекс, охраняющий доступ */
/* к переменной условия и nlines_total */
static pthread_mutex_t nlines_mutex =
PTHREAD_MUTEX_INITIALIZER;
/* * * * * * * * * * * * * * */
/* Функция, вызываемая */
/* при завершении операции */
/* асинхронного ввода/вывода */
/* * * * * * * * * * * * * * */
static void end_of_aio_op (union sigval sg_vl) {
int no; /* Номер выполненного запроса */
/* в общем списке */
ssize_t nb; /* Число прочитанных байт */
long nlines = 0; /* Число строк в одной */
/* прочитанной порции */
no = sg_vl.sival_int;
if ((nb = aio_return (lio_ptr [no])) <= 0) {
/* Дошли до конца файла */
/* или чтение завершилось ошибкой */
(void) close (lio_ptr [no]->aio_fildes);
free ((void *) lio_ptr [no]->aio_buf);
free (lio_ptr [no]);
lio_ptr [no] = NULL;
(void) pthread_cond_signal (open_files_cond);
} else {
/* Обработаем прочитанные данные */
char *p; /* Текущее начало поиска перевода строки */
char *p1; /* Позиция, где нашли перевод строки */
p = (char *) lio_ptr [no]->aio_buf;
while ((p1 = (char *) memchr (p, '\n',
nb - (p – (char *) lio_ptr [no]->aio_buf)))
!= NULL) {
nlines++;
p = p1 + 1;
}
/* Прибавим локальную сумму к общей */
(void) pthread_mutex_lock (nlines_mutex);
nlines_total += nlines;
(void) pthread_mutex_unlock (nlines_mutex);
/* Инициируем новый запрос на чтение */
lio_ptr [no]->aio_offset += nb;
(void) aio_read (lio_ptr [no]);
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция проверяет, сколько осталось открытых файлов */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
static int n_open_files (int nfiles) {
int nof = 0; /* Число открытых файлов */
int i;
for (i = 0; i < nfiles; i++) {
if (lio_ptr [i] != NULL) {
nof++;
}
}
return (nof);
}
/* * * * * * * * * * * * * * * * * * * * */
/* Обработка аргументов командной строки, */
/* инициация начальных запросов на чтение, */
/* ожидание завершения обработки файлов, */
/* вывод результатов */
/* * * * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
int dup_stdin = 0; /* Признак повторного указания */
/* стандартного ввода */
int i;
/* Сведем случай с отсутствием аргументов к общему, */
/* воспользовавшись одним из немногих */
/* стандартизованных файлов */
if (argc == 1) {
argv [0] = "-";
} else {
argv [0] = "/dev/null";
}
/* Зарезервируем память, откроем заданные файлы */
/* и инициируем начальные запросы на чтение */
assert ((lio_ptr = (struct aiocb **) malloc
(sizeof (struct aiocb *) * argc)) != NULL);
for (i = 0; i < argc; i++) {
assert ((lio_ptr [i] = (struct aiocb *) malloc
(sizeof (struct aiocb))) != NULL);
if (strcmp (argv [i], "-") == 0) {
if (dup_stdin == 0) {
lio_ptr [i]->aio_fildes = STDIN_FILENO;
dup_stdin = 1;
} else {
lio_ptr [i]->aio_fildes = fcntl (STDIN_FILENO,
F_DUPFD, 0);
}
} else if ((lio_ptr [i]->aio_fildes = open
(argv [i], O_RDONLY)) == -1) {
perror ("OPEN");
free (lio_ptr [i]);
lio_ptr [i] = NULL;
continue;
}
lio_ptr [i]->aio_offset = 0;
assert ((lio_ptr [i]->aio_buf = malloc (BUFSIZ)) != NULL);
lio_ptr [i]->aio_nbytes = BUFSIZ;
lio_ptr [i]->aio_reqprio = 0;
lio_ptr [i]->aio_sigevent.sigev_notify = SIGEV_THREAD;
lio_ptr [i]->aio_sigevent.sigev_signo = SIGRTMIN;
lio_ptr [i]->aio_sigevent.sigev_value.sival_int = i;
lio_ptr [i]->aio_sigevent.sigev_notify_function =
end_of_aio_op;
lio_ptr [i]->aio_sigevent.sigev_notify_attributes =
NULL;
/* Запрос готов, можно отправлять его на выполнение */
(void) aio_read (lio_ptr [i]);
} /* for (i < argc) */
/* Дождемся завершения обработки всех указанных */
/* в командной строке файлов */
while (n_open_files (argc) > 0) {
(void) pthread_cond_wait (open_files_cond,
nlines_mutex);
}
printf ("%ld\n", nlines_total);
return 0;
}
В модифицированном варианте можно отметить применение pthread_cond_signal(). Обратим внимание на то, что в этот момент вызывающий поток не является владельцем мьютекса, ассоциированного с
В стандарте POSIX-2001 предусмотрена только
Если операционная система знает, что приложение осуществляет
Приложение может проинформировать ОС и о том, что данные не подлежат повторному использованию. Следовательно, их не нужно
Описанные возможности реализуют функции posix_fadvise(), posix_fallocate(), posix_madvise() и posix_memalign() (см. листинг 7.9).
#include <fcntl.h>
int posix_fadvise (int fd,
off_t offset,
size_t len,
int advice);
#include <fcntl.h>
int posix_fallocate (int fd,
off_t offset,
size_t len);
#include <sys/mman.h>
int posix_madvise (void *addr,
size_t len,
int advice);
#include <stdlib.h>
int posix_memalign (void **memptr,
size_t alignment,
size_t size);
Функция posix_fadvise() информирует реализацию об ожидаемом fd, которая начинается с позиции offset и имеет длину len байт (если значение аргумента len равно нулю, рекомендация распространяется до конца файла).
Ожидаемое advice, который может принимать следующие значения.
POSIX_FADV_NORMAL
Подразумеваемое
POSIX_FADV_SEQUENTIAL
Предполагается, что приложение будет осуществлять доступ к указанной части файла
POSIX_FADV_RANDOM
Специфицирует
POSIX_FADV_WILLNEED
Предполагается, что данные из указанной части файла скоро понадобятся.
POSIX_FADV_DONTNEED
Предполагается, что данные из указанной части файла в ближайшее время не понадобятся.
POSIX_FADV_NOREUSE
Специфицирует
Нормальный результат функции posix_fadvise() равен нулю; при обнаружении ошибки возвращается ее номер.
Функция posix_fallocate()
Если сумма ( offset + len ) превышает текущий размер файла, он будет соответственно увеличен; в противном случае размер не изменится.
Влияние предыдущих вызовов posix_fadvise() на стратегию выделения долговременной памяти зависит от реализации.
Функция posix_madvise() информирует реализацию об ожидаемом addr и имеет длину len байт. Подобная информация может оказаться полезной, если в указанную область памяти
Ожидаемое advice, который может принимать следующие значения.
POSIX_MADV_NORMAL
Подразумеваемое
POSIX_MADV_SEQUENTIAL
Предполагается, что приложение будет осуществлять доступ к указанной части адресного пространства последовательно, от меньших адресов к большим.
POSIX_MADV_RANDOM
Специфицирует
POSIX_MADV_WILLNEED
Предполагается, что данные из указанной части адресного пространства скоро понадобятся.
POSIX_MADV_DONTNEED
Предполагается, что данные из указанной части адресного пространства в ближайшее время не понадобятся.
Функция posix_memalign() служит для size с начальной границей, выровненной в соответствии со значением аргумента alignment. Результирующий адрес записывается по указателю memptr. Значение alignment должно быть кратным величине sizeof (void *), которая является степенью двойки.
Область памяти, posix_memalign(), в дальнейшем можно освободить с помощью функции free().
При <limits.h>, которые можно опросить, обращаясь к функции pathconf().
POSIX_ALLOC_SIZE_MIN
Минимальное число байт долговременной памяти, реально выделяемое для любой части файла.
POSIX_REC_MIN_XFER_SIZE
Минимальное рекомендуемое число передаваемых байт при обмене данными с файлами. Рекомендуется также, чтобы и смещение передаваемой порции данных от начала файла было кратным POSIX_REC_MIN_XFER_SIZE.
POSIX_REC_MAX_XFER_SIZE
Максимальное рекомендуемое число передаваемых байт при обмене данными с файлами.
POSIX_REC_INCR_XFER_SIZE
Рекомендуемое приращение числа передаваемых байт при обмене данными с файлами (в диапазоне от POSIX_REC_MIN_XFER_SIZE до POSIX_REC_MAX_XFER_SIZE ).
POSIX_REC_XFER_ALIGN
Рекомендуемое значение для выравнивания границы
Применение функций
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа подсчитывает сумму байт в файле – */
/* аргументе командной строки, */
/* пытаясь оптимизировать чтение данных с помощью */
/* функций */
/* рекомендательных интерфейсов */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#define _XOPEN_SOURCE 600
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main (int argc, char *argv []) {
int fd; /* Дескриптор файла-аргумента */
long sum = 0; /* Сумма байт в файле */
char *pbuf; /* Указатель на буфер обмена */
ssize_t nb; /* Число прочитанных байт */
int i;
if (argc != 2) {
fprintf (stderr, "Использование: %s имя_файла\n",
argv [0]);
return (1);
}
/* Откроем заданный файл на чтение */
if ((fd = open (argv [1], O_RDONLY)) == -1) {
perror ("OPEN");
return (2);
}
/* Опишем дисциплину работы с файлом */
if (((errno = posix_fadvise (fd, 0, 0,
POSIX_FADV_SEQUENTIAL)) != 0) || ((errno =
posix_fadvise (fd, 0, 0, POSIX_FADV_NOREUSE)) != 0)) {
perror ("POSIX_FADVISE");
}
/* Зарезервируем память под буфер обмена, */
/* следуя рекомендациям */
/* на выравнивание и размер. */
/* Предполагается, что значение BUFSIZ */
/* кратно всем нужным величинам. */
if ((errno = posix_memalign ((void **) pbuf,
BUFSIZ, BUFSIZ)) != 0) {
perror ("POSIX_MEMALIGN");
return (errno);
}
/* Прочитаем файл и обработаем содержащиеся в нем данные */
while ((nb = read (fd, pbuf, BUFSIZ)) > 0) {
for (i = 0; i < nb; i++) {
sum += pbuf [i];
}
}
printf ("%ld\n", sum);
free (pbuf);
return (close (fd));
}
В этой программе задействовано несколько оптимизирующих факторов. Во-первых, посредством вызовов posix_fadvise() системе сообщается, что ко всему файлу (от начала до конца) будет осуществляться только
Средства
Вообще говоря,
Операция
За исключением параллельной работы с инициировавшим их приложением и интерфейсных отличий, read(), write(), lseek() и fsync(). Выдать
Параллельные асинхронные и синхронные операции обмена с одним и тем же файлом изменяют его так, как если бы они выполнялись последовательно.
После завершения
"Жизненный цикл"
После завершения каждого из этапов приложение получает
Пока операция не завершена, ее EINPROGRESS. Теоретически, приложение может дожидаться завершения асинхронной операции, периодически опрашивая EINPROGRESS.
Из общих соображений следует, что если есть возможность
Полезно иметь возможность за один вызов
Отметим, что потенциальное
Наиболее важным объектом, обслуживающим aiocb со следующими полями.
int aio_fildes; // Файловый дескриптор
off_t aio_offset; // Позиция в файле
volatile void *aio_buf; // Адрес буфера
size_t aio_nbytes; // Число передаваемых
// байт
int aio_reqprio; // Величина понижения
// приоритета
struct sigevent aio_sigevent; // Номер и
// значение
// сигнала
int aio_lio_opcode; // Запрошенная
// операция
Значением элемента aio_fildes является
Элемент aio_offset задает (абсолютную) O_APPEND или заданное дескриптором устройство не поддерживает позиционирования, операции записи
Поля aio_buf и aio_nbytes имеют тот же смысл, что и соответствующие аргументы функций read() и write().
Если реализация поддерживает aio_reqprio позволяет понизить (но не повысить) AIO_PRIO_DELTA_MAX включительно; оно вычитается из
Элемент aio_sigevent, имеющий структурный тип sigevent, определяет aio_sigevent.sigev_notify равно SIGEV_NONE, никаких
Элемент aio_lio_opcode используется только в
EINPROGRESS. Разумеется, приложение не должно модифицировать в это время структуру aiocb.
Основными
#include <aio.h> int aio_read (struct aiocb *aiocbp); int aio_write (struct aiocb *aiocbp);
Функция aio_read() инициирует aiocbp->aio_nbytes байт из файла, ассоциированного с дескриптором aiocbp->aio_fildes, начиная с абсолютной позиции aiocbp->aio_offset, в aiocbp->aio_buf. Возврат из функции произойдет тогда, когда -1 (в совокупности с errno ) свидетельствует об ошибке (например, исчерпаны ресурсы и
Функция aio_write() осуществляет соответствующие действия для записи данных. Если для aiocbp->aio_fildes установлен флаг O_APPEND,
Функция lio_listio() (см. листинг 7.2) позволяет за один вызов инициировать (
#include <aio.h>
int lio_listio (
int mode, struct aiocb *restrict const
listio [restrict], int nent,
struct sigevent *restrict sigev);
Элементы массива listio [] специфицируют отдельные aio_lio_opcode указуемых структур типа aiocb могут располагаться значения LIO_READ ( LIO_WRITE ( LIO_NOP (пустой aio_read() или aio_write() ). listio [] задается аргументом nent.
mode определяет LIO_WAIT ) или LIO_NOWAIT ). В первом случае возврат из вызова lio_listio() произойдет только после завершения всех заказанных sigev игнорируется. Во втором случае возврат из lio_listio() произойдет немедленно, а по завершении всех sigev будет сгенерировано асинхронное
Нормальный результат выполнения функции lio_listio() равен нулю, но для двух описанных случаев он имеет разный смысл – успешное завершение всех aio_return() и aio_error() (см. листинг 7.3).
#include <aio.h>
ssize_t aio_return (
struct aiocb *aiocbp);
int aio_error (
const struct aiocb *aiocbp);
Аргументом этих функций служит aio_return() выдает значение, которое вернула бы соответствующая функция обычного ввода/вывода (если aio_return() можно применить ровно один раз; последующие вызовы aio_return() и aio_error() могут завершиться неудачей. (Конечно, если в той же указуемой структуре типа aiocb сформировать новый aio_return() и aio_error() появляется снова.)
Результатом вызова aio_error() служит значение, которое установила бы в переменную errno соответствующая функция обычного ввода/вывода (или, если операция еще не завершена, результат равен EINPROGRESS ). Если aio_error() вернет нулевой результат. Функцию aio_error() к
Ожидающие обработки aio_cancel() (см. листинг 7.4).
#include <aio.h>
int aio_cancel (int fildes,
struct aiocb *aiocbp);
Функция aio_cancel() пытается аннулировать один (если значение аргумента aiocbp отлично от NULL ) или все ожидающие обработки fildes. После ECANCELED, а в качестве -1.
Если
Результат вызова aio_cancel() равен AIO_CANCELED, если все указанные AIO_NOTCANCELED (а со статусом lio_listio(), нужно разбираться индивидуально, пользуясь функцией aio_error() ). Результат AIO_ALLDONE свидетельствует о том, что выполнение всех указанных aio_cancel() равен -1.
Согласно стандарту POSIX-2001, списком можно не только инициировать aio_suspend() (см. листинг 7.5).
#include <aio.h>
int aio_suspend (
const struct aiocb *const listio [],
int nent,
const struct timespec *timeout);
Поток управления, вызвавший aio_suspend(), приостанавливает выполнение на время, не большее, чем задано аргументом timeout, до тех пор пока не завершится по крайней мере одна из listio ( nent – число его элементов), или
Нулевой результат свидетельствует о завершении по крайней мере одной из aio_error() и aio_return().
#include <unistd.h>
void sync (void);
#include <unistd.h>
int fsync (int fildes);
#include <unistd.h>
int fdatasync (int fildes);
#include <aio.h>
int aio_fsync (int op,
struct aiocb *aiocbp);
Функция имеет глобальный характер – она планирует запись в
Функция fsync() обеспечивает fildes. Более того, после успешного возврата из функции (с нулевым результатом) окажутся выполненными все стоявшие в
Функция fdatasync() аналогична fsync(), но гарантируется лишь
aio_fsync() осуществляет op, равном O_DSYNC, эквивалентно вызову fdatasync (aiocbp->aio_fildes), а при op, равном O_SYNC, – fsync (aiocbp->aio_fildes). Как и для других aio_error() и aio_return(), а после завершения генерируется асинхронное aiocbp->aio_sigevent. Все остальные поля указуемой структуры типа aiocb игнорируются
Проиллюстрируем использование функций
/* * * * * * * * * * * * * * * * * * */
/* Программа подсчитывает суммарное */
/* число строк – в файлах аргументах */
/* командной строки. */
/* Если аргументы отсутствуют или в */
/* качестве имени задан минус, */
/* читается стандартный ввод. */
/* * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <aio.h>
#include <assert.h>
#include <errno.h>
int main (int argc, char *argv []) {
/* Указатель на массив */
/* указателей на управляющие */
/* блоки запросов операций */
/* асинхронного чтения */
struct aiocb **lio_ptr;
// Общее число строк в файлах
long nlines = 0;
// Признак повторного указания
// стандартного ввода
int dup_stdin = 0;
int i;
/* Сведем случай с отсутствием */
/* аргументов к общему, */
/* воспользовавшись одним из */
/* немногих стандартизованных */
/* файлов */
if (argc == 1) {
argv [0] = "-";
} else {
argv [0] = "/dev/null";
}
/* Зарезервируем память, */
/* откроем заданные файлы */
/* и сформируем начальный список */
/* запросов на чтение */
assert ((lio_ptr =
(struct aiocb **) malloc
(sizeof (struct aiocb *) * argc))
!= NULL);
for (i = 0; i < argc; i++) {
assert ((lio_ptr [i] =
(struct aiocb *) malloc
(sizeof (struct aiocb)))
!= NULL);
if (strcmp (argv [i], "-") == 0) {
if (dup_stdin == 0) {
lio_ptr [i]->aio_fildes =
STDIN_FILENO;
dup_stdin = 1;
} else {
lio_ptr [i]->aio_fildes =
fcntl (STDIN_FILENO,
F_DUPFD, 0);
}
} else
if ((lio_ptr [i]->aio_fildes =
open (argv [i],
O_RDONLY)) == -1) {
perror ("OPEN");
free (lio_ptr [i]);
lio_ptr [i] = NULL;
continue;
}
lio_ptr [i]->aio_offset = 0;
assert ((lio_ptr [i]->aio_buf =
malloc(BUFSIZ)) != NULL);
lio_ptr [i]->aio_nbytes = BUFSIZ;
lio_ptr [i]->aio_reqprio = 0;
lio_ptr
[i]->aio_sigevent.sigev_notify
= SIGEV_NONE;
lio_ptr [i]->aio_lio_opcode =
LIO_READ;
} /* for (i < argc) */
/* Поехали ... */
assert (lio_listio (LIO_NOWAIT,
lio_ptr, argc,
lio_ptr [0]->aio_sigevent)
== 0);
/* Будем ждать завершения */
/* операций ввода/вывода, */
/* обрабатывать прочитанные */
/* данные и */
/* инициировать новые запросы */
/* на чтение */
while (aio_suspend ((
const struct aiocb **)
lio_ptr,
argc, NULL) == 0) {
/* Выясним, какой запрос */
/* и как выполнен */
/* Число недочитанных файлов */
int nreqs = 0;
for (i = 0; i < argc; i++) {
if (lio_ptr [i] == NULL) {
continue;
}
/* Есть обслуживаемые */
/* запросы */
nreqs++;
if (aio_error (lio_ptr [i])
== EINPROGRESS) {
continue;
}
{
// Запрос выполнен
// Число прочитанных байт
ssize_t nb;
if ((nb = aio_return
(lio_ptr [i])) <= 0) {
/* Дошли до конца файла*/
/* или чтение */
/* завершилось ошибкой */
(void) close(lio_ptr
[i]->aio_fildes);
free ((void *) lio_ptr
[i]->aio_buf);
free (lio_ptr [i]);
lio_ptr [i] = NULL;
nreqs--;
} else {
/* Обработаем прочитанные */
/* данные */
/* Текущее начало поиска */
/* перевода строки */
char *p;
/* Позиция, где нашли */
/* перевод строки */
char *p1;
p = (char *) lio_ptr [i]->aio_buf;
while ((p1 =
(char *) memchr
(p, '\n',
nb - (p – (char *)
lio_ptr
[i]->aio_buf)))
!= NULL) {
nlines++;
p = p1 + 1;
}
/* Инициируем новый */
/* запрос на чтение */
lio_ptr [i]->aio_offset
+= nb;
(void) aio_read
(lio_ptr [i]);
}
}
} /* for (i < argc) */
/* Остались недочитанные */
/* файлы? */
if (nreqs == 0) {
break;
}
} /* while */
printf ("%ld\n", nlines);
return 0;
}
Отметим списочную форму начального представления и aiocb (функция lio_listio() игнорирует такие элементы) и в качестве аргумента timeout функции aio_suspend() (означает бесконечное
Второй вариант решения той же задачи (см. листинг 7.8) демонстрирует другой
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа подсчитывает суммарное число строк */
/* в файлах – аргументах командной строки. */
/* Если аргументы отсутствуют или в качестве имени */
/* задан минус, читается стандартный ввод. */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <aio.h>
#include <pthread.h>
#include <assert.h>
/* Указатель на массив указателей на управляющие */
/* блоки запросов операций асинхронного чтения */
static struct aiocb **lio_ptr;
/* Общее число строк в файлах */
static long nlines_total = 0;
/* Переменная условия, на которой ждут */
/* окончания обработки всех файлов */
static pthread_cond_t open_files_cond =
PTHREAD_COND_INITIALIZER;
/* Мьютекс, охраняющий доступ */
/* к переменной условия и nlines_total */
static pthread_mutex_t nlines_mutex =
PTHREAD_MUTEX_INITIALIZER;
/* * * * * * * * * * * * * * */
/* Функция, вызываемая */
/* при завершении операции */
/* асинхронного ввода/вывода */
/* * * * * * * * * * * * * * */
static void end_of_aio_op (union sigval sg_vl) {
int no; /* Номер выполненного запроса */
/* в общем списке */
ssize_t nb; /* Число прочитанных байт */
long nlines = 0; /* Число строк в одной */
/* прочитанной порции */
no = sg_vl.sival_int;
if ((nb = aio_return (lio_ptr [no])) <= 0) {
/* Дошли до конца файла */
/* или чтение завершилось ошибкой */
(void) close (lio_ptr [no]->aio_fildes);
free ((void *) lio_ptr [no]->aio_buf);
free (lio_ptr [no]);
lio_ptr [no] = NULL;
(void) pthread_cond_signal (open_files_cond);
} else {
/* Обработаем прочитанные данные */
char *p; /* Текущее начало поиска перевода строки */
char *p1; /* Позиция, где нашли перевод строки */
p = (char *) lio_ptr [no]->aio_buf;
while ((p1 = (char *) memchr (p, '\n',
nb - (p – (char *) lio_ptr [no]->aio_buf)))
!= NULL) {
nlines++;
p = p1 + 1;
}
/* Прибавим локальную сумму к общей */
(void) pthread_mutex_lock (nlines_mutex);
nlines_total += nlines;
(void) pthread_mutex_unlock (nlines_mutex);
/* Инициируем новый запрос на чтение */
lio_ptr [no]->aio_offset += nb;
(void) aio_read (lio_ptr [no]);
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция проверяет, сколько осталось открытых файлов */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
static int n_open_files (int nfiles) {
int nof = 0; /* Число открытых файлов */
int i;
for (i = 0; i < nfiles; i++) {
if (lio_ptr [i] != NULL) {
nof++;
}
}
return (nof);
}
/* * * * * * * * * * * * * * * * * * * * */
/* Обработка аргументов командной строки, */
/* инициация начальных запросов на чтение, */
/* ожидание завершения обработки файлов, */
/* вывод результатов */
/* * * * * * * * * * * * * * * * * * * * */
int main (int argc, char *argv []) {
int dup_stdin = 0; /* Признак повторного указания */
/* стандартного ввода */
int i;
/* Сведем случай с отсутствием аргументов к общему, */
/* воспользовавшись одним из немногих */
/* стандартизованных файлов */
if (argc == 1) {
argv [0] = "-";
} else {
argv [0] = "/dev/null";
}
/* Зарезервируем память, откроем заданные файлы */
/* и инициируем начальные запросы на чтение */
assert ((lio_ptr = (struct aiocb **) malloc
(sizeof (struct aiocb *) * argc)) != NULL);
for (i = 0; i < argc; i++) {
assert ((lio_ptr [i] = (struct aiocb *) malloc
(sizeof (struct aiocb))) != NULL);
if (strcmp (argv [i], "-") == 0) {
if (dup_stdin == 0) {
lio_ptr [i]->aio_fildes = STDIN_FILENO;
dup_stdin = 1;
} else {
lio_ptr [i]->aio_fildes = fcntl (STDIN_FILENO,
F_DUPFD, 0);
}
} else if ((lio_ptr [i]->aio_fildes = open
(argv [i], O_RDONLY)) == -1) {
perror ("OPEN");
free (lio_ptr [i]);
lio_ptr [i] = NULL;
continue;
}
lio_ptr [i]->aio_offset = 0;
assert ((lio_ptr [i]->aio_buf = malloc (BUFSIZ)) != NULL);
lio_ptr [i]->aio_nbytes = BUFSIZ;
lio_ptr [i]->aio_reqprio = 0;
lio_ptr [i]->aio_sigevent.sigev_notify = SIGEV_THREAD;
lio_ptr [i]->aio_sigevent.sigev_signo = SIGRTMIN;
lio_ptr [i]->aio_sigevent.sigev_value.sival_int = i;
lio_ptr [i]->aio_sigevent.sigev_notify_function =
end_of_aio_op;
lio_ptr [i]->aio_sigevent.sigev_notify_attributes =
NULL;
/* Запрос готов, можно отправлять его на выполнение */
(void) aio_read (lio_ptr [i]);
} /* for (i < argc) */
/* Дождемся завершения обработки всех указанных */
/* в командной строке файлов */
while (n_open_files (argc) > 0) {
(void) pthread_cond_wait (open_files_cond,
nlines_mutex);
}
printf ("%ld\n", nlines_total);
return 0;
}
В модифицированном варианте можно отметить применение pthread_cond_signal(). Обратим внимание на то, что в этот момент вызывающий поток не является владельцем мьютекса, ассоциированного с
В стандарте POSIX-2001 предусмотрена только
Если операционная система знает, что приложение осуществляет
Приложение может проинформировать ОС и о том, что данные не подлежат повторному использованию. Следовательно, их не нужно
Описанные возможности реализуют функции posix_fadvise(), posix_fallocate(), posix_madvise() и posix_memalign() (см. листинг 7.9).
#include <fcntl.h>
int posix_fadvise (int fd,
off_t offset,
size_t len,
int advice);
#include <fcntl.h>
int posix_fallocate (int fd,
off_t offset,
size_t len);
#include <sys/mman.h>
int posix_madvise (void *addr,
size_t len,
int advice);
#include <stdlib.h>
int posix_memalign (void **memptr,
size_t alignment,
size_t size);
Функция posix_fadvise() информирует реализацию об ожидаемом fd, которая начинается с позиции offset и имеет длину len байт (если значение аргумента len равно нулю, рекомендация распространяется до конца файла).
Ожидаемое advice, который может принимать следующие значения.
POSIX_FADV_NORMAL
Подразумеваемое
POSIX_FADV_SEQUENTIAL
Предполагается, что приложение будет осуществлять доступ к указанной части файла
POSIX_FADV_RANDOM
Специфицирует
POSIX_FADV_WILLNEED
Предполагается, что данные из указанной части файла скоро понадобятся.
POSIX_FADV_DONTNEED
Предполагается, что данные из указанной части файла в ближайшее время не понадобятся.
POSIX_FADV_NOREUSE
Специфицирует
Нормальный результат функции posix_fadvise() равен нулю; при обнаружении ошибки возвращается ее номер.
Функция posix_fallocate()
Если сумма ( offset + len ) превышает текущий размер файла, он будет соответственно увеличен; в противном случае размер не изменится.
Влияние предыдущих вызовов posix_fadvise() на стратегию выделения долговременной памяти зависит от реализации.
Функция posix_madvise() информирует реализацию об ожидаемом addr и имеет длину len байт. Подобная информация может оказаться полезной, если в указанную область памяти
Ожидаемое advice, который может принимать следующие значения.
POSIX_MADV_NORMAL
Подразумеваемое
POSIX_MADV_SEQUENTIAL
Предполагается, что приложение будет осуществлять доступ к указанной части адресного пространства последовательно, от меньших адресов к большим.
POSIX_MADV_RANDOM
Специфицирует
POSIX_MADV_WILLNEED
Предполагается, что данные из указанной части адресного пространства скоро понадобятся.
POSIX_MADV_DONTNEED
Предполагается, что данные из указанной части адресного пространства в ближайшее время не понадобятся.
Функция posix_memalign() служит для size с начальной границей, выровненной в соответствии со значением аргумента alignment. Результирующий адрес записывается по указателю memptr. Значение alignment должно быть кратным величине sizeof (void *), которая является степенью двойки.
Область памяти, posix_memalign(), в дальнейшем можно освободить с помощью функции free().
При <limits.h>, которые можно опросить, обращаясь к функции pathconf().
POSIX_ALLOC_SIZE_MIN
Минимальное число байт долговременной памяти, реально выделяемое для любой части файла.
POSIX_REC_MIN_XFER_SIZE
Минимальное рекомендуемое число передаваемых байт при обмене данными с файлами. Рекомендуется также, чтобы и смещение передаваемой порции данных от начала файла было кратным POSIX_REC_MIN_XFER_SIZE.
POSIX_REC_MAX_XFER_SIZE
Максимальное рекомендуемое число передаваемых байт при обмене данными с файлами.
POSIX_REC_INCR_XFER_SIZE
Рекомендуемое приращение числа передаваемых байт при обмене данными с файлами (в диапазоне от POSIX_REC_MIN_XFER_SIZE до POSIX_REC_MAX_XFER_SIZE ).
POSIX_REC_XFER_ALIGN
Рекомендуемое значение для выравнивания границы
Применение функций
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Программа подсчитывает сумму байт в файле – */
/* аргументе командной строки, */
/* пытаясь оптимизировать чтение данных с помощью */
/* функций */
/* рекомендательных интерфейсов */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */
#define _XOPEN_SOURCE 600
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main (int argc, char *argv []) {
int fd; /* Дескриптор файла-аргумента */
long sum = 0; /* Сумма байт в файле */
char *pbuf; /* Указатель на буфер обмена */
ssize_t nb; /* Число прочитанных байт */
int i;
if (argc != 2) {
fprintf (stderr, "Использование: %s имя_файла\n",
argv [0]);
return (1);
}
/* Откроем заданный файл на чтение */
if ((fd = open (argv [1], O_RDONLY)) == -1) {
perror ("OPEN");
return (2);
}
/* Опишем дисциплину работы с файлом */
if (((errno = posix_fadvise (fd, 0, 0,
POSIX_FADV_SEQUENTIAL)) != 0) || ((errno =
posix_fadvise (fd, 0, 0, POSIX_FADV_NOREUSE)) != 0)) {
perror ("POSIX_FADVISE");
}
/* Зарезервируем память под буфер обмена, */
/* следуя рекомендациям */
/* на выравнивание и размер. */
/* Предполагается, что значение BUFSIZ */
/* кратно всем нужным величинам. */
if ((errno = posix_memalign ((void **) pbuf,
BUFSIZ, BUFSIZ)) != 0) {
perror ("POSIX_MEMALIGN");
return (errno);
}
/* Прочитаем файл и обработаем содержащиеся в нем данные */
while ((nb = read (fd, pbuf, BUFSIZ)) > 0) {
for (i = 0; i < nb; i++) {
sum += pbuf [i];
}
}
printf ("%ld\n", sum);
free (pbuf);
return (close (fd));
}
В этой программе задействовано несколько оптимизирующих факторов. Во-первых, посредством вызовов posix_fadvise() системе сообщается, что ко всему файлу (от начала до конца) будет осуществляться только
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.