
- •Введение
- •Глава 1. Фундаментальные концепции unix Систем
- •Программы, процессы и потоки
- •Сигналы
- •Идентификаторы процессов, группы процессов и сеансы
- •Система прав
- •Другие атрибуты процесса
- •Межпроцессное взаимодействие
- •Использование системных вызовов
- •Краткие описания функций и обработка ошибок
- •Контрольные вопросы
- •Литература
- •Глава 2. Базовые операции ввода-вывода
- •Файловые операции ввода - вывода
- •Стандартные дескрипторы
- •Системные вызовы open и creat
- •Системный вызов umask
- •Системный вызов unlink
- •Текущая позиция в файле
- •Системный вызов write
- •2.8. Системный вызов read
- •2.9. Системный вызов close
- •2.10. Системный вызов lseek
- •2.11. Системные вызовы pread и pwrite
- •2.12. Системные вызовы truncate и ftruncate
- •Контрольные вопросы
- •Литература
- •Глава 3. Дополнительные операции файлового ввода_вывода
- •Низкоуровневый доступ к файловой системе
- •Жесткие и символические ссылки
- •Системный вызов getcwd
- •Отображение метаданных файла
- •Системные вызовы getpwuid, getgrgid и getlogin
- •Каталоги
- •Системные вызовы chdir и fchdir
- •Системные вызовы mkdir и rmdir
- •Контрольные вопросы
- •Литература
- •Глава 4. Процессы и потоки
- •4.1. Среда окружения
- •Системный вызов exec
- •Системный вызов fork
- •Завершение процесса и системные вызовы exit
- •Системные вызовы wait, waitpid и waitid
- •Получение и изменение идентификаторов пользователя и группы
- •Получение и изменение приоритета
- •Контрольные вопросы
- •Литература
- •Глава 5. Механизмы межпроцессного взаимодействия
- •5.1. Каналы
- •5.2. Системные вызовы dup и dup2
- •5.3. Двунаправленное взаимодействие с использованием однонаправленных каналов
- •Контрольные вопросы
- •Литература
- •Глава 6.Механизмы взаимодействия процессов
- •Именованные каналы (fifo)
- •Системные вызовы для работы с очередями сообщений posix
- •Семафоры
- •Системные вызовы для работы с общей памятью posix
- •Контрольные вопросы
- •Литература
- •Глава 7.Сетевое взаимодействие и сокеты
- •Основные системные вызовы для работы с сокетами, образующими логические соединения
- •Обслуживание нескольких клиентов
- •Адресация сокетов
- •In_port_t sin_port; /* номер порта (uint16_t) */
- •In_addr_t s_addr; /* адрес iPv4 (uint32_t) */
- •Домен адресов af_inet6
- •In_port_t sin6_port; /* номер порта (uint16_t) */
- •Доменная система именования
- •Параметры сокетов
- •Контрольные вопросы
- •Литература
- •Глава 8.Сигналы и таймеры
- •Введение в сигналы
- •Жизненный цикл сигналов
- •Типы сигналов
- •Системный вызов sigaction
- •Контрольные вопросы
- •Литература
- •Заключение
- •Список литературы
- •Глава 2. Базовые операции ввода-вывода 14
- •Глава 3. Дополнительные операции файлового ввода_вывода 25
- •Глава 6. Механизмы взаимодействия процессов 58
5.2. Системные вызовы dup и dup2
Вызов dup дублирует существующий дескриптор файла, возвращая новый дескриптор, открытый для того же файла или канала:
dup – дублирует дескриптор файла
#include <unistd.h>
int dup (
int fd /* дублируемый дескриптор файла */
);
/* Возвращает новый дескриптор файла или -1 в случае ошибки (код ошибки в переменной errno) */
dup2 – дублирует дескриптор файла
#include <unistd.h>
int dup2 (
int fd, /* дублируемый дескриптор файла */
int fd2 /* используемый дескриптор файла */
);
/* Возвращает новый дескриптор файла или -1 в случае ошибки (код ошибки в переменной errno) */
Оба дескриптора разделяют одно описание файла. Вызов завершается неудачей, если указан некорректный аргумент (дескриптор не открыт) или если нет доступных дескрипторов файлов. Вызов dup возвращает наименьший из доступных дескрипторов, так что если вы знаете, какие дескрипторы открыты, то можете узнать, что будет возвращено. В этом случае легче использовать вызов dup2, который позволяет указать при помощи аргумента fd2, какой дескриптор файла следует возвратить. Если нужно, вызов dup2 закрывает дескриптор fd2, чтобы сделать его доступным. Вызов dup(fd) эквивалентен вызову: fcntl(fd, F_DUPFD, 0), а вызов dup2(fd, fd2) эквивалентен вызову:
close(fd2);
fcntl(fd, F_DUPFD, fd2);
Рассмотрим использование dup2 на примере. Следующая функция формирует канал, создает дочерний процесс, который будет читать данные из канала, делает дескриптор STDIN_FILENO дочернего процесса дескриптором стороны чтения канала и вызывает для чтения данных из канала команду cat. Теперь не нужна специальная программа вроде piperead, которую мы использовали в примере из предыдущего раздела.
void pipewrite2(void)
{
int pfd[2];
pid_t pid;
pipe(pfd);
switch (pid = fork()) {
case -1:
EC_FAIL
case 0: /* дочерний процесс */
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
close(pfd[1]);
execlp(“cat”, “cat”, NULL);
EC_FAIL
default: /* родительский процесс */
close(pfd[0]);
write(pfd[1], “hello”, 6);
close(pfd[1]);
waitpid(pid, NULL, 0);
}
return;
EC_CLEANUP_BGN
EC_FLUSH(“pipewrite2”);
EC_CLEANUP_END
}
Результатом выполнения этой программы является строка: hello. Канал может быть организован не только от родительского процесса к дочернему. В следующем примере реализован эквивалент команды $ who | wc, которая позволяет узнать число пользователей, вошедших в систему:
void who_wc(void)
{
int pfd[2];
pid_t pid1, pid2;
pipe(pfd);
switch (pid1 = fork()) {
case -1:
EC_FAIL
case 0: /* первый дочерний процесс */
dup2(pfd[1], STDOUT_FILENO);
close(pfd[0]);
close(pfd[1]);
execlp(“who”, “who”, NULL);
EC_FAIL
/* родительский процесс */
switch (pid2 = fork()) {
case -1:
EC_FAIL
case 0: /* второй дочерний процесс */
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
close(pfd[1]);
execlp(“wc”, “wco”, “-1”, NULL);
EC_FAIL
}
/* и опять родительский процесс */
close(pfd[0]);
close(pfd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
return;
EC_CLEANUP_BGN
EC_FLUSH(“who_wc”);
EC_CLEANUP_END
}
Результатом выполнения этого кода является 1.