[Чтение заметок] Разработка ядра Linux и системный вызов реализации

Реализация интерфейса системного вызова в основном предназначена для обеспечения стабильности и надежности системы и во избежание произвольного применения приложения.

1. Yonai Nuclear Communication

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

  1. Предоставляет аппаратный абстрактный интерфейс для пользовательского пространства;
  2. Обеспечить стабильность и безопасность системы;
  3. Каждый процесс выполняется в виртуальной системе и обеспечивает такой общий интерфейс в пространстве пользователя и остальной части системы.

PS: В системах Linux системные вызовы являются единственным средством доступа к ядру в пространстве пользователя, за исключением исключений и ловушек, они являются единственной допустимой точкой входа для ядра.

2. API, POSIX и C библиотеки

В общем, приложения программируются через интерфейсы прикладного программирования (API), реализованные в пространстве пользователя, а не напрямую через системные вызовы.
PS: Приложения, которые используют этот интерфейс программирования, на самом деле не должны соответствовать системным вызовам, предоставляемым ядром.

Как показано на рисунке, чтобы углубить понимание:
Вставьте описание изображения здесь
существует принцип о дизайне интерфейса Unix « предоставить механизм (какие функции необходимо обеспечить) вместо стратегии (как выполнить эти функции) ». То есть системный вызов Unix абстрагирует функции для достижения определенной цели. Что касается того, как использовать эти функции, ядро ​​вообще не должно интересоваться.

3. Системный вызов

Доступ к системному вызову (часто называемому syscall в Linux) обычно осуществляется через вызов функции, определенный в библиотеке C.
При возникновении ошибки системного вызова библиотека C запишет код ошибки в глобальную переменную errno. Эта переменная может быть преобразована в строку ошибки, которую пользователь может понять, вызвав библиотечную функцию oerror ().

Q: Как определить системный вызов?
A: Для обеспечения совместимости 32-битных и 64-битных систем системные вызовы имеют разные типы возвращаемых значений в пользовательском пространстве и пространстве ядра, int в пользовательском пространстве и long в пространстве ядра. Имя функции также имеет префикс sys_. Например, системный вызов getpid () определяется как sys_getpid () в ядре.

3.1 Системный вызов, соответствующий функции системного вызова

В Linux каждому системному вызову присваивается номер системного вызова (уникальный).
Когда процесс в пространстве пользователя выполняет системный вызов, номер системного вызова используется для указания того, какой системный вызов должен быть выполнен.Процесс не упоминает имя системного вызова (поэтому номер системного вызова важен).

В Linux есть «не реализованный» системный вызов sys_ni_syscall (), который ничего не делает, кроме возврата -ENOSYS.

Ядро записывает список всех зарегистрированных системных вызовов в таблицу системных вызовов и сохраняет их в sys_call_table. В этой таблице указан уникальный номер системного вызова для каждого действительного системного вызова.

3.2 Производительность системных вызовов

Системные вызовы Linux выполняются быстрее, чем многие другие операционные системы.
почему?
A: Короткое время переключения контекста в Linux является важной причиной: вход и выход ядра оптимизированы для простоты и эффективности,
второй - это обработчик системных вызовов, и каждый системный вызов также очень прост.

4. Обработчик системного вызова

Программы пользовательского пространства не могут напрямую выполнять код ядра.
Приложение должно каким-то образом уведомить систему и позволить ядру выполнять системные вызовы в пространстве ядра от имени приложения.
Механизм уведомления ядра реализован с помощью программных прерываний : при возникновении исключения система переключается в состояние ядра для выполнения обработчика исключений - обработчика системного вызова.

4.1 Укажите соответствующий системный вызов

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

4.2 Передача параметров

В дополнение к номеру системного вызова большинство системных вызовов также требуют ввода некоторых внешних параметров. Поэтому, когда происходит ловушка, эти параметры должны быть переданы из пространства пользователя в ядро. Самый простой способ - сохранить эти параметры в регистре, например, передав номер системного вызова.

5. Внедрение системного вызова

5.1 Реализация системных вызовов

Первым шагом в реализации нового системного вызова является определение его цели. Каждый системный вызов должен иметь четкую цель. Не рекомендуется использовать многоцелевые системные вызовы в Linux (системный вызов выбирает выполнение разных задач путем передачи разных значений параметров).

5.2 Проверка параметров

Системные вызовы должны тщательно проверять, являются ли все их параметры допустимыми и действительными . Запретить пользователям передавать нелегальный ввод в ядро.
Каждый параметр должен быть проверен, чтобы убедиться, что он не только действителен и действителен, но и корректен. Процесс не должен позволять ядру получать доступ к ресурсам, к которым у него нет доступа.
Наиболее важным видом проверки является проверка правильности указателя, предоставленного пользователем.
Прежде чем получить указатель пространства пользователя, ядро ​​должно гарантировать следующее:

  1. Область памяти, на которую указывает указатель, принадлежит пользовательскому пространству. Процесс не должен уговаривать ядро ​​читать данные в пространстве ядра;
  2. Область памяти, на которую указывает указатель, находится в адресном пространстве процесса. Процессы не должны обманывать ядро ​​для чтения данных из других процессов;
  3. Если он читается, память должна быть помечена как читаемая, если она записана, память должна быть помечена как доступная для записи, если она исполняемая, память должна быть помечена как исполняемая. Процессы не должны обходить ограничения доступа к памяти.

Ядро предоставляет два метода для выполнения необходимых проверок и копирования данных назад и вперед между пространством ядра и пространством пользователя. Как показано в следующей таблице:

метод функция объяснение Возвращаемое значение
copy_to_user () Записать данные в пространство пользователя Первый параметр - это адрес памяти назначения в пространстве процесса, второй - адрес источника в пространстве ядра, а последний параметр - длина копируемых данных (количество байтов). Ошибка выполнения: количество байтов данных, которые не удалось скопировать; успех: 0; при возникновении вышеуказанной ошибки системный вызов возвращает стандарт -EFAULT
copy_from_user () Читать данные из пространства пользователя Также есть три параметра, аналогичных copy_to_user, данные в позиции, указанной вторым параметром, копируются в позицию, указанную первым параметром, а длина скопированных данных определяется третьим параметром. То же, что и выше

PS: copy_to_user () и copy_from_user () могут вызывать блокировку. Когда страницы, содержащие пользовательские данные, выгружаются на жесткий диск, а не в физическую память, происходит блокировка. На этом этапе процесс будет находиться в спящем режиме до тех пор, пока обработчик ошибок страницы не вернет страницу с жесткого диска обратно в физическую память.

Последняя проверка для законных полномочий .
Новая версия ядра Linux предоставляет более детальный механизм «возможностей», а новая система позволяет проверять специальные разрешения для определенных ресурсов. Вызывающая сторона может использовать функцию enable (), чтобы проверить, имеет ли она право работать с указанным ресурсом. Если она возвращает ненулевое значение, вызывающая сторона имеет право работать и возвращает 0, чтобы не иметь права.
например:

if(!capable(CAP_SYSY_BOOT))	/* 启动系统的系统管理员 */
	return -EPERM;

Обратитесь к <linux /abilities.h> для получения списка возможностей владения и их разрешений.

6. Контекст системного вызова

Ядро находится в контексте процесса при выполнении системных вызовов. Текущий указатель указывает на текущую задачу, процесс, который вызвал системный вызов.

В контексте процесса , ядро может спать (например, при блокировке вызова в системе или явного вызова графику ()) и может быть вытеснен .

6.1 Последний шаг привязки системного вызова

В: После записи системного вызова, как зарегистрировать его как формальный системный вызов?
A:

  1. Добавьте запись в конце таблицы системных вызовов (для большинства архитектур эта таблица находится в файле entry.s). Каждая аппаратная система, которая поддерживает системный вызов, должна выполнять эту работу. Начиная с 0, позиция системного вызова в этой таблице является его номером системного вызова;
  2. Для различных поддерживаемых архитектур номер системного вызова должен быть определен в <asm / unistd.h>. В файле привычка вызывать хорошие комментарии обычно добавляется каждые 5 записей в файле. Для удобства при звонке
  3. Системные вызовы должны быть скомпилированы в образ ядра (не могут быть скомпилированы в модули). Просто поместите его в соответствующий файл в kernel /, например sys.c, который содержит различные системные вызовы.

6.2 Доступ к системным вызовам из пространства пользователя - C Library / Linux Macro

Обычно системные вызовы поддерживаются библиотекой C. Включая стандартные заголовочные файлы и связываясь с библиотекой C, пользовательские программы могут использовать системные вызовы (или вызывать функции библиотеки, которые фактически вызываются функциями библиотеки).
Сам Linux предоставляет набор макросов для прямого доступа к системным вызовам (не нужно вводить заголовочные файлы библиотеки C), он устанавливает регистры и инструкции по перехвату вызовов.
Этими макросами являются _syscalln (), где n варьируется от 0 до 6. Это число параметров, которые необходимо передать системному вызову, потому что макрос должен знать, сколько параметров помещено в регистр в каком порядке.
Например:
определение системного вызова open ():

long open(const char *filename, int flags, int mode);

Без поддержки библиотеки C форма прямого вызова через макрос:

#define NR_open 5		/* <asm/unistd.h>中定义的系统调用号 */
_syscall3(long, open, const char*, filename, int, flags, int, mode)  

Для каждого макроса есть 2 + 2xn параметров.
Первый параметр соответствует типу возвращаемого значения системного вызова.
Второй параметр - это имя системного вызова.
Ниже приведены тип и имя каждого параметра, расположенного в порядке параметров системного вызова.

6.3 Почему бы не использовать системный вызов

Системы Linux стараются избегать простого добавления нового системного вызова всякий раз, когда появляется новая абстракция.
Обычно заменяют системный вызов, который вы хотите реализовать, следующим образом:
реализуйте узел устройства и реализуйте read () и write () для этого. Используйте ioctl для работы с конкретными настройками или получения конкретной информации.

  1. Некоторые интерфейсы, такие как семафоры, могут быть представлены дескрипторами файлов, поэтому они могут работать, как описано выше;
  2. Поместите добавленную информацию в виде файла в подходящее место в sysfs.
Опубликовано 91 оригинальных статей · Оценено 17 · 50000+ просмотров

рекомендация

отblog.csdn.net/qq_23327993/article/details/105382862