В предыдущей статье мы научились запускать Hello World ядро и написали пару функций для работы со строками. Теперь пришло время расширить библиотеку С чтобы можно было реализовать kprintf и другие необходимые функции. Поехали!
Сначала необходимо реализовать типы с явным указанием размерности.
Поскольку мы будем собирать под одну платформу, пока наши определения и реализации будут верны. Это не универсально, но именно поэтому читаемо и просто. Я придерживаюсь подхода, что иногда лучше сделать что-то не универсальное, но удовлетворяющее текущим требованиям, поскольку поддерживать универсальные решения крайне тяжело.
Не помешает и ввести Булевский тип.
Также не помешает нам и парочка макросов для работы с байтами.
Для работы с переменным количеством аргументов нужны следующие макросы. Этот подход работает только в том случае если функция следует соглашению вызова языка Си, в котором аргументы функции передаются через стек начиная с последнего.
Конечно можно было пойти и другим путем. Вместо определения собственных функций постараться использовать встроенную библиотеку и заменять функции которые будут обращаться к ядру через LD_PRELOAD. Но я люблю контролировать процесс полностью, поэтому оставим этот вариант в качестве идеи для тех кто начинает писать свою ОС по этому туториалу.
Далее, в видеоуроке мы рассмотрим реализацию следующих библиотечных функций. Реализация не претендует на оптимальность и полноту, но на простоту и читаемость думаю претендует. Отмечу только что мы используем потокобезопасную реализацию функции strtok, которая называется strtok_r. А функции strinv и strext мы придумали сами в прошлом уроке. Если ты знаком с языком С, думаю тебе будут знакомы почти все перечисленные ниже функции.
С рутиной покончили. Конец шаблонного кода. Следующий урок будет намного сложнее и интереснее. Если хочешь поконтрибьютить проект, можешь предложить свои оптимальные реализации библиотечных функций.
Разработка монолитной Unix подобной OS — Начало
Видеоурок к этой статье
Исходный код (тебе нужна ветка lesson2)
Оглавление
- Система сборки (make, gcc, gas). Первоначальная загрузка (multiboot). Запуск (qemu). Библиотека C (strcpy, memcpy, strext).
- Библиотека C (sprintf, strcpy, strcmp, strtok, va_list ...). Сборка библиотеки в режиме ядра и в режиме пользовательского приложения.
- Системный журнал ядра. Видеопамять. Вывод на терминал (kprintf, kpanic, kassert).
- Динамическая память, куча (kmalloc, kfree).
- Организация памяти и обработка прерываний (GDT, IDT, PIC, syscall). Исключения.
- Виртуальная память (каталог страниц и таблица страниц).
- Процесс. Планировщик. Многозадачность. Системные вызовы (kill, exit, ps).
- Файловая система ядра (initrd), elf и его внутренности. Системные вызовы (exec).
- Драйверы символьных устройств. Системные вызовы (ioctl, fopen, fread, fwrite). Библиотека C (fopen, fclose, fprintf, fscanf).
- Оболочка как полноценная программа для ядра.
- Пользовательский режим защиты (ring3). Сегмент состояния задачи (tss).
Библиотека С
Сначала необходимо реализовать типы с явным указанием размерности.
Поскольку мы будем собирать под одну платформу, пока наши определения и реализации будут верны. Это не универсально, но именно поэтому читаемо и просто. Я придерживаюсь подхода, что иногда лучше сделать что-то не универсальное, но удовлетворяющее текущим требованиям, поскольку поддерживать универсальные решения крайне тяжело.
typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned int u_long;
Не помешает и ввести Булевский тип.
#pragma once /* types */ typedef int bool; #define true 1 #define false 0 #define null 0
Также не помешает нам и парочка макросов для работы с байтами.
typedef unsigned long size_t; #define HIGH_WORD(addr) ((addr & 0xffff0000) >> 16) #define LOW_WORD(addr) ((addr & 0xffff)) #define LOW_BYTE(addr) ((addr & 0x00ff)) #define HIGH_BYTE(addr) ((addr & 0xff00) >> 8)
Для работы с переменным количеством аргументов нужны следующие макросы. Этот подход работает только в том случае если функция следует соглашению вызова языка Си, в котором аргументы функции передаются через стек начиная с последнего.
typedef size_t* va_list; #define va_start(l, a) (l = (void*)((size_t)&a) + sizeof(a)) #define va_end(l) (l = (void*)0) #define va_arg(l, s) (*(s*)(l++))
Конечно можно было пойти и другим путем. Вместо определения собственных функций постараться использовать встроенную библиотеку и заменять функции которые будут обращаться к ядру через LD_PRELOAD. Но я люблю контролировать процесс полностью, поэтому оставим этот вариант в качестве идеи для тех кто начинает писать свою ОС по этому туториалу.
Далее, в видеоуроке мы рассмотрим реализацию следующих библиотечных функций. Реализация не претендует на оптимальность и полноту, но на простоту и читаемость думаю претендует. Отмечу только что мы используем потокобезопасную реализацию функции strtok, которая называется strtok_r. А функции strinv и strext мы придумали сами в прошлом уроке. Если ты знаком с языком С, думаю тебе будут знакомы почти все перечисленные ниже функции.
extern int strlen(const char* s); extern char* strcpy(char* s1, const char* s2); extern char* strncpy(char* s1, const char* s2, u_int n); extern void* memcpy(void* buf1, const void* buf2, u_int bytes); extern void* memset(void* buf1, u8 value, u_int bytes); extern int strcmp(const char* s1, const char* s2); extern int strncmp(const char* s1, const char* s2, u_int n); extern char* strcat(char* s1, const char* s2); extern char* strext(char* buf, const char* str, char sym); extern int strspn(char* str, const char* accept); extern int strcspn(char* str, const char* rejected); char* strchr(const char* str, char ch); extern char* strtok_r(char* str, const char* delims, char** save_ptr); extern char* memext(void* buff_dst, u_int n, const void* buff_src, char sym); extern char* itoa(unsigned int value, char* str, unsigned int base); extern unsigned int atou(char* str); extern char* strinv(char* str); extern unsigned int sprintf(char* s1, const char* s2, ...); extern unsigned int snprintf(char* s1, u_int n, const char* s2, ...); extern unsigned int vsprintf(char* s1, const char* s2, va_list list); extern unsigned int vsnprintf(char* s1, unsigned int n, const char* s2, va_list list);
С рутиной покончили. Конец шаблонного кода. Следующий урок будет намного сложнее и интереснее. Если хочешь поконтрибьютить проект, можешь предложить свои оптимальные реализации библиотечных функций.
Ссылки
Разработка монолитной Unix подобной OS — Начало
Видеоурок к этой статье
Исходный код (тебе нужна ветка lesson2)
Список литературы
- James Molloy. Roll your own toy UNIX-clone OS.
- Зубков. Ассемблер для DOS, Windows, Unix
- Калашников. Ассемблер — это просто!
- Таненбаум. Операционные системы. Реализация и разработка.
- Роберт Лав. Ядро Linux. Описание процесса разработки.
