Пример минимального запуска системного вызова Linux
Linux устанавливает обработчик прерываний для 0x80
таким образом, чтобы он реализовывал системные вызовы, способ взаимодействия пользовательских программ с ядром.
.data
s:
.ascii "hello world\n"
len = . - s
.text
.global _start
_start:
movl $4, %eax /* write system call number */
movl $1, %ebx /* stdout */
movl $s, %ecx /* the data to print */
movl $len, %edx /* length of the buffer */
int $0x80
movl $1, %eax /* exit system call number */
movl $0, %ebx /* exit status */
int $0x80
Скомпилируйте и запустите с:
as -o main.o main.S
ld -o main.out main.o
./main.out
Результат: программа выводит на стандартный вывод:
hello world
и чисто выходит.
Вы не можете устанавливать свои собственные обработчики прерываний непосредственно из пользовательского пространства, потому что у вас есть только ring 3, а Linux не позволяет вам сделать это .
GitHub upstream . Протестировано на Ubuntu 16.04.
Лучшие альтернативы
int 0x80
был заменен лучшими альтернативами для системных вызовов: сначала sysenter
, затем VDSO.
x86_64 имеет новую syscall
инструкцию .
См. Также: Что лучше "int 0x80" или "syscall"?
Минимальный 16-битный пример
Сначала узнайте, как создать минимальный загрузчик ОС и запустить его на QEMU и реальном оборудовании, как я объяснил здесь: https://stackoverflow.com/a/32483545/895245
Теперь вы можете работать в 16-битном реальном режиме:
movw $handler0, 0x00
mov %cs, 0x02
movw $handler1, 0x04
mov %cs, 0x06
int $0
int $1
hlt
handler0:
/* Do 0. */
iret
handler1:
/* Do 1. */
iret
Это будет сделано по порядку:
Do 0.
Do 1.
hlt
: прекратить выполнение
Обратите внимание, как процессор ищет первый обработчик по адресу 0
, а второй по 4
: это таблица обработчиков, называемая IVT , и каждая запись имеет 4 байта.
Минимальный пример, который делает IO , чтобы сделать обработчики видимыми.
Пример минимального защищенного режима
Современные операционные системы работают в так называемом защищенном режиме.
У обработки в этом режиме больше опций, поэтому она более сложная, но дух тот же.
Ключевым этапом является использование инструкций LGDT и LIDT, которые указывают адрес структуры данных в памяти (таблица дескрипторов прерываний), которая описывает обработчики.
Минимальный пример