Нейл Мэтью - Основы программирования в Linux
- Название:Основы программирования в Linux
- Автор:
- Жанр:
- Издательство:«БХВ-Петербург»
- Год:2009
- Город:Санкт-Петербург
- ISBN:978-5-9775-0289-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Нейл Мэтью - Основы программирования в Linux краткое содержание
В четвертом издании популярного руководства даны основы программирования в операционной системе Linux. Рассмотрены: использование библиотек C/C++ и стандартных средств разработки, организация системных вызовов, файловый ввод/вывод, взаимодействие процессов, программирование средствами командной оболочки, создание графических пользовательских интерфейсов с помощью инструментальных средств GTK+ или Qt, применение сокетов и др. Описана компиляция программ, их компоновка c библиотеками и работа с терминальным вводом/выводом. Даны приемы написания приложений в средах GNOME® и KDE®, хранения данных с использованием СУБД MySQL® и отладки программ. Книга хорошо структурирована, что делает обучение легким и быстрым.
Для начинающих Linux-программистов
Основы программирования в Linux - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
buffer[chars_read - 1] = '\0';
printf("Reading:-\n %s\n", buffer);
chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
}
pclose(read_fp);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
Выполнив эту программу, вы получите следующий вывод:
$ ./popen4
Reading:-
94
Как это работает
Программа показывает, что вызывается командная оболочка для того, чтобы развернуть popen*.с
в список всех файлов, начинающихся с popen
и заканчивающихся .с
, а также для обработки символа канала ( |
) и отправки вывода команды cat
в команду wс
. Вы вызываете командную оболочку, программы cat
и
wc
и задаете перенаправление — все в одном вызове popen
. Программа, вызвавшая команду, видит только заключительный вывод.
Вызов pipe
Вы познакомились с высокоуровневой функцией popen
, а теперь пойдем дальше и рассмотрим низкоуровневую функцию pipe
. Она предоставляет средства передачи данных между двумя программами без накладных расходов на вызов командной оболочки для интерпретации запрашиваемой команды. Эта функция также позволит вам лучше управлять чтением и записью данных.
У функции pipe
следующее объявление:
#include
int pipe(int file_descriptor[2]);
Функции pipe
передается указатель на массив из двух целочисленных файловых дескрипторов. Она заполняет массив двумя новыми файловыми дескрипторами и возвращает 0. В случае неудачи она вернет -1 и установит переменную errno
для указания причины сбоя. В интерактивном справочном руководстве Linux на странице, посвященной функций pipe
(в разделе 2 руководства), определены следующие ошибки:
□ EMFILE
— процесс использует слишком много файловых дескрипторов;
□ ENFILE
— системная таблица файлов полна;
□ EFAULT
— некорректный файловый дескриптор.
Два возвращаемых файловых дескриптора подсоединяются специальным образом. Любые данные, записанные в file_descriptor[1]
, могут быть считаны обратно из file_descriptor[0]
. Данные обрабатываются по алгоритму "первым пришел, первым обслужен", обычно обозначаемому как FIFO. Это означает, что если вы записываете байты 1
, 2
, 3
в file_descriptor[1]
, чтение из file_descriptor[0]
выполняется в следующем порядке: 1
, 2
, 3
. Этот способ отличается от стека, который функционирует по алгоритму "последним пришел, первым обслужен", который обычно называют сокращенно LIFO.
Важно уяснить, что речь идет о файловых дескрипторах, а не о файловых потоках, поэтому для доступа к данным вы должны применять низкоуровневые системные вызовы read
и write
вместо библиотечных функций потоков fread
и fwrite
.
В упражнении 13.5 приведена программа pipe1.с, которая использует вызов pipe
для создания канала.
pipe
Следующий пример — программа pipe1.c. Обратите внимание на массив file_pipes
, который передается функции pipe
как параметр.
#include
#include
#include
#include
int main() {
int data_processed;
int filepipes[2];
const char some_data[] = "123";
char buffer[BUFSIZ + 1];
memset(buffer, '\0', sizeof(buffer));
if (pipe(file_pipes) == 0) {
data_processed = write(file_pipes[1], some_data, strlen(somedata));
printf("Wrote %d bytes\n", data_processed);
data_processed = read(file_pipes[0], buffer, BUFSIZ);
printf("Read %d bytes: %s\n", data_processed, buffer);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
Если вы выполните программу, то получите следующий вывод:
$ ./pipe1
Wrote 3 bytes
Read 3 bytes: 123
Как это работает
Программа создает канал с помощью двух файловых дескрипторов из массива file_pipes[]
. Далее она записывает данные в канал, используя файловый дескриптор file_pipes[1]
, и считывает их обратно из file_pipes[0]
. Учтите, что у канала есть внутренняя буферизация, позволяющая хранить данные между вызовами функций write
и read
.
Следует знать, что реакция на попытку писать с помощью дескриптора file_descriptor[0]
или читать с помощью дескриптора file_descriptor[1]
не определена, поэтому поведение программы может быть очень странным и меняться без каких-либо предупреждений. В системах авторов такие вызовы заканчивались аварийно и возвращали -1, что, по крайней мере, гарантирует легкость обнаружения такой ошибки.
На первый взгляд этот пример использования канала ничего не предлагает такого, чего мы не могли бы сделать с помощью простого файла. Действительные преимущества каналов проявятся, когда вам нужно будет передавать данные между двумя процессами. Как вы видели в главе 11, когда программа создает новый процесс с помощью вызова fork
, уже открытые к этому моменту файловые дескрипторы так и остаются открытыми. Создав канал в исходном процессе и затем сформировав с помощью fork
новый процесс, вы сможете передать данные из одного процесса в другой через канал (упражнение 13.6).
fork
1. Это пример pipe2.c. Он выполняется также как первый до того момента, пока вы не вызовете функцию fork
.
#include
#include
#include
#include
int main() {
int data_processed;
int file_pipes[2];
const char some_data[] = "123";
char buffer[BUFSIZ + 1];
pid_t fork_result;
memset(buffer, '0', sizeof(buffer));
if (pipe(file_pipes) == 0) {
fork_result = fork();
if (fork_result == -1) {
fprintf(stderr, "Fork failure");
exit(EXIT_FAILURE);
}
2. Вы убедились, что вызов fork
отработал, поэтому, если его результат равен нулю, вы находитесь в дочернем процессе:
if (fork_result == 0) {
data_processed = read(file_pipes[0], buffer, BUFSIZ);
printf("Read %d bytes: %s\n", data_processed, buffer);
exit(EXIT_SUCCESS);
}
3. В противном случае вы должны быть в родительском процессе:
else {
data_processed = write(file_pipes[1], some_data,
strlen(some_data));
printf("Wrote %d bytes\n", data_processed);
}
Интервал:
Закладка: