Нейл Мэтью - Основы программирования в Linux
- Название:Основы программирования в Linux
- Автор:
- Жанр:
- Издательство:«БХВ-Петербург»
- Год:2009
- Город:Санкт-Петербург
- ISBN:978-5-9775-0289-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Нейл Мэтью - Основы программирования в Linux краткое содержание
В четвертом издании популярного руководства даны основы программирования в операционной системе Linux. Рассмотрены: использование библиотек C/C++ и стандартных средств разработки, организация системных вызовов, файловый ввод/вывод, взаимодействие процессов, программирование средствами командной оболочки, создание графических пользовательских интерфейсов с помощью инструментальных средств GTK+ или Qt, применение сокетов и др. Описана компиляция программ, их компоновка c библиотеками и работа с терминальным вводом/выводом. Даны приемы написания приложений в средах GNOME® и KDE®, хранения данных с использованием СУБД MySQL® и отладки программ. Книга хорошо структурирована, что делает обучение легким и быстрым.
Для начинающих Linux-программистов
Основы программирования в Linux - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Каналы, применяемые как стандартные ввод и вывод
Теперь, когда вы знаете, как заставить вызов read, примененный к пустому каналу, завершиться аварийно, можно рассмотреть более простой метод соединения каналом двух процессов. Вы устраиваете так, что у одного из файловых дескрипторов канала будет известное значение, обычно стандартный ввод, 0, или стандартный вывод, 1. Его немного сложнее установить в родительском процессе, но при этом значительно упрощается программа дочернего процесса.
Одно неоспоримое достоинство заключается в том, что вы можете вызывать стандартные программы, которым не нужен файловый дескриптор как параметр. Для этого вам следует применить функцию dup, с которой вы встречались в главе 3. Существуют две тесно связанные версии функции dup, которые объявляются следующим образом:
#include
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. В результате вы создадите два файловых дескриптора, которые ссылаются на один и тот же файл или канал и один из них будет стандартным вводом.
Легче всего понять, что происходит, когда вы закрываете файловый дескриптор 0 и затем вызываете dup, если рассмотреть состояние первых четырех файловых дескрипторов, изменяющихся последовательно друг за другом (табл. 13.1).
Таблица 13.1
| Номер файлового дескриптора | Первоначально | После закрытия файлового дескриптора 0 | После вызова dup |
|---|---|---|---|
| 0 | Стандартный ввод | {closed} | Файловый дескриптор канала |
| 1 | Стандартный вывод | Стандартный вывод | Стандартный вывод |
| 2 | Стандартный поток ошибок | Стандартный поток ошибок | Стандартный поток ошибок |
| 3 | Файловый дескриптор канала | Файловый дескриптор канала | Файловый дескриптор канала |
А теперь выполните упражнение 13.8.
dupДавайте вернемся к предыдущему примеру, но на этот раз вы измените дочернюю программу, заменив в ней файловый дескриптор stdin концом считывания readсозданного вами канала. Вы также выполните некоторую реорганизацию файловых дескрипторов, чтобы дочерняя программа могла правильно определить конец данных в канале. Как обычно, мы пропустили некоторые проверки ошибок для краткости.
Превратите программу pipe3.c в pipe5.c с помощью следующего программного кода:
#include
#include
#include
#include
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 bytes\n", (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будет ждать, когда данные станут ей доступны, как если бы она ждала ввода с терминала пользователя. В действительности без специального программного кода, позволяющего непосредственно выяснить разницу, она не будет знать, что ввод приходит из канала, а не с терминала.
Родительский процесс начинает с закрытия конца чтения канала, file_pipes[0], потому что он никогда не будет читать из канала. Затем он пишет данные в канал. Когда все данные записаны, родительский процесс закрывает конец записи в канал и завершается. Поскольку теперь нет файловых дескрипторов, открытых для записи в канал, программа odсможет считать три байта, записанных в канал, но последующие операции чтения далее будут возвращать 0 байтов, указывая на конец файла. Когда readвернет 0, программа odзавершится. Это аналогично выполнению команды od, введенной с терминала, и последующему нажатию комбинации клавиш + для отправки признака конца файла команде od.
Интервал:
Закладка: