файловый дескриптор ФД (созданный через вызов open
или унаследованный от родительского процесса), является целочисленным индексом в таблице ссылок на структуры открытых файлов процесса. Сама структура, связанная с открытым файлом, содержит дальнейшую ссылку на виртуальный Inode файла в памяти ядра, а так же флаги доступа к файлу (чтение, запись), текущую позицию чтения/записи в файле и некоторые дополнительные данные. Информация о блокировках используется совместно несколькими процессами, а потому вынесена в виртуальный Inode.
По соглашениям, принятым в ОС Unix, в момент запуска программы должны быть открыты ФД 0, 1 и 2, которые интерпретируются как STDIN, STDOUT и STDERR соответственно. Другие ФД так же могут быть открыты, но это никак не регламентируется. При выделении нового ФД при вызове open
, pipe
, dup
и т.п., выбирается наименьший свободный ФД.
Процесс, имея ФД, может создавать на его основе новые ФД, которые будут ссылками на ту же структуру данных в ядре, что и оригинальный ФД и соответственно те же флаги и позицию чтения/записи. Закрытие ФД уменьшает количество ссылок на открытый файл. Фактическое закрытие файла произойдёт тогда, когда на него не будет ссылаться ни один ФД.
Для создания ссылок используются вызовы dup
и dup2
. dup
возвращает первый свободный номер ФД, а dup2
позволяет явно указать номер нового ФД. Если желаемый номер ФД окажется занятым, то он сначала будет закрыт.
int newfd=dup(oldfd)
int newfd=dup2(oldfd,1)
Самое частое использование dup2
это перенаправление стандартного ввода-вывода. Например, shell, выполняя команду:
grep str infile > outfile
может выполнить следующую последовательность действий
Вариант 1:
if (!fork() ){
// Дочерний процесс для запуска grep
// Освобождаем ФД==1 (STDOUT)
close(1);
// open должен вернуть наименьший свободный ФД т.е. 1
open("outfile", O_CREAT|OTRUNC|O_WRONLY, 0544);
// Запускаем программу grep, наследующую STDOUT -> "outfile"
execlp("grep", "str", "infile",NULL);
}
Вариант 1 в многопоточной среде может привести к гонкам за захват ФД==1, поэтому рекомендуется Варинт2:
if (!fork() ){
// Дочерний процесс для запуска grep
// open возвращает какой-то ФД
int fd= open("outfile", O_CREAT|OTRUNC|O_WRONLY, 0544);
// Связываем 1 (STDOUT) с "outfile" через fd
dup2(fd,1);
// Освобождаем fd
close(fd);
// Запускаем программу grep, наследующую STDOUT -> "outfile"
execlp("grep", "str", "infile",NULL);
}
Перенаправление через pipe
выполняется аналогично.