
Язык программирования Си поможет мне подняться
Добрый день, уважаемые читатели. Я думаю настал тот момент, когда пора бы уже и перейти на уровень разработки повыше. Лучший помощник в этом язык программирования Си. Этот язык появился в 1972 году.
Кстати, есть одна несправедливость, автор языка Си, Деннис Ритчи, умер на третий день, после смерти Стива Джобса. О смерти основателя Apple не написал только ленивый. Легендарный разработчик же ушел тихо, будто никому не нужный. Так вот, это не так. Мы помним о тебе, Деннис! Надеюсь читатели поймут меня и не осудят за этот эмоциональный абзац.
Деннис Ритчи - создатель языка программирования Си

Так вот, Деннис Ритчи, работая в Bell Labs, разработал язык Си. Дизайн языка предполагал, что использоваться он будет в тех местах, где раньше использовался ассемблер. Более того, данный язык программирования разрабатывался специально для создания операционной системы Unix.

Исходник на Си
Что-же, начнем. Нужно создать новый файл с наименованием - kernel.с. Исходники на языке Си используют расширение файла - .c. Реализуем функцию вывода наименования и версии операционной системы. Язык программирования Си очень похож на ассемблер, поэтому мы, фактически, просто перепишем наш код, который я написал в статье: Ядро операционной системы - первые шаги, используя синтаксис Си.
/*
* kernel.c
*/
void kmain(void)
{
const char *str = "Simple OS, version 0.0.1";
/* Помещаем в указатель vidptr адрес первого байта видеопамяти */
char *vidptr = (char*)0xb8000;
unsigned int i = 0;
unsigned int j = 0;
/* Очистка экрана */
while(j < 80 * 25 * 2) {
vidptr[j] = ' ';
vidptr[j+1] = 0x07;
j = j + 2;
}
/* Не забываем обнулять переменные, мы в ядре */
j = 0;
/* Вывод наименования ОС и версии ядра */
while(str[j] != '\0') {
vidptr[i] = str[j];
vidptr[i+1] = 0x07;
/* Указатель на строку увеличиваем на 1, а указатель видеопамяти
на 2 байта (символ + атрибут цвета) */
++j;
i = i + 2;
}
return;
}
Очень просто, не так ли. Комментарии в коде ясно показывают где и что происходит.
Доработка загрузчика
Для внедрения данного кода в загрузчик ядра Simple OS, нужно немного поправить ассемблерный исходник. Как Вы помните там есть список инструкций для вывода текста. Удалим их! Должен остаться такой код:
section .text
bits 32
global start
start:
cli
mov esp, stack_space
; Удаляем весь код, связанный с выводом строки и очисткой экрана
; Удаляем метку done
hlt
; Удаляем строку OS_DETAILS
section .bss
Настала пора подняться на уровень выше! (барабанная дробь) Добавляем в код вызов процедуры из исходника Си. Функция называется kmain. Покажем загрузчику кто есть кто, перед меткой start, что она находится в другом файле:
extern kmain
А теперь вызов после установки адреса стека:
call kmain
В целом код получается такой:
section .multiboot_header
header_start:
dd 0xE85250D6
dd 0
dd header_end - header_start
dd 0x100000000 - (0xE85250D6 + 0 + (header_end - header_start))
dw 0
dw 0
dd 8
header_end:
section .text
bits 32
global start
extern kmain ;kmain определена в файле Си
start:
cli
mov esp, stack_space
call kmain ;Вызов функции
hlt
section .bss
resb 8192
stack_space:
Сборка с помощью GNU LD
Теперь у нас есть два файла исходников: boot.asm и kernel.c. Чтобы собрать единое ядро используем компоновщик, или как его правильно называют: редактор связей, из пакета GNU LD. По ссылке можно посмотреть документацию на русском языке. Мой файл - link.ld - это файл с конфигурацией для компоновщика на языке управления линкером,очень простой:
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.boot : { *(.multiboot_header) }
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
В нем указан формат выходного файла, точка входа и секции бинарника.
В терминале нужно будет написать следующую команду:
ld -m elf_i386 -T link.ld -o kernel.bin boot.o kernel.o
kernel.o и boot.o - это файлы объектного кода, компилированного из исходников: kernel.c и boot.asm. На выходе получается собранное ядро - kernel.bin.
После этого стандартно собираем iso-образ диска, запускаем в эмуляторе. И, вуаля:

Автоматизация сборки
Добавлю знания, почерпнутые в теоретической обзорной статье: Автоматизация — используем make.
BUILD_DIR=./build
CC=/usr/bin/gcc
CC_FLAGS=-m32 -c
ASM=/usr/bin/nasm
ASM_FLAGS=-f elf32
LD=/usr/bin/ld
MKDIR=mkdir -p
RM=rm -f -r
project_structure:
${MKDIR} ${BUILD_DIR}
boot: project_structure boot.asm
${ASM} ${ASM_FLAGS} boot.asm -o ${BUILD_DIR}/boot.o
kernel: project_structure kernel.c
${CC} ${CC_FLAGS} kernel.c -o ${BUILD_DIR}/kernel.o
link: boot kernel link.ld
${LD} -m elf_i386 -T link.ld -o ${BUILD_DIR}/kernel.bin ${BUILD_DIR}/boot.o ${BUILD_DIR}/kernel.o
iso: link
${MKDIR} ${BUILD_DIR}/isofiles/boot/grub
cp grub.cfg ${BUILD_DIR}/isofiles/boot/grub
cp ${BUILD_DIR}/kernel.bin ${BUILD_DIR}/isofiles/boot
grub-mkrescue -o ${BUILD_DIR}/simpleos.iso ${BUILD_DIR}/isofiles
qemu: iso
qemu-system-i386 build/simpleos.iso
qemu_cd: iso
qemu-system-i386 -cdrom build/simpleos.iso
clean:
${RM} ${BUILD_DIR}/*
Пробегусь быстренько по целям:
- project_structure - подготовка структуры проекта
- boot - компиляция загрузчика ядра ОС
- kernel - компиляция ядра операционной системы, написанного на языке программирования Си
- link - компоновка ядра
- iso - сборка образа диска
- qemu - запуск подготовленного образа в эмуляторе в качестве жесткого диска
- qemu_cd - запуск образа в качестве cd-диска
- clean - очистка директории сборки
Ну, вот. Немного облегчил себе жизнь. Теперь для того, чтобы пройти весь процесс нужно, всего лишь, в терминале написать команду:
make qemu_cd
Надеюсь Вам понравилась статья. А, пока, до скорых встреч!
Картинки найдены на просторах интернета.
Комментарии
Комментариев пока нет
Войдите, чтобы оставить комментарий