Ядро Linux в комментариях

       

Что такое системный вызов?


Системный вызов возникает, когда пользовательский процесс (наподобие emacs) требует некоторой службы, реализуемой ядром (такой как открытие файла), и вызывает специальную функцию (например, open). В этот момент пользовательский процесс переводится в режим ожидания. Ядро анализирует запрос, пытается его выполнить и передает результаты пользовательскому процессу, который затем возобновляет свою работу. Несколько ниже весь механизм будет рассматриваться более подробно.

Системные вызовы в общем случае защищают доступ к ресурсам, которыми управляет ядро, при этом самые большие категории системных вызовов имеют дело с вводом/выводом (open, close, read, write, poll и многие другие), процессами (fork, execve, kill и т.д.), временем (time, settimeofday и т.п.) и памятью (mmap, brk и пр.) Под это категории подпадают практически все системные вызовы.

Однако за сценой системный вызов может и не оказаться тем, чем выглядит. Во-первых, библиотека С в Linux реализует некоторые системные вызовы полностью в терминах других системных вызовов. Например, реализация waitpid сводится к простому вызову wait4, однако в документации на обе функции ссылаются как на системные вызовы. Другие, более традиционные системные вызовы, наподобие sigmask и ftime, реализованы почти полностью в библиотеке С, а не в ядре Linux.

Разумеется, подобная ловкость рук вполне безобидна, поскольку с точки зрения приложения системный вызов выглядит как вызов любой другой функции; до тех пор пока результаты получаются необходимым образом, приложение не в состоянии сказать, что и как привлекает ядро в каждом конкретном случае. (Имеет место даже скрытый выигрыш: пользователи обращаются напрямую к меньшему объему кода ядра, что минимизирует проблемы, связанные с нарушением безопасности.) Однако беспорядок, появляющийся в результате подобных фокусов, может существенно усложнить обсуждение. На практике термин «системный» вызов зачастую означает любой системный вызов в первой версии Unix, используемой тем, кто о нем говорит! Однако, в данной главе нас будут интересовать только «истинные» системные вызовы — то есть те, которые имеют дело с пользовательским процессом, обращающимся к определенному коду ядра.


Системный вызов должен возвращать значение типа int и только int. В соответствие с принятыми соглашениями, возвращаемое значение равно 0 или любому положительному числу в случае успеха и любому отрицательному числу в случае неудачи. Ветеранам программирования на С хорошо известно, что в случае, когда какая-либо функция из стандартной библиотеки С завершается неудачей, она устанавливает глобальную целочисленную переменную errno для отражения природы возникшей ошибки; те же соглашения актуальны и для системных вызовов. Однако, способы, в соответствие с которыми это происходит в случае с системными вызовами, невозможно предугадать, изучая лишь исходный код ядра. В случае сбоя системные вызовы возвращают отрицательные значения кодов ошибок, а за оставшуюся обработку отвечает стандартная библиотека С. (В нормальных ситуациях системные функции ядра не вызываются непосредственно из пользовательского кода, а через тонкий слой кода в рамках стандартной библиотеки С, который в точности ответственен за подобного рода трансляцию.) Взглянем для примера на строку (часть sys_nanosleep), возвращающую -EINVAL для указания на то, что передаваемое значение выходит за пределы допустимого диапазона. Код в стандартной библиотеке С, ответственный за вызов sys_nanosleep, замечает отрицательное возвращаемое значение, устанавливает errno в EINVAL, а сам возвращает –1 вызывающему коду.

Так сложилось, что в недавних версиях ядра отрицательные значения возврата из системных вызовов больше не указывают на наличие ошибки. Несколько системных вызовов (подобных lseek) реализованы таким образом, что они даже в случае успеха возвращают большие отрицательные значения; в настоящий момент возвращаемые значения, соответствующие ошибке, лежат в пределах от –1 до –4095. Теперь стандартная библиотека С более искушена в интерпретации значений возврата из системных вызовов — собственно ядро при получении отрицательных значений возврата не предпринимает никаких особых действий.


Содержание раздела