Системные вызовы

Как программа просит ядро об услуге — и почему это не обычный вызов функции.

Системный вызов (system call) — это запрос программы к ядру ОС на выполнение привилегированной операции: открыть файл, выделить память, создать процесс, отправить данные в сеть.

Чем системный вызов отличается от обычной функции

Когда вы вызываете обычную функцию, процессор просто прыгает на её адрес — всё в одном режиме. Системный вызов другой: он переключает процессор в режим ядра, передаёт управление ядру, а после выполнения возвращает результат и режим. Это похоже на обращение к «другой власти», а не на простой переход внутри своей программы.

Обычно вы не пишете системные вызовы вручную. Их прячут библиотечные функции. Например, в C функция printf() в итоге дёргает системный вызов write(), а fopen()open(). Программисту удобнее звать библиотеку, а та уже общается с ядром.

Категории системных вызовов

КатегорияПримеры (Linux)Зачем
Процессыfork, execve, exit, waitсоздание и завершение процессов
Файлыopen, read, write, closeработа с файлами
Памятьmmap, brk, munmapвыделение и освобождение памяти
Устройства/сетьioctl, socket, sendввод-вывод и сеть
Информацияgetpid, time, unameсведения о системе

Как выглядит реальный код

Вот фрагмент на C, который открывает файл и читает из него. Это реальный системный код — он не запускается в браузере, поэтому здесь только подсветка. Обратите внимание: open, read, close — это и есть системные вызовы.

int fd = open("data.txt", O_RDONLY);   // системный вызов open
char buf[128];
int n = read(fd, buf, sizeof(buf));     // системный вызов read
close(fd);                              // системный вызов close

А вот как с помощью утилиты strace можно подсмотреть, какие системные вызовы делает любая программа в Linux:

strace -c ls
# покажет таблицу: какие системные вызовы и сколько раз вызвала команда ls

Почему вызовов «много, но дёшево быть не должно»

Каждый системный вызов — это переключение режима, а оно стоит сотни-тысячи тактов. Поэтому хорошие программы стараются делать меньше системных вызовов, но крупнее. Считать файл одним read на 64 КБ быстрее, чем 65536 вызовами по одному байту. Эту экономию обеспечивает буферизация — к ней мы ещё вернёмся.

Модель «запрос — обслуживание»

Симулируем счётчик системных вызовов: программа просит ядро об операциях, ядро их обслуживает. Это игрушечная модель, но она показывает суть «программа просит — ядро делает».

requests = ["open", "read", "read", "read", "write", "close"]
cost = {"open": 50, "read": 30, "write": 40, "close": 20}  # условные такты

total = 0
for call in requests:
    c = cost[call]
    total += c
    print(f"syscall {call:6} -> ядро обслужило за {c} тактов")

print(f"Всего системных вызовов: {len(requests)}")
print(f"Суммарная стоимость: {total} тактов")
print(f"Если бы read шёл по байту x100, добавилось бы {30*100} тактов")

Вывод:

syscall open   -> ядро обслужило за 50 тактов
syscall read   -> ядро обслужило за 30 тактов
syscall read   -> ядро обслужило за 30 тактов
syscall read   -> ядро обслужило за 30 тактов
syscall write  -> ядро обслужило за 40 тактов
syscall close  -> ядро обслужило за 20 тактов
Всего системных вызовов: 6
Суммарная стоимость: 200 тактов
Если бы read шёл по байту x100, добавилось бы 3000 тактов

Итог

  • Системный вызов — запрос к ядру на привилегированную операцию.
  • В отличие от обычной функции он переключает процессор в режим ядра.
  • Категории: процессы, файлы, память, устройства/сеть, информация.
  • Библиотеки (например, printf) прячут системные вызовы за удобным API.
  • Системные вызовы дороги — лучше делать их реже, но крупнее.
Проверьте себя
1. Чем системный вызов принципиально отличается от обычного вызова функции?
AОн всегда быстрее обычной функции
BОн переключает процессор в режим ядра
CОн не возвращает результат
DОн выполняется без участия процессора
2. К какой категории относится системный вызов fork?
AРабота с файлами
BУправление процессами
CУправление памятью
DСетевые операции
3. Почему лучше делать меньше системных вызовов, но крупнее?
AЯдро ограничивает число вызовов в секунду
BКаждый вызов переключает режим, а это дорого
CМаленькие вызовы запрещены стандартом
DКрупные вызовы не нагружают диск
Поддержать проект