KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Марк Митчелл - Программирование для Linux. Профессиональный подход

Марк Митчелл - Программирование для Linux. Профессиональный подход

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

  MAP_SHARED, fd, 0);

 close(fd);


 /* Чтение целого числа и вывод его на экран. */

 sscanf(file_memory, "%d", &integer);

 printf("value: %dn", integer);

 /* Удваиваем число и записываем его обратно в файл. */

 sprintf((char*)file_memory, "%dn", 2 * integer);

 /* Освобождение памяти (не обязательно, так как программа

    завершается). */

 munmap(file_memory, FILE_LENGTH);

 return 0;

}

Программа mmap-read читает число из файла, а затем удваивает его и записывает обратно в файл. Сначала файл открывается для чтения/записи. Поскольку предполагается, что файл содержит число, проверка с помощью функции lseek(), как в предыдущей программе, не требуется. Чтение содержимого памяти и его анализ выполняет функция lseek(). Функция sprintf() форматирует число и записывает его в память.

Ниже показан пример запуска обеих программ. Им на вход передается файл /tmp/integer-file.

% ./mmap-write /tmp/integer-file

% cat /tmp/integer-file

42

% ./mmap-read /tmp/integer-file

value: 42

% cat /tmp/integer-file

84

Обратите внимание: значение 42 оказалось записано в файл на диске, хотя функция write() не вызывалась. Последующее чтение файла осуществлялось без функции read(). Целое число записывалось в файл и извлекалось из него в текстовом виде (с помощью функций sprintf() и sscanf()). Это сделано исключительно в демонстрационных целях. В действительности отображаемый файл может содержать не только текст, но и двоичные данные.

5.3.3. Совместный доступ к файлу

Процессы могут взаимодействовать друг с другом через области отображаемой памяти, связанные с одним и тем же файлом. Если в функции mmap() указать флаг MAP_SHARED, все данные, заносимые в отображаемую память, будут немедленно записываться в файл, т.е. становиться видимыми другим процессам. При отсутствии этого флага ОС Linux может осуществлять предварительную буферизацию записываемых данных.

С другой стороны, с помощью функции msync() можно заставить операционную систему перенести содержимое буфера в дисковый файл. Первые два параметра этой функции такие же, как и в функции munmap(). Третий параметр может содержать следующие флаги.

■ MS_ASYNC. Операция обновления ставится в очередь планировщика и будет выполнена, но не обязательно до того, как функция завершится.

■ MS_SYNC. Операция обновления выполняется немедленно. До ее завершения функция блокируется. Флаги MS_ASYNC и MS_SYNC нельзя указывать одновременно.

■ MS_INVALIDATE. Все остальные отображаемые области помечаются как недействительные и подлежащие обновлению.

Следующая функция обновляет файл, область отображения которого начинается с адреса mem_addr и имеет длину mem_length:

msync(mem_addr, mem_length, MS_SYNC | MS_INVALIDATE);

Как и в случае совместного использования сегментов памяти, при работе с отображаемыми областями необходимо придерживаться определенного порядка во избежание конкуренции. Например, можно создать семафор, который позволит только одному процессу обращаться к отображаемой памяти в конкретный момент времени. Можно также воспользоваться функцией fcntl() и поставить на файл блокировку чтения или записи (об этом рассказывается в разделе 8.3, "Функция fcntl(): блокировки и другие операции над файлами").

5.3.4. Частные отображаемые области

Если в функции mmap() указан флаг MAP_PRIVATE, отображаемая область создается в режиме "копирование при записи". Любые операции записи в эту область имеют эффект только в адресном пространстве текущего процесса. Другие процессы, связанные с тем же самым отображаемым файлом, не узнают об изменениях. Изменения заносятся не на страницу, доступную всем процессам, а в частную копию этой страницы. Все последующие операции чтения и записи в данном процессе будут выполняться по отношению к этой копии.

5.3.5. Применения функции mmap()

Функция mmap() может использоваться не только для организации взаимодействия процессов. Часто она выступает в качестве замены функциям read() и write(). Например, вместо того чтобы непосредственно загружать содержимое файла в память, программа может связать файл с отображаемой памятью и сканировать его путем обращения к памяти. Иногда это удобнее и быстрее, чем выполнять операции файлового ввода-вывода.

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

Другой удобный прием — отображение в памяти файла /dev/zero (описывается в разделе 6.5.2, "/dev/zero"). Этот файл ведет себя так, как будто содержит бесконечное число нулевых байтов. Операции записи в него игнорируются. Описываемый прием часто применяется в пользовательских функциях выделения памяти, которым необходимо инициализировать блоки памяти.

5.4. Каналы

Канал — это коммуникационное устройство, допускающее однонаправленное взаимодействие. Данные, записываемые на "входном" конце канала, читаются на "выходном" его конце. Каналы являются последовательными устройствами: данные всегда читаются в том порядке, в котором они были записаны. Канал обычно используется как средство связи между двумя потоками одного процесса или между родительским и дочерним процессами.

В интерпретаторе команд канал создается оператором |. Например, показанная ниже команда заставляет интерпретатор запустить два дочерних процесса, один — для программы ls, а второй — для программы less:

% ls | less

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

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

5.4.1. Создание каналов

Канал создается с помощью функции pipe(). Ей необходимо передать массив из двух целых чисел. В элементе с индексом 0 функция сохраняет дескриптор файла, соответствующего выходному концу канала, а в элементе с индексом 1 сохраняется дескриптор файла, соответствующего входному концу канала. Рассмотрим следующий фрагмент программы

int pipe_fds[2];

int read_fd;

int write_fd;


pipe(pipe_fds);

read_fd = pipe_fds[0];

write_fd = pipe_fds[1];

Данные, записываемые в файл write_fd, могут быть прочитаны из файла read_fd.

5.4.2. Взаимодействие родительского и дочернего процессов

Функция pipe() создает два файловых дескриптора, которые действительны только в текущем процессе и его потомках. Эти дескрипторы нельзя передать постороннему процессу. Дочерний процесс получает копии дескрипторов после завершения функции fork().

В программе, показанной в листинге 5.7. родительский процесс записывает в канал строку, а дочерний процесс читает ее. С помощью функции fdopen() файловые дескрипторы приводятся к типу FILE*. Благодаря этому появляется возможность использовать высокоуровневые функции ввода-вывода, такие как printf() и fgets().

Листинг 5.7. (pipe.c) Общение с дочерним процессом посредством канала

#include <stdlib.h>

#include <stdio.h>

#include <unistd.h>


/* Запись указанного числа копий (COUNT) сообщения (MESSAGE)

   в поток (STREAM) с паузой между каждой операцией. */

void writer(const char* message, int count, FILE* stream) {

 for (; count > 0; --count) {

 /* Запись сообщения в поток с немедленным "выталкиванием"

    из буфера. */

 fprintf(stream, "%sn", message);

 fflush(stream);

 /* Небольшая пауза. */

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*