Оглавление
1. Система сборки (make, gcc, gas). Первоначальная загрузка (multiboot). Запуск (qemu). Библиотека C (strcpy, memcpy, strext).
2. Библиотека C (sprintf, strcpy, strcmp, strtok, va_list …). Сборка библиотеки в режиме ядра и в режиме пользовательского приложения.
3. Системный журнал ядра. Видеопамять. Вывод на терминал (kprintf, kpanic, kassert).
4. Динамическая память, куча (kmalloc, kfree).
6. Организация памяти и обработка прерываний (GDT, IDT, PIC, syscall). Исключения.
5. Виртуальная память (каталог страниц и таблица страниц).
6. Процесс. Планировщик. Многозадачность. Системные вызовы (kill, exit, ps).
7. Файловая система ядра (initrd), elf и его внутренности. Системные вызовы (exec).
8. Драйверы символьных устройств. Системные вызовы (ioctl, fopen, fread, fwrite). Библиотека C (fopen, fclose, fprintf, fscanf).
9. Оболочка как полноценная программа для ядра.
10. Пользовательский режим защиты (ring3). Сегмент состояния задачи (tss).
Системный журнал ядра
Перед тем как начать, нам понадобится ввести несколько полезных функций для работы с портами ввода-вывода. Порты ввода-вывода для программиста ничем не отличаются от обычных ячеек в памяти, за исключением того что для оперирования ими существуют отдельные команды. Устройства, оперирующие этими портами, подключены к шине памяти. Также для них существует выделенное адресное пространство. Нам потребуется две ассемблерные функции для работы с портами ввода-вывода, ведь как вы уже помните, я не терплю ассемблерных вставок.
extern u_char asm_read_port(u_char port);
extern void asm_write_port(u_int port, u_char data);
Аналогично две команды для управления маскируемыми прерываниями процессора.
extern void asm_lock();
extern void asm_unlock();
Ну и для экономии электроэнергии после неисправимых ошибок нужна команда остановки процессора.
extern void asm_hlt();
Как ты помнишь, видеопамять начинается по адресу 0xB8000, но я предлагаю писать сообщения сначала в буфер в обычном текстовом формате. А потом просто этот буфер копировать в видеопамять с учетом аттрибутов цвета. Для этого я реализовал несколько утилит для работы с тамими буферами. Один буфер будет для системного журнала ядра, и остальные для виртуальных терминалов. Прокрутка экрана также будет выполняться над буфером. И только функция video_flush будет копировать буффер в видеопамять, расширяя его аттрибутами.
extern void video_init();
extern void video_disable_cursor();
extern void* video_scroll(char const* video_buff, char* pos);
extern char* video_clear(char const* video_buff);
extern void video_flush(char const* video_buff);
Теперь самое время ввести самые часто используемые функции. Две последних будут использоваться для отладки ядра, когда лень отлаживать дебаггером. Поверь, я не разу не юзал дебаггер когда писал это ядро.
extern void kpanic(char* message, ...);
extern void kassert(const char* file, u_int line, bool expr);
extern void kunreachable(const char* file, u_int line);
Ну и собственно, функции для работы с системным журналом ядра. Для того чтобы управлять тем, что на экран выводится, системный журнал или пользовательская консоль я ввел функцию kmode. А для чтения системного журнала в буффер нужна будет функция klog, ибо у пользовательских процессов не будет доступа к ядру кроме как через системные вызовы.
extern void kclear();
extern void kprintf(const char* format, ...);
extern void kvprintf(const char* format, va_list list);
extern void kmode(bool is_early);
extern void klog(char* buf, u_int n);
Наиболее интересные функции привожу тут:
/* * Api - Scroll video buffer up * Returns new position */ extern void* video_scroll(char const* video_buff, char* pos) { char* ptr = (void*)video_buff; /* scroll up */ for (int i = 1; i < VIDEO_SCREEN_HEIGHT; ++i) { for (int j = 0; j < VIDEO_SCREEN_WIDTH; ++j) { ptr[(i - 1) * VIDEO_SCREEN_WIDTH + j] = ptr[i * VIDEO_SCREEN_WIDTH + j]; } } /* empty last line */ for (int j = 0; j < VIDEO_SCREEN_WIDTH; ++j) { ptr[(VIDEO_SCREEN_HEIGHT - 1) * VIDEO_SCREEN_WIDTH + j] = ' '; } /* move position up */ pos -= VIDEO_SCREEN_WIDTH; return pos; }
/* * Api - Print kernel message */ extern void kvprintf(const char* format, va_list list) { char buff[VIDEO_SCREEN_WIDTH]; int len = vsprintf(buff, format, list); for (int i = 0; i < len; ++i) { if (buff[i] != '\n') { kputc(buff[i]); } else { int line_pos = (syslog_pos - syslog) % VIDEO_SCREEN_WIDTH; for (int j = 0; j < VIDEO_SCREEN_WIDTH - line_pos; ++j) { kputc(' '); } } } kflush(); }
/* * Put character to syslog */ static void kputc(char ch) { if ((size_t)syslog_pos - (size_t)syslog + 1 < VIDEO_SCREEN_SIZE) { *syslog_pos++ = ch; } else { syslog_pos = video_scroll(syslog, syslog_pos); kputc(ch); } }
Подробный туториал смотри в видеоуроке.
Ссылки
Видеоурок к этой статье
Исходный код (тебе нужна ветка lesson3)
Список литературы
1. James Molloy. Roll your own toy UNIX-clone OS.
2. Зубков. Ассемблер для DOS, Windows, Unix
3. Калашников. Ассемблер — это просто!
4. Таненбаум. Операционные системы. Реализация и разработка.
5. Роберт Лав. Ядро Linux. Описание процесса разработки.
ссылка на оригинал статьи https://habr.com/ru/post/466765/
Добавить комментарий