В вычислении, ioctl
(сокращение от элемент управления вводом / выводом ) - это системный вызов для специфичных для устройства операций ввода / вывода и других операций, которые не могут быть выражены обычными системными вызовами. Требуется параметр, указывающий код запроса; эффект вызова полностью зависит от кода запроса. Коды запросов часто зависят от устройства. Например, драйвер устройства CD-ROM , который может дать команду физическому устройству извлечь диск, предоставит для этого код запроса ioctl
. Коды запросов, не зависящие от устройства, иногда используются для предоставления пользовательского пространства доступа к функциям ядра, которые используются только основным системным программным обеспечением или все еще находятся в стадии разработки.
Системный вызов ioctl
впервые появился под этим именем в версии 7 из Unix. Он поддерживается большинством Unix и Unix-подобных систем, включая Linux и macOS, хотя доступные коды запросов различаются от системы к системе. Microsoft Windows предоставляет аналогичную функцию под названием «DeviceIoControl
» в своем Win32 API.
Обычные операционные системы могут быть разделенным на два уровня: пользовательское пространство и ядро . Код приложения, такой как a, находится в пользовательском пространстве, в то время как базовые возможности операционной системы, такие как сетевой стек, находятся в ядре. Код ядра обрабатывает конфиденциальные ресурсы и реализует барьеры безопасности и надежности между приложениями; по этой причине операционная система не позволяет приложениям пользовательского режима напрямую обращаться к ресурсам ядра.
Пользовательское пространство приложения обычно делают запросы к ядру с помощью системных вызовов, код которых находится на уровне ядра. Системный вызов обычно принимает форму «вектора системного вызова», в котором желаемый системный вызов обозначается порядковым номером. Например, exit ()
может быть номером системного вызова 1, а write ()
номером 4. Затем вектор системного вызова используется для поиска требуемой функции ядра для запроса. Таким образом, обычные операционные системы обычно предоставляют несколько сотен системных вызовов пользовательскому пространству.
Несмотря на то, что это целесообразный дизайн для доступа к стандартным средствам ядра, системные вызовы иногда не подходят для доступа к нестандартным периферийным устройствам оборудования. По необходимости, большинство аппаратных периферийных устройств (также называемых устройствами) имеют прямую адресацию только внутри ядра. Но пользовательскому коду может потребоваться прямая связь с устройствами; например, администратор может настроить тип носителя на интерфейсе Ethernet. Современные операционные системы поддерживают различные устройства, многие из которых предлагают большой набор функций. Некоторые из этих возможностей могут быть не предусмотрены разработчиком ядра, и, как следствие, ядру трудно обеспечить системные вызовы для использования устройств.
Чтобы решить эту проблему, ядро спроектировано так, чтобы быть расширяемым, и может принимать дополнительный модуль, называемый драйвером устройства, который работает в пространстве ядра и может напрямую обращаться к устройству. Интерфейс ioctl
- это отдельный системный вызов, с помощью которого пользовательское пространство может взаимодействовать с драйверами устройств. Запросы к драйверу устройства направляются относительно этого системного вызова ioctl
, обычно с помощью дескриптора устройства и номера запроса. Таким образом, базовое ядро может позволить пользовательскому пространству получить доступ к драйверу устройства, ничего не зная о средствах, поддерживаемых устройством, и без необходимости в неуправляемом большом наборе системных вызовов.
Обычно ioctl
используется для управления аппаратными устройствами.
Например, в системах Win32 вызовы ioctl
могут обмениваться данными с устройствами USB или могут обнаруживать информацию о геометрии дисков подключенного устройства хранения данных.
В OpenBSD и NetBSD, ioctl
используется bio(4)
драйвер псевдоустройства и утилита bioctl
для реализации управления томами RAID в унифицированном независимом от производителя интерфейсе, аналогичном ifconfig
.
В NetBSD, ioctl
также используется фреймворком sysmon
.
Один использование ioctl
в коде, доступном для приложений конечного пользователя, является терминальным вводом-выводом.
Операционные системы Unix традиционно интенсивно использовали интерфейсы командной строки. Интерфейс командной строки Unix построен на псевдотерминалах (ptys), которые имитируют аппаратные текстовые терминалы, такие как VT100s. Pty управляется и настраивается, как если бы это было аппаратное устройство, с помощью вызовов ioctl
. Например, размер окна объекта устанавливается с помощью вызова TIOCSWINSZ
. Функция ioctl TIOCSTI (управление вводом-выводом терминала, имитация ввода-вывода) может протолкнуть символ в поток устройства.
Когда приложениям необходимо расширить ядро, например, для ускорения при сетевой обработке вызовы ioctl
предоставляют удобный способ связать код пользовательского пространства с расширениями ядра. Расширения ядра могут предоставить место в файловой системе, которое может быть открыто по имени, через которое может быть отправлено произвольное количество вызовов ioctl
, что позволяет программировать расширение без добавления системных вызовов к операционной системе.
По словам разработчика OpenBSD, ioctl
и sysctl
являются двумя системные вызовы для расширения ядра, причем sysctl
, возможно, является более простым из двух.
В NetBSD, sysmon_envsys
framework для аппаратного мониторинга использует ioctl
- proplib
; тогда как OpenBSD и DragonFly BSD вместо этого используют sysctl
для своих соответствующих hw.sensors
фреймворк. Исходная версия envsys
в NetBSD была реализована с помощью ioctl
до того, как был доступен proplib
, и содержала сообщение о том, что структура является экспериментальной и должна быть заменена интерфейс sysctl (8)
, если он будет разработан, что потенциально объясняет выбор sysctl
в OpenBSD с его последующим введением hw.sensors
в 2003 году. Однако, когда структура envsys
была переработана в 2007 году около proplib
, системный вызов остался как ioctl
, и сообщение было удалено.
Системный вызов ioctl
впервые появился в версии 7 Unix, как a переименован в stty
. Вызов ioctl
принимает в качестве параметров :
ядро обычно отправляет ioctl
вызовите прямо к драйверу устройства, который может интерпретировать номер запроса и данные любым необходимым способом. Авторы каждого документа драйвера запрашивают номера для этого конкретного драйвера и предоставляют их как константы в заголовочном файле.
Некоторые системы Unix, включая Linux, имеют соглашения, которые кодируют в номере запроса - размер данных, которые должны быть переданы в / из драйвера устройства, направление передачи данных и идентификатор драйвера, реализующего запрос. Независимо от того, соблюдается ли такое соглашение, ядро и драйвер взаимодействуют, чтобы доставить единый код ошибки (обозначается символической константой ENOTTY
) приложению, которое делает запрос к драйверу, который его не распознает..
Мнемоника ENOTTY
(традиционно связанная с текстовым сообщением «Не пишущая машинка ») происходит от самых ранних систем, которые включали вызов ioctl
, где только устройство телетайпа (tty
) вызвало эту ошибку. Хотя символическая мнемоника фиксируется требованиями совместимости, некоторые современные системы более эффективно отображают более общее сообщение, такое как «Несоответствующая операция управления устройством» (или его локализация ).
TCSETS
иллюстрирует вызов ioctl
на последовательном порту. Обычные вызовы чтения и записи через последовательный порт принимают и отправляют байты данных. Вызов ioctl (fd, TCSETS, data)
, отдельный от такого обычного ввода-вывода, управляет различными параметрами драйвера, такими как обработка специальных символов или выходных сигналов порта (например, как сигнал DTR ).
Win32 DeviceIoControl
принимает в качестве параметров:
OVERLAPPED
, если используется перекрывающийся ввод-вывод.Управляющий код устройства Win32 принимает во внимание режим работы выполняется.
Существует 4 определенных режима работы, влияющих на безопасность драйвера устройства -
METHOD_IN_DIRECT
: адрес буфера проверяется на возможность чтения вызывающим режимом пользовательского режима.METHOD_OUT_DIRECT
: Адрес буфера проверяется на возможность записи вызывающим режимом пользовательского режима.METHOD_NEITHER
: виртуальные адреса пользовательского режима передаются драйверу без сопоставления или проверки.METHOD_BUFFERED
: общий доступ, управляемый диспетчером ввода-вывода буферы используются для перемещения данных в пользовательский режим и из него.Устройства и расширения ядра могут быть связаны с пользовательским пространством с помощью дополнительных новых системные вызовы, хотя этот подход применяется редко, потому что разработчики операционных систем стараются сохранить интерфейс системных вызовов сфокусированным и эффективным.
В операционных системах Unix популярны два других интерфейса векторных вызовов: системный вызов fcntl
(«управление файлами») настраивает открытые файлы и используется в таких ситуациях, как включение неблокирующий ввод / вывод ; а системный вызов setsockopt
("установить параметр сокета") настраивает открытые сетевые сокеты, средство, используемое для настройки межсетевого экрана пакетов ipfw
в системах BSD Unix.
ioctl
, но затем используют системные вызовы отображения памяти, чтобы связать часть своего адресного пространства с адресным пространством ядра.. Этот интерфейс - гораздо более эффективный способ обеспечить массовую передачу данных между устройством и приложением пользовательского пространства ; отдельные системные вызовы ioctl
или чтение / запись вызывают накладные расходы из-за повторяющихся переходов из пользовательского пространства в ядро, когда доступ к диапазону адресов, отображаемых в памяти, не вызывает таких накладных расходов.DeviceIoControl METHOD_
.Netlink - это механизм, подобный сокету, для межпроцессного взаимодействия (IPC), разработанный как более гибкий преемник для вызовов ioctl
.
ioctl
, минимизирует сложность интерфейса системных вызовов ядра. Однако, предоставляя разработчикам место для «спрятать» кусочки и части программных интерфейсов ядра, вызовы ioctl
усложняют общий API взаимодействия пользователя с ядром. Ядро, которое предоставляет несколько сотен системных вызовов, может обеспечивать несколько тысяч вызовов ioctl.
Хотя интерфейс для вызовов ioctl
несколько отличается от обычных системных вызовов, на практике разница между вызовом ioctl
и системным вызовом незначительна; вызов ioctl
- это просто системный вызов с другим механизмом диспетчеризации. Поэтому многие аргументы против расширения интерфейса системного вызова ядра можно применить к интерфейсам ioctl
.
Для разработчиков приложений системные вызовы не отличаются от подпрограмм приложения; это просто вызовы функций, которые принимают аргументы и возвращают значения. Библиотеки среды выполнения ОС маскируют сложность, связанную с вызовом системных вызовов. К сожалению, библиотеки времени выполнения не делают вызовы ioctl
прозрачными. Простые операции, такие как обнаружение IP-адресов для машины, часто требуют запутанных беспорядочных вызовов ioctl
, каждый из которых требует магических чисел и структур аргументов.
Libpcap и являются двумя примерами сторонних библиотек-оболочек Unix, предназначенных для маскировки сложности интерфейсов ioctl
для захвата пакетов и ввода-вывода пакетов соответственно.
Интерфейсы пользователь-ядро в основных операционных системах перед выпуском часто подвергаются тщательному аудиту на предмет недостатков кода и уязвимостей безопасности. Эти аудиты обычно фокусируются на хорошо задокументированных интерфейсах системных вызовов; например, аудиторы могут гарантировать, что конфиденциальные вызовы безопасности, такие как изменение идентификаторов пользователей, доступны только администраторам.
ioctl
интерфейсы сложнее, разнообразнее и, следовательно, труднее контролировать, чем системные вызовы. Кроме того, поскольку вызовы ioctl
могут предоставляться сторонними разработчиками, часто после выпуска основной операционной системы реализации вызовов ioctl
могут подвергаться меньшему вниманию и, таким образом, содержать больше уязвимостей. Наконец, многие вызовы ioctl
, особенно для сторонних драйверов устройств, недокументированы.
Поскольку обработчик вызова ioctl
находится непосредственно в режиме ядра, ввод из пользовательского пространства следует тщательно проверять. Уязвимости в драйверах устройств могут быть использованы локальными пользователями путем передачи недопустимых буферов вызовам ioctl
. Операционные системы
Win32 и Unix могут защитить имя устройства пользовательского пространства от доступа приложений с определенными элементами управления доступом, применяемыми к устройству. Проблемы безопасности могут возникнуть, когда разработчики драйверов устройств не применяют соответствующие средства управления доступом к доступному объекту пользовательского пространства.
Некоторые современные операционные системы защищают ядро от использования вредоносного кода пользовательского пространства (например, приложений, зараженных эксплойтами переполнения буфера ). Оболочки системных вызовов реализуют ролевое управление доступом, указывая, какие системные вызовы могут быть вызваны какими приложениями; Обертки могут, например, использоваться для "отзыва" права почтовой программы на порождение других программ. Интерфейсы ioctl
усложняют обертки системных вызовов, поскольку их большое количество, каждый из которых принимает разные аргументы, некоторые из которых могут потребоваться для обычных программ.
ioctl (2)
– Версия 7 Unix Руководство программиста ioctl (2)
– Linux Руководство программиста - Системные вызовыioctl (2)
– FreeBSD Системные вызовы Руководство ioctl (2)
– OpenBSD Системные вызовы Руководство ioctl (2)
– Solaris 10 Справочник по системным вызовам Руководство