- Стресс-тестирование систем в Linux – утилита stress-ng
- Основные особенности и возможности stress-ng
- Синтаксис stress-ng
- Основные опции stress-ng
- Тестирование процессора
- Тестирование дисковой подсистемы
- Тестирование памяти
- Комплексное тестирование
- Заключение
- Как я могу произвести высокую загрузку процессора на сервере Linux?
- Загрузка ядра Linux. Часть 1
- Волшебная кнопка включения, что дальше?
- Загрузчик
- Начало этапа установки ядра
- Выравнивание регистров сегментов
- Настройка стека
- Настройка BSS
- Переход к main
- Вывод
Стресс-тестирование систем в Linux – утилита stress-ng
Для организации и проведения нагрузочного стресс-тестирования в Linux-системах существует утилита stress-ng. С помощью неё несложно сгенерировать реальную рабочую нагрузку на тестируемые подсистемы и, соответственно, оценить её возможности. Утилита, традиционно для Linux, предоставляет для работы интерфейс командной строки. Однако, это ни в коей мере не делает её неудобной. Со своими задачами она справляется на «отлично». В данной статье приведены инструкции, отражающие основы работы с утилитой stress-ng для некоторых самых распространённых ситуаций в стресс-тестировании систем на основе Linux.
Основные особенности и возможности stress-ng
Возможности, которыми обладает утилита stress-ng, довольно широки. Об этом свидетельствует огромное количество всевозможных опций, которыми её наделили разработчики.
Но ключевой особенностью stress-ng является то, что это полноценный инструмент со встроенными тестами. В отличие от многих других аналогов, при выполнении теста не производится обращений к сторонним и/или внешним ресурсам. Таким образом, stress-ng абсолютно самодостаточна. Практически в любом дистрибутиве Linux она доступна в стандартном репозитории и устанавливается с помощью системы управления пакетами (СУП) дистрибутива. Например, в Ubuntu:
Кроме всего прочего, stress-ng в своём составе очень качественные тесты для тестирования процессоров, в совокупности позволяющие наиболее полно сгенерировать нагрузку на CPU, используя такие методы как целочисленные и с плавающей запятой, битовые операции, комплексные вычисления и т. д.
Синтаксис stress-ng
Как уже было отмечено, stress-ng имеет настолько огромный набор опций, что в рамках данной статьи целесообразнее остановиться лишь на основных, позволяющих протестировать все основные подсистемы: CPU, виртуальную память, а также дисковую подсистему.
Синтаксис stress-ng довольно прост:
Задаёт конкретный метод тестирования виртуальной памяти. По-умолчанию выполняются все доступные для данной категории тесты, последовательно друг за другом. Подробнее в официальном руководстве по команде man stress-ng.
—vm-method mЗадаёт конкретный метод тестирования виртуальной памяти. По-умолчанию выполняются все доступные для данной категории тесты, последовательно друг за другом. Подробнее в официальном руководстве по команде man stress-ng.
Основные опции stress-ng
В таблице ниже указаны основные опции утилиты
Опция | Значение |
—class name | Задаёт тип теста. В качестве name указывается например cpu, memory, vm, io и другие. |
—metrics | Указывает, что по завершению теста должна быть выведена статистика основных метрик, отражающих поведение системы во время теста. |
—metrics-brief | То же, что и —metrics, но выводит ненулевые метрики. |
—cpu-method method | Задаёт метод генерации нагрузки для процессора. По-умолчанию выполняются все доступные для данной категории тесты, последовательно друг за другом. Более подробно об этой опции можно узнать, выполнив команду man stress-ng. |
—cpu N | Запускает для стресс-теста процессора N стрессоров для каждого его потока. |
—cpu-ops N | Указывает, через какое количество bogo-операций необходимо остановить тест CPU. |
—hdd-ops N | Указывает, через какое количество bogo-операций необходимо остановить тест жёстких дисков. |
—hdd-bytes N | Записывает N байт для каждого процесса работы с жёстким диском. По-умолчанию равно 1 Гб. |
—vm N | Запускает для стресс-теста виртуальной памяти N стрессоров. |
—vm-bytes N | Размещает N байт для каждого процесса работы с памятью. По-умолчанию равно 256 Мб. Объём также может быть указан в процентах от общего объёма виртуальной памяти в системе. Значения можно задавать в бфйтах, килобайтах, мегабайтах и гигабайтах, используя суффиксы b, k, m и g соответственно. |
—sequential N | Задает N количество потоков для выполнения тестов, если N не указано или равно 0, то количество потоков равно числу процессоров. |
Для удобства и быстрого составления необходимых тестов рекомендуется пользоваться также некоторыми вспомогательными опциями, например:
- что бы запустить несколько экземпляров каждого стресс-теста используется опция —all N, где N – необходимое количество экземпляров;
- для установки таймаута, т. е. времени продолжительности стресс-теста используется опция —timeout.
Тестирование процессора
Для подавляющего большинства ситуаций классическим примером стресс-теста можно использовать тест, выполняемый следующей командой:
В данном тесте задействованы 16 потоков для тестирования 16-поточного процессора. Вывод результатов может быть следующим:
Естественно количество потоков следует задавать в соответствии со спецификацией используемого процессора.
Тестирование дисковой подсистемы
Для проведения стресс-тестирования накопителей, таких как жёсткие диски можно для начала провести низкоуровневый тест ввода вывода
Вывод команды будет следующим
Еще один стресс-тест дисков можно выполнить командой
В данном случае будет запущено 5 стрессоров для жёстких дисков, которые будут остановлены по завершении 100 тыс. bogo-операций.
Во время тестирования можно смотреть загрузку командой iostat
Тестирование памяти
Что бы провести стресс-тест памяти используйте команду
После окончания мы получим результат проверки приблизительно следующего вида
Комплексное тестирование
Если необходимо провести комплексное стресс-тестирование, можно задействовать работу нескольких основных подсистем вместе одной командой:
Эта команда запустит тест для CPU в 8 потоков, тест виртуальной памяти с размещением в ней одного гигабайта данных, а также 4 стрессора для тестирования операций ввода/вывода.
Что бы запустить тестирование всего «железа», используется команда
Эта команда запустит все тесты. После выполнения результат будет выведен в консоль. Во время выполнения команды лучше компьютер не трогать
Заключение
В заключение стоит ещё раз отметить, что утилита stress-ng по своим возможностям очень универсальна и позволяет качественно протестировать любую систему. Приведенные выше примеры охватывают наиболее распространённые ситуации по нагрузочному тестированию Linux-систем. Для проведения специфичных или более сложных тестов рекомендуется обращаться к официальному руководству по использованию утилиты, доступному по команде man stress-ng.
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
Источник
Как я могу произвести высокую загрузку процессора на сервере Linux?
В настоящее время я нахожусь в процессе отладки установки Cacti и хочу создать загрузку ЦП для отладки графиков загрузки ЦП.
Я попытался просто запустить cat /dev/zero > /dev/null , который прекрасно работает, но использует только 1 ядро:
Есть ли лучший метод тестирования / максимизации системных ресурсов под нагрузкой?
Попробуйте stress Это в значительной степени эквивалент Windows consume.exe :
Нет необходимости устанавливать какой-либо дополнительный пакет, ваш старый добрый шелл может сделать это самостоятельно.
Этот однострочный загрузит ваши четыре ядра 1 на 100%:
Как это работает, довольно просто, он запускает четыре бесконечных цикла. Каждый из них повторяет нулевую инструкцию ( : ). Каждый цикл способен загружать ядро процессора на 100%.
Если вы используете bash , ksh93 и другие оболочки поддерживаете диапазоны, (т.е. не dash старше ksh ), вы можете использовать это не портативный синтаксис:
Замените 4 на количество процессоров, которые вы хотите загрузить, если отличается от 4 .
Предполагая, что при запуске одного из этих циклов у вас уже не было фоновой работы, вы можете остановить генерацию нагрузки с помощью этой команды:
Отвечая на комментарий @ underscore_d, вот улучшенная версия, которая значительно упрощает остановку загрузки, а также позволяет указать тайм-аут (по умолчанию 60 секунд.) A Control — C также уничтожит все циклы разгона. Эта функция оболочки работает как минимум под bash и ksh .
1 Обратите внимание, что с процессорами, поддерживающими более одного потока на ядро (Hyper-threading), ОС распределяет нагрузку на все виртуальные процессоры. В этом случае поведение загрузки зависит от реализации (для каждого потока может быть указано, что он занят на 100% или нет). ,
Источник
Загрузка ядра Linux. Часть 1
От загрузчика к ядру
Если вы читали предыдущие статьи, то знаете о моём новом увлечении низкоуровневым программированием. Я написал несколько статей о программировании на ассемблере для x86_64 Linux и в то же время начал погружаться в исходный код ядра Linux.
Мне очень интересно разобраться, как работают низкоуровневые штуки: как программы запускаются на моём компьютере, как они расположены в памяти, как ядро управляет процессами и памятью, как работает сетевой стек на низком уровне и многое другое. Итак, я решил написать еще одну серию статей о ядре Linux для архитектуры x86_64.
Обратите внимание, что я не профессиональный разработчик ядра и не пишу код ядра на работе. Это всего лишь хобби. Мне просто нравятся низкоуровневые вещи и интересно в них копаться. Поэтому если заметите какую-то путаницу или появилятся вопросы/замечания, свяжитесь со мной в твиттере, по почте или просто создайте тикет. Буду благодарен.
Все статьи публикуются в репозитории GitHub, и если что-то не так с моим английским или содержанием статьи, не стесняйтесь отправить пулл-реквест.
Обратите внимание, что это не официальная документация, а просто обучение и обмен знаниями.
- Понимание кода на C
- Понимание кода ассемблера (синтаксис AT&T)
Во всяком случае, если вы только начинаете изучать такие инструменты, я постараюсь что-то объяснить в этой и следующих статьях. Окей, с вступлением закончили, пора погрузиться в ядро Linux и низкоуровневые вещи.
Я начал писать эту книгу во времена ядра Linux 3.18, и многое могло измениться с тех пор. Если есть изменения, я буду соответственно обновлять статьи.
Волшебная кнопка включения, что дальше?
Хотя это статьи о ядре Linux, мы пока не дошли до него — по крайней мере, в этом параграфе. Как только вы нажмете волшебную кнопку питания на своём ноутбуке или настольном компьютере, он начинает работать. Материнская плата посылает сигнал к блоку питания. После получения сигнала он обеспечивает компьютеру необходимое количество электроэнергии. Как только материнская плата получает сигнал «Питание в норме», то пытается запустить CPU. Тот сбрасывает все оставшиеся данные в своих регистрах и устанавливает предопределённые значения для каждого из них.
У процессоров 80386 и более поздних версий после перезагрузки должны быть такие значения в регистрах CPU:
Процессор начинает работать в реальном режиме. Давайте немного вернемся назад и попытаемся понять сегментацию памяти в этом режиме. Реальный режим поддерживается на всех x86-совместимых процессорах: от 8086 до современных 64-разрядных процессоров Intel. В процессоре 8086 используется 20-битная шина адресов, то есть он может работать с адресным пространством 0-0xFFFFF или 1 мегабайт . Но у него есть только 16-битные регистры с максимальным адресом 2^16-1 или 0xffff (64 килобайта).
Сегментация памяти нужна для использования всего доступного адресного пространства. Вся память делится на небольшие сегменты фиксированного размера по 65536 байт (64 КБ). Поскольку с 16-битными регистрами мы не можем обратиться к памяти выше 64 КБ, был разработан альтернативный метод.
Адрес состоит из двух частей: 1) селектор сегмента с базовым адресом; 2) смещение от базового адреса. В реальном режиме базовым адресом селектора сегмента является селектор сегмента * 16 . Таким образом, чтобы получить физический адрес в памяти, нужно умножить часть селектора сегмента на 16 и добавить к нему смещение:
Например, если у регистра CS:IP значение 0x2000:0x0010 , то соответствующий физический адрес будет таким:
Но если взять селектор наибольшего сегмента и смещение 0xffff:0xffff , то получается адрес:
то есть 65520 байт после первого мегабайта. Поскольку в реальном режиме доступен только один мегабайт, 0x10ffef становится 0x00ffef с отключенной линией A20.
Хорошо, теперь мы немного знаем о реальном режиме и адресации памяти в этом режиме. Вернемся к обсуждению значений регистров после сброса.
Регистр CS состоит из двух частей: видимого селектора сегментов и скрытого базового адреса. Хотя базовый адрес обычно формируется путём умножения значения селектора сегмента на 16, но во время аппаратного сброса селектор сегмента в регистре CS получает значение 0xf000 , а базовый адрес — 0xffff0000 . Процессор использует этот специальный базовый адрес, пока не изменится CS.
Начальный адрес формируется добавлением базового адреса к значению в регистре EIP:
Мы получаем 0xfffffff0 , что на 16 байт ниже 4 ГБ. Эта точка называется вектором сброса. Это расположение в памяти, где CPU ждёт первую инструкцию для выполнения после сброса: операцию перехода (jmp), которая обычно указывает на точку входа BIOS. Например, если посмотреть исходный код coreboot ( src/cpu/x86/16bit/reset16.inc ), мы увидим:
Здесь мы видим код операции (опкод) jmp , а именно 0xe9 , и адрес назначения _start16bit — ( . + 2) .
Мы также видим, что раздел reset составляет 16 байт, и он компилируется для запуска с адреса 0xfffff0 ( src/cpu/x86/16bit/reset16.ld ):
Теперь запускается BIOS; после инициализации и проверки оборудования BIOS необходимо найти загрузочное устройство. Порядок загрузки сохраняется в конфигурации BIOS. При попытке загрузки с жёсткого диска BIOS пытается найти загрузочный сектор. На дисках с разметкой разделов MBR загрузочный сектор хранится в первых 446 байтах первого сектора, где каждый сектор равен 512 байтам. Последние два байта первого сектора — 0x55 и 0xaa . Они показывают BIOS, что это загрузочное устройство.
Собираем и запускаем:
nasm -f bin boot.nasm && qemu-system-x86_64 boot
QEMU получает команду использовать двоичный файл boot , который мы только что создали как образ диска. Так как двоичный файл, сгенерированный выше, удовлетворяет требованиям загрузочного сектора (начало в 0x7c00 и завершение магической последовательностью), то QEMU будет рассматривать двоичный файл как главную загрузочную запись (MBR) образа диска.
В этом примере мы видим, что код выполняется в 16-битном реальном режиме и начинается с адреса 0x7c00 в памяти. После запуска он вызывает прерывание 0x10, которое просто печатает символ ! ; заполняет оставшиеся 510 байт нулями и заканчивается двумя волшебными байтами 0xaa и 0x55 .
Двоичный дамп можно посмотреть утилитой objdump :
nasm -f bin boot.nasm
objdump -D -b binary -mi386 -Maddr16,data16,intel boot
Конечно, в реальном загрузочном секторе — код для продолжения процесса загрузки и таблица разделов вместо кучи нулей и восклицательного знака :). С этого момента BIOS передаёт управление загрузчику.
Примечание: как объясняется выше, CPU находится в реальном режиме; где вычисление физического адреса в памяти происходит следующим образом:
У нас только 16-битные регистры общего назначения, а максимальное значение 16-битного регистра 0xffff , поэтому на самых больших значениях результат будет:
где 0x10ffef равно 1 МБ + 64 КБ — 16 байт . В процессоре 8086 (первый процессор с реальным режимом) 20-битная адресная линия. Поскольку 2^20 = 1048576 , то фактически доступная память составляет 1 МБ.
В целом адресация памяти реального режима выглядит следующим образом:
В начале статьи написано, что первая инструкция для процессора находится по адресу 0xFFFFFFF0 , что намного больше 0xFFFFF (1 МБ). Как CPU получить доступ к этому адресу в реальном режиме? Ответ в документации coreboot:
0xFFFE_0000 — 0xFFFF_FFFF: 128 килобайт ROM транслируются в адресное пространство
В начале выполнения BIOS находится не в RAM, а в ROM.
Загрузчик
Ядро Linux можно загружать разными загрузчиками, такими как GRUB 2 и syslinux. В ядре есть протокол загрузки, который определяет требования к загрузчику для реализации поддержки Linux. В данном примере мы работаем с GRUB 2.
Продолжая процесс загрузки, BIOS выбрал загрузочное устройство и передал управление загрузочному сектору, выполнение начинается с boot.img. Из-за ограниченного объёма это очень простой код. Он содержит указатель для перехода к основному образу GRUB 2. Тот начинается с diskboot.img и обычно хранится сразу после первого сектора в неиспользуемом пространстве перед первым разделом. Приведённый выше код загружает в память остальную часть образа, который содержит ядро GRUB 2 и драйверы для обработки файловых систем. После этого выполняется функция grub_main.
Функция grub_main инициализирует консоль, возвращает базовый адрес для модулей, устанавливает корневое устройство, загружает/парсит конфигурационный файл grub, загружает модули и т.д. В конце выполнения она переводит grub в нормальный режим. Функция grub_normal_execute (из исходного файла grub-core/normal/main.c ) завершает последние приготовления и показывает меню для выбора операционной системы. Когда мы выбираем один из пунктов меню grub, запускается функция grub_menu_execute_entry , которая выполняет команду grub boot и загружает выбранную ОС.
Как указано в протоколе загрузки ядра, загрузчик должен прочитать и заполнить некоторые поля заголовка установки ядра, который начинается со смещения 0x01f1 от кода установки ядра. Это смещение указано в скрипте линкера. Заголовок ядра arch/x86/boot/header.S начинается с:
Загрузчик должен заполнить этот и остальные заголовки (которые помечены только как тип write в протоколе загрузки Linux, как в данном примере) значениями, которые получил из командной строки или рассчитал во время загрузки. Сейчас мы не будем подробно останавливаться на описаниях и пояснениях для всех полей заголовка. Позже обсудим, как ядро их использует. Описание всех полей см. в протоколе загрузки.
Как видим в протоколе загрузки ядра, память будет отображаться следующим образом:
Итак, когда загрузчик передаёт управление ядру, оно начинается с адреса:
где X — адрес загрузочного сектора ядра. В нашем случае X равен 0x10000 , как видно в дампе памяти:
Загрузчик перенёс ядро Linux в память, заполнил поля заголовка, а затем перешёл на соответствующий адрес памяти. Теперь мы можем перейти непосредственно к коду установки ядра.
Начало этапа установки ядра
Наконец-то мы в ядре! Хотя технически оно ещё не запущено. Сначала часть установки ядра должна кое-что настроить, в том числе декомпрессор и некоторые вещи с управлением памятью. После всего этого она распакует настоящее ядро и перейдёт к нему. Выполнение установки начинается в arch/x86/boot/header.S с символа _start.
На первый взгляд это может показаться немного странным, так как перед ним есть несколько инструкций. Но давным-давно у ядра Linux был собственный загрузчик. Теперь же если запустить, например,
Собственно, файл header.S начинается с магического числа MZ (см. скриншот дампа выше), текста сообщения об ошибке и заголовка PE:
Он нужен для загрузки операционной системы с поддержкой UEFI. Его устройство рассмотрим в следующих главах.
Фактическая точка входа для установки ядра:
Загрузчик (grub2 и другие) знает об этой точке (смещение 0x200 от MZ ) и переходит прямо к ней, хотя header.S начинается с раздела .bstext , где находится текст сообщения об ошибке:
Точка входа установки ядра:
Здесь мы видим код операции jmp ( 0xeb ), который переходит к точке start_of_setup-1f . В нотации Nf , например, 2f ссылается на локальную метку 2: . В нашем случае это метка 1 , которая присутствует сразу после перехода, и она содержит остальную часть заголовка setup. Сразу после заголовка установки мы видим раздел .entrytext , который начинается с метки start_of_setup .
Это первый фактически выполняемый код (кроме предыдущих инструкций перехода, конечно). После того, как часть установки ядра получает управление от загрузчика, первая инструкция jmp находится по смещению 0x200 от начала реального режима ядра, то есть после первых 512 байт. Это можно увидеть как в протоколе загрузки ядра Linux, так и в исходном коде grub2:
В нашем случае ядро загружается по адресу 0x10000 . Это означает, что после запуска установки ядра регистры сегментов будут иметь следующие значения:
gs = fs = es = ds = ss = 0x10000
cs = 0x10200
После перехода к start_of_setup ядро должно сделать следующее:
- Убедиться, что все значения регистров сегментов одинаковы
- При необходимости настроить правильный стек
- Настроить bss
- Перейти к коду C в arch/x86/boot/main.с
Посмотрим, как это реализовано.
Выравнивание регистров сегментов
Прежде всего ядро проверяет, что регистры сегментов ds и es указывают на один и тот же адрес. Затем очищает флаг направления с помощью инструкции cld :
Как я писал ранее, grub2 по умолчанию загружает код установки ядра по адресу 0x10000 , а cs по адресу 0x10200 , потому что выполнение начинается не с начала файла, а с перехода сюда:
Это смещение на 512 байт от 4d 5a. Также необходимо выровнять cs с 0x10200 до 0x10000 , как и все остальные регистры сегментов. После этого устанавливаем стек:
Эта инструкция помещает на стек значение ds , за ним следуют адрес метки 6 и инструкция lretw , которая загружает адрес метки 6 в регистр счётчика команд и загружает cs со значением ds . После этого у ds и cs будут одинаковые значения.
Настройка стека
Почти весь этот код — часть процесса подготовки окружения для языка C в реальном режиме. Следующий шаг — проверить значение регистра ss и создать корректный стек, если значение ss неверное:
Это может инициировать три разных сценария:
- у ss допустимое значение 0x1000 (как у всех остальных регистров, кроме cs )
- у ss недопустимое значение, и флаг CAN_USE_HEAP установлен (см. ниже)
- у ss недопустимое значение, и флаг CAN_USE_HEAP не установлен (см. ниже)
Рассмотрим все сценарии по порядку:
- У ss допустимое значение ( 0x1000 ). В этом случае мы переходим к метке 2:
Здесь мы устанавливаем выравнивание регистра dx (который содержит значение sp , указанное загрузчиком) по 4 байтам и проверяем на нуль. Если он равен нулю, то помещаем в dx значение 0xfffc (выровненный по 4 байтам адрес перед максимальным размером сегмента 64 КБ). Если он не равен нулю, то продолжаем использовать значение sp , заданное загрузчиком ( 0xf7f4 в нашем случае). Затем помещаем значение ax в ss , что сохраняет правильный адрес сегмента 0x1000 и устанавливает правильный sp . Теперь у нас есть правильный стек:
- Во втором сценарии ss != ds . Сначала помещаем значение _end (адрес конца кода установки) в dx и проверяем поле заголовка loadflags , используя инструкцию testb , чтобы проверить, можно ли использовать кучу. loadflags — это заголовок битовой маски, который определяется следующим образом:
и, как указано в протоколе загрузки:
Имя поля: loadflags
Это поле является битовой маской.
Бит 7 (запись): CAN_USE_HEAP
Установите этот бит равным 1, чтобы указать, что значение
heap_end_ptr допустимо. Если это поле пусто, будет отключена
часть функциональности установки.
Если установлен бит CAN_USE_HEAP , то в dx ставим значение heap_end_ptr (которое указывает на _end ) и добавляем к нему STACK_SIZE (минимальный размер стека 1024 байта). После этого переходим к метке 2 (как в предыдущем случае) и делаем правильный стек.
- Если CAN_USE_HEAP не установлен, просто используем минимальный стек от _end до _end + STACK_SIZE :
Настройка BSS
Нужны ещё два шага, прежде чем перейти к основному коду C: это настройка области BSS и проверка «волшебной» подписи. Сначала проверка подписи:
Инструкция просто сравнивает setup_sig с магическим числом 0x5a5aaa55. Если они не равны, сообщается о неустранимой ошибке.
Если магическое число совпадает и у нас есть набор правильных регистров сегментов и стек, то осталось лишь настроить раздел BSS перед переходом к коду C.
Раздел BSS используется для хранения статически выделенных неинициализированных данных. Linux тщательно проверяет, что эта область памяти обнулилась:
Первым делом начальный адрес __bss_start перемещается в di . Затем адрес _end + 3 (+3 для выравнивания по 4 байтам) перемещается в cx . Регистр eax очищается (с помощью инструкции xor ), вычисляется размер раздела bss ( cx-di ) и он помещается в cx . Затем cx делится на четыре (размер «слова») и многократно используется инструкция stosl , сохраняя значение еах (нуль) в адрес, указывающий на di , автоматически увеличивая di на четыре и повторяя это до тех пор, пока сх не достигнет нуля). Чистый эффект этого кода заключается в том, что нули записываются во все слова в памяти от __bss_start до _end :
Переход к main
Вот и всё: у нас есть стек и BSS, так что можно перейти к функции main() C:
Функция main() находится в arch/x86/boot/main.c. О ней поговорим в следующей части.
Вывод
Это конец первой части об устройстве ядра Linux. Если у вас есть вопросы или предложения, свяжитесь со мной в твиттере, по почте или просто создайте тикет. В следующей части мы увидим первый код на C, который выполняется при установке ядра Linux, реализацию подпрограмм памяти, таких как memset , memcpy , earlyprintk , раннюю реализацию и инициализацию консоли и многое другое.
Источник