<b>#include <unistd.h></b>
<b>int dup(int file_descriptor);</b>
<b>int dup2(int file_descriptor_one, int file_descriptor_two);</b>
Назначение вызова
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) {
<i> close(0);</i>
<i> dup(file_pipes[0];</i>
<i> close(file_pipes[0]);</i>
<i> close(file_pipes[1]);</i>
<i> execlp("od", "od", "-c", (char*)0);</i>
exit(EXIT_FAILURE);
} else {
<i> close(file_pipes[0]);</i>
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);
}
У этой программы следующий вывод:
$ <b>./pipe5</b>
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, его стандартный ввод.