Ну чтож, начинаем погружение в интересную тему - рассмотрим то, без чего не обходится ни одно системное приложение - системные и библиотечные вызовы.
Тут, на самом деле, можно довольно глубоко копнуть. Если проявится ваш интерес, сделаем ряд постов, где вдоль и поперек разберем принцип работы вызовов, практики их использования и т.д.
Что такое системный вызов?
Можно считать, что все пользовательские программы работают внутри некоторой песочницы, из которой нельзя так просто выполнять ряд действий: получать доступ к файлам, передавать данные по сети, выводить текст на экран, создавать процессы и т.д. Для этого нужно обратиться к внутренним сервисам ядра и попросить его сделать что требуется через системные вызовы.
Системный вызов изменяет состояние процессора, переводя его из пользовательского режима в режим ядра, позволяя ему таким образом получить доступ к защищенной памяти ядра.
(Системные вызовы Linux перечисляются на странице руководства syscalls(2))
Это может вас немного напугать, но, обычно, системные вызовы недоступны напрямую. Все, с чем вы работаете - это тонкие функции-обертки библиотки glibc. Да-да, не удивляйтесь: read, write, fork являются всего лишь понятным пользователю интерфейсом, через который отрабатывают системные вызовы.
И да, важно помнить то, что системные вызовы - это платформозависимая история. Чтение файлов, взаимодействие с процессами, работа с сокетами, управление памятью и прочие системные операции реализованы по разному на каждой системе и могут отличаться друг от друга по API.
Как сделать прямой системный вызов?
Иногда бывает полезно явно обраться к системному вызову в случае, если для него не реализована сишная функция-обертка. Сделать это можно через библиотечный вызов syscall() из unistd.h, который первым параметром принимает номер системного вызова из "sys/syscall.h", а далее - аргументы самого вызова:
extern long int syscall (long int __sysno, ...) __THROW;
Таким образом, системный вызов write() можно инициировать по разному:
#include <unistd.h>
#include <sys/syscall.h>
int main(void) {
write(1, "hello, world!\n", 14)
syscall(SYS_write, 1, "hello, world!\n", 14);
return 0;
}
Что такое библиотечный вызов?
Библиотечным вызовом можно считать вызов функции стандартной библиотеки C, в которой не содержится прямой реализации системного вызова: системного прерывания, переноса аргументов в определенные, необходимые ядру, регистры, копирования номера системного вызова в регистр (%eax) и т.д.
Эти функции предназначены для решения широкого круга разнообразных задач: открытия файлов, преобразования времени в формат, понятный человеку, сравнения двух символьных строк и т.д.
Многие библиотечные функции вообще не используют системные вызовы (например, функции для работы со сроками). С другой стороны, некоторые библиотечные функции являются надстройками над системными вызовами с платформозависимой реализацией.
Например, библиотечная функция fopen() использует для открытия файла системный вызов open().
Зачастую библиотечные функции разработаны для предоставления более удобного интерфейса вызова по сравнению с тем, что имеется у исходного системного вызова.
Например, функция printf() предоставляет форматирование вывода и буферизацию данных, а внутренний системный вызов write() просто выводит блок байтов.
Аналогично этому функции malloc() и free() выполняют различные вспомогательные задачи, существенно облегчающие выделение и высвобождение оперативной памяти по сравнению с использованием исходного системного вызова brk().
#linuxcore #linux #cpp #cppcore