Нейл Мэтью - Основы программирования в 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).
fork1. Это пример 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);
}
Интервал:
Закладка: