Pull to refresh

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

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

Оглавление


  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).
  5. Организация памяти и обработка прерываний (GDT, IDT, PIC, syscall). Исключения.
  6. Виртуальная память (каталог страниц и таблица страниц).
  7. Процесс. Планировщик. Многозадачность. Системные вызовы (kill, exit, ps).
  8. Файловая система ядра (initrd), elf и его внутренности. Системные вызовы (exec).
  9. Драйверы символьных устройств. Системные вызовы (ioctl, fopen, fread, fwrite). Библиотека C (fopen, fclose, fprintf, fscanf).
  10. Оболочка как полноценная программа для ядра.
  11. Пользовательский режим защиты (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. Описание процесса разработки.
Tags:
Hubs:
Total votes 20: ↑20 and ↓0+20
Comments2

Articles