
Отint80h.org, учебник по языку ассемблера FreeBSD
Соглашение [Linux Calling] имеет большой недостаток по сравнению с Unix, по крайней мере, в том, что касается программирования на языке ассемблера: каждый раз, когда вы делаете вызов ядра, вы должны нажать регистры, а затем вытащить их позже. Это делает ваш код громоздким и медленным.
Продолжая, мы говорим о том, что FreeBSD поддерживает как Linux Convention, так и "Unix Convention"
Если вы пишете код специально для FreeBSD, вам всегда следует использовать соглашение Unix: это быстрее, вы можете хранить глобальные переменные в регистрах, вам не нужно маркировать исполняемый файл и вам не придется навязывать установку пакета эмуляции Linux на целевой системе.
Мне кажется странным, что Linux-способ будет громоздким и медленным. Кажется, есть два варианта,
- Сохраните только те регистры, которые вам необходимо сохранить, а именно:
- те изменчивые регистры, которые могут быть затерты системным вызовом (насколько мне известно
ecx
) - или регистры, необходимые для отправки соответствующих аргументов ядру для выполнения
syscall
(которые могут бытьeax
,ecx
,edx
,esi
,edi
,ebp
)
- те изменчивые регистры, которые могут быть затерты системным вызовом (насколько мне известно
- Сохраните 100% аргументов ядра в стеке.
Похоже, что FreeBSD — этохудшийСценарий кейса конвенции Linux. Что я упускаю? Как конвенция FreeBSD (которую они называют "Unix way") менее громоздкая и быстрая?
решение1
По моему мнению, это действительно сводится к мнению автора.
В соглашениях FreeBSD («Unix») вы помещаете аргументы в стек, указываете номер системного вызова в EAX
и вызываете прерывание 0x80 (с дополнительным операндом в стеке, поскольку ожидается вызов из отдельной функции).
В соглашении Linux i386 вы помещаете аргументы в соответствующие регистры и вызываете прерывание 0x80.
Аргумент о громоздкости/медленности, вероятно, исходит из того факта, что в соответствии с конвенцией Linux,звонящийнеобходимо разобраться с использованием регистров. Если системному вызову требуются аргументы в регистрах, которые содержат значения, которые интересуют вызывающего, ему необходимо их сохранить, что приводит к дополнительной работе;см. этот пример из библиотеки C. В этом примере системному вызову нужны значения в EAX, EBX, EDX, EDI и ESI; но вызывающий заботится только о сохранении EBX, EDI и ESI, поэтому он помещает в стек только их. Общий случай:немного сложнее(но это также результат работы со смесью языков C и ассемблера, попытки сгенерировать оптимальный код во всех случаях), однако при написании на языке ассемблера, что и является смыслом сайта, на который вы ссылаетесь, это не будет такой уж большой проблемой.
Мне кажется, что их шесть с половиной: в конвенции FreeBSD вы помещаете данные в стек во всех случаях, в конвенции Linux вы помещаете данные в стек (или куда-то еще) в зависимости от того, что вы делаете вокруг места вызова. Вы можете утверждать, что конвенция Linux позволяет выполнять более быстрый код, поскольку вы можете выполнять все свои вычисления в регистрах... КакРобОднако, как отмечает автор, в Linux регистры все равно в конечном итоге передаются (для создания экземпляра struct pt_regs
, который используется для предоставления аргументов функциям C, обрабатывающим системные вызовы), поэтому общая стоимость на стороне Linux выше, чем на стороне FreeBSD.
В любом случае, спорить о производительности, когда речь идет о стековом или регистровом коде вокруг системного вызова, кажется довольно педантичным, учитывая стоимость выполнения самого системного вызова. Любой сэкономленный цикл, конечно, хорош в абсолютном выражении, но относительное улучшение будет крошечным.