При разработку программы написанный код работает в режиме пользователя(user mode). Драйверы устройств и файловые системы, наоборот, работают в режиме ядра(kernel mode). В пользовательском режиме программы тщательно защищены от повреждений, вызванных их взаимодействием друг с другом или остальной частью системы. Код, работающий в режиме ядра, имеет полный доступ к компьютеру и может делать или же разрушать все, что угодно.
Драйвер, который разработан для управления и контроля аппаратного устройства, должен иметь полный доступ к нему. Устройство нужно защитить от случайных программ, чтобы они не смогли нарушить свою работу или работу друг друга, повредив само устройство. Память, с которой работает устройство, также защищена от случайных программ. Весь код, работающий в режиме ядра, существует исключительно для обслуживания кода, который работает в режиме пользователя. Системный вызов— это то, посредством чего код приложения, выполняющегося в пользовательском режиме, запрашивает службу, предоставляемую кодом, который выполняется в режиме ядра.
Возьмем, к примеру, процесс выделения памяти. Пользовательский процесс запрашивает память у защищенного кода, выполняющегося в режиме ядра, который и производит выделение физической памяти для процесса. В качестве следующего примера возьмем файловые системы, которые нуждаются в защите для поддержания целостности данных на диске (или в сети), но вашим повседневным, обычным процессам требуется считывать файлы из файловой системы.
Неприятные детали вызова через барьер пространств пользователь/ядро часто скрыты в библиотеке С. Процесс вызова через этот барьер не использует обычные вызовы функций; он использует неуклюжий интерфейс, оптимизированный по скорости и обладающий существенными ограничениями. Библиотека С скрывает большинство интерфейсов от глаз пользователя, предлагая взамен обычные функции С, которые являются оболочками для системных вызовов. Применение этих функций станет понятнее, если получить некоторое представление о том, что происходит внутри них.
9.2.1. Ограничения системных вызовов
Режим ядра защищен от влияния режима пользователя. Одна из таких защит состоит в том, что тип данных, передаваемых между режимами ядра и пользователя, ограничен, легко верифицируется и следует строгим соглашениям.
• Длина каждого аргумента, передаваемого из режима пользователя в режим ядра, практически всегда соответствует размеру слов, используемых машиной для представления указателей. Этого размера достаточно как для передачи указателей, так и длинных целых. Переменные типа char
и short
перед передачей расширяются до большего типа.
• Тип возвращаемого значения ограничен размером слова со знаком. Первые несколько сотен небольших отрицательных целых чисел зарезервированы в качестве кодов ошибок, и в пределах системных вызовов имеют одинаковое значение. Это значит, что системные вызовы, возвращающие указатель, не могут вернуть некоторые указатели, соответствующие адресам в верхней области доступной виртуальной памяти. К счастью, эти адреса находятся в зарезервированном пространстве и в любом случае никогда не возвращаются, потому возвращаемые слова со знаком могут быть без проблем преобразованы в указатели.
В отличие от соглашений С о вызовах, в котором структуры С могут передаваться по значению в стеке, нельзя передавать структуры по значению из пользовательского режима в режим ядра; точно также ядро не может вернуть структуру в пользовательский режим. Большие элементы данных можно передавать только по ссылке. Передавайте указатели на структуры, как всегда поступаете, когда их необходимо модифицировать.
9.2.2. Коды возврата системных вызов
Коды возврата, зарезервированные для всех системных вызовов — это универсальные коды возврата ошибок, представленные небольшими отрицательными числами. Библиотека С проверяет наличие ошибок каждый раз, когда происходит системный вызов. При возникновении ошибки библиотека помещает значение ошибки в глобальную переменную errno
[15] Для многопоточных приложений библиотека хранит код ошибки там, где функция errno() , которой известно, какой поток является текущим, может получить ее. Разные потоки могут содержать разные текущие коды возврата ошибок.
. В большинстве случаев все, что вам необходимо при проверке ошибки — посмотреть, отрицательный ли код возврата. Коды ошибок определены в , и errno
можно сравнить с любым номером ошибки из этого файла, после чего обработать ее специальным образом.
Читать дальше