Linux Kernel (Ядро линукса) (часть 2)


Инструкция sysexit

Ассемблерная инструкция sysexit является парной ДЛЯ инструкции sysenter: она позволяет быстро переключиться из режима ядра в режим пользователя. Когда эта инструкция выполняется, управляющий блок процессора совершает следующие действия:
1. Прибавляет 16 к значению в регистре sysenter cs msr и загружает результат в регистр cs.
2. Копирует содержимое регистра edx в регистр eip.
3. Прибавляет 24 к значению в регистре sysenter cs msr и загружает результат в регистр ss.
4. Копирует содержимое регистра есх в регистр esp.
Поскольку в регистре sysenter cs msr находится селектор сегмента кода ядра, в регистр cs загружается селектор сегмента пользовательского кода, а в регистр ss — селектор сегмента пользовательских данных
В результате процессор переключается из режима ядра в режим пользователя и приступает к выполнению инструкции, адрес которой находится в регистре edx.

Код SYSENTERRETURN

Код за меткой sysenter return расположен на странице vsyscall, и он выполняется, когда выполнение системного вызова, сделанного с помощью инструкции sysenter, заканчивается С ПОМОЩЬЮ инструкции iret ИЛИ sysexit.

Код просто восстанавливает оригинальное содержимое регистров ebp, edx и есх, сохраненных в стеке режима пользователя, и возвращает управление интерфейсной процедуре из стандартной библиотеки:
SYSENTER_RETURN: popl ebp popl edx popl ecx ret

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

Подобно обычным функциям, системные вызовы часто имеют входные и/или выходные параметры, которые могут содержать числовые значения, адреса переменных в адресном пространстве процесса режима пользователя или даже адреса структур, содержащих указатели на функции режима пользователя Поскольку функции system call И sysenter entry ЯВЛЯЮТСЯ общими ТОЧ- ками входа для всех системных вызовов в Linux, у каждой есть хотя бы один параметр — номер системного вызова, передаваемый через регистр еах. Например, если прикладная программа вызывает интерфейсную процедуру
fork , В регистр еах Записывается ЧИСЛО 2 (то есть NR_fork) до того, как будет выполнена ассемблерная инструкция int $0x80 или sysenter. Поскольку значения в этот регистр записываются интерфейсными процедурами, включенными в библиотеку libc, программистам обычно нет дела до номера системного вызова.

Системному вызову fork другие параметры не требуются. Однако многим системным вызовам нужны дополнительные параметры, которые прикладная программа должна передать явным образом. Например, системному вызову mmap может понадобиться до шести параметров (не считая номера системного вызова).

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

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

Однако, чтобы передать параметры через регистры, необходимо выполнение двух условий:
- длина каждого параметра не может превышать длину регистра (32 бита)4;
- количество параметров не может быть больше шести (не считая номер системного вызова, передаваемый через регистр еах), поскольку количество регистров в архитектуре 80x86 весьма ограничено.

Первое условие выполнено всегда, поскольку, согласно стандарту POSIX, большие параметры, не умещающиеся в 32-битовом регистре, должны передаваться по ссылке. Типичным примером является системный вызов settimeofday , который обращается к 64-битовой структуре.

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

Для хранения номера и параметров системного вызова используются следующие регистры, в порядке возрастания: еах (для номера системного вызова), ebx, есх, edx, esi, edi И ebp. Как МЫ видели ранее, функции system call и sysenter entry о сохраняют значения этих регистров в стеке режима ядра при помощи макроса save all. Таким образом, когда служебная процедура системного вызова обратится к стеку, она найдет адрес возврата в функцию system call или sysenter entry , а за ним — параметр, сохраненный в регистре ebx (первый параметр системного вызова), параметр, сохраненный в регистре есх, и т. д. Такая конфигурация стека в точности соответствует конфигурации для вызова обычной функции, и, следовательно, служебная процедура легко найдет свои параметры с помощью обычных конструкций языка С.

Рассмотрим пример. Служебная процедура sys_write, работающая с системным вызовом write , объявлена следующим образом:
int sys_write (unsigned int fd, const char buf, unsigned int count)
Компилятор С генерирует функцию на языке ассемблера, которая ожидает найти параметры fd, buf и count наверху стека, сразу под адресом возврата, в ячейках, использованных для сохранения содержимого регистров ebx, есх и edx соответственно.

В некоторых случаях, даже если системному вызову не требуются параметры, соответствующая служебная процедура должна знать содержимое регистров процессора непосредственно перед выполнением системного вызова. Например, функция do fork , реализующая системный вызов fork , должна знать содержимое регистров, чтобы продублировать их в поле thread процес- са-потомка В таких случаях один параметр типа pt regs позволяет служебной процедуре найти значения, сохраненные в стеке режима ядра, с помощью макроса save all:
int sys_fork (struct pt_regs regs)
Значение, возвращаемое служебной процедурой, должно быть записано в регистр еах. Это делается автоматически компилятором С, когда выполняется оператор return

Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | Следующая страница




Возможно, Вас также заинтересует:

ОС Knoppix - это Linux без про...

ВведениеЕсли вы цените свое время, умеете считать деньги и знаете стоимость информации, то эта книга...

Linux Kernel (Ядро линукса) (ч...

Спин-блокировкаСпин-блокировка необходима в многопроцессорной системе, потому что могут возникнуть...

Linux Kernel (Ядро линукса) (ч...

Копирование при записи В системах Unix первых поколений создание процесса было реализовано довольно...

Linux Kernel (Ядро линукса) (ч...

Буферы блоков и головы буферовУ каждого буфера есть дескриптор голова буфера, имеющий тип buffer...