ОБЗОР
#define _GNU_SOURCE /* см. feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h> /* для определений SYS_xxx */
long syscall(long number, ...);
ОПИСАНИЕ
syscall() — это маленькая библиотечная функция, которая делает системный
вызов, чей интерфейс ассемблерного языка указывается в number, с
дополнительными аргументами. Выполнение syscall() нужно, например, для
запуска системного вызова, у которого нет обёрточной функции в библиотеке C.
При вызове syscall() сохраняет регистры ЦП до выполнения системного вызова, восстанавливает регистры при возврате из системного вызова и если возникла ошибка, то сохраняет любой код, полученный от системного вызова, в errno(3).
Символьные константы для системных вызовов можно найти в заголовочном файле <sys/syscall.h>.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
Возвращаемое значение определяется вызываемым системным вызовом. При
успешном выполнении обычно возвращается 0. При ошибке возвращается -1, при
этом код ошибки сохраняется в errno.
ЗАМЕЧАНИЯ
Вызов syscall() впервые появился в 4BSD.
Требования, зависящие от архитектуры
Каждый ABI архитектуры имеет свои собственные требования по передаче аргументов системного вызова в ядро. Для системных вызовов, имеющих обёртку в glibc (большинство системных вызовов), копирование аргументов в правильные регистры с учётом архитектуры выполняется в самой glibc. Однако при выполнении системного вызова через syscall(), вызывающий сам должен учитывать особенности архитектуры; чаще всего это относится к 32-битным архитектурам.Например, на архитектуре ARM Embedded ABI (EABI) 64-битное значение (long long) должно быть выровнено по чётной паре регистров. То есть, при использовании syscall() вместо обёрточной функции glibc системный вызов readahead() на ARM вызывался бы с учётом EABI следующим образом:
syscall(SYS_readahead, fd, 0, (unsigned int) (offset >> 32), (unsigned int) (offset & 0xFFFFFFFF), count);
Так как смещение аргумента 64 бита, и первый аргумент (fd) передаётся в регистре r0, вызывающий должен разделить и выровнять 64-битное значение так, чтобы оно передавалось в паре регистров r2/r3. Это выполняется вставкой пустого значения в r1 (второго аргумент 0).
Подобные сложности можно видеть на MIPS с O32 ABI, на PowerPC с 32-битным ABI и на Xtensa.
Это относится к системным вызовам fadvise64_64(2), ftruncate64(2), posix_fadvise(2), pread64(2), pwrite64(2), readahead(2), sync_file_range(2) и truncate64(2).
Архитектурные соглашения по вызовам
В каждой архитектуре есть собственный способ передачи аргументов вызову ядра. Особенности различных архитектур перечислены в двух таблицах ниже.Поля первой таблицы: инструкция для перехода в режим ядра (может быть не быстрым или лучшим способом перехода в ядро, лучше использовать vdso(7)), регистр для указания номера системного вызова, регистр возврата результата работы системного вызова и регистр сигнализации ошибки.
Во второй таблице показаны регистры, которые используются для передачи аргументов в системный вызов.
Заметим, что эти таблицы не описывают полное соглашение о вызове — некоторые архитектуры могут затирать другие регистры и это здесь не описано.
ПРИМЕР
#define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> #include <signal.h> int main(int argc, char *argv[]) { pid_t tid; tid = syscall(SYS_gettid); tid = syscall(SYS_tgkill, getpid(), tid, SIGHUP); }