Kthreadd linux что это

Обновлено: 02.07.2024

Я видел повсюду разбросанную информацию, но, похоже, не могу найти ни одного окончательного ответа. Как вы очистите ядро ​​от зомби?

Просто для того, чтобы удостовериться и создать окончательный правильный способ обработки потоков в ядре, я хотел бы задать этот вопрос более широко. Как создать, завершить и очистить поток в ядре Linux?

Что у меня есть, так это:

Самое близкое, что я нашел к решению для очистки, это release_task Но я не нашел нигде, чтобы говорить об этом. Я представлял себе, что функции потока: kthread_create , kthread_run и т. д., должно быть kthread_join или kthread_wait , но не было. do_wait также представляется вероятным, но для этого не требуется struct task_struct *

Кроме того, я не уверен, является ли do_exit хорошей идеей или вообще необходима ли она. Может кто-нибудь придумать минимальный набросок того, как kthread должен быть создан, завершен и очищен?

1 ответ

Один из "правильных" способов сделать это - заставить вашу функцию потока проверить, является ли она kthread_should_stop , и просто вернуть ее, если она нужно остановиться.

Вам не нужно звонить do_exit , и если вы собираетесь kthread_stop это из функции выхода из модуля, вы, вероятно, не должны.

Это можно увидеть, просмотрев документацию по kthread_create_on_node в kernel/kthread.c (извлечение из ядра Linux 3.3. 1):

/**
* kthread_create_on_node - создать kthread.
* @threadfn: функция, запускаемая до достижения сигнала (текущего).
* @data: данные для @threadfn.
* @node: номер узла памяти.
* @namefmt: имя в стиле printf для потока.
*
* Описание: эта вспомогательная функция создает и называет ядро ​​
* нить. Поток будет остановлен: используйте wake_up_process () для запуска
* Это. Смотрите также kthread_run ().
*
* Если поток будет связан с конкретным процессором, укажите его узел
* в @node, чтобы получить сходство NUMA для стека kthread или же дать -1.
* Когда проснется, поток запустит @threadfn () с @data в качестве своего
* аргумент @threadfn () может вызвать do_exit () напрямую, если это
* автономный поток, для которого никто не будет вызывать kthread_stop () или
* вернуть, когда kthread_should_stop () 'имеет значение true (что означает
* kthread_stop () был вызван). Возвращаемое значение должно быть нулевым
* или отрицательный номер ошибки; он будет передан kthread_stop ().
*
* Возвращает task_struct или ERR_PTR (-ENOMEM).
* /

«Соответствующий» комментарий присутствует для kthread_stop :

Если threadfn () может вызывать саму do_exit (), вызывающая сторона должна убедиться, что task_struct не исчезнет .

(И я не уверен, как вы это сделаете - вероятно, держитесь за struct_task с помощью get_task_struct )

Если вы пройдете путь создания потока, вы получите что-то вроде:

kthreadd_task настроен в init/main.c в reset_init . Он запускает функцию kthreadd (из kthread.c )

И сама функция kthread делает:

. Так что, если your_thread_function просто вернется, do_exit будет вызываться с возвращаемым значением. Не нужно делать это самостоятельно.

ещё буквально пара слов о потоках ядра. Вероятно, Вы замечали в выводе ps -ef поток ядра kthreadd. Наверняка, у Вас даже возникал вопрос, для чего он нужен? На самом деле, всё достаточно просто. Опосредованно взаимодействуя с помощью определённых API с данным потоком, различные части ядра могут ставить в очередь на создание новые потоки, которые и создаёт kthreadd. Данные API ядра используются наряду с функцией kernel_thread(), с тем отличием, что создание нового процесса происходит не сразу же. Сам поток kthreadd стартует после инициализации основного потока ядра в функции rest_init() init/main.c: Описатель задачи (struct task_struct) kthreadd хранится в переменной ядра kthreadd_task. Заметьте, что функция kernel_thread() возвращает идентификатор нового процесса (потока). Для того, чтобы получить описатель задачи, в ядре, в частности в приведённом коде, используется функция find_task_by_pid_ns(), первый аргумент которой - идентификатор потока, чей описатель задачи нам нужен, а второй - пространство идентификаторов процесса-предка (в данном случае - init).

  1. в переменную create типа struct create_thread_nfo* получаем элемент списка;
  2. удаляем элемент из списка;
  3. снимаем спин-лок со списка, так что теперь в него снова можно добавлять новые элементы извне;
  4. используя данные, находящиеся по адресу, сохранённому в create, создаём новый поток с помощью вспомогательной функции create_kthread() (не путать с kthread_create() и kernel_thread()! в отличие от них, create_kthread() не экспортируется за пределы kthread.o);
  5. ну и наконец снова взводим спин-лок на списке, чтобы не произошло ничего неожиданного, пока мы будем проверять пуст ли список потоков к созданию :)

Очередь потоков к созданию - это нечто иное, как двусвязный список, о ктором я уже писал. Вот как выглядит структура-элемент этого списка: Самые интересные на данный момент поля здесь - это threadfn - указатель на функцию, которая должна выполняться в отдельном потоке, data - указатель на данные, которые будут использоваться потоком, result - указатель на описатель задачи для нового потока.

В список новые элементы добавляются с помощью функции kthread_create(): kthread_create() принимает указатель на функцию, которая должна выполняться в отдельном потоке (threadfn), указатель на данные для потока (data) и имя нового потока (namefmt). Сперва kthread_create() инициализирует поля переменной create типа struct kthread_create_info. Затем на время добавления нового элемента в список потоков, "ждущих" создания, kthread_create() взводит спин-лок, чтобы никто больше не мог добавить новые элементы и внести сумятицу в наши дела :) Новый элемент списка добавляется в хвост с помощью макроса list_add_tail(). Затем спин-лок снимается - список снова свободен. Далее, kthread_create() будит поток kthreadd с помощью wake_up_process(), который должен будет проверить очередь и запустить новый поток, как описывалось выше. Ну и наконец, если при создании нового потока не возникло ошибок, то подготавливаем такие реквизиты нового потока, как имя и параметры планирования. Оставив новый поток в состоянии сна, возвращаем управления. Вот и всё, что делает kthread_create(). Если кратко, то она ставит в очередь новый запрос на создание потока, дожидается, пока не отработает рабочий поток kthreadd и не будет создан новый спящий поток.

На этом матрёшка не заканчивается. В упомянутой функции kthreadd() мы сознательно пропустили одно место: create_kthread() - ещё одна вспомогательная внутренняя функция, которая с помощью уже знакомого нам вызова kernel_thread() создаёт реальный поток. Но, не всё так просто. На самом деле, здесь создаётся не тот поток, который указывался в качестве аргумента threadfn для kthread_create()! Создаётся всего лишь новый поток kthread - опять же, внутренняя неэкспортируемая за пределы единицы трансляции функция :) По результату, который будет положительным числом - идентификатором процесса (pid) в сучае успеха или отрицательным - код ошибки, узнаём, как всё прошло.

Что же делает kthread()? Не так и много. Сначала, новый поток копирует все необходимые данные из переданного ему аргумента _create - т.е., адрес функции потока (threadfn) и данные для потока (data). Во внутреннюю пемеременную self типа struct kthread записываем "состояние" потока - should_stop - не 0, если поток должен быть остановлен и exited - код возврата, если поток завершился. Здесь: мы устанавливаем состояние потока в TASK_UNINTERRUPTIBLE (спящий процесс). В описатель задачи - result - записываем указатель на текущий контекст (ведь когда kthread была запущена через kernel_thread(), у нас уже свой контекст выполнения для данного экземпляра kthread()). Далее, сигнализируем о завершении инициализации описателя нового процесса и состояния задачи с помощью complete() (здесь я намеренно пока не углубляюсь в то, что такое атомарное ожидание). Просим ядро выполнить перепланирование процессов. В этом месте, по сути, выполнение нашего нового потока приостанавливается, т.к. планировщик не будет выделять ему процессорное время ввиду того, что поток спит. Следущие строки будут выполнены только после того, как поток будет разбужен: Тут, как будто, ничего мистического нет. Сразу, как только поток вновь получит процессор в своё владение (будет кем-то разбужен), в переменную-код возврата мы записываем код ошибки EINTR - "процесс прерван". Затем необходимо проверить, не успел ли кто-то отменить выполнение потока. Если нет, то наконец-то выполняем именно нашу функцию - threadfn(), передавая ей в качестве аргумента данные для работы. Ну а после этого - делаем do_exit() после того, как функция threadfn() возратит управление.

Для того, чтобы лучше понять суть сказанного, попробуем реализовать свой собственный поток ядра и начнём с постановки задачи: допустим, нам нужен такой поток, который бы вёл наблюдение за состоянием ядра и асинхронно с помощью вспомогательной пользовательской программы уведомлял Вас, что определённые структуры ядра находятся в состоянии, не вполне хорошем или неудовлетворительном. Как например ситуация, когда свободное место в принимающем буфере сетевого драйвера почти иссякло. Чтобы решить поставленную задачу необходимо следующее:

  1. код должен быть оформлен, как фоновое задание, которое ожидает наступления некого асинхронного события;
  2. наш код должен иметь доступ к структурам данных ядра, ведь определение критического уровня буфера выполняется другими частями ядра;
  3. наш код должен вызывать вспомогательное пользовательское приложение, что затратно в плане времени.

Наш поток освобождает процессор и пробуждается только тогда, когда наступает ожидаемое событие, например, изменение в структуре, за которой мы ведём наблюдение. Пробудившись, поток должен вызвать вспомогательное приложение, которое и обязано уведомить пользователя.

Приведённый далее вызов создаёт поток ядра:
Наш поток можно создать там, где это наиболее подходит. Ну, хотя бы в init/main.c. Набор флагов, передаваемый функции kernel_thread(), определяет, какие ресурсы должны разделяться между родителем и его потоками-потомками.

В коде, приводимом далее, функция ядра daemonize() создаёт поток без ассоциированных с ним пользовательских ресурсов. Следующий вызов reparent_to_init() изменяет родителя взывающего потока на поток init. Каждый поток (равно, как и процесс) в Linux (да и не только в нём) имеет единственного родителя. В случае, когда родительский процесс завершается, не дождавшись окончания работы своего дочернего потока (процесса), такой осиротевший процесс становится зомби. Проще говоря, процесс-зомби уже не планируется на выполнение, но в то же время запись о процессе, поставленная ему в соответствие и описывающая его не удаляется. Вся информация сохраняется, хотя, по существу, процесс уже завершён и больше никому не нужен. Переназначение процесса-родителя позволяет избежать таких неприятностей. В ядрах ветки 2.6 функция daemonize() сама вызывает reparent_to_init(). Так как вызов daemonize() по умолчанию блокирует все сигналы, нам необходимо вызвать функцию allow_signal(), чтобы указать, какие сигналы можно доставлять нашему потоку. Т.к. в ядре нет обработчиков сигналов, как например, в glibc, необходимо использовать специальную функцию signal_pending(), с помощью которой можно определить, был ли потоку послан сигнал и если да, то обработать его соответственно. В нашем примере мы обрабатываем только SIGKILL, по которому поток завершается.

Если Вы откомпилируете приведённый код в составе ядра, то при загрузке с новым ядром по команде ps сможете увидеть свой новый поток, который выполняется, как потомок init:

Прежде чем снова углубляться в детали реализации потоков ядра, взглянем на код ниже:

Полезную работу ядро выполняет используя контекст процесса или контекст прерывания. Причём контекст процесса и контекст прерывания не связаны друг с другом. Чтобы не вносить путаницу, необходимо вероятно пояснить, что когда речь идёт о контексте прерывания, то подразумевается не обязательно сам факт прерывания а всего лишь контекст, в котором ядро выполняет отложенные вызовы. Самый первый кусок кода нашей реализации потока выполняется в контексте потока. В то время как код, приведённый абзацем ранее, использует контекст прерывания для обработки отложенной функции и в равной же степени он может быть использован в контексте процесса. Оба вида контекстов связываются посредством структур данных ядра. myevent_id и myevent_waitqueue в приведённом нами коде используются для связывания контекстов. Доступ к переменной myevent_id управляется спин-блокировкой (spin lock). Потоки ядра выполняются в режиме вытесняющей многозадачности только если при конфигурировании ядра включен параметр CONFIG_PREEMPT. Без включения этого параметра и патча вытесняющей многозадачности в ядрах ветки 2.4 наш поток заморозит всю систему, если он сам не заснёт. Если в первом куске кода, приводимом в начале этой статьи, закомментировать вызов schedule(), то с выключенным параметром CONFIG_PREEMPT ядро также окажется заблокированным.

Давайте ещё раз посмотрим на код, который усыпляет наш поток mykthread до наступления события.

  • TASK_RUNNING: Процесс выполняется (использует процессор) или находится в очереди выполнения, ожидая выделения процессорного времени.
  • TASK_INTERRUPTIBLE: Процесс приостановлен до наступления определенного события. Это состояние может быть прервано сигналами. После получения сигнала или возобновления путем явного выполнения "пробуждающего" вызова процесс переходит в состояние TASK_RUNNING.
  • TASK_UNINTERRUPTIBLE: Данное состояние аналогично TASK_INTERRUPTIBLE, с той лишь разницей, что в нем не происходит обработка сигналов. Прерывание процесса, находящегося в этом состоянии, может быть нежелательным, поскольку ожидание может быть частью некоторой важной задачи. При наступлении ожидаемого события процесс возобновляется путем явного выполнения "пробуждающего" вызова.
  • TASK_STOPPED: Выполнение процесса остановлено, он не выполняется и не может начать выполняться. Процесс переходит в это состояние по получении таких сигналов, как SIGSTOP, SIGTSTP и т.д. Процесс сможет снова стать исполняемым после получения сигнала SIGCONT.
  • TASK_TRACED: Процесс находится в этом состоянии при выполнении его мониторинга такими процессами, как, например, отладчики.
  • EXIT_ZOMBIE: Процесс завершен. Он будет находиться в системе до момента получения родительским процессом статистической информации по его выполнению.
  • EXIT_DEAD: Конечное состояние (соответствующее своему названию). Процесс переходит в это состояние при его удалении из системы после того, как родительский процесс получит всю статистическую информацию, выполнив системный вызов wait4() или waitpid().

Вернёмся к нашему потоку. mykthread спит, находясь в очереди ожидания (myevent_waitqueue) и изменяет своё состояние на TASK_INTERRUPTIBLE, давая понять, что он отказывается от ожидания в очереди выполнения и, стало быть, квантов времени, которые ему может выделить планировщик выполнения процессов. Вызов функции schedule() необходим для того, чтобы планировщик выбрал другой процесс из очереди выполнения. Когда другая часть ядра будит mykthread с помощью wake_up_interruptible(), поток помещается обратно в очередь выполнения планировщика, а состояние задачи меняется на TASK_RUNNING, т.о. ситуация гонок отсутствует даже в том случае, когда процесс пробуждается, находясь в состоянии TASK_INTERRUPTIBLE (к слову: механизм пробуждения процесса из очереди ожидания, а точнее, механизм самой очереди ожидания предотвращает ситуацию, известную, как "стадо перед грозой", когда несколько процессов ждут одного события - например, освобождения ресурса. Когда ядро будит их, при эсклюзивном доступе к ресурсу получается, что большинство процессов пробуждаются лишь для того, чтобы поучаствовать в гонке за ресурсом и не солоно хлебавши продолжить спать - таких процессов из чсла разбуженных будет большинство, ведь победитель возможен только один) и происходит вызов функции планирования - schedule(). Поток перемещается также в очередь выполнения, если ему доставлен сигнал SIGKILL. Когда планировщик в порядке очерёдности извлекает поток mykthread из очереди выполнения, выполнение потока продолжается в точке А.

Теперь, для полной реализации задуманного, всё, что нам остаётся сделать, это реализовать вспомогательное приложение, которое будет запускаться потоком ядра и, собственно, уведомлять пользователя о событии. Итак, хорошая новость. Для нашего удобства ядро поддерживает механизм вызова пользовательских приложений, если требуется выполнить какие-то определённые действия. Например, если включена функция автоматической подгрузки модулей, то ядро по необходимости динамически подгружает необходимые модули, используя для этой цели пользовательский загрузчик модулей. По умолчанию, загрузчик модулей - /sbin/modprobe, но ничто не мешает нам заменить его на другой, зарегистрировав альтернативный загрузчик с помощью файла /proc/sys/kernel/modprobe. Аналогичным образом ядро уведомляет пользовательское окружение о событиях, связанных с устройствами, поддерживающими горячее подключение (hot-plug). В данном случае вспомогательное приложение по умолчанию - /sbin/hotplug, которое можно заменить, переписав содержимое файла /proc/sys/kernel/hotplug. В приведённом далее коде реализована функция, с помощью которой поток ядра mykthread уведомляет пользовательское окружение о наступлении события. Приложение, которое необходимо вызвать, регистрируется в виртуальной файловой системе /proc системным вызовом sysctl, если конечно при конфигурировании ядра был разрешён интерфейс sysctl (CONFIG_SYSCTL). Для того, чтобы добавить свой sysctl-параметр ядра, необходимо добавить новый элемент в массив kern_table в файле kernel/sysctl.c:

Благодаря этому при загрузке в модифицированное ядро на файловой системе /proc появится такой файл: /proc/sys/kernel/myevent_handler.

Чтобы зарегистрировать свой обработчик, достаточно в командной строке вполнить следующую команду:


Т.о., при наступлении ожидаемого события будет выполнен наш /path/to/kernel-helper.
Информацию о структуре данных ядра, которая по каким-то причинам может вызвать у нас интерес, мы передаём пользовательскому процессу через переменную окружения (TROUBLED_DS). В принципе, вспомогательным приложением может быть даже простой шелл-скрипт, который уведомит пользователя по эл.почте о наступившем событии, при чём информацию о наблюдаемой структуре данных ядра он также сможет спокойно получить из переменной окружения: Функция call_usermodehelper() выполняется в контексте запускаемого процесса и работает с полномочиями пользователя root (root capabilities). В ядрах ветки 2.6 этот механизм реализуется как очередь заданий (work queue).

Процесс запуска системы Android 8.0 при запуске ядра Linux - незанятый процесс (1)

1 Обзор


После включения сначала загрузите программу загрузки uboot с помощью инструкций по сборке, а затем uboot загружает образ ядра из раздела и запускает ядро. В данной статье анализ начнется с загрузки ядра, процесс загрузки компиляции мы здесь анализировать не будем, и желающие могут изучить его самостоятельно.
Запуск ядра Linux в основном включает 3 специальных процесса: незанятый процесс (PID = 0), процесс инициализации (PID = 1) и процесс kthreadd (PID = 2), эти три процесса Основа ядра.
idle-процесс - это первый процесс в системе Linux, это родительский процесс для процесса init и процесса kthreadd.
Процесс init - это первый пользовательский процесс в системе Linux и предок системного приложения Android. Наши приложения прямо или косвенно используют его в качестве родительского процесса.
Процесс kthreadd - это менеджер ядра системы Linux, все потоки ядра прямо или косвенно принимают его как родительский процесс.

2. Запускается процесс ожидания.

Начало простаивающего процесса написано на языке ассемблера, и соответствующий файл - kernel / msm-4.4 / arch / arm64 / kernel / head.S. Поскольку все они написаны на языке ассемблера, я не буду их подробно рассказывать, но перехватываю ключ Части следующие:

Оператор b start_kernel, b означает переход, переход к start_kernel.h, соответствующая реализация этого заголовочного файла находится в kernel / msm-4.4 / init / main.c

В конце функция start_kernel вызовет функцию rest_init.Эта функция запускает процесс init и процесс kthreadd. Функция rest_init будет проанализирована ниже.

3、rest_init

Определено в kernel / msm-4.4 / init / main.c

Rest_init буквально означает оставшуюся инициализацию, но ее совсем не оставляют. Он создает два важных процесса в системе Linux, init и kthreadd, и превращает процесс init_task в неактивный процесс. Затем я изменю метод в rest_init Анализируйте один за другим, чтобы все поняли.

3.1 rcu_scheduler_starting

Определено в kernel / msm-4.4 / kernel / rcu / Tree.c

3.2 kernel_thread

Определено в kernel / msm-4.4 / kernel / fork.c

Для создания процесса используется функция do_fork: сначала она вызывает copy_process () для создания нового процесса, а затем вызывает wake_up_new_task (), чтобы поместить процесс в очередь выполнения и запустить новый процесс. Первый параметр kernel_thread - это указатель на функцию, которая будет выполняться после создания процесса, а третий параметр - это метод создания процесса, как показано ниже:

имя параметра эффект
CLONE_PARENT Родительский процесс созданного дочернего процесса является родительским процессом вызывающего, а новый процесс и процесс, создавший его, становятся «братьями», а не «отцом и сыном».
CLONE_FS Дочерний и родительский процессы используют одну и ту же файловую систему, включая корень, текущий каталог, umask.
CLONE_FILES Дочерний процесс и родительский процесс используют одну и ту же таблицу дескрипторов файлов.
CLONE_NEWNS Запустить дочерний процесс в новом пространстве имен. Пространство имен описывает файловую иерархию процесса.
CLONE_SIGHAND Дочерний процесс и родительский процесс используют одну и ту же таблицу обработчиков сигналов.
CLONE_PTRACE Если отслеживается родительский процесс, отслеживается и дочерний процесс.
CLONE_UNTRACED Если отслеживается родительский процесс, дочерний процесс не отслеживается
CLONE_VFORK Родительский процесс приостанавливается до тех пор, пока дочерний процесс не освободит ресурсы виртуальной памяти.
CLONE_VM Дочерний процесс и родительский процесс работают в одном пространстве памяти.
CLONE_PID Когда создается дочерний процесс, PID согласуется с родительским процессом.
CLONE_THREAD Добавлен в Linux 2.4 для поддержки стандарта потоков POSIX, дочерний процесс и родительский процесс используют одну и ту же группу потоков.

3.3 kernel_init

Определен в kernel / msm-4.4 / init / main.c; эта функция более важна и отвечает за запуск процесса инициализации. Этот процесс будет проанализирован позже.

3.4 numa_default_policy

Определено в файле kernel / msm-4.4 / mm / mempolicy.c

3.5 kthreadd

Определяется в kernel / msm-4.4 / kernel / kthread.c; процесс kthreadd будет рассмотрен во второй статье. Это важный процесс в ядре, отвечающий за планирование и управление потоками ядра. Потоки ядра в основном основаны на нем. Родительский процесс

3.6 rcu_read_lock & rcu_read_unlock

Он определен в kernel / msm-4.4 / include / linux / rcupdate.h; RCU (обновление для чтения и копирования) - это способ синхронизации данных, который играет важную роль в текущем ядре Linux. Основным объектом данных RCU является связанный список. Цель состоит в том, чтобы повысить эффективность просмотра и чтения данных. Для достижения цели используется механизм RCU для чтения данных без трудоемких операций блокировки в связанном списке. Таким образом, несколько потоков могут читать связанный список одновременно и разрешать одному потоку изменять связанный список (при изменении его необходимо заблокировать)

3.7 find_task_by_pid_ns

Определено в kernel / msm-4.4 / kernel / pid.c
task_struct называется дескриптором процесса. Эта структура содержит всю информацию, необходимую процессу, и определена в файле kernel / msm-4.4 / include / linux / sched.h.
Его структура очень сложна, в этой статье мы не будем на ней останавливаться, вы можете обратиться к структуре дескриптора процесса Linux task_struct для подробного объяснения.

Роль find_task_by_pid_ns - получить task_struct, соответствующий pid в хеш-таблице согласно pid

3.8 init_idle_bootup_task

Определено в kernel / msm-4.4 / kernel / sched / core.c

Linux реализует 5 классов планировщика, основанных на различных стратегиях планирования.Класс планировщика может использовать одну или несколько стратегий планирования для планирования определенного типа процесса, или он может использоваться для особых ситуаций или процессов со специальными функциями. Порядок приоритета: stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class.
Видно, что idle_sched_class имеет самый низкий приоритет, а неактивный процесс вызывается только тогда, когда система находится в режиме ожидания.

3.9 schedule_preempt_disabled

Определено в kernel / msm-4.4 / kernel / sched / core.c

3.10 cpu_startup_entry

Определено в kernel / msm-4.4 / kernel / cpu / idle.c

4. Резюме

Неактивный процесс - это первый процесс системы Linux. Номер процесса равен 0. После завершения инициализации системной среды запускаются два важных процесса, процесс init и процесс kthreadd. После выполнения работы по созданию запускается бесконечный цикл, отвечающий за Планирование процессов. В следующей статье я буду использовать kthreadd как точку входа для объяснения процесса kthreadd дворецкого ядра системы Linux, продолжение следует. . .

Интеллектуальная рекомендация


Краткое описание общих функций MPI

содержание 1, основная функция MPI 2, точка-точка функция связи 3, коллективная функция связи 1, основная функция MPI MPI_Init(&argc, &argv) Информировать системы MPI для выполнения всех необх.

Процесс загрузки Android 8.0 (2) Запуск процесса Kthreadd kthreadd linux

Процесс запуска запуска процесса простоя ядра Linux (PID = 0) анализируется в предыдущей статье, а процесс Kthreadd и процесс INITE. Процесс INIT более сложный и помещен в следующий анализ. В этой статье анализируется процесс запуска процесса Kthreadd.

Kthreadd определяет в ядре \ Kernel \ kthread.c

Прежде чем узнать выше, процесс Kthreadd создан в Rest_init (). Структура Task_Struct - это дескриптор процесса, который включает в себя такую ​​информацию, как PID Thread_info и процесс состояния.

Состояние Описывать
TASK_RUNNING Указывает состояние, в котором процесс выполняется или готов выполнить
TASK_INTERRUPTIBLE Процессы, потому что некоторые условия заблокированы (приостановлены), процесс преобразует состояние достижения из этого состояния после установления условия.
TASK_UNINTERRUPTIBLE Значение похоже на Task_IntureBreble, но мы передаем любую информацию и т. Д., Не могу их проснуться, только когда она доступна, она будет пробуждено.
TASK_STOPPED Процесс остановлен
TASK_TRACED Процесс контролируется процессом отладчика и других процессов
EXIT_ZOMBIE Исполнение процесса прекращается, но его родительский процесс не использовал системные вызовы, которые нужно вызвать, чтобы узнать его информацию о прекращении, и процесс становится процессом зомби.
EXIT_DEAD Процесс убит, то есть окончательное состояние процесса
TASK_KILLABLE Когда процесс находится в этом новом состоянии сна, его принцип работы похож на Task_unintureBere, но может реагировать только на фатальные сигналы.

В цикле Kthreaddd проверьте тему, которая будет создана из списка KTHREAD_CREATE_LIST. Если нет, пусть CPU вступит в состояние сна. Если вы назвали текут создания Create_kThread ().

Create_kthread определяет в ядре \ kernel \ kthread.c

Создайте темы в create_kThread (). Затем выполните функцию Kthread. Kthreadd - это резьба демона для ядра PID = 2. KTHREAD - это просто функция потока. Примечание

Kthread определяет в ядре \ Kernel \ kthread.c

Кthred создает новую тему, а затем введите состояние сна. Вам нужно вручную просыпаться на потоках и выполнять соответствующие функции. Анализ процесса Kthreadd, The Dead Cycle выглядит из списка KTHREAD_CREATE_LIST () и необходимо для создания нового потока.

Существует два способа создания потока в ядре Linux, один - kthread_create (), упомянутый выше (), но поток, созданный kthread_create (), не просыпается, вы должны быть где-то. Поток, созданный Kthread_Run ().

Kthread_create () и Kthread_Run () Определено в Kernel \ включают \ Linux \ kthread.h

Kthread_Create () и Kthread_Run () - два макроса Linux

Kthread_create и kthread_run не являются функцией, а определение макроса, замена соответствующего кода при компиляции, параметры макроса не имеют определения типа типа, не определение типа multi-line добавит \

Kthread_create_on_node определяет в ядре \ kernel \ kthread.c

Kthread_create () и kthread_run ()-> kthread_create_on_node -> __kthread_create_on_node -> list_add_tail

Это добавит созданную резьбу на хвост Kthread_Crate_List, и Kthreadd всегда будет сравниваться с подключенным списком Kthread_Create_List, если есть нить.

Список подключенного KTHREAD_CREATE_LIST будет цикл в процессе Kthread, если будет создан новый поток. Если вы не входите в состояние вашего сна, пусть CPU

KTHREAD_CREATE () и KTREAD_RUN () INNUX KERNEL Создайте нить Call__kThTread_create_On_node Функция, добавьте новый поток на хвост Kthread_Crate_List.

Читайте также: