В качестве примера давайте рассмотрим, как реализована функция C write()
в Linux.
Реализация библиотеки C по существу:
ssize_t write(int fd, const void *buf, size_t count)
{
long retval;
retval = syscall(__NR_write, fd, buf, count);
if (retval < 0) {
errno = -retval;
return -1;
} else
return retval;
}
Функция syscall()
специфична для каждой аппаратной архитектуры, операционной системы и ядра. Обычно написано на ассемблере. Он загружает параметры в конкретные регистры (в соответствии с используемыми соглашениями о вызовах ядра) и вызывает в ядре.
В этот момент граница безопасности пересекается, и выполнение перемещается в пространство ядра.
Сама реализация syscall write()
в ядре использует таблицу описания файлов вызывающего процесса для поиска набора файловых операций, специфичных для этого файла или сокета; Точный путь к коду зависит от того, является ли дескриптор файла файлом (и в этом случае файловой системой, в которой он находится), каналом, сокетом, символьным устройством или блочным устройством.
Почти все системные вызовы возвращают неотрицательное значение для успеха и отрицательный код ошибки для ошибки.
В ядре Linux реализация системного вызова редко вызывает другую функцию, которая реализует системный вызов. Вместо этого общие части учитывают внутренние функции ядра, которые вызываются обеими функциями реализации syscall. Это облегчает обслуживание.
Если мы посмотрим, как ядро Linux реализует системные вызовы faccessat()
и access()
, мы увидим, что оба просто вызывают внутреннюю функцию ядра do_faccessat()
.