Что подразумевается под «системным вызовом», если не за реализацией на языке программирования?


14

Я хотел бы понять термин «системный вызов». Мне знакомо, что системные вызовы используются для получения служб ядра из приложения пользовательского пространства.

Часть, с которой мне нужно уточнить, - это разница между «системным вызовом» и «реализацией системного вызова на C».

Вот цитата, которая смущает меня:

В Unix-подобных системах этот API обычно является частью реализации библиотеки C (libc), такой как glibc, которая предоставляет функции-оболочки для системных вызовов, часто называемых так же, как системные вызовы, которые они вызывают

Каковы «системные вызовы, которые они вызывают»? Где их источник? Могу ли я включить их непосредственно в мой код?

Является ли «системный вызов» в общем смысле просто интерфейсом, определенным POSIX, но чтобы реально увидеть реализацию, можно изучить источник C и посмотреть, как на самом деле происходит фактическое пространство пользователя для связи с ядром?

Фоновое примечание: я пытаюсь понять, в конце концов, каждая функция c заканчивается взаимодействием с устройствами из /dev.

Ответы:


21

Системные вызовы как таковые являются концепцией. Они представляют действия, которые процессы могут попросить ядро ​​выполнить.

Эти системные вызовы реализованы в ядре UNIX-подобной системы. Эта реализация (написанная на C и в asm для небольших частей) фактически выполняет действие в системе.

Затем процессы используют интерфейс, чтобы запросить у системы выполнение системных вызовов. Этот интерфейс определяется POSIX. Это набор функций стандартной библиотеки C. Они на самом деле являются обертками, они могут выполнять некоторые проверки и затем вызывать системную функцию в ядре, которая сообщает ему, что он должен выполнять действия, требуемые системным вызовом. И хитрость в том, что те функции, которые являются интерфейсом, называются так же, как системные вызовы, и часто называются «системными вызовами».

Вы можете вызвать функцию в ядре, которая выполняет системный вызов напрямую через механизм, специфичный для системы. Проблема в том, что он делает ваш код абсолютно не переносимым.

Итак, системный вызов это:

  • концепция, последовательность действий, выполняемых ядром для предоставления услуги пользовательскому процессу
  • функция стандартной библиотеки C, которую вы должны использовать в своем коде, чтобы получить этот сервис из ядра.

1
У вас есть пример «функции-обертки» и реальный системный вызов? (пути к файлам в Linux или ссылки на источники)
TheMeaningfulEngineer

3
Например, это реализация getpidсистемного вызова в ядре Linux: lxr.free-electrons.com/source/kernel/timer.c?v=2.6.35#L1337 . И это функция-обертка в стандартной библиотеке GNU C glibc-2.19: fossies.org/dox/glibc-2.19/… .
lgeorget

@Igeorget: ваши ссылки больше не работают. Обновленная ссылка для реализации ядра: github.com/torvalds/linux/blob/… . Я не мог найти то, что делает glibc в эти дни, этот код невозможно перемещаться.
rchard2scout

6

Системный вызов является способом задать операционную систему (ядро) , чтобы сделать некоторые операции от имени вашей программы, что программа не может сделать сам по себе (или просто неудобно). Причина неспособности выполнить какую-либо операцию обычно заключается в том, что случайное выполнение программы может поставить под угрозу целостность системы, например, выполнение ввода-вывода (непосредственно в ОЗУ, перезапись чего-либо).

POSIX определяет интерфейс для программ, определенные функции, которые ваша программа может вызывать. Некоторые из них переводят более или менее непосредственно на системные вызовы, другие требуют более тщательной проработки. Это среда выполнения для вашего языка, например, библиотеки C, которая отвечает за предложение интерфейса POSIX, а также за упаковку аргументов и получение результатов для передачи вызывающей стороне.

Системы Unixy предлагают интерфейсы POSIX более или менее напрямую, как системные вызовы. Обычно есть способ вызывать системные вызовы напрямую, ищите syscall(2)подробности о том, как использовать эту возможность в Linux.


1
Вы затрагиваете важный момент, против которого другие ответы просто противоречат. Любая функция , которая способна разумно программист может написать для себя (например strlen, strcpy, sqrt, и qsort) может быть и , вероятно , находится в пространстве пользователя, загружается из библиотеки. (В основном libc; математические функции, подобные sqrtи тригонометрические и гиперболические функции, вероятно, находятся в libm, математической библиотеке.)… (Продолжение)
Скотт,

1
(продолжение) ... Но нет никакого способа , пользователь может написать свою собственную fork, killили openфункцию, так как они требуют доступа к пространству ядра операционной системы в памяти (например, таблицы процесса) или привилегированные команды (например, I / O). Поэтому код, выполняющий эти функции, должен находиться в ядре операционной системы; следовательно, системные функции или системные вызовы.
Скотт

5

Конечно, давайте сделаем «как много направлений мы можем посмотреть на этого слона»? вещь.

Фактический системный вызов в вашей встроенной программе - машинная инструкция, которая запускает повышение привилегий в режиме ядра, а в самом ядре это код, который эта инструкция вызывает. Код libc (и каждая языковая среда выполнения) настраивает машинные регистры и параметры хранения, где код ядра ожидает их найти, что может быть странным местом из-за ограничений на эту машинную инструкцию.

Когда-то в самом коде ОС, есть немного раскручивания зеркального отображения машинно-специфических вещей, которые выполняла среда выполнения пользователя, а затем совершенно обычный вызов подпрограммы.
Если вы хотите увидеть, как это работает в полномасштабную ОС, вытаскивать исходный код ядра ( git clone https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/) и сделать , например git grep -i system\ call. Потяните источник glibc и сделайте то же самое.


Это правда, но рыться в Linux или glibc немного сложновато ...
vonbrand

3

В Linux, по крайней мере, механизм системных вызовов работает в большинстве архитектур, помещая некоторые специально отформатированные данные (обычно это своего рода структура) в некоторые регистры или предопределенные адреса памяти.

Однако проблема заключается в том, чтобы заставить процессор переключаться в пространство ядра, чтобы он мог запустить привилегированный код ядра для обслуживания вызова. Это делается путем принудительной обработки какой-либо ошибки (ошибка деления на 0, неопределенного переполнения или ошибки сегмента и т. Д.), Что заставляет ядро ​​взять на себя выполнение для обработки ошибки.

Обычно ядро ​​обрабатывает ошибки, либо убивая вызывающий процесс, либо запуская пользовательский обработчик. Однако в случае системного вызова он вместо этого проверит предопределенные регистры и области памяти, и если они содержат запрос системного вызова, он выполнит это, используя данные, предоставленные пользовательским процессом в структуре в памяти. Обычно это нужно делать с какой-то специально созданной вручную сборкой, и чтобы упростить использование системного вызова для пользователя, системная библиотека C должна обернуть его как функцию. Для интерфейса более низкого уровня, пожалуйста, смотрите http://man7.org/linux/man-pages/man2/syscall.2.html для получения некоторой информации о том, как работают системные вызовы и как вы можете тогда вызывать без оболочки C.

Это слишком упрощено, это не так во всех архитектурах (в mips есть специальная инструкция syscall) и не обязательно работает одинаково на всех ОС. Тем не менее, если у вас есть какие-либо комментарии или вопросы, пожалуйста, задавайте.

Исправлено: Обратите внимание, что относительно вашего комментария о вещах в / dev / это на самом деле интерфейс более высокого уровня с ядром, а не нижний. Эти устройства фактически используют около 4 системных вызовов. Запись для них аналогична системному вызову записи, чтению системного вызова чтения, открытию / закрытию их, эквивалентным системным вызовам open и close, и запуску ioctl вызывает специальный системный вызов ioctl, который сам по себе является интерфейсом для доступа к одному из многих системных ioctl. вызовы (специальные, обычно вызовы, специфичные для устройства, с слишком узким использованием, чтобы написать для них целый системный вызов).


1

С каждым системным вызовом связано целое число. Это целое число является функцией возвращаемого значения системного вызова, количества аргументов системного вызова и типа аргументов. Этот номер системного вызова является не чем иным, как смещением в глобальный вектор системных вызовов, этот вектор, который доступен только в привилегированном режиме, содержит указатель на соответствующие обработчики. В процессе вызова системного вызова будет сгенерировано программное прерывание (прерывание прерывания), следовательно, будет запущен обработчик прерываний, который определяет, какой системный вызов следует вызывать. Затем ядро ​​скопирует аргументы системного вызова, переданного пользователем, находящимся в стеке, в регистры процессора, и по завершении запрошенной услуги данные будут скопированы обратно в стек из регистров процессора. Это одна из причин, по которой аргументы системных вызовов ограничены,


Каждый вызов (операция) внутренне идентифицируется номером, правда. Но число зависит от операции , а не от возвращаемого значения или количества аргументов. Объяснение того, как это работало в userland на x86, здесь
vonbrand
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.