Самый быстрый системный вызов Linux
В системе Intel x86-64, которая поддерживает syscall
и sysret
Какой" самый быстрый " системный вызов из 64-разрядного пользовательского кода на ядре vanilla?
В частности, это должен быть системный вызов, который осуществляет syscall
/sysret
пользователь переход ядра1, но делает наименьший объем работы сверх этого. Ему даже не нужно делать сам syscall: какой-то тип ранней ошибки, которая никогда не отправляется в конкретный вызов на стороне ядра, хорош, пока он не замедляется. путь из-за этого.
Такой вызов может быть использован для оценки необработанных syscall
и sysret
накладных расходов независимо от любой работы, выполняемой вызовом.
1 в частности, это исключает вещи, которые кажутся системными вызовами, но реализуются в VDSO (например, clock_gettime
) или кэшируются средой выполнения (например, getpid
).
4 ответа:
Тот, который не существует, и поэтому возвращается-ENOSYS быстро.
Из arch / x86 / entry / entry_64.S:
#if __SYSCALL_MASK == ~0 cmpq $__NR_syscall_max, %rax #else andl $__SYSCALL_MASK, %eax cmpl $__NR_syscall_max, %eax #endif ja 1f /* return -ENOSYS (already in pt_regs->ax) */ movq %r10, %rcx /* * This call instruction is handled specially in stub_ptregs_64. * It might end up jumping to the slow path. If it jumps, RAX * and all argument registers are clobbered. */ #ifdef CONFIG_RETPOLINE movq sys_call_table(, %rax, 8), %rax call __x86_indirect_thunk_rax #else call *sys_call_table(, %rax, 8) #endif .Lentry_SYSCALL_64_after_fastpath_call: movq %rax, RAX(%rsp) 1:
Используйте недопустимый номер системного вызова, чтобы диспетчерский код просто возвращался с
eax = -ENOSYS
вместо отправки в функцию обработки системных вызовов вообще.Если только это не заставляет ядро использовать медленный путь
iret
вместоsysret
/sysexit
. Это может объяснитьизмерения , показывающие недопустимое число, которое на 17 циклов медленнее, чемsyscall(SYS_getpid)
, потому что обработка ошибок glibc (установкаerrno
), вероятно, не объясняет этого. Но из моего чтения исходного кода ядра, Я не вижу никаких причин, почему бы ему не использоватьsysret
при возврате-ENOSYS
.
Этот ответ для
sysenter
, неsyscall
. Вопрос первоначально сказал:sysenter
/sysret
( что было странно, потому чтоsysexit
идет сsysenter
, аsysret
идет сsyscall
). Я ответил на основеsysenter
для 32-битного процесса на ядре x86-64.Родной 64-битный
syscall
обрабатывается более эффективно внутри ядра. (Обновление; с исправлениями смягчения Meltdown / Spectre, он все еще депеши через Cdo_syscall_64
в 4.16-rc2).
My Что произойдет, если вы используете 32-битный int 0x80 Linux ABI в 64-битном коде? Q&A дает обзор ядра точки входа системных вызовов из режима compat в ядро x86-64 (
entry_64_compat.S
). Этот ответ просто берет соответствующие части этого.Ссылки в этом ответе и здесь относятся к источникам Linux 4.12, которые не содержат манипуляций с таблицами страниц для смягчения распада, так что это будет значительные дополнительные накладные расходы.
int 0x80
иsysenter
имеют разные точки входа. Ты ищешь ...entry_SYSENTER_compat
. AFAIK,sysenter
всегда идет туда, даже если вы выполняете его в 64-битном процессе пользовательского пространства. Точка входа Linux выдвигает константу__USER32_CS
в качестве сохраненного значения CS, поэтому она всегда будет возвращаться в пользовательское пространство в 32-разрядном режиме.После нажатия регистров, чтобы построить
struct pt_regs
в стеке ядра, естьTRACE_IRQS_OFF
крюк (не знаю, сколько инструкций это составляет to), тоcall do_fast_syscall_32
, который записан в C. (собственная 64-битнаяsyscall
диспетчеризация выполняется непосредственно из asm, но 32-битные системные вызовы compat всегда отправляются через C).
do_syscall_32_irqs_on
вarch/x86/entry/common.c
это довольно легкий вес: просто проверка, отслеживается ли процесс (я думаю, что именно такstrace
может перехватывать системные вызовы черезptrace
), затем... if (likely(nr < IA32_NR_syscalls)) { regs->ax = ia32_sys_call_table[nr]( ... arg ); } syscall_return_slowpath(regs); }
AFAIK, ядро может использовать
sysexit
после возврата этой функции.Таким образом, обратный путь одинаков независимо от того, имел ли EAX допустимый номер системного вызова, и, очевидно, возвращение без диспетчеризации вообще является самым быстрым путем через эту функцию, особенно в ядре со спектром смягчения, где косвенная ветвь в таблице указателей функций будет проходить через retpoline и всегда неправильно интерпретируется.
Если вы хотите действительно протестировать sysenter / sysexit без всех этих дополнительных накладных расходов, вам нужно будет модифицировать Linux, чтобы поставить гораздо более простую точку входа без проверки трассировки или выталкивания / выталкивания всех зарегистрирует.
Вы, вероятно, также захотите изменить ABI, чтобы передать обратный адрес в регистре (как
syscall
делает сам по себе) вместо сохранения в стеке пользовательского пространства, который делает текущийsysenter
ABI Linux; он долженget_user()
прочитать значение EIP, к которому он должен вернуться.
Если все эти накладные расходы являются частью того, что вы хотите измерить, вы определенно все настроены с eax, который дает вам
-ENOSYS
; в худшем случае вы получите один дополнительный промах ветви из диапазона-проверьте, если ветвь предикторы являются горячими для этой ветви, основанной на обычных 32-разрядных системных вызовах.
Некоторые системные вызовы даже не проходят через переход user->kernel, читай vdso(7).
Я подозреваю, что этиVDSO системные вызовы (например, Время(2), ...) являются самыми быстрыми. Вы можете утверждать, что нет никаких" реальных " системных вызовов.
Кстати, вы можете добавить фиктивный системный вызов к вашему ядру (например, некоторый системный вызов, всегда возвращающий 0, или системный вызов hello world, см. Также this) и измерить его.
В этот бенчмарк Брендана Грегга (связанный с этой записью в блоге, которая представляет интерес для чтения по теме)
close(999)
(или какой-то другой fd, не используемый) рекомендуется.