O Torvalds Linus

Сайт о *nix системах и всем что с ними связано

Облегченные процессы создаются в Linux с помощью функции clone (), которая принимает следующие параметры:

fn —задает функцию, которую должен выполнить новый процесс; когда эта функция возвратит управление, потомок завершит свою работу. Функция возвращает целое число, представляющее собой код возврата процес – са – потомка;

arg —указывает на данные, необходимые для функции fn ();

flags —разнообразная информация. Младший байт задает номер сигнала, посылаемого родителю, когда потомок завершит работу (как правило, выбирается сигнал sigchld). Остальные три байта задают группу флагов клона, перечисленных в табл. 3.8;

chiid stack —задает указатель стека в режиме пользователя, который должен быть записан в регистр esp процесса – потомка. Вызывающий процесс (родитель) должен всегда выделять стек для нового потомка;

tis —задает адрес структуры, определяющей сегмент локальной памяти потока для нового облегченного процесса (см. главу 2). Имеет смысл только при установленном флаге clone settls;

ptid —задает адрес переменной процесса – родителя в режиме пользователя, где будет храниться идентификатор нового облегченного процесса. Имеет смысл только при установленном флаге clone parent settid;

ctid —задает адрес переменной нового облегченного процесса в режиме пользователя, где будет храниться идентификатор этого процесса. Имеет смысл только при установленном флаге clone child settid.

Функция clone () фактически является интерфейсной функцией, определенной в библиотеке С (см. разд. “API – интерфейсы стандарта POSIX и системные вызовы” в главе 10). Она устанавливает стек для нового облегченного

процесса и делает системный вызов clone о, скрытый от программиста. У служебной процедуры sys cione (), реализующей системный вызов clone (), нет параметров fn и а г д. Интерфейсная функция сохраняет указатель fn в стеке потомка в позиции, соответствующей адресу возврата самой интерфейсной функции, а указатель arg сохраняется в стеке потомка сразу под fn. Когда интерфейсная функция завершается, процессор извлекает адрес возврата из стека и выполняет функцию fn (arg).

Традиционный системный вызов fork о реализован в Linux в виде системного вызова clone о, у которого параметр flags задает сигнал sigchld и определяет, что все флаги клона сброшены, а параметр chiid stack является указателем на текущий стек процесса – родителя. Таким образом, родитель и потомок временно совместно используются одним стеком режима пользователя.

Системный вызов vforko, упомянутый в предыдущем разделе, реализован в Linux в виде системного вызова clone о, у которого параметр flags задает сигнал sigchld и флаги clone_vm и clone_vfork, а параметр chiid_stack является указателем на текущий стек процесса – родителя.

Функция do_fork()

Функция do_fork(), обрабатывающая системные вызовы clone о, fork о и vfork (), принимает следующие параметры:

clone flags —ТО же, ЧТО параметр flags функции clone ();

stack start —ТО же, ЧТО параметр chiid stack функции clone ();

regs —указатель на содержимое регистров общего назначения, сохраненное в стеке режима ядра при переключении из пользовательского режима в режим ядра.

stack size —не используется (всегда равен 0);

parent tidptr, child tidptr —ТО же, ЧТО параметры ptid И ctid фуНКЦИИ clone().

Функция do fork () вызывает служебную функцию copy process (), чтобы установить дескриптор процесса и прочие структуры данных ядра, необходимые для работы потомка. Приведем основные действия, выполняемые функцией do_fork():

1. Выделяет идентификатор процесса для потомка, просматривая битовую карту pidinap_array.

2. Проверяет поле ptrace процесса – родителя (то есть поле current ->ptrace). Если оно не равно нулю, значит, родитель отслеживается другим процессом, и тогда функция do_f or к () проверяет, хочет ли отладчик отслеживать процесс – потомок по своей инициативе (то есть независимо от флага clone ptrace, задаваемого родителем). В этом случае, если потомок не является потоком ядра (флаг clone untraced сброшен), функция устанавливает флаг CLONE_PTRACE.

3. Вызывает функцию copy process о, чтобы создать копию дескриптора процесса. Если все необходимые ресурсы доступны, функция возвращает адрес только что созданного дескриптора task struct. Эта функция —”рабочая лошадка” процедуры ветвления процесса, и мы обсудим ее сразу ПОСЛе фуНКЦИИ do_fork ().

4. Если либо установлен флаг clone stopped, либо необходим мониторинг процесса – потомка (то есть флаг рт ptraced в поле p ->ptrace установлен), функция переводит процесс – потомок в состояние task stopped и добавляет к нему сигнал sigstop, подлежащий обработке. Потомок остается в состоянии task stopped, пока какой-то другой процесс (предположительно, отслеживающий выполнение или родитель) не переведет его в состояние task running с помощью сигнала SIGCONT.

5. Если флаг clone stopped не установлен, функция вызывает функцию wake up new task (), которая выполняет следующие действия:

•настраивает параметры планирования родителя и потомка (см. разд. “Алгоритм планирования” главы 7);

•если потомок будет выполняться на том же процессоре, что и родитель8, и родитель с потомком не обращаются к одному набору таблиц страниц (флаг clone vm сброшен), функция заставляет процесс – потомок выполняться до родителя, занося процесс – потомок в очередь на выполнение, в которой стоит родитель, непосредственно перед родителем.

•в противном случае, если потомку не предстоит работать на одном процессоре с родителем, или если родитель с потомком совместно используют один набор таблиц страниц (флаг clone vm установлен),

функция ставит процесс – потомок на последнее месте родительской очереди на выполнение.

6. Если флаг clone stopped установлен, функция переводит процесс – потомок в состояние task_stopped.

7. Если выполнение процесса – родителя отслеживается, функция сохраняет идентификатор процесса – потомка в поле ptrace message текущего процесса и вызывает функцию ptrace notify о, которая фактически останавливает текущий процесс и отправляет сигнал sigchld его родителю. “Дедушкой”процесса – потомка здесь является отладчик, отслеживающий выполнение родителя. Сигнал sigchld уведомляет отладчик, что текущий процесс породил процесс, чей идентификатор может быть получен из поля current ->ptrace_message.

8. Если установлен clone vfork, функция ставит процесс – родитель в очередь ожидания и приостанавливает его выполнение до тех пор, пока потомок не освободит адресное пространство (то есть пока он не завершит работу или не запустит новую программу).

9. Завершает работу и возвращает идентификатор процесса – потомка. Функция copy_process()

Функция copy process () устанавливает дескриптор процесса и любые другие структуры данных ядра, которые могут потребоваться для выполнения потомка. Ее параметры такие же, как у функции do fork (), плюс идентификатор процесса – потомка. Она выполняет следующие действия:

1. Проверяет, совместимы ли флаги, переданные в параметре cione fiags. В частности, она возвращает код ошибки в следующих случаях:

•установлены флаги clone_newns и clone_fs;

•флаг clone_thread установлен, но флаг clone_sighand сброшен (облегченные процессы в одной группе потоков должны иметь общие сигналы);

•флаг clone sighand установлен, но флаг clone vm сброшен (облегченные процессы, имеющие общие обработчики сигналов, должны иметь и общий дескриптор памяти).

2. Выполняет дальнейшие защитные проверки, для чего вызывает функцию security_task_create() И, чуть ПОЗЖе, функцию security_task_alloc (). Ядро Linux 2.6 предлагает перехватчики для расширений безопасности, чтобы установить модель безопасности более строгую, чем та, что принята в традиционных Unix – подобных системах (см. главу 20).

3. Вызывает функцию dup task structо, чтобы получить дескриптор процесса для потомка. Эта функция выполняет следующие действия:

•вызывает функцию uniazy_fpu() для текущего процесса, чтобы сохранить, если необходимо, содержимое регистров FPU, ММХ и SSE/SSE2 в структуре thread info процесса – родителя. Впоследствии функция dup task struct о скопирует эти значения в структуру thread info процесса – потомка;

•выполняет макрос aiioc task structо, чтобы получить дескриптор (структуру task struct) для нового процесса, и сохраняет его адрес в локальной переменной tsk;

•выполняет макрос aiioc thread info, чтобы получить свободную область памяти для хранения структуры thread info и стека режима ядра для нового процесса, а затем сохраняет ее адрес в локальной переменной ti. Как было сказано в разд. “Идентификация процесса”ранее в этой главе, размер этой области памяти равен либо 8 Кбайт, либо 4 Кбайт;

•копирует содержимое дескриптора текущего процесса в структуру task struct, на которую указывает локальная переменная tsk, затем записывает Значение ti В ПОЛе tsk ->thread_info;

•копирует содержимое дескриптора thread info текущего процесса в структуру, на которую указывает локальная переменная ti, а затем записывает значение tsk в поле ti ->task;

•записывает 2 в счетчик обращений дескриптора нового процесса (tsk ->usage), чтобы показать, что дескриптор процесса используется и что соответствующий процесс “жив” (его состояние не exit zombie и не exit_dead);

•возвращает указатель на дескриптор нового процесса (tsk).

4. Проверяет значение, хранящееся В поле current ->signal ->rlim[RLIMIT_ nproc] .riim cur. Если оно меньше или равно текущему количеству процессов, принадлежащих пользователю, возвращается код ошибки, если у процесса нет привилегий root. Функция получает текущее количество процессов, принадлежащих пользователю, из пользовательской структуры по имени user struct. Эту структуру можно найти по указателю в поле user дескриптора процесса.

5. Увеличивает счетчик обращений структуры user struct (поле tsk -> user -> count) и счетчик процессов, принадлежащих пользователю (поле tsk ->user ->processes).

6. Убеждается, что количество процессов в системе (хранящееся в переменной nr threads) не превышает значение переменной max threads. Значение этой переменной, принимаемое по умолчанию, зависит от объема оперативной памяти в системе. Общее правило гласит, что память, занимаемая всеми дескрипторами thread inf о и стеками режима ядра, не может превышать 1/8 объема физической памяти. Впрочем, системный администратор может изменить это значение, записав новое в файл /proc/sys/kernel/threads – max.

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

8. Устанавливает несколько важных полей, имеющих отношение к состоянию процесса:

•инициализирует счетчик глобальной блокировки ядра tsk ->iock_depth значением 1.

•инициализирует поле tsk ->did_exec значением 0. Это счетчик системных вызовов execve (), сделанных процессом;

•обновляет некоторые флаги в поле tsk ->fiags, скопированные у родителя: вначале сбрасывает флаг pf superpriv, показывающий, воспользовался ли процесс какими-либо привилегиями суперпользователя, а затем устанавливает флаг pf forknoexec, показывающий, что потомок еще не сделал системный вызов execve ().

9. Сохраняет идентификатор нового процесса в поле tsk ->pid.

10. Если флаг clone_parent_settid в параметре cione_fiags установлен, функция копирует идентификатор процесса – потомка в переменную пользовательского режима, на которую указывает параметр parent tidptr.

11. Инициализирует структуры list head и спин – блокировки, входящие в состав дескриптора процесса – потомка, и устанавливает некоторые другие поля, имеющие отношение к ожидающим доставки сигналам, таймерам и хронометрической статистики.

12. Вызывает функции copy_semundo (), copy_files (), copy_fs(), copy_ sighand (), copy_signal (), copy_mm () И copy_namespace (), чтобы создать НОВЬЮ структуры и занести в них значения соответствующих структур родителя, если противоположное не указано с помощью параметра

clone_flags.

13. Вызывает функцию copy thread (), чтобы инициализировать стек режима ядра для процесса – потомка значениями, содержавшимися в регистрах

процессора в момент выдачи системного вызова clone о (эти значения были сохранены в родительском стеке режима ядра; см. главу 10). При этом, однако, функция принудительно записывает 0 в поле, соответствующее регистру еах (это код возврата системного вызова fork о или clone о в процессе – потомке). Поле thread, esp в дескрипторе потомка инициализируется базовым адресом стека режима ядра процесса – потомка, а адрес ассемблерной функции ret_from_fork() сохраняется в поле thread, eip. Если родитель пользуется битовой картой разрешений ввода/вывода, потомок получает копию этой карты. Наконец, если установлен флаг clone settls, потомок получает TLS – сегмент, определяемый структурой данных режима пользователя, на которую указывает параметр tIs системного вызова clone () 9.

14. Если установлен флаг clone_child_settid или clone_child_cleartid в параметре cione fiags, функция копирует значение параметра chiid tidptr В ПОЛе tsk ->set_chid_tid ИЛИ tsk ->clear_child_tid соответственно. Эти флаги говорят о том, что значение переменной, на которую указывает параметр chiid tidptr, в адресном пространстве потомка в режиме пользователя должно быть изменено, хотя физически операции записи будут выполнены позже.

15. Сбрасывает флаг tif syscall trace в структуре thread info процесса – потомка, чтобы функция ret_from_fork () не уведомляла процесс – отладчик о завершении системного вызова. Заметим, что при этом системный вызов на мониторинг потомка не отменяется, потому что этим управляет флаг PTRACE_SYSCALL В ПОЛе tsk ->ptrace.

16. Инициализирует поле tsk ->exit_signai номером сигнала, указанным в младших битах параметра cione fiags, если флаг clone thread не установлен. В последнем случае инициализирует это поле значением – 1. Как мы увидим в разд. “Завершение процесса” далее в этой главе, только завершение работы последнего члена группы потоков (как правило, это лидер группы) приводит к отправке уведомляющего сигнала родителю лидера группы.

17. Вызывает функцию sched forko, чтобы завершить инициализацию структуры данных для планировщика, принадлежащей новому процессу. Функция также переводит новый процесс в состояние task running и записывает 1 В поле preempt count Структуры thread info, отключая тем самым вытеснение в ядре. Кроме того, чтобы планирование процессов имело справедливый характер, функция делит оставшийся отрезок времени родителя между родителем и потомком.

18. Записывает в поле сри структуры thread info нового процесса номер локального процессора, полученный ОТ функции smp_processor_id().

19. Инициализирует поля, определяющие отношения между родителем и потомком. В частности, если флаг clone_parent или clone_thread установлен, функция инициализирует поля tsk ->reai_parent и tsk ->parent значением, хранящимся в поле current ->reai_parent, и в результате родитель потомка выступает в качестве родителя текущего процесса. В противном случае функция записывает в эти поля значение current.

20. Если мониторинг потомка не требуется (флаг clone ptrace не установлен), функция записывает 0 в поле tsk ->ptrace. Это поле содержит несколько флагов, используемых при мониторинге одного процесса другим. Таким образом, даже если текущий процесс отслеживается, потомок отслеживаться не будет.

21. Выполняет макрос set links, чтобы занести дескриптор нового процесса в список процессов.

22. Если мониторинг потомка требуется (флаг pt ptraced в поле tsk ->ptrace установлен), функция записывает в поле current ->parent значение поля tsk ->parent и заносит процесс – потомок в список отслеживаемых процессов, принадлежащий отладчику.

23. Вызывает функцию attach pid (), чтобы занести идентификатор нового процесса в хеш – таблицу pidhash [pidtype_pid] .

24. Если потомок является лидером группы потоков (флаг clone thread сброшен), функция:

•инициализирует поле tsk ->tgid значением из поля tsk ->pid;

•инициализирует поле tsk ->group_ieader значением из поля tsk;

•трижды вызывает функцию attach pido, чтобы занести идентификатор процесса – потомка в хеш – таблицы pidtype tgid, pidtype pgid и

PIDTYPE SID.

25. В противном случае, т. е. если потомок принадлежит к группе потоков своего родителя (флаг clone thread установлен), функция:

•инициализирует ПОЛе tsk ->tgid Значением ИЗ ПОЛЯ tsk ->current ->tgid;

•инициализирует поле tsk ->group_leader Значением из ПОЛЯ current -> group_leader;

•вызывает функцию attach pid (), чтобы занести процесс – потомок в хеш – таблицу pidtype tgid (точнее, в список, соответствующий идентификатору процесса и принадлежащий процессу current ->group_ leader).

26. Итак, новый процесс добавлен к множеству существующих процессов. Функция увеличивает значение переменной nr threads.

27. Устанавливает значение переменной totai forks, чтобы отразить в ней количество ответвленных процессов.

28. Завершает выполнение, возвращая указатель на дескриптор процесса – потомка (tsk).

В будущем, при очередном переключении процессов, планировщик окажет нашему процессу такую любезность, загрузив в соответствующие регистры процессора значения из поля thread дескриптора этого процесса. В частности, в регистр esp будет загружено значение thread.esp (то есть адрес стека процесса – потомка в режиме ядра), а в регистр eip —адрес функции ret from fork (). Эта функция, написанная на ассемблере, вызывает функцию scheduie taii о, загружает в остальные регистры значения, хранящиеся в стеке, и переводит процессор в режим пользователя, выполнение нового процесса начнется сразу по окончании работы системного вызова fork о, vfork () или clone (). Значение, возвращенное системным вызовом, содержится в регистре еах: оно равно 0 для потомка и идентификатору процесса для родителя. Чтобы понять, как это получается, вернитесь к описанию действий функции copy thread () над регистром еах для процесса – потомка. Потомок выполняет тот же код, что и родитель, с той разницей, что системный вызов fork() возвращает 0 (шаг 13 в описании функции copy process о). Разработчик приложения может воспользоваться этим фактом, применив прием, знакомый всем, кто программирует в Unix.

Add A Comment