Как добавить новые системные вызовы
В этой статье мы узнаем, как изменить ядро Linux, добавить свои собственные уникальные системные вызовы и, наконец, собрать ядро с добавленной функциональностью.
Прежде чем мы начнем модифицировать ядро Linux, нам нужно будет каким-то образом загрузить его.
Я собираюсь указать шаги, которые я предпринял, поэтому убедитесь, что вы точно следуете им, чтобы гарантировать такие же результаты.
- Загрузите программное обеспечение виртуальной машины, такое как Vmware \ VirtualBox
- Загрузите образ Ubuntu 18.04 https://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
# search the CONFIG_LOCALVERSION parameter and set it to "-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, с Linux4.15.18-custom»
- Установите ‘GRUB_TIMEOUT_STYLE в меню
- Установите «GRUB_TIMEOUT» на 5
- Добавьте строку: «GRUB_DISABLE_SUBMENUE=y» в конце
Чтобы закончить, нам нужно будет сгенерировать файл конфигурации GRUB и перезагрузиться с помощью
>> sudo update-grub
>> sudo reboot
После загрузки ОС убедитесь, что вы загрузили собственное ядро.
>> uname -r
Должно получиться «4.15.18-custom».
Вот и все, с предварительными условиями покончено, пора кодировать.
Функциональность, которую мы собираемся добавить, называется весами процессов.
Как следует из названия, мы назначим каждому процессу вес, который будет отражать его «тяжелость».
Два поведения, которые мы хотим сохранить:
- Когда процесс разветвляется, дочерний процесс будет иметь тот же вес, что и его отец.
- Вес процесса инициализации будет равен 0
Системные вызовы, которые мы собираемся реализовать, смогут
- Установить вес текущего процесса
- Получить общий вес текущего процесса рекурсивно
Во-первых, нам нужно каким-то образом сказать каждому процессу: «К вашему сведению, теперь у вас есть вес».
Для этого откройте ~/linux-4.15.18-custom/include/linux/sched.h
И внутри структуры
task_struct
добавьте атрибут int weight
struct task_struct { #ifdef CONFIG_THREAD_INFO_IN_TASK /* * For reasons of header soup (see current_thread_info()), this * must be the first element of task_struct. */ struct thread_info thread_info; #endif /* -1 unrunnable, 0 runnable, >0 stopped: */ int weight; #line 569 volatile long state; /* * This begins the randomizable portion of task_struct. Only * scheduling-critical items should be added above here. */ 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
Можете ли вы придумать какие-нибудь новые интересные функции, которые вы можете добавить? Возможно, это будет ваш следующий проект по программированию.
Последнее слово
Если вы хотите увидеть больше на эту и другие темы, загляните в мой блог.