netlib.narod.ru | < Назад | Оглавление | Далее > |
Традиционные реализации Unix предоставляют только один способ создания новых процессов после того, как система запущена: системный вызов fork. (Если читателей интересует происхождение первого процесса, то оно описано в главе 4.) Когда процесс вызывает функцию fork, он делится на два — подобно развилке дороги — после чего родительский и дочерний процессы вольны следовать различными путями. Непосредственно после выполнения функции fork родительский и дочерний процессы почти идентичны — все их переменные имеют одинаковые значения, у них открыты одни и те же файлы и т.п. Но если родительский процесс изменяет значение переменной, дочерний процесс не видит этого изменения и наоборот. Дочерний процесс — это копия родительского процесса (по крайней мере, вначале), но они не используют ресурсы совместно.
В Linux сохранена традиционная функция fork и добавлена более общая функция __clone. (Два символа подчеркивания говорят о том, что обычный код приложения не должен вызывать функцию __clone непосредственно, а вместо этого должен вызывать функции из библиотек потоков, построенные на основе __clone.) В то время как fork создает новый дочерний процесс, являющийся копией родительского процесса, но не использующий совместно с ним никакие ресурсы, функция __clone позволяет указывать, какие ресурсы родительский и дочерний процессы должны использовать совместно. Если функции __clone не будет передан ни один из пяти распознаваемых ею флагов, дочерний процесс не будет ничего использовать совместно с родительским процессом. Если передать все пять флагов, дочерний процесс будет использовать все совместно с родительским процессом, подобно обычному потоку. Остальные комбинации флагов создают промежуточные ситуации.
Попутно следует отметить, что с помощь функции kernel_thread (строка 2426) ядро создает несколько задач для собственного использования. Пользователи никогда не вызывают эту функцию — в действительности они и не могут это сделать; она предназначена исключительно для создания специальных процессов, таких как kswapd (он освещен в главе 8), которые по существу являются частями ядра, для удобства обрабатываемыми в качестве задач. Создаваемые функцией kernel_thread задачи имеют некоторые необычные свойства, которые не будут подробно освещаться (например, они не могут быть предварительно освобождены), но на данном этапе главное запомнить, что функция kernel_thread использует подпрограмму do_fork для выполнения всей рутинной работы. Таким образом, строго говоря, на практике даже эти специальные процессы создаются так же, как обычные.
23953: do_fork — это подпрограмма ядра, реализующая обе функции: и fork, и __clone.
23963: Выделяет структуру struct task_struct для представления нового процесса.
23967: Присваивает новой структуре struct task_struct ее начальное значение, скопированное непосредственно из текущего процесса. Остальная часть работы, выполняемой подпрограммой do_fork, в основном заключается в создании новых копий любой информации, которая не будет использоваться родительским и дочерни процессами совместно, (current, присутствующий в этой и других строках программы ядра, — это макрос, который вычисляет значение указателя на структуру struct task_struct, представляющую выполняющийся в текущий момент процесс. Этот макрос определяется в строке 10285, но это всего лишь вызов функции get_current, которая определена в строке 10277.)
23981: Новому процессу требуется слот в массиве task; этот слот отыскивается посредством функции find_empty_process (строка 23598; эта функция принципиально зависит от функции get_free_taskslot, определенной в строке 16532). Однако, работа этих функций несколько запутанна: значения не используемых членов массива task устанавливаются равными не NULL, а значению следующего элемента свободного списка (с помощью функции add_free_taskslot, в строке 16523). Следовательно, неиспользуемые записи массива task указывают на другие неиспользуемые записи массива task в связанном списке, a tarray_freelist просто указывает на заголовок этого списка. Таким образом, возврат свободной позиции — просто вопрос возврата заголовка списка (и, естественно, распространение указателя заголовка на следующий элемент). Для управления этой информацией было бы удобнее использовать отдельную структуру данных, но в ядре основное внимание всегда уделяется минимизации используемого объема памяти.
23999: PID присваивается новой задаче (подробности этого процесса вскоре будут освещены).
24045: Эта и несколько следующих строк, в которых используются вспомогательные функции, определенные где-либо в другом месте файла, создают для дочернего процесса его собственные копии выбранных фрагментов структуры родительского процесса, исходя из значения переданного аргумента clone_flags. Этот аргумент показывает, что важные фрагменты должны использоваться совместно вместо того, чтобы быть скопированы; в этом случае вспомогательная функция просто увеличивает значение счетчика ссылок и осуществляет возврат; в противном случае она создает новую копию, принадлежащую новому процессу.
24078: На данный момент все собственные структуры данных процесса установлены, но большинство структур данных ядра, которые отслеживают процессы, еще не установлены. Установка начинается с добавления процессов в граф процессов.
24079: Посредством вызова функции hash_pid вводит новый процесс в таблицу pidhash.
24088: Помещает новый процесс в состояние TASK_RUNNING и посредством вызова функции wake_up_process вводит его в текущую очередь (строка 26356).
Обратите внимание, что теперь не только заполнена структура struct task_struct, но и все важные структуры данных — список свободных слотов, список задач, граф процессов, текущая очередь и хеш PID — правильно изменены с учетом нового процесса. Можете себя поздравить!
netlib.narod.ru | < Назад | Оглавление | Далее > |