Ну название говорит само за себя. Является ли функция main()
абсолютно необходимой для программы на C?
Я спрашиваю об этом, потому что просматривал код ядра Linux и не видел функции main().
Ну название говорит само за себя. Является ли функция main()
абсолютно необходимой для программы на C?
Я спрашиваю об этом, потому что просматривал код ядра Linux и не видел функции main().
Нет, в стандарте ISO C указано, что функция main
требуется только для размещенной среды (например, с базовой ОС).
Для автономной среды, такой как встроенная система (или сама операционная система), это определяется реализацией. Из C99 5.1.2
:
Определены две среды выполнения: автономная и размещенная. В обоих случаях запуск программы происходит, когда назначенная функция C вызывается средой выполнения.
В автономной среде (в которой выполнение программы C может происходить без каких-либо преимуществ операционной системы) имя и тип функции, вызываемой при запуске программы, определяются реализацией.
Что касается запуска самого Linux, отправной точкой для ядра Linux является start_kernel, однако для получения более полной картины всего процесса загрузки следует запустить здесь.
start_kernel
в init/main.c
.
- person paxdiablo; 06.11.2010
C99 указывает, что main()
вызывается в размещенной среде «при запуске программы», однако вам не нужно использовать поддержку среды выполнения C. Ваша операционная система выполняет файлы изображений и запускает программу по адресу, предоставленному компоновщиком.
Если вы хотите написать свою программу, соответствующую требованиям операционной системы, а не требованиям C99, вы можете сделать это без main(). Однако чем более современная (и сложная) система, тем больше проблем у вас будет с библиотекой C, делающей предположения, что используется стандартный запуск во время выполнения.
Вот пример для линукса...
$ cat > nomain.S
.text
_start:
call iamnotmain
movl $0xfc, %eax
xorl %ebx, %ebx
int $0x80
.globl _start
$ cat > demo.c
void iamnotmain(void) {
static char s[] = "hello, world\n";
write(1, s, sizeof s);
}
$ as -o nomain.o nomain.S
$ cc -c demo.c
$ ld -static nomain.o demo.o -lc
$ ./a.out
hello, world
Однако теперь это, возможно, не «программа C99», а просто «программа Linux» с объектным модулем, написанным на C.
Функция main()
вызывается объектным файлом, включенным в libc. Поскольку ядро не связывается с libc, у него есть собственная точка входа, написанная на ассемблере.
ответ Paxdiablo охватывает два случая, когда вы выиграли не встречал главного. Добавлю еще парочку:
main()
.main()
. (Вместо этого у них WinMain()
.)main()
, потому что _start()
(я думаю) находится в шаблонном коде в libc
, который делает некоторые вещи, а затем вызывает main()
. Я не уверен, что вы имеете в виду здесь. Или вы думали, что main()
вызывается непосредственно операционной системой?
- person JUST MY correct OPINION; 07.11.2010
Загрузчик операционной системы должен вызывать единую точку входа; в компиляторе GNU точка входа определяется в связанном объектном файле crt0.o, источником для которого является файл ассемблера crt0.s, который вызывает main() после выполнения различных задач запуска во время выполнения (таких как установка стек, статическая инициализация). Поэтому при создании исполняемого файла, который связывает crt0.o по умолчанию, вы должны иметь main(), иначе вы получите ошибку компоновщика, поскольку в crt0.o main() является неразрешенным символом.
Можно было бы (хотя и несколько извращенно и необязательно) модифицировать crt0.s для вызова другой точки входа. Просто убедитесь, что вы делаете такой объектный файл специфичным для вашего проекта, а не изменяете версию по умолчанию, иначе вы сломаете каждую сборку на этой машине.
Сама ОС имеет свой собственный запуск среды выполнения C (который будет вызываться из загрузчика), поэтому может вызывать любую точку входа, которую пожелает. Я не смотрел исходный код Linux, но представьте, что у него есть собственный crt0.s, который будет вызывать любую точку входа кода C.
main()
.
- person JUST MY correct OPINION; 07.11.2010
main вызывается glibc, который является частью приложения (кольцо 3), а не ядра (кольцо 0).
у драйвера есть другая точка входа, например, база драйвера Windows на WDM запускается из DRIVERENTRY
В машинном языке все выполняется последовательно, то, что приходит первым, выполняется первым. Таким образом, по умолчанию компилятор вызывает основной метод, соответствующий стандарту C.
Ваша программа работает как библиотека, представляющая собой набор скомпилированных функций. Основное различие между библиотекой и стандартным исполняемым файлом заключается в том, что для второго компилятор генерирует ассемблерный код, который вызывает одну из функций вашей программы.
Но вы можете написать ассемблерный код, который вызывает вашу произвольную функцию программы на C (на самом деле так же, как работают вызовы библиотечных функций), и это будет работать так же, как и другие исполняемые файлы. Но дело в том, что вы не можете сделать это в простом стандартном C, вам нужно прибегнуть к ассемблеру или даже к некоторым другим трюкам, специфичным для компилятора.
Это было задумано как общее и поверхностное объяснение, есть некоторые технические различия, которые я намеренно избегал, поскольку они не кажутся уместными.