- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Основы программирования в Linux - Нейл Мэтью
Шрифт:
Интервал:
Закладка:
Вызов execl применен для вызова программы pipe4. В нем использованы следующие аргументы:
□ вызванная программа;
□ argv[0], принимающий имя программы;
□ argv[1], содержащий номер файлового дескриптора, из которого программа должна читать;
□ (char *)0, завершающий список параметров.
Программа pipe4 извлекает номер файлового дескриптора из строки аргументов и затем читает из него данные.
Чтение закрытых каналов
Прежде чем двигаться дальше, необходимо более внимательно рассмотреть файловые дескрипторы, которые открыты. До этого момента вы разрешали читающему процессу просто читать какие-то данные и завершаться, полагая, что ОС Linux уберет файлы в ходе завершения процесса.
В большинстве программ, читающих данные из стандартного ввода, это делается несколько иначе, чем в виденных вами до сих пор примерах. Обычно программы не знают, сколько данных они должны считать, поэтому они, как правило, выполняют цикл — чтение данных, их обработка и затем снова чтение данных и так до тех пор, пока не останется данных для чтения.
Вызов read обычно будет задерживать выполнение процесса, т.е. он заставит процесс ждать до тех пор, пока не появятся данные. Если другой конец канала был закрыт, следовательно, нет ни одного процесса, имеющего канал для записи, и вызов read блокируется. Поскольку это не очень полезно, вызов read, пытающийся читать из канала, не открытого для записи, возвращает 0 вместо блокирования. Это позволит читающему процессу обнаружить канальный эквивалент метки "конец файла" и действовать соответствующим образом. Учтите, что это не то же самое, что чтение некорректного дескриптора файла, которое вызов read считает ошибкой и обозначает возвратом -1.
Если вы применяете канал с вызовом fork, есть два файловых дескриптора, которые можно использовать для записи в канал: один в родительском, а другой в дочернем процессах. Вы должны закрыть файловые дескрипторы записи в канал в обоих этих процессах, прежде чем канал будет считаться закрытым и вызов read для чтения из канала завершится аварийно. Мы рассмотрим пример этого позже, когда вернемся к данной теме, для того чтобы подробно обсудить флаг O_NONBLOCK и каналы FIFO.
Каналы, применяемые как стандартные ввод и вывод
Теперь, когда вы знаете, как заставить вызов read, примененный к пустому каналу, завершиться аварийно, можно рассмотреть более простой метод соединения каналом двух процессов. Вы устраиваете так, что у одного из файловых дескрипторов канала будет известное значение, обычно стандартный ввод, 0, или стандартный вывод, 1. Его немного сложнее установить в родительском процессе, но при этом значительно упрощается программа дочернего процесса.
Одно неоспоримое достоинство заключается в том, что вы можете вызывать стандартные программы, которым не нужен файловый дескриптор как параметр. Для этого вам следует применить функцию dup, с которой вы встречались в главе 3. Существуют две тесно связанные версии функции dup, которые объявляются следующим образом:
#include <unistd.h>
int dup(int file_descriptor);
int dup2(int file_descriptor_one, int file_descriptor_two);
Назначение вызова dup — открыть новый дескриптор файла, немного похоже на то, как это делает вызов open. Разница в том, что файловый дескриптор, созданный dup, ссылается на тот же файл (или канал), что и существующий файловый дескриптор. В случае вызова dup новый файловый дескриптор всегда имеет самый маленький доступный номер, а в случае dup2 — первый доступный дескриптор, больший чем значение параметра file_descriptor_two.
ПримечаниеТого же эффекта, что и применение вызовов dup и dup2 можно добиться, применяя более общий вызов fcntl с командой F_DUPFD. Как говорилось, вызов dup легче использовать, поскольку он разработан специально для создания дубликатов файловых дескрипторов. Он также очень широко применяется, поэтому вы встретите его гораздо чаще в существующих программах, чем вызов fcntl и команду F_DUPFD.
Итак, как же dup помогает в обмене данными между процессами? Хитрость кроется в знании того, что дескриптор стандартного файла ввода всегда 0 и что dup всегда возвращает новый файловый дескриптор, применяя наименьший доступный номер. Сначала закрыв дескриптор 0, а затем вызвав dup, вы получите новый файловый дескриптор с номером 0. Поскольку новый файловый дескриптор — это дубликат существующего, стандартный ввод изменится и получит доступ к файлу или каналу, файловый дескриптор которого вы передали в функцию dup. В результате вы создадите два файловых дескриптора, которые ссылаются на один и тот же файл или канал и один из них будет стандартным вводом.
Управление файловым дескриптором с помощью close и dupЛегче всего понять, что происходит, когда вы закрываете файловый дескриптор 0 и затем вызываете dup, если рассмотреть состояние первых четырех файловых дескрипторов, изменяющихся последовательно друг за другом (табл. 13.1).
Таблица 13.1
Номер файлового дескриптора Первоначально После закрытия файлового дескриптора 0 После вызова dup 0 Стандартный ввод {closed} Файловый дескриптор канала 1 Стандартный вывод Стандартный вывод Стандартный вывод 2 Стандартный поток ошибок Стандартный поток ошибок Стандартный поток ошибок 3 Файловый дескриптор канала Файловый дескриптор канала Файловый дескриптор каналаА теперь выполните упражнение 13.8.
Упражнение 13.3. Каналы и dupДавайте вернемся к предыдущему примеру, но на этот раз вы измените дочернюю программу, заменив в ней файловый дескриптор stdin концом считывания read созданного вами канала. Вы также выполните некоторую реорганизацию файловых дескрипторов, чтобы дочерняя программа могла правильно определить конец данных в канале. Как обычно, мы пропустили некоторые проверки ошибок для краткости.
Превратите программу pipe3.c в pipe5.c с помощью следующего программного кода:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
int data_processed;
int file pipes[2];
const char some_data[] = "123";
pid_t fork_result;
if (pipe(file_pipes) == 0) {
fork_result = fork();
if (fork_result == (pid_t)-1) {
fprintf(stderr, "Fork failure");
exit(EXIT_FAILURE);
}
if (fork_result == (pid_t)0) {
close(0);
dup(file_pipes[0];
close(file_pipes[0]);
close(file_pipes[1]);
execlp("od", "od", "-c", (char*)0);
exit(EXIT_FAILURE);
} else {
close(file_pipes[0]);
data_processed = write(file_pipes[1], some_data,
strlen(some_data));
close(file_pipes[1]);
printf("%d — wrote %d bytesn", (int)getpid(), data_processed);
}
}
exit(EXIT_SUCCESS);
}
У этой программы следующий вывод:
$ ./pipe5
22495 - wrote 3 bytes
0000000 1 2 3
0000003
Как это работает
Как и прежде, программа создает канал, затем выполняет вызов fork, создавая дочерний процесс. В этот моменту обоих процессов, родительского и дочернего, есть файловые дескрипторы для доступа к каналу, по одному для чтения и записи, т.е. всего четыре открытых файловых дескриптора.
Давайте первым рассмотрим дочерний процесс. Он закрывает свой стандартный ввод с помощью close(0) и затем вызывает dup(file_pipes[0]). Этот вызов дублирует файловый дескриптор, связанный с концом read канала, как файловый дескриптор 0, стандартный ввод. Далее дочерний процесс закрывает исходный файловый дескриптор для чтения из канала, file_pipes[0]. Поскольку этот процесс никогда не будет писать в канал, он также закрывает файловый дескриптор для записи в канал, file_pipes[1]. Теперь у дочернего процесса единственный файловый дескриптор, связанный с каналом, файловый дескриптор 0, его стандартный ввод.
Далее дочерний процесс может применить exec для вызова любой программы, которая читает стандартный ввод. В данном случае мы используем команду od. Команда od будет ждать, когда данные станут ей доступны, как если бы она ждала ввода с терминала пользователя. В действительности без специального программного кода, позволяющего непосредственно выяснить разницу, она не будет знать, что ввод приходит из канала, а не с терминала.

