VST Operating System 1.0 Stable
Сердце нашей операционки на C++
Загрузка...
Поиск...
Не найдено
Файл kernel.cpp

Сердце нашей операционки. Подробнее...

#include <stddef.h>
#include <stdint.h>
Граф включаемых заголовочных файлов для kernel.cpp:

См. исходные тексты.

Перечисления

enum  vga_color {
  VGA_COLOR_BLACK = 0 , VGA_COLOR_BLUE = 1 , VGA_COLOR_GREEN = 2 , VGA_COLOR_CYAN = 3 ,
  VGA_COLOR_RED = 4 , VGA_COLOR_MAGENTA = 5 , VGA_COLOR_BROWN = 6 , VGA_COLOR_LIGHT_GREY = 7 ,
  VGA_COLOR_DARK_GREY = 8 , VGA_COLOR_LIGHT_BLUE = 9 , VGA_COLOR_LIGHT_GREEN = 10 , VGA_COLOR_LIGHT_CYAN = 11 ,
  VGA_COLOR_LIGHT_RED = 12 , VGA_COLOR_LIGHT_MAGENTA = 13 , VGA_COLOR_LIGHT_BROWN = 14 , VGA_COLOR_WHITE = 15
}
 Таблица цветов для VGA. Подробнее...

Функции

static void outb (uint16_t port, uint8_t val)
 Это наши "руки" для работы с портами
static uint8_t inb (uint16_t port)
 Забирает байт из порта
static void io_wait (void)
 Маленькая задержка
size_t strlen (const char *str)
 Считаем длину строки
bool strcmp (const char *s1, const char *s2)
 Сравниваем две строки
void memset (void *dest, char val, size_t count)
 Просто забиваем кусок памяти нужным символом
void terminal_initialize (void)
 Чистим экран при запуске и ставим курсор в начало
void terminal_scroll ()
 Скроллинг экрана
void terminal_putentryat (char c, uint8_t color, size_t x, size_t y)
 Вспомогательная штука, чтобы положить символ в конкретную точку экрана
void terminal_putchar (char c)
 Основная функция печати символа
void terminal_write (const char *data)
 Просто печатаем строку целиком
void terminal_writeln (const char *data)
 Печать строки с переходом на новую
void terminal_backspace ()
 Стираем последний символ
void cmd_reboot ()
 Жесткий метод перезагрузки
void cmd_poweroff ()
 Попытка выключить комп
void cmd_led (int status)
 Управление лампочками на клаве
void execute_command ()
 Наш обработчик команд
void kernel_main (void)
 Точка входа в ядро

Переменные

static const size_t VGA_WIDTH = 80
 Ширина текстового экрана (почти везде по дефолту 80).
static const size_t VGA_HEIGHT = 25
 Высота текстового экрана (почти везде по дефолту 25).
uint16_t *const VGA_MEMORY = (uint16_t *)0xB8000
 Волшебный адрес VGA_MEMORY.
size_t terminal_row
 Текущая строка курсора
size_t terminal_column
 Текущий столбец курсора
uint8_t terminal_color
 Текущий цвет текста
uint16_t * terminal_buffer
 Указатель на буфер экрана
char kbd_US [128]
 Карта клавиш
char cmd_buffer [128]
 Буфер, куда мы записываем то, что печатает юзер, пока он не нажмет энтер
int cmd_len = 0
 Длина текущей введенной команды

Подробное описание

Сердце нашей операционки.

так, ну погнали. Это основной файл ядра.

См. определение в файле kernel.cpp

Перечисления

◆ vga_color

enum vga_color

Таблица цветов для VGA.

Стандартные 16 цветов из эпохи DOS.

Элементы перечислений
VGA_COLOR_BLACK 
VGA_COLOR_BLUE 
VGA_COLOR_GREEN 
VGA_COLOR_CYAN 
VGA_COLOR_RED 
VGA_COLOR_MAGENTA 
VGA_COLOR_BROWN 
VGA_COLOR_LIGHT_GREY 
VGA_COLOR_DARK_GREY 
VGA_COLOR_LIGHT_BLUE 
VGA_COLOR_LIGHT_GREEN 
VGA_COLOR_LIGHT_CYAN 
VGA_COLOR_LIGHT_RED 
VGA_COLOR_LIGHT_MAGENTA 
VGA_COLOR_LIGHT_BROWN 
VGA_COLOR_WHITE 

См. определение в файле kernel.cpp строка 89

90{
95 VGA_COLOR_RED = 4,
106 VGA_COLOR_WHITE = 15,
107};
@ VGA_COLOR_BLACK
Определения kernel.cpp:91
@ VGA_COLOR_GREEN
Определения kernel.cpp:93
@ VGA_COLOR_LIGHT_GREY
Определения kernel.cpp:98
@ VGA_COLOR_LIGHT_MAGENTA
Определения kernel.cpp:104
@ VGA_COLOR_LIGHT_GREEN
Определения kernel.cpp:101
@ VGA_COLOR_BLUE
Определения kernel.cpp:92
@ VGA_COLOR_MAGENTA
Определения kernel.cpp:96
@ VGA_COLOR_RED
Определения kernel.cpp:95
@ VGA_COLOR_LIGHT_BROWN
Определения kernel.cpp:105
@ VGA_COLOR_BROWN
Определения kernel.cpp:97
@ VGA_COLOR_WHITE
Определения kernel.cpp:106
@ VGA_COLOR_LIGHT_CYAN
Определения kernel.cpp:102
@ VGA_COLOR_LIGHT_BLUE
Определения kernel.cpp:100
@ VGA_COLOR_LIGHT_RED
Определения kernel.cpp:103
@ VGA_COLOR_CYAN
Определения kernel.cpp:94
@ VGA_COLOR_DARK_GREY
Определения kernel.cpp:99

Функции

◆ cmd_led()

void cmd_led ( int status)

Управление лампочками на клаве

CapsLock, NumLock и т.д.

См. определение в файле kernel.cpp строка 301

302{
303 while ((inb(0x64) & 2) != 0)
304 ;
305 outb(0x60, 0xED); // говорим, что сейчас будем менять состояние диодов
306
307 while ((inb(0x64) & 2) != 0)
308 ;
309 if (status == 1)
310 outb(0x60, 0x07); // зажечь всё
311 else
312 outb(0x60, 0x00); // погасить всё
313}
static uint8_t inb(uint16_t port)
Забирает байт из порта
Определения kernel.cpp:27
static void outb(uint16_t port, uint8_t val)
Это наши "руки" для работы с портами
Определения kernel.cpp:16

Перекрестные ссылки inb() и outb().

Граф вызовов:
Граф вызова функции:

◆ cmd_poweroff()

void cmd_poweroff ( )

Попытка выключить комп

Работает в эмуляторах типа QEMU.

См. определение в файле kernel.cpp строка 288

289{
290 outb(0x604, 0x2000);
291 outb(0xB004, 0x2000);
292 terminal_writeln("ACPI Shutdown not available via Ports.");
293 terminal_writeln("Please turn off power manually.");
294 asm volatile("hlt");
295}
void terminal_writeln(const char *data)
Печать строки с переходом на новую
Определения kernel.cpp:224

Перекрестные ссылки outb() и terminal_writeln().

Граф вызовов:
Граф вызова функции:

◆ cmd_reboot()

void cmd_reboot ( )

Жесткий метод перезагрузки

Через контроллер клавиатуры.

См. определение в файле kernel.cpp строка 270

271{
272 uint8_t temp;
273 asm volatile("cli"); // вырубаем прерывания, чтобы ничего не мешало
274 do
275 {
276 temp = inb(0x64); // ждем, пока контроллер освободится
277 if (temp & 1)
278 inb(0x60);
279 } while (temp & 2);
280 outb(0x64, 0xFE); // шлем команду на ресет
281 asm volatile("hlt");
282}

Перекрестные ссылки inb() и outb().

Граф вызовов:
Граф вызова функции:

◆ execute_command()

void execute_command ( )

Наш обработчик команд

Это простейший парсер ввода.

См. определение в файле kernel.cpp строка 324

325{
327 if (cmd_len == 0)
328 return;
329
330 // проверяем, что там ввел пользователь
331 if (strcmp(cmd_buffer, "about"))
332 {
334 terminal_writeln("-------------------------");
335 terminal_writeln(" VST Operating System Stable");
336 terminal_writeln(" FOR LEARNING ONLY");
337 terminal_writeln("-------------------------");
339 }
340 else if (strcmp(cmd_buffer, "cat"))
341 {
343 terminal_writeln(" /\\_/\\ ");
344 terminal_writeln(" ( o.o ) ");
345 terminal_writeln(" > ^ < ");
346 terminal_writeln(" / ^ \\ ");
348 }
349 else if (strcmp(cmd_buffer, "help"))
350 {
351 terminal_writeln("Available commands:");
352 terminal_writeln(" about - OS info");
353 terminal_writeln(" ascii - Show art");
354 terminal_writeln(" clear - Clear screen");
355 terminal_writeln(" led 1 - Turn ON keyboard LEDs");
356 terminal_writeln(" led 0 - Turn OFF keyboard LEDs");
357 terminal_writeln(" reboot - Restart system");
358 terminal_writeln(" power 0 - Shutdown (QEMU)");
359 }
360 else if (strcmp(cmd_buffer, "reboot"))
361 {
362 terminal_writeln("Rebooting...");
363 cmd_reboot();
364 }
365 else if (strcmp(cmd_buffer, "power 0"))
366 {
367 terminal_writeln("Shutting down...");
368 cmd_poweroff();
369 }
370 else if (strcmp(cmd_buffer, "led 1"))
371 {
372 cmd_led(1);
373 terminal_writeln("LEDs ON");
374 }
375 else if (strcmp(cmd_buffer, "led 0"))
376 {
377 cmd_led(0);
378 terminal_writeln("LEDs OFF");
379 }
380 else if (strcmp(cmd_buffer, "clear"))
381 {
383 }
384 else
385 {
387 terminal_write("Unknown command: ");
390 }
391
392 // после выполнения чистим буфер для новой команды
393 for (int i = 0; i < 128; i++)
394 cmd_buffer[i] = 0;
395 cmd_len = 0;
396 terminal_write("> ");
397}
uint8_t terminal_color
Текущий цвет текста
Определения kernel.cpp:123
bool strcmp(const char *s1, const char *s2)
Сравниваем две строки
Определения kernel.cpp:62
char cmd_buffer[128]
Буфер, куда мы записываем то, что печатает юзер, пока он не нажмет энтер
Определения kernel.cpp:316
void cmd_reboot()
Жесткий метод перезагрузки
Определения kernel.cpp:270
void terminal_write(const char *data)
Просто печатаем строку целиком
Определения kernel.cpp:215
void cmd_poweroff()
Попытка выключить комп
Определения kernel.cpp:288
void terminal_initialize(void)
Чистим экран при запуске и ставим курсор в начало
Определения kernel.cpp:130
void cmd_led(int status)
Управление лампочками на клаве
Определения kernel.cpp:301
int cmd_len
Длина текущей введенной команды
Определения kernel.cpp:318

Перекрестные ссылки cmd_buffer, cmd_led(), cmd_len, cmd_poweroff(), cmd_reboot(), strcmp(), terminal_color, terminal_initialize(), terminal_write(), terminal_writeln(), VGA_COLOR_LIGHT_CYAN, VGA_COLOR_LIGHT_GREEN, VGA_COLOR_LIGHT_GREY и VGA_COLOR_LIGHT_RED.

Граф вызовов:
Граф вызова функции:

◆ inb()

uint8_t inb ( uint16_t port)
inlinestatic

Забирает байт из порта

Так мы узнаем, какую клавишу нажали.

Аргументы
portПорт устройства
Возвращает
Считанный байт

См. определение в файле kernel.cpp строка 27

28{
29 uint8_t ret;
30 asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
31 return ret;
32}
Граф вызова функции:

◆ io_wait()

void io_wait ( void )
inlinestatic

Маленькая задержка

Иногда железо слишком медленное для проца.

См. определение в файле kernel.cpp строка 38

39{
40 outb(0x80, 0);
41}

Перекрестные ссылки outb().

Граф вызовов:

◆ kernel_main()

void kernel_main ( void )

Точка входа в ядро

Именно сюда GRUB передаст управление после загрузки.

См. определение в файле kernel.cpp строка 403

404{
405 // стартуем экран
407
408 // приветствие
410 terminal_writeln("VST Operating System");
411 terminal_writeln("Type 'help' for commands.");
412 terminal_writeln("------------------------------");
414 terminal_write("> ");
415
416 // основной цикл, тут и живет система
417 while (1)
418 {
419 // опрашиваем порт клавиатуры на нажатие чего либо
420 if (inb(0x64) & 1)
421 {
422 uint8_t scancode = inb(0x60);
423
424 // если старший бит 1, значит клавишу отпустили, нам это пока не интересно
425 if (scancode & 0x80)
426 {
427 }
428 else
429 {
430 // получаем символ из нашей таблицы
431 char c = kbd_US[scancode];
432
433 if (c == '\n') // нажали энтер - выполняем
434 {
436 }
437 else if (c == '\b') // нажали бакспейс - стираем
438 {
439 if (cmd_len > 0)
440 {
442 cmd_buffer[--cmd_len] = 0;
443 }
444 }
445 else if (c > 0) // обычная буква - пишем в буфер и на экран
446 {
447 if (cmd_len < 79)
448 {
450 cmd_buffer[cmd_len++] = c;
451 cmd_buffer[cmd_len] = 0;
452 }
453 }
454 }
455 }
456 }
457}
void terminal_putchar(char c)
Основная функция печати символа
Определения kernel.cpp:181
void execute_command()
Наш обработчик команд
Определения kernel.cpp:324
char kbd_US[128]
Карта клавиш
Определения kernel.cpp:259
void terminal_backspace()
Стираем последний символ
Определения kernel.cpp:234

Перекрестные ссылки cmd_buffer, cmd_len, execute_command(), inb(), kbd_US, terminal_backspace(), terminal_color, terminal_initialize(), terminal_putchar(), terminal_write(), terminal_writeln(), VGA_COLOR_LIGHT_GREY и VGA_COLOR_WHITE.

Граф вызовов:

◆ memset()

void memset ( void * dest,
char val,
size_t count )

Просто забиваем кусок памяти нужным символом

Аргументы
destАдрес начала
valЧем забиваем
countСколько байт

См. определение в файле kernel.cpp строка 78

79{
80 char *temp = (char *)dest;
81 for (; count != 0; count--)
82 *temp++ = val;
83}

◆ outb()

void outb ( uint16_t port,
uint8_t val )
inlinestatic

Это наши "руки" для работы с портами

Процессор общается с железками через них.

Аргументы
portПорт устройства
valБайт, который шлем (например, скомандовать клавиатуре мигнуть лампочкой)

См. определение в файле kernel.cpp строка 16

17{
18 asm volatile("outb %0, %1" : : "a"(val), "Nd"(port));
19}
Граф вызова функции:

◆ strcmp()

bool strcmp ( const char * s1,
const char * s2 )

Сравниваем две строки

Чтобы понимать, какую команду ввел юзер.

Возвращает
true если строки одинаковые

См. определение в файле kernel.cpp строка 62

63{
64 while (*s1 && (*s1 == *s2))
65 {
66 s1++;
67 s2++;
68 }
69 return *s1 == *s2;
70}
Граф вызова функции:

◆ strlen()

size_t strlen ( const char * str)

Считаем длину строки

Пока нет стандартной библиотеки, пишем всё сами.

Аргументы
strУказатель на строку
Возвращает
Количество символов

См. определение в файле kernel.cpp строка 49

50{
51 size_t len = 0;
52 while (str[len])
53 len++;
54 return len;
55}
Граф вызова функции:

◆ terminal_backspace()

void terminal_backspace ( )

Стираем последний символ

Нужно для работы Backspace.

См. определение в файле kernel.cpp строка 234

235{
236 if (terminal_column == 0 && terminal_row > 0)
237 {
238 terminal_row--;
240 }
241 if (terminal_column > 0)
242 {
245
246 // не забываем вернуть мигающий курсор назад
247 uint16_t pos = terminal_row * VGA_WIDTH + terminal_column;
248 outb(0x3D4, 0x0F);
249 outb(0x3D5, (uint8_t)(pos & 0xFF));
250 outb(0x3D4, 0x0E);
251 outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF));
252 }
253}
size_t terminal_column
Текущий столбец курсора
Определения kernel.cpp:121
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
Вспомогательная штука, чтобы положить символ в конкретную точку экрана
Определения kernel.cpp:171
size_t terminal_row
Текущая строка курсора
Определения kernel.cpp:119
static const size_t VGA_WIDTH
Ширина текстового экрана (почти везде по дефолту 80).
Определения kernel.cpp:110

Перекрестные ссылки outb(), terminal_color, terminal_column, terminal_putentryat(), terminal_row и VGA_WIDTH.

Граф вызовов:
Граф вызова функции:

◆ terminal_initialize()

void terminal_initialize ( void )

Чистим экран при запуске и ставим курсор в начало

См. определение в файле kernel.cpp строка 130

131{
132 terminal_row = 0;
133 terminal_column = 0;
136 for (size_t y = 0; y < VGA_HEIGHT; y++)
137 {
138 for (size_t x = 0; x < VGA_WIDTH; x++)
139 {
140 const size_t index = y * VGA_WIDTH + x;
141 // забиваем всё пробелами
142 terminal_buffer[index] = (uint16_t)' ' | (uint16_t)terminal_color << 8;
143 }
144 }
145}
uint16_t *const VGA_MEMORY
Волшебный адрес VGA_MEMORY.
Определения kernel.cpp:116
uint16_t * terminal_buffer
Указатель на буфер экрана
Определения kernel.cpp:125
static const size_t VGA_HEIGHT
Высота текстового экрана (почти везде по дефолту 25).
Определения kernel.cpp:112

Перекрестные ссылки terminal_buffer, terminal_color, terminal_column, terminal_row, VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY, VGA_HEIGHT, VGA_MEMORY и VGA_WIDTH.

Граф вызова функции:

◆ terminal_putchar()

void terminal_putchar ( char c)

Основная функция печати символа

Она обрабатывает перевод строки и двигает курсор.

  • Заметки
    Тут магия
    Говорим видеокарте передвинуть мигающую палочку вслед за текстом.

См. определение в файле kernel.cpp строка 181

182{
183 if (c == '\n') // если встретили энтер, прыгаем на новую строку
184 {
185 terminal_column = 0;
186 if (++terminal_row == VGA_HEIGHT)
188 return;
189 }
190
191 // рисуем символ
193
194 // двигаем координату вправо
195 if (++terminal_column == VGA_WIDTH)
196 {
197 terminal_column = 0;
198 if (++terminal_row == VGA_HEIGHT)
200 }
201
205 uint16_t pos = terminal_row * VGA_WIDTH + terminal_column;
206 outb(0x3D4, 0x0F);
207 outb(0x3D5, (uint8_t)(pos & 0xFF));
208 outb(0x3D4, 0x0E);
209 outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF));
210}
void terminal_scroll()
Скроллинг экрана
Определения kernel.cpp:151

Перекрестные ссылки outb(), terminal_color, terminal_column, terminal_putentryat(), terminal_row, terminal_scroll(), VGA_HEIGHT и VGA_WIDTH.

Граф вызовов:
Граф вызова функции:

◆ terminal_putentryat()

void terminal_putentryat ( char c,
uint8_t color,
size_t x,
size_t y )

Вспомогательная штука, чтобы положить символ в конкретную точку экрана

См. определение в файле kernel.cpp строка 171

172{
173 const size_t index = y * VGA_WIDTH + x;
174 terminal_buffer[index] = (uint16_t)c | (uint16_t)color << 8;
175}

Перекрестные ссылки terminal_buffer и VGA_WIDTH.

Граф вызова функции:

◆ terminal_scroll()

void terminal_scroll ( )

Скроллинг экрана

Если дошли до низа экрана, надо всё сдвинуть вверх.

См. определение в файле kernel.cpp строка 151

152{
153 for (size_t y = 0; y < VGA_HEIGHT - 1; y++)
154 {
155 for (size_t x = 0; x < VGA_WIDTH; x++)
156 {
157 terminal_buffer[y * VGA_WIDTH + x] = terminal_buffer[(y + 1) * VGA_WIDTH + x];
158 }
159 }
160 // последнюю строчку чистим
161 for (size_t x = 0; x < VGA_WIDTH; x++)
162 {
163 terminal_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = (uint16_t)' ' | (uint16_t)terminal_color << 8;
164 }
166}

Перекрестные ссылки terminal_buffer, terminal_color, terminal_row, VGA_HEIGHT и VGA_WIDTH.

Граф вызова функции:

◆ terminal_write()

void terminal_write ( const char * data)

Просто печатаем строку целиком

См. определение в файле kernel.cpp строка 215

216{
217 for (size_t i = 0; i < strlen(data); i++)
218 terminal_putchar(data[i]);
219}
size_t strlen(const char *str)
Считаем длину строки
Определения kernel.cpp:49

Перекрестные ссылки strlen() и terminal_putchar().

Граф вызовов:
Граф вызова функции:

◆ terminal_writeln()

void terminal_writeln ( const char * data)

Печать строки с переходом на новую

См. определение в файле kernel.cpp строка 224

225{
226 terminal_write(data);
227 terminal_putchar('\n');
228}

Перекрестные ссылки terminal_putchar() и terminal_write().

Граф вызовов:
Граф вызова функции:

Переменные

◆ cmd_buffer

char cmd_buffer[128]

Буфер, куда мы записываем то, что печатает юзер, пока он не нажмет энтер

См. определение в файле kernel.cpp строка 316

◆ cmd_len

int cmd_len = 0

Длина текущей введенной команды

См. определение в файле kernel.cpp строка 318

◆ kbd_US

char kbd_US[128]
Инициализатор
= {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0,
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ',
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

Карта клавиш

Железо присылает номер кнопки, а мы превращаем его в букву.

См. определение в файле kernel.cpp строка 259

259 {
260 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
261 '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
262 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0,
263 '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ',
264 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

◆ terminal_buffer

uint16_t* terminal_buffer

Указатель на буфер экрана

См. определение в файле kernel.cpp строка 125

◆ terminal_color

uint8_t terminal_color

Текущий цвет текста

См. определение в файле kernel.cpp строка 123

◆ terminal_column

size_t terminal_column

Текущий столбец курсора

См. определение в файле kernel.cpp строка 121

◆ terminal_row

size_t terminal_row

Текущая строка курсора

См. определение в файле kernel.cpp строка 119

◆ VGA_HEIGHT

const size_t VGA_HEIGHT = 25
static

Высота текстового экрана (почти везде по дефолту 25).

См. определение в файле kernel.cpp строка 112

◆ VGA_MEMORY

uint16_t* const VGA_MEMORY = (uint16_t *)0xB8000

Волшебный адрес VGA_MEMORY.

Сюда надо писать буквы, чтобы они появились на мониторе.

См. определение в файле kernel.cpp строка 116

◆ VGA_WIDTH

const size_t VGA_WIDTH = 80
static

Ширина текстового экрана (почти везде по дефолту 80).

См. определение в файле kernel.cpp строка 110