Ответ 1
С точки со стороны пользователя механики идентичны созданию syscall в 32-битном собственном ядре - все коды usermode, включая 32-разрядный glibc, работают одинаково.
Со стороны ядра устанавливаются старые точки входа IA32 из пользовательского пространства (например, int 0x80
) для вызова процедуры ассемблера ia32_syscall
. (Переход к ядру занимает процессор, загружающий селектор сегмента кода ядра, что приводит к переходу в 64-битный "длинный" режим).
Процедура ia32_syscall
затем перетасовывает некоторые аргументы вокруг в соответствии с соглашением о вызове syscall x86_64:
movl %edi,%r8d
.if \noebp
.else
movl %ebp,%r9d
.endif
xchg %ecx,%esi
movl %ebx,%edi
movl %edx,%edx /* zero extension */
Затем он использует номер syscall IA32 для вызова функции через таблицу, ia32_sys_call_table
. Это, по существу, соответствует номерам столбцов IA32 с реализацией самонастройки (значения syscall сильно различаются между IA32 и x86_64). Первая часть этой таблицы выглядит так:
ia32_sys_call_table:
.quad sys_restart_syscall
.quad sys_exit
.quad stub32_fork
.quad sys_read
.quad sys_write
Для большинства системных вызовов реализация x86_64 теперь может быть вызвана напрямую - например, exit()
. Для других, таких как fork()
, предоставляется оболочка, которая правильно реализует ожидаемую семантику IA32 (в частности, если требуется расширение знака от 32 до 64 бит).
Как вы можете видеть, накладные расходы в коде ядра минимальны - несколько тривиальных модификаций для регистрации значений и для нескольких функций - дополнительный вызов функции. Я не уверен, что загрузка селектора сегмента кода, который приводит к переходу от 32-битного режима к 64-битовому режиму, медленнее для процессора, чем тот, который этого не делает, - проверьте руководства по архитектуре процессора для этого.