Различные способы вызова системных вызовов

В некотором коде я вижу, что системный вызов вызывается странным образом, возьмем для примера sched_yield:

#define __NR_sys_sched_yield    __NR_sched_yield

inline _syscall0(void, sys_sched_yield);

И тогда мы можем использовать sys_sched_yield(). Мне любопытно, в чем разница между прямым использованием sched_yield и этим способом. В src / include / asm / unistd определяется _syscall0:

#define _syscall0(type,name)  \
type name(void)     \
{        \
    long __res;      \
    __asm__ volatile ("int $0x80"  \
    : "=a" (__res)    \
    : "0" (__NR_##name));   \
    __syscall_return(type,__res);  \
}

person Will Wang    schedule 02.11.2015    source источник


Ответы (2)


Предположительно, это для систем, где sched_yield может быть недоступен. Что касается различий, sched_yield возвращает -1 при ошибке и устанавливает ERRNO, тогда как эта реализация предположительно возвращает исходное значение из ядра. Не могу сказать наверняка, поскольку вы не предоставили определение _syscall0, который должен быть макросом.

person Jester    schedule 02.11.2015
comment
Спасибо за ваш ответ. Я отредактировал свой пост, чтобы что-то обновить. - person Will Wang; 02.11.2015

Это Linux, использующий glibc. В BSD есть sched_yield, но есть собственная библиотека libc.

Это не странно. Макрос syscall0 выдает инструкцию ассемблера int 0x80, а номер системного вызова находится в регистре rax [архитектура x86]. Это стандартный интерфейс системных вызовов для Linux. Под капотом все функции linux glibc, которые являются оболочками системных вызовов, будут делать это [или использовать более современные пары инструкций sysenter / sysexit x86]

glibc имеет тенденцию «узурпировать» системные вызовы в своих функциях-оболочках и добавлять что-то вокруг них. Например, когда вы вызываете fork, он [в конечном итоге] вызывает __libc_fork, который выполняет огромное количество дополнительных функций, связанных с потоками, закрытием файлов и т. Д.

Как правило, glibc делает правильный выбор. Но иногда опытным программистам Linux-приложений требуется необработанное поведение системных вызовов, особенно если они пишут системные утилиты или программы / библиотеки, которые должны тесно взаимодействовать с ядром, драйверами устройств или оборудованием устройства.

На самом деле __libc_fork не вызывает fork системный вызов, он вызывает clone системный вызов, который [сложнее использовать] надмножество fork. Но старый добрый системный вызов fork все еще существует. Итак, если вы этого хотите, вам нужны макросы - и я уверен, что где-то есть определение sys_fork.

С другой стороны, glibc может реализовать sched_yield ala POSIX как nop, возвращающий -1 и устанавливающий для errno значение ENOSYS. Я только что проверил последний исходный код glibc и не смог найти "настоящую" реализацию, кроме mach. Вероятно, он действительно работает, я просто не мог его найти.

Иногда в linux есть системный вызов, но glibc не хочет его поддерживать, или они считают его слишком опасным для прикладного программиста, поэтому не используют функцию-оболочку. Итак, макросы - это способ «закончить» glibc.

Вероятная причина того, что glibc реализует sched_yield как nop, не говоря уже о posix, заключается в том, что они считают это "плохим" и, вероятно, советуют вам использовать вместо этого nanosleep. Я использовал оба, и они не одинаковы, в зависимости от вашего варианта использования и желаемого эффекта.

Иногда вам нужно выполнить необработанный встроенный системный вызов. Например, загрузчик ELF [каждая система, поддерживающая двоичные файлы ELF, должна иметь такой, а Linux - ld-linux.so] вызывается ядром для загрузки двоичного файла ELF. Он должен работать до того, как glibc.so станет доступным, потому что это то, что фактически связывает в glibc.so, загрузчик ELF должен иметь некоторые встроенные системные вызовы для open и read

Кроме того, в большинстве систем есть syscall библиотека функция, которая принимает переменное количество аргументов. Вы можете реализовать:

#define my_sched_yield() syscall(__NR_sched_yield)
#define my_read(_fd,_buf,_len) syscall(__NR_read,_fd,_buf,_len)

Эта функция обрабатывает возвращаемое системным вызовом значение / ошибку ядра и устанавливает errno. Это то, что должен был сделать макрос __syscall_return.

Префикс __NR_* - это то, что использует Linux, но в других системах есть AUE_* или SYS_*

person Craig Estey    schedule 02.11.2015