Какая связь между `task_struct` и`pid_namespace`?
Я изучаю некоторый код ядра и пытаюсь понять, как структуры данных связаны друг с другом. Я знаю основную идею о том, как работает планировщик и что такое PID. Тем не менее, я понятия не имею, что такое пространство имен в этом контексте, и не могу понять, как все они работают вместе.
Я прочитал некоторые объяснения (включая части O'Reilly "Понимание ядра Linux") и понимаю, что возможно, что один и тот же PID попал в два процесса, потому что один завершился, а идентификатор был перераспределен. Но я не могу понять, как все это делается.
Так:
- Что такое пространство имен в этом контексте?
- Какова связь между
task_struct
а такжеpid_namespace
? (Я уже понял, что это связано сpid_t
но не знаю как)
Некоторые ссылки:
- Значение
pid_namespace
- Значение
task_struct
- Значение
upid
(смотрите такжеpid
просто под ним)
1 ответ
Возможно, эти ссылки могут помочь:
- Пространства имен PID в действии
- Краткое введение в пространства имен PID (это от системного администратора)
После просмотра второй ссылки становится ясно, что пространства имен - отличный способ изолировать ресурсы. И в любой ОС, включая Linux, процессы являются одним из наиболее важных ресурсов. Своими словами
Да, все, с этим пространством имен можно перезапустить нумерацию PID и получить свой собственный процесс "1". Это можно рассматривать как "chroot" в дереве идентификатора процесса. Это очень удобно, когда вам нужно иметь дело с пидами в повседневной работе и застряли с 4-значными числами...
Таким образом, вы создаете свое собственное частное дерево процессов, а затем назначаете его конкретному пользователю и / или конкретной задаче. Внутри этого дерева процессам не нужно беспокоиться о том, что PID конфликтуют с теми, которые находятся вне этого "контейнера". Следовательно, это все равно, что передать это дерево другому "корневому" пользователю. Этот молодец проделал замечательную работу по объяснению вещей с хорошим небольшим примером, чтобы завершить его, поэтому я не буду повторять это здесь.
Что касается ядра, я могу дать вам несколько советов, чтобы вы начали. Я не эксперт здесь, но я надеюсь, что это поможет вам в некоторой степени.
В этой статье, посвященной LWN, описывается старый и новый взгляд на PID. Своими словами:
Все PID, которые может иметь задача, описаны в
struct pid
, Эта структура содержит значение идентификатора, список задач с этим идентификатором, счетчик ссылок и узел списка хеширования, которые должны быть сохранены в хеш-таблице для более быстрого поиска. Еще несколько слов о списках задач. В основном задача имеет три идентификатора PID: идентификатор процесса (PID), идентификатор группы процессов (PGID) и идентификатор сеанса (SID). PGID и SID могут совместно использоваться между задачами, например, когда две или более задач принадлежат одной и той же группе, поэтому каждый идентификатор группы обращается к более чем одной задаче. С пространствами имен PID эта структура становится эластичной. Теперь каждый PID может иметь несколько значений, причем каждое из них является действительным в одном пространстве имен. То есть задача может иметь PID 1024 в одном пространстве имен и 256 в другом. Итак, бывшийstruct pid
изменения. Вот какstruct pid
Выглядело так, как до введения пространств имен PID:struct pid { atomic_t count; /* reference counter */ int nr; /* the pid value */ struct hlist_node pid_chain; /* hash chain */ struct hlist_head tasks[PIDTYPE_MAX]; /* lists of tasks */ struct rcu_head rcu; /* RCU helper */ };
И вот как это выглядит сейчас:
struct upid { int nr; /* moved from struct pid */ struct pid_namespace *ns; /* the namespace this value * is visible in */ struct hlist_node pid_chain; /* moved from struct pid */ }; struct pid { atomic_t count; struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; int level; /* the number of upids */ struct upid numbers[0]; };
Как видите,
struct upid
теперь представляет значение PID - оно хранится в хэше и имеет значение PID. Чтобы преобразоватьstruct pid
к PID или наоборот можно использовать набор помощников, таких какtask_pid_nr()
,pid_nr_ns()
,find_task_by_vpid()
, так далее.
Хотя и немного устаревшая, эта информация достаточно справедлива, чтобы вы начали. Здесь есть еще одна важная структура, о которой стоит упомянуть. это struct nsproxy
, Эта структура является фокусом всего пространства имен вещей в отношении процессов, с которыми она связана. Он содержит указатель на пространство имен PID, которое будут использовать дочерние элементы этого процесса. Пространство имен PID для текущего процесса находится с помощью task_active_pid_ns
,
В struct task_struct
у нас есть прокси-указатель пространства имен, точно названный nsproxy
, который указывает на этот процесс struct nsproxy
состав. Если вы проследите шаги, необходимые для создания нового процесса, вы можете найти отношения между task_struct
, struct nsproxy
а также struct pid
,
Новый процесс в Linux всегда отбрасывается из существующего процесса, и его образ позже заменяется execve
(или аналогичные функции из семейства exec). Таким образом, как часть do_fork
, copy_process
вызывается.
В рамках копирования родительского процесса происходят следующие важные вещи:
task_struct
сначала дублируется с помощьюdup_task_struct
,- пространства имен родительского процесса также копируются с использованием
copy_namespaces
, Это также создает новыйnsproxy
структура для дочернего элемента, и его указатель nsproxy указывает на эту вновь созданную структуру Для процесса, отличного от INIT (первоначальный глобальный PID, то есть первый процесс, созданный при загрузке),
PID
структура выделяется с помощьюalloc_pid
который фактически выделяет новую структуру PID для новогоfork
процесс ред. Краткий фрагмент этой функции:nr = alloc_pidmap(tmp); if(nr<0) goto out_free; pid->numbers[i].nr = nr; pid->numbers[i].ns = tmp;
Это заполняет upid
структура, давая ему новый PID, а также пространство имен, к которому он в данный момент принадлежит.
Дальше как часть copy process
функция, этот вновь назначенный PID затем связывается с соответствующим task_struct
через функцию pid_nr
то есть его глобальный идентификатор (который является исходным номером PID, как видно из пространства имен INIT) хранится в поле pid
в task_struct
,
На заключительных этапах copy_process
устанавливается связь между task_struct
и этот новый pid
структурировать через pid_link
поле внутри task_struct
через функцию attach_pid
,
Это намного больше, но я надеюсь, что это, по крайней мере, даст вам некоторое преимущество.
ПРИМЕЧАНИЕ. Я имею в виду последнюю (на данный момент) версию ядра, а именно. 3.17.2.