Путешествие по процессам Linux – PID 2 (kthreadd)

283
Путешествие по процессам Linux - PID 2 (kthreadd)
Путешествие по процессам Linux - PID 2 (kthreadd)

После объяснения PID 1, теперь мы поговорим о PID 2. По сути, kthreadd – это “демон потоков ядра”. Создание нового потока ядра происходит с помощью kthreadd (мы рассмотрим весь процесс). Таким образом, PPID всех потоков ядра равен 2 (проверьте ps, чтобы убедиться в этом). Как объяснялось в посте о PID 1 (init), создание “kthreadd” осуществляется функцией ядра “rest_init” (https://elixir.bootlin.com/linux/latest/source/init/main.c#L680 – показывает исходный код). Есть вызов функции “kernel_thread” (после создания init).

В основном, ядро использует ” kernel threads” (далее kthreads) для выполнения фоновых операций. Поэтому неудивительно, что множество подсистем ядра используют kthreads для выполнения асинхронных операций и/или периодических операций. В общем, цель kthreadd – сделать доступным интерфейс, с помощью которого ядро может динамически порождать новые kthreads, когда это необходимо.

В целом, kthreadd непрерывно работает (бесконечный цикл- https://elixir.bootlin.com/linux/latest/source/kernel/kthread.c#L730) и проверяет “kthread_create_list” на наличие новых kthreads, которые должны быть созданы (Вы можете проверить код здесь –https://elixir.bootlin.com/linux/latest/source/kernel/kthread.c#L717). Для создания kthread используется функция “kthread_create” (https://elixir.bootlin.com/linux/latest/source/include/linux/kthread.h#L27). используется вспомогательный макрос для “kthread_create_on_node” (https://elixir.bootlin.com/linux/latest/source/kernel/kthread.c#L503). Можно также использовать “kthread_run”, это просто обертка для “kthread_create” (https://elixir.bootlin.com/linux/latest/source/include/linux/kthread.h#L51). Аргументы, передаваемые создающей функции, включают: функцию для запуска в потоке, args для функции и имя.

Просматривая исходный код, мы увидели, что “kthread_create” вызывает “kthread_create_on_node”, который инстанцирует структуру “kthread_create_info” (на основе args функции). После этого эта структура ставится в очередь в конце “kthread_create_list” и пробуждается “kthreadd” (и ждет, пока kthread не будет создан, это делается “__kthread_create_on_node” – https://elixir.bootlin.com/linux/latest/source/kernel/kthread.c#L435). Что делает “kthreadd”, так это вызывает “create_thread” на основе информации, поставленной в очередь (https://elixir.bootlin.com/linux/latest/source/kernel/kthread.c#L745). “create_thread” вызывает “kernel_thread” (https://elixir.bootlin.com/linux/latest/source/kernel/kthread.c#L730), который затем вызывает “kernel_clone” (https://elixir.bootlin.com/linux/latest/source/kernel/fork.c#L2697). “kernel_clone” вызывает “copy_process”, который создает новый процесс как копию старого (https://elixir.bootlin.com/linux/latest/source/kernel/fork.c#L2655) – вызывающему нужно запустить созданный процесс (или поток в нашем случае). Кстати, поток создания новой задачи (напомним, что каждый процесс/поток в Linux называется task и представлен “struct task_struct”) из пользовательского режима также попадает в “copy_process”.

Для простоты я создал график потока, который показывает процесс создания kthread, здесь не все вызовы, только те, которые я посчитал достаточно важными. Кроме того, в обоих случаях макросов/функций я использовал глагол ” calls”.

Процесс создания потоков ядра
Процесс создания потоков ядра