Разработка монолитной Unix подобной OS — Системный журнал ядра

от автора

В предыдущей второй по счету статье мы с вами разработали необходимые функции для работы со строками из библиотеки С. В этом уроке мы реализуем полноценный отладочный вывод на экран — системный журнал ядра.

Оглавление

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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *