Что такое файловый дескриптор в linux

Русские Блоги

Подробные файловые дескрипторы Linux

Заявление об авторском праве: Пожалуйста, укажите источник при перепечатке, спасибо https://blog.csdn.net/xlinsist/article/details/51147212

Overview

Важно понимать, как Linux обрабатывает ввод и вывод. Как только мы поймем принцип, мы сможем правильно и умело использовать сценарий, чтобы вывести содержимое в нужное место. Мы также можем лучше понять перенаправление ввода и перенаправление вывода.

Стандартные файловые дескрипторы Linux

Дескриптор файла сокращение описание
0 STDIN Стандартный ввод
1 STDOUT Стандартный вывод
2 STDERR Стандартный вывод ошибок

Системы Linux рассматривают все устройства как файлы, а Linux использует файловые дескрипторы для идентификации каждого файлового объекта. Фактически, мы можем представить, что дисплей и клавиатура нашего компьютера рассматриваются как файлы в системе Linux, и все они имеют соответствующие файловые дескрипторы, соответствующие им.

На самом деле наше взаимодействие с компьютером заключается в том, что я могу вводить некоторые инструкции, и это дает мне некоторую информацию. Тогда мы можем поставитьФайловый дескриптор 0 понимается как мой ввод при взаимодействии с компьютером, и этот ввод по умолчанию направлен на клавиатуру; Файловый дескриптор 1 понимается как вывод, когда я взаимодействую с компьютером, и этот вывод по умолчанию направляется на дисплей; Файловый дескриптор 2 понимается как выходная информация об ошибке компьютера, когда я взаимодействую с компьютером, и этот вывод по умолчанию соответствует местоположению, указанному файловым дескриптором 1;

Как я уже говорил выше, так как они по умолчанию, я могу их изменить. Следующая команда изменяет расположение стандартного вывода в файл xlinsist:

Таким же образом мы также можем изменить положение стандартного ввода. Во-первых, давайте посмотрим на то, что не изменилось:

То есть мы читаем xlinsist в пользовательскую переменную с клавиатуры. Это чтение требует моего ввода. Теперь я хочу изменить стандартную позицию стандартного ввода:

Как вы можете видеть из приведенной выше команды чтения, меня ни о чем не просили.

Разница между стандартным выводом ошибки и стандартным выводом заключается в том, что он выводится в случае ошибки команды. Это не так уж отличается, мы также можем изменить его вывод в любое место, которое мы хотим. Просто нам нужно изменить стандартный вывод с 1 на 2. Команда выглядит следующим образом:

Если вы действительно понимаете вышеприведенные принципы, мы можем воспроизвести любое перенаправление ввода, перенаправление вывода, и это небольшой случай. Теперь давайте возьмем более сложный пример, чтобы помочь вам организовать ваши идеи. Сценарий выглядит следующим образом:

Давайте разберемся с вышеприведенной командой шаг за шагом: во-первых, дескриптор файла 1 по умолчанию указывает на монитор. Используйте &, чтобы найти целевой файл, на который указывает дескриптор файла 1, который является монитором. Таким образом, дескриптор файла 3 также указывает на отображение. Затем мы изменили файл, на который указывает дескриптор файла 1, в тестовый файл. Затем выходные данные двух команд echo будут естественно искать дескриптор файла 1, а затем он обнаруживает, что дескриптор файла 1 указывает на тестовый файл, поэтому он записывает выходные данные в тестовый файл. Наконец, мы используем &, чтобы найти целевой файл, на который указывает дескриптор файла 3, который является дисплеем, и затем мы модифицируем файл, на который указывает дескриптор файла 1, для отображения. Следовательно, последняя команда echo естественным образом найдет дескриптор файла 1 и выведет его на дисплей.

Весь процесс таков, пока вы понимаете их принципы, вы не будете чувствовать растерянность, независимо от того, как вы будете обрабатывать перенаправления в сценариях в будущем. Ниже я представлю некоторые команды оболочки, относящиеся к файловым дескрипторам, которые могут сделать вас еще более мощным.

Некоторые команды оболочки, связанные с файловыми дескрипторами

На следующем рисунке показано значение вышеперечисленных пунктов

Теперь я модифицирую стандартный вывод ошибок:

Файл / dev / null, это очень специальный файл, все, что вы пишете, будет очищено. Вы можете записать данные, чтобы попробовать эффект.

Linux использует каталог / tmp для хранения файлов, которые не нужно хранить постоянно. Большинство систем Linux автоматически удаляют все файлы в каталоге / tmp при запуске.

Доступны следующие команды:

tee команда-чтение из стандартного ввода, запись в стандартный вывод и файлы.

Разница между каналами и перенаправлениями

Источник

Что такое файловый дескриптор в linux

С точки зрения модели КИС (Клиент-Интерфейс-Сервер), сервером стандартных механизмов ввода вывода языка C (printf, scanf, FILE*, fprintf, fputc и т. д.) является библиотека языка. А сервером низкоуровневого ввода-вывода в Linux, которому посвящена эта глава книги, является само ядро операционной системы.

Пользовательские программы взаимодействуют с ядром операционной системы посредством специальных механизмов, называемых системными вызовами (system calls, syscalls). Внешне системные вызовы реализованы в виде обычных функций языка C, однако каждый раз вызывая такую функцию, мы обращаемся непосредственно к ядру операционной системы. Список всех системных вызовов Linux можно найти в файле /usr/include/asm/unistd.h. В этой главе мы рассмотрим основные системные вызовы, осуществляющие ввод-вывод: open(), close(), read(), write(), lseek() и некоторые другие.

5.2. Файловые дескрипторы

Вы уже знаете из предыдущей главы, что при запуске программы в системе создается новый процесс (здесь есть свои особенности, о которых пока говорить не будем). У каждого процесса (кроме init) есть свой родительский процесс (parent process или просто parent), для которого новоиспеченный процесс является дочерним (child process, child). Каждый процесс получает копию окружения (environment) родительского процесса. Оказывается, кроме окружения дочерний процесс получает в качестве багажа еще и копию таблицы файловых дескрипторов.

Возникает вопрос: сколько файлов может открыть процесс? В каждой системе есть свой лимит, зависящий от конфигурации. Если вы используете bash или ksh (Korn Shell), то можете воспользоваться внутренней командой оболочки ulimit, чтобы узнать это значение. Если вы работаете с оболочкой C-shell (csh, tcsh), то в вашем распоряжении команда limit:

В командной оболочке, в которой вы работаете (bash, например), открыты три файла: стандартный ввод (дескриптор 0), стандартный вывод (дескриптор 1) и стандартный поток ошибок (дескриптор 2). Когда под оболочкой запускается программа, в системе создается новый процесс, который является для этой оболочки дочерним процессом, следовательно, получает копию таблицы дескрипторов своего родителя (то есть все открытые файлы родительского процесса). Таким образом программа может осуществлять консольный ввод-вывод через эти дескрипторы. На протяжении всей книги мы будем часто играть с этими дескрипторами.

Таблица дескрипторов, помимо всего прочего, содержит информацию о текущей позиции чтения-записи для каждого дескриптора. При открытии файла, позиция чтения-записи устанавливается в ноль. Каждый прочитанный или записанный байт увеличивает на единицу указатель текущей позиции. Мы вернемся к этой теме в разделе 5.7.

5.3. Открытие файла: системный вызов open()

Чтобы получить возможность прочитать что-то из файла или записать что-то в файл, его нужно открыть. Это делает системный вызов open(). Этот системный вызов не имеет постоянного списка аргументов (за счет использования механизма va_arg); в связи с этим существуют две «разновидности» open(). Не только в С++ есть перегрузка функций ;-) Если интересно, то о механизме va_arg можно прочитать на man-странице stdarg (man 3 stdarg) или в книге Б. Кернигана и Д. Ритчи «Язык программирования Си». Ниже приведены адаптированные прототипы системного вызова open().

Системный вызов open() объявлен в заголовочном файле fcntl.h. Ниже приведен общий адаптированный прототип open().

5.4. Закрытие файла: системный вызов close()

Системный вызов close() закрывает файл. Вообще говоря, по завершении процесса все открытые файлы (кроме файлов с дескрипторами 0, 1 и 2) автоматически закрываются. Тем не менее, это не освобождает нас от самостоятельного вызова close(), когда файл нужно закрыть. К тому же, если файлы не закрывать самостоятельно, то соответствующие дескрипторы не освобождаются, что может привести к превышению лимита открытых файлов. Простой пример: приложение может быть настроено так, чтобы каждую минуту открывать и перечитывать свой файл конфигурации для проверки обновлений. Если каждый раз файл не будет закрываться, то в моей системе, например, приложение может «накрыться медным тазом» примерно через 17 часов. Автоматически! Кроме того, файловая система Linux поддерживает механизм буферизации. Это означает, что данные, которые якобы записываются, реально записываются на носитель (синхронизируются) только через какое-то время, когда система сочтет это правильным и оптимальным. Это повышает производительность системы и даже продлевает ресурс жестких дисков. Системный вызов close() не форсирует запись данных на диск, однако дает больше гарантий того, что данные останутся в целости и сохранности.

Системный вызов close() объявлен в файле unistd.h. Ниже приведен его адаптированный прототип.

Теперь можно написать простенкую программу, использующую системные вызовы open() и close(). Мы еще не умеем читать из файлов и писать в файлы, поэтому напишем программу, которая создает файл с именем, переданным в качестве аргумента (argv[1]) и с правами доступа 0600 (чтение и запись для пользователя). Ниже приведен исходный код программы.

Обратите внимание, если запустить программу дважды с одним и тем же аргументом, то на второй раз open() выдаст ошибку. В этом виноват флаг O_EXCL (см. Таблицу 4 Приложения 2), который «дает добро» только на создание еще не существующих файлов. Наглядности ради, флаги открытия и флаги режима мы занесли в отдельные переменные, однако можно было бы сделать так: Или так:

5.5. Чтение файла: системный вызов read()

Системный вызов read(), объявленный в файле unistd.h, позволяет читать данные из файла. В отличие от библиотечных функций файлового ввода-вывода, которые предоставляют возможность интерпретации считываемых данных. Можно, например, записать в файл следующее содержимое:

Теперь, используя библиотечные механизмы, можно читать файл по-разному:

Системный вызов read() читает данные в «сыром» виде, то есть как последовательность байт, без какой-либо интерпретации. Ниже представлен адаптированный прототип read().

Теперь напишем программу, которая просто читает файл и выводит его содержимое на экран. Имя файла будет передаваться в качестве аргумента (argv[1]). Ниже приведен исходный код этой программы.

Как можно заметить, в нашем примере системный вызов read() вызывается ровно столько раз, сколько байт содержится в файле. Иногда это действительно нужно; но не здесь. Чтение-запись посимвольным методом (как в нашем примере) значительно замедляет процесс ввода-вывода за счет многократных обращений к системным вызовам. По этой же причине возрастает вероятность возникновения ошибки. Если нет действительной необходимости, файлы нужно читать блоками. О том, какой размер блока предпочтительнее, будет рассказано в последующих главах книги. Ниже приведен исходный код программы, которая делает то же самое, что и предыдущий пример, но с использованием блочного чтения файла. Размер блока установлен в 64 байта.

Теперь можно примерно оценить и сравнить скорость работы двух примеров. Для этого надо выбрать в системе достаточно большой файл (бинарник ядра или видеофильм, например) и посмотреть на то, как быстро читаются эти файлы:

5.6. Запись в файл: системный вызов write()

Для записи данных в файл используется системный вызов write(). Ниже представлен его прототип.

В этом примере нам уже не надо изощеряться в попытках вставить нуль-терминатор в строку для записи, поскольку системный вызов write() не запишет большее количество байт, чем мы ему указали. В данном случае для демонстрации write() мы просто записывали данные в файл с дескриптором 1, то есть в стандартный вывод. Но прежде, чем переходить к чтению следующего раздела, попробуйте самостоятельно записать что-нибудь (при помощи write(), естественно) в обычный файл. Когда будете открывать файл для записи, обратите пожалуйста внимание на флаги O_TRUNC, O_CREAT и O_APPEND. Подумайте, все ли флаги сочетаются между собой по смыслу.

5.7. Произвольный доступ: системный вызов lseek()

Как уже говорилось, с каждым открытым файлом связано число, указывающее на текущую позицию чтения-записи. При открытии файла позиция равна нулю. Каждый вызов read() или write() увеличивает текущую позицию на значение, равное числу прочитанных или записанных байт. Благодаря этому механизму, каждый повторный вызов read() читает следующие данные, и каждый повторный write() записывает данные в продолжение предыдущих, а не затирает старые. Такой механизм последовательного доступа очень удобен, однако иногда требуется получить произвольный доступ к содержимому файла, чтобы, например, прочитать или записать файл заново.

Для изменения текущей позиции чтения-записи используется системный вызов lseek(). Ниже представлен его прототип.

Первый вызов устанавливает текущую позицию в начало файла. Второй вызов смещает позицию вперед на 20 байт. В третьем случае текущая позиция перемещается на 10 байт назад относительно конца файла.

Я долго думал, какой бы пример придумать, чтобы продемонстрировать работу lseek() наглядным образом. Наиболее подходящим примером мне показалась идея создания программы рисования символами. Программа оказалась не слишком простой, однако если вы сможете разобраться в ней, то можете считать, что успешно овладели азами низкоуровневого ввода-вывода Linux. Ниже представлен исходный код этой программы.

Функция init_draw() пишет в файл N_ROWS строк, каждая из которых содержит N_COLS пробелов, заканчивающихся переводом строки. Это процедура подготовки «холста».

Функция draw_point() вычисляет позицию (исходя из значений координат), перемещает туда текущую позицию ввода-вывода файла, и записывает в эту позицию символ (FG_CHAR), которым мы рисуем «картину».

Функция draw_hline() заполняет часть строки символами FG_CHAR. Так получается горизонтальная линия. Функция draw_vline() работает иначе. Чтобы записать вертикальную линию, нужно записывать по одному символу и каждый раз «перескакивать» на следующую строку. Эта функция работает медленнее, чем draw_hline(), но иначе мы не можем.

Полученное изображение записывается в файл image. Будьте внимательны: чтобы разгрузить исходный код, из программы исключены многие проверки (read(), write(), close(), диапазон координат и проч.). Попробуйте включить эти проверки самостоятельно.

Источник

Файловые дескрипторы Linux

Основными операциями, предоставляемыми ядром операционной системы программам (а точнее — процессам) для работы с файлами, являются системные вызовы open read, write и close. В соответствии со своими именами, эти системные вызовы предназначены для открытия и закрытия файла, для чтения из файла и записи в файл. Дополнительный системный вызов ioctl (input output control) используется для управления драйверами устройств и, как следствие, применяется в основном для специальных файлов устройств.

При запросе процесса на открытие файла системным вызовом оpen производится его однократный (относительно медленный) поиск имени файла в дереве каталогов и для запросившего процесса создается так называемый файловый дескриптор (описатель, от англ, descriptor).

Файловый дескриптор «содержит» информацию, описывающую файл, например индексный дескриптор inode файла на файловой системе, номера major и minor устройства, на котором располагается файловая система файла, режим открытия файла, и прочую служебную информацию.

При последующих операциях read и write доступ к самим данным файла происходит с использованием файлового дескриптора (что исключает медленный поиск файла в дереве каталогов).

Файловые дескрипторы пронумерованы и содержатся в таблице открытых процессом файлов, которую можно получить при помощи диагностической программы lsof.

В обратную сторону получить список процессов, открывших тот или иной файл, можно при помощи программ lsof и fuser, что бывает полезно для идентификации программ, «занявших» файловую систему, подлежащую отмонтированию.

Таблица файловых дескрипторов

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

# lsof /dev/log

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

rsyslogd 543 syslog 0u unix 0xefef5680 0t0 1338 /dev/log

# fuser /dev/log

# ps p 543

# lsof /var/log/syslog

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

rsyslogd 543 syslog lw REG 252,0 29039 26214496 /var/log/syslog

В первом примере из листинга выше показано получение списка файловых дескрипторов (столбец FD) процесса командного интерпретатора bash пользователя john, на котором файловый дескриптор номер 1 описывает открытый на чтение и запись и специальный символьный CHR файл устройства /dev/pts/2.

Во втором примере показано получение информации о процессе, открывшем файловый сокет unix с именем /dev/log (файловый дескриптор номер 0 на чтение и запись u) и обычный файл REG с именем /var/log/sysog (файловый дескриптор номер 1 на запись w).

Пронаблюдать за использованием системных вызовов файлового программного интерфейса в момент выполнения программам позволяет системный трассировщик strace.

Трассировка файлового программного интерфейса

$ date

Вт. окт. 15 18:17:42 MSK % 2018

open(«/etc/localtime», 0_RDONLY|0_CLOEXEC) = 3

$ file /etc/localtime
/etc/localtime: timezone data, version 2, 13 gmt time flags, 13 std time flags, no leap

seconds, 77 transition tines, 13 abbreviation char
[email protected]:

Предположив, что программа date показывает правильное московское время, потому что узнаёт заданную временную зону MSK из некоего конфигурационного файла операционной системы, при трассировке ее «работы можно установить его точное имя — /etc/localtime.

Аналогично предположив, что программа eject открывает лоток привода CD/DVD при помощи специального файла устройства, при трассировке можно узнать имя файла /dev/sr0, номер файлового дескриптора при работе с файлом 3 и команду CDROMEJECT соответствующего устройству
драйвера ioctl_List.

Трассировка команды setleds показывает, что она вообще не открывает никаких файлов, но пользуется файловым дескриптором о так называемого стандартного потока ввода (прикрепленного, к текущему терминалу) и командами kdgetled и kdsetled драйвера консоли console_ioctl.

Источник

Понравилась статья? Поделиться с друзьями:

Не пропустите наши новые статьи:

  • Что такое файл подкачки и зачем он нужен windows
  • что такое файл название программы
  • что такое файл в программировании
  • Что такое файл hosts в windows 10
  • Что такое файл aae и как его открыть на компе windows

  • Операционные системы и программное обеспечение
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии