Pull to refresh
2675.55
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

Заметки о Unix: С-функция main() — одно из мест, где видны различия между API пользовательского пространства и ядра Unix

Reading time3 min
Views7.8K
Original author: Chris Siebenmann
В современных Unix-дистрибутивах часто проводят формальную границу между API, предоставляемыми пользовательскому пространству ядром, и Unix API, которые предоставляет программам «стандартная библиотека», под которой подразумевается стандартная библиотека C. Кое-кого, включая меня, это не вполне устраивает (я уже писал на эту тему). Но, независимо от того, что я об этом думаю, в Unix уже давно существует одно место, в котором видна разница между обычным API, которым пользуются все, и API, который реализован в ядре. Я говорю о традиционной точке входа в программы, написанные на языке C, о функции main(), с которой начинается выполнение таких программ.



Все знакомы с простой формой функции main(), в которой используются аргументы argc и argv. Такая функция вызывается с передачей ей количества аргументов и массива строк. При несколько более продвинутом способе работы с этой функцией применяется ещё и третий аргумент — envp. Он представляет собой массив переменных окружения. Этот формат существует в Linux очень давно. Версия main() с двумя аргументами существует, как минимум, со времён exec(2) Research Unix V4. А форма этой функции с третьим аргументом, похоже, появилась в exec(2) V7.

Но это, на самом деле, не реальная точка входа в программу, которую ядро Unix V7 использует при запуске программы. Реальная точка входа имеет API, отличный от main(). Обычно C-программы в V7 начинают работу с метки, имеющей символическое имя start. Самая простая версия ассемблерного кода, в котором это используется, представлена в файле crt0.s, и тут, очевидно, выполняется некий объём подготовительной работы. Есть и другие версии подобного кода, их можно найти здесь. Тут выполняется больше вспомогательных операций, например — подготовка к профилированию кода.

(В Research Unix V6 тоже был файл crt0.s, но несколько иной. Полагаю, тут, например, нет циклов. Если бы я понимал язык ассемблера PDP-11, то я лучше бы разобрался с тем, что тут, на самом деле, происходит.)

В V7 между API пользовательского пространства для main() и API ядра имеется лишь небольшая разница. В актуальных дистрибутивах Unix там часто происходит очень много всего, особенно тогда, когда пользуются динамическими загрузчиками и чем-то вроде «вспомогательного вектора», который имеется в некоторых дистрибутивах. Я подозреваю, что самую простую современную версию этого механизма можно найти в musl libc для Linux, где crt1.c и функции libc для подготовки к работе main() сравнительно просты.

(Некоторый код тут присутствует из-за того, что среда выполнения C нуждается в предварительной настройке (и да, в современном C есть среда выполнения), но определённый объём этого кода предназначен для согласования того, как ядро вызывает программы, с тем, как хочет быть вызвана функция main(). Например, обратите внимание на то, что функция musl libc для запуска main() не вызывается с передачей ей argc в виде явно заданного аргумента. Она извлекает argc из памяти.)

Примечание: V7 и адрес данных 0


В конце каждой версии файла crt0.s V7 есть код, который поначалу меня озадачил:

.data
   .=.+2   / loc 0 for I/D; null ptr points here.

Оказалось, что он резервирует два байта в начале раздела данных. Unix V7 работает на компьютерах PDP-11, которые поддерживают разделение адресного пространства инструкций и данных. В результате раздел данных начинается с адреса (данных) 0. Резервирование двух байтов в начале адресного пространства позволяет обеспечить то, что ни переменную, ни что-то другое в разделе данных нельзя расположить по адресу 0. В результате NULL в C всегда отличается от действительных указателей.

Приходилось ли вам сталкиваться с различиями API пользовательского пространства и ядра Unix?

Tags:
Hubs:
+31
Comments15

Articles

Information

Website
ruvds.com
Registered
Founded
Employees
11–30 employees
Location
Россия
Representative
ruvds