48: }
Глава 13
Расширенная обработка файлов
В Linux файлы применяются при решении большого количества задач, среди которых, например, хранение долговременных данных, организация сетей с помощью сокетов и доступ к устройствам посредством файлов устройств. Разнообразие приложений, работающих с файлами, привело к созданию множества специальных способов управления файлами. В главе 11 рассматривались наиболее распространенные действия с файлами; в настоящей же главе исследуются специализированные файловые операции. В частности, мы рассмотрим следующие вопросы: использование одновременно нескольких файлов, отображение файлов на системную память, блокировка файлов, чтение и запись вразброс.
13.1. Мультиплексирование входных и выходных данных
Многим клиент-серверным приложениям необходимо считывать входные данные или записывать выходные данные с помощью одновременно нескольких файловых дескрипторов. Например, современные Web-браузеры открывают одновременно несколько сетевых подключений, чтобы уменьшить время загрузки Web-страницы. Это позволяет им загружать множество изображений, имеющихся на большинстве Web- страниц, быстрее, чем с помощью последовательных подключений. Кроме канала межпроцессных взаимодействий (IPC), используемого графическими браузерами для связи с X-сервером, на котором они отображаются, браузеры работают с множеством файловых дескрипторов.
Браузеру легче всего обработать эти файлы, считывая и обрабатывая данные из них (системный вызов read()
в сетевом подключении, так же, как и в канале, возвращает доступные в настоящий момент данные и блокирует их только в случае неготовности). Этот подход эффективен, пока все подключения доставляют данные достаточно регулярно.
Если одно из сетевых подключений является медленным, начинают возникать проблемы. Когда браузер снова считывает из этого файла, он перестает работать, в то время как read()
блокируется в ожидании поступления данных. Не стоит и упоминать, что подобное поведение не является удобоваримым для пользователя браузера.
Для иллюстрации этих проблем рассмотрим короткую программу, считывающую из двух файлов, p1
и p2
. Для ее испытания откройте три сеанса работы с X-терминалом (или воспользуйтесь тремя виртуальными консолями). Создайте каналы под именами p1
и p2
(с помощью команды mknod
), затем запустите cat > p1
и cat > p2
в двух терминалах, одновременно запустив mpx-blocks
в третьем. После этого набирайте любой текст в каждом окно cat
и смотрите, как он появляется. Помните, что две команды cat
не будут записывать данные в каналы до конца строки.
1: /* mpx-blocks.с */
2:
3: #include <fcntl.h>
4: #include <stdio.h>
5: #include <unistd.h>
6:
7: int main(void) {
8: int fds[2];
9: char buf[4096];
10: int i;
11: int fd;
12:
13: if ((fds[0] = open('p1', O_RDONLY) ) < 0) {
14: perror('open p1');
15: return 1;
16: }
17:
18: if ( (fds[1] = open('p2', O_RDONLY)) < 0) {
19: perror('open p2');
20: return 1;
21: }
22:
23: fd = 0;
24: while (1) {
25: /* если данные доступны, прочитать и отобразить их */
26: i = read (fds[fd], buf, sizeof (buf) - 1);
27: if (i < 0) {
28: perror('read');
29: return 1;
30: } else if (!i) {
31: printf('канал закрыт
');
32: return 0;
33: }
34:
35: buf[i] = ' ';
36: printf ('чтение: %s', buf);
37:
38: /* читать из другого файлового дескриптора */
39: fd = (fd + 1) % 2;
40: }
41: }
Хотя программа mpx-blocks
может считывать одновременно из обоих каналов, это не является особо эффективным. Она считывает из каждого канала по очереди. После запуска программа читает из первого файла, пока в нем доступны данные, второй файл игнорируется вплоть до возврата из read()
для первого файла. Как только произошел возврат, первый файл игнорируется вплоть до чтения данных из второго файла. Этот метод не поддерживает гладкое мультиплексирование данных. На рис. 13.1 показана программа mpx-blocks
во время выполнения.

Рис. 13.1. Примеры запуска мультиплексной передачи
13.1.1. Неблокируемый ввод-вывод