
В этой статье мы научимся изменять ядро Linux, добавим собственные уникальные системные вызовы и в завершении соберем ядро с новой функциональностью.
Прежде чем перейти к модификации ядра, его нужно скачать.
Я буду описывать все свои шаги и рекомендую следовать им в точности, чтобы гарантированно получить такой же результат.
- Скачать ПО для запуска виртуальной машины, например Vimware или VirtualBox.
- Скачать образ Ubuntu 18.04 (http://releases.ubuntu.com/18.04/).
- Настроить виртуальную ОС, используя скачанный образ.
После загрузки Ubuntu открыть терминал и следовать дальнейшим инструкциям:
Установка необходимых компонентов:
>> sudo sed -i "s/# deb-src/deb-src/g" /etc/apt/sources.list >> sudo apt update -y >> sudo apt install -y build-essential libncurses-dev bison flex >> sudo apt install -y libssl-dev libelf-dev
Скачивание исходного кода Linux:
>> cd ~ >> apt source linux
Изменение разрешений и переименование каталога:
>> sudo chown -R student:student ~/linux-4.15.0/ >> mv ~/linux-4.15.0 ~/linux-4.15.18-custom
Настройка сборки ядра:
>> cd ~/linux-4.15.18-custom >> cp -f /boot/config-$(uname -r) .config >> geany .config # Найти параметр CONFIG_LOCALVERSION и установить его как "-custom" >> yes '' | make localmodconfig >> yes '' | make oldconfig
Компиляция ядра:
>> make -j $(nproc)
Установка модулей ядра и образа:
>> sudo make modules_install >> sudo make install
Настройка GRUB:
>> sudo geany /etc/default/grub
После открытия файла сделайте следующее:
- Установите
GRUB_DEFAULTкакUbuntu, with Linux4.15.18-custom; - Установите
GRUB_TIMEOUT_STYLEкакmenu; - Установите
GRUB_TIMEOUTкак5; - В конце добавьте строку:
GRUB_DISABLE_SUBMENUE=y.
Для завершения нам понадобится сгенерировать файл конфигурации GRUB и выполнить перезагрузку:
>> sudo update-grub >> sudo reboot
После запуска системы убедитесь, что загрузили кастомное ядро:
>> uname -r
Вывод должен быть
4.15.18-custom.На этом с подготовительной частью мы закончили.
Код
В качестве новой функциональности мы добавим веса процессов.
Как и следует из названия, мы будем присваивать каждому процессу вес, представляющий его значимость.
Нам нужно реализовать поддержку двух поведенческих паттернов:
- При ответвлении дочерний процесс будет получать тот же вес, что и его родитель;
- Процесс
initбудет иметь вес 0.
Системный вызовы, которые мы собираемся реализовать, смогут:
- Устанавливать вес текущего процесса;
- Получать общий вес текущего процесса рекурсивно.
Первым делом нам потребуется каким-то образом сообщать каждому процессу о том, что у него появился вес.
Для этого откройте
~/linux-4.15.18-custom/include/linux/sched.hи в структуре
task_struct добавьте целочисленный атрибут веса.struct task_struct { #ifdef CONFIG_THREAD_INFO_IN_TASK /* * По причинам, описанным в заголовочном файле (смотрите current_thread_info()), это * должен быть первый элемент в task_struct. */ struct thread_info thread_info; #endif /* -1 unrunnable, 0 runnable, >0 stopped: */ int weight; #line 569 volatile long state; /* * С этого начинается рандомизируемая часть task_struct. Выше можно * добавлять только критически важные для планировщика элементы. */ randomized_struct_fields_start
Теперь нужно сообщить каждому процессу, каков его начальный вес. Для этого в том же каталоге, что и ранее, откройте
init_task.h, в нем перейдите к макроопределению INIT_TASK и добавьте в атрибут веса инициализацию.#define INIT_TASK(tsk) \ { \ INIT_TASK_TI(tsk) \ .weight = 0, \ #line 228 .state = 0, \ .stack = init_stack, \ ...
В последнем блоке мы сообщили каждому процессу, что у него теперь есть новый атрибут веса, и что при каждом создании нового процесса этот атрибут нужно инициализировать как
0.В текущем же мы настроим основу для новых системных вызовов.
Перейдите в
~/linux-4.15.18-custom/arch/x86/entry/syscalls/ и откройте syscall_64.tbl.Промотайте вниз файла и зарезервируйте номера системных вызовов.
... 332 common statx sys_statx 333 common hello sys_hello 334 common set_weight sys_set_weight 335 common get_total_weight sys_get_total_weight ...
Далее мы создадим сигнатуру системного вызова. В том же каталоге откройте
syscalls.h и промотайте вниз файла:... asmlinkage long sys_pkey_free(int pkey); asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags, unsigned mask, struct statx __user *buffer); asmlinkage long sys_hello(void); asmlinkage long sys_set_weight(int weight); #line 944 asmlinkage long sys_get_total_weight(void); #endif
Все настроено. Осталось только реализовать эти системные вызовы.
Перейдите в
~/linux-4.15.18-custom/kernel и создайте новый файл syscalls_weight.c.Не забудьте в том же каталоге открыть
Makefile и добавить ваш новый файл в процесс сборки:# SPDX-License-Identifier: GPL-2.0 # # Makefile for the linux kernel. # obj-y = fork.o exec_domain.o panic.o \ cpu.o exit.o softirq.o resource.o \ sysctl.o sysctl_binary.o capability.o ptrace.o user.o \ signal.o sys.o umh.o workqueue.o pid.o task_work.o \ extable.o params.o \ kthread.o sys_ni.o nsproxy.o \ notifier.o ksysfs.o cred.o reboot.o \ async.o range.o smpboot.o ucount.o hello_syscall.o syscalls_weight.o ...
Откройте созданный
syscalls_weight.c, и давайте переходить к реализации.Сначала добавляем библиотеки:
#include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/sched.h>
Саму же реализацию начнем с
sys_set_weight.asmlinkage long sys_get_weight(int weight){ if(weight < 0){ return -EINVAL; } current->weight = weight; return 0; }
Обратите внимание:
current– это указатель на текущую активную задачу;- При работе с системными вызовами принято возвращать
0в случае успешного выполнения и отрицательное значение при возникновении ошибки, как мы и прописали (учитывая, что мы не хотим допускать отрицательный вес).
Переходя к реализации следующего системного вызова, мы сначала определяем вспомогательную функцию:
int traverse_children_sum_weight(struct task_struct *root_task){ struct task_struct *task; struct list_head *list; int sum = root_task->weight; list_for_each(list, &root_task->children){ task = list_entry(list, struct task_struct, sibling); sum += traverse_children_sum_weight(task, true); } return sum;
После чего пишем саму реализацию:
asmlinkage long sys_get_total_weight(void){ return traverse_children_sum_weight(current); }
Вот и все. Вам осталось только собрать ядро, перезапустить систему и начинать пользоваться новой функциональностью.
Для сборки и перезагрузки выполните следующие команды:
make -j $(nproc) sudo cp -f arch/x86/boot/bzImage /boot/vmlinuz-4.15.18-custom sudo reboot
Автор оригинала статьи выражает признательность за ваше внимание и приглашает посетить его блог CodingKaiser, где вы найдете много интересных материалов из мира технологий и разработки.

