Модификация ядра Linux: добавляем новые системные вызовы

от автора


В этой статье мы научимся изменять ядро 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, где вы найдете много интересных материалов из мира технологий и разработки.


ссылка на оригинал статьи https://habr.com/ru/articles/581444/


Комментарии

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

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