Подтвердить что ты не робот

Как 32-разрядные приложения выполняют системные вызовы в 64-разрядной Linux?

Некоторые (многие? все?) 64-разрядные 1 дистрибутивы Linux позволяют запускать 32-разрядные приложения, отправляя параллельные коллекции 32-разрядных и 64-разрядных библиотек (включая libc). Таким образом, 32-битное приложение может связываться с 32-разрядными библиотеками и работать под 64-разрядным ядром.

Мне хотелось бы знать, как 32-разрядные приложения выполняют системные вызовы в 64-битном ядре. Я подозреваю, что ответ находится где-то в libc и/или исходном коде ядра, но мне было бы много времени погружаться в источник, так как я не знаю, где искать.

И еще более важный вопрос: есть ли накладные расходы на производительность? 2 Логически, вызов из 32-битного системного вызова приложения придется перевести на 64-битную внутреннюю среду ядра. Как и где это достигается?

1 "32-бит" = IA-32 и "64-бит" = AMD64
2 В вашем ответе сделайте предположение, что это важно:)

4b9b3361

Ответ 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-битовому режиму, медленнее для процессора, чем тот, который этого не делает, - проверьте руководства по архитектуре процессора для этого.