Чтение онлайн

ЖАНРЫ

Разработка ядра Linux
Шрифт:

clone(SIGCHLD, 0);

а вызов

vfork
в таком виде:

clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0);

Флаги, которые передаются в системный вызов

clone
, помогают указать особенности поведения нового процесса и детализировать, какие ресурсы должны быть общими для родительского и порожденного процессов. В табл. 3.1 приведены флаги системного вызова
clone
и их эффект.

Таблица 3.1. Флаги системного вызова clone

Флаг Описание
CLONE_FILES
Родительский и порожденный процессы совместно используют открытые файлы
CLONE_FS
Родительский и порожденный процессы совместно используют
информацию о файловой системе
CLONE_IDLETASK
Установить значение PID в нуль (используется только для холостых (idle) задач)
CLONE_NEWNS
Создать новое пространство имен для порожденной задачи
CLONE_PARENT
Родительский процесс вызывающего процесса становится родительским и для порожденного
CLONE_PTRACE
Продолжить трассировку и для порожденного процесса
CLONE_SETTID
Возвратить значение идентификатора TID в пространство пользователя
CLONE_SETTLS
Для порожденного процесса создать новую область локальных данных потока (thread local storage, TLS)
CLONE_SIGHAND
У порожденного и родительского процессов будут общие обработчики сигналов
CLONE_SYSVSEM
У родительского и порожденного процессов будет общая семантика обработки флага
SEM_UNDO
для семафоров System V
CLONE_THREAD
Родительский и порожденный процессы будут принадлежать одной группе потоков
CLONE_VFORK
Использовать
vfork
: родительский процесс будет находиться а приостановленном состоянии, пока порожденный процесс не возобновит его работу
CLONE_UNTRACED
Запретить родительскому процессу использование флага
CLONE_PTRACE
для порожденного процесса
CLONE_STOP
Запустить процесс в состоянии
TASK_STOPPED
CLONE_CHILD_CLEARTID
Очистить идентификатор TID для порожденного процесса
CLONE_CHILD_SETTID
Установить идентификатор TID для порожденного процесса
CLONE_PARENT_SETTID
Установить идентификатор TID для родительского процесса
CLONE_VM
У порожденного и родительского процессов будет общее адресное пространство

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

Часто в ядре полезно выполнить некоторые операции в фоновом режиме. В ядре такая возможность реализована с помощью потоков пространства ядра (kernel thread) — обычных процессов, которые выполняются исключительно в пространстве ядра. Наиболее существенным отличием между потоками пространства ядра и обычными процессами является то, что потоки в пространстве ядра не имеют адресного пространства (значение указателя

mm
для них равно
NULL
). Эти потоки работают только в пространстве ядра, и их контекст не переключается в пространство пользователя. Тем не менее потоки в пространстве ядра планируются и вытесняются так же, как и обычные процессы.

В ядре Linux потоки пространства ядра выполняют определенные задания, наиболее часто используемые, — это pdfush и ksoftirq. Эти потоки создаются при загрузке системы другими потоками пространства ядра. В действительности поток в пространстве ядра может быть создан только другим потоком, работающим в пространстве ядра. Интерфейс для запуска нового потока в пространстве ядра из уже существующего потока следующий:

int kernel_thread(int (*fn)(void*), void* arg, unsigned long flags);

Новая задача создается с помощью обычного системного вызова

clone
с соответствующими значениями флагов, указанными в параметре flags. При возврате из системного вызова родительский поток режима ядра завершается и возвращает указатель на структуру
task_struct
порожденного процесса.
Порожденный процесс выполняет функцию, адрес которой указан в параметре
fn
, в качестве аргумента этой функции передается параметр
arg
. Для указания обычных флагов потоков пространства ядра существует флаг
CLONE_KERNEL
, который объединяет в себе флаги
CLONE_FS
,
CLONE_FILES
и
CLONE_SIGHAND
, так как большинство потоков пространства ядра должны указывать эти флаги в параметре
flags
.

Чаще всего поток пространства ядра продолжает выполнять свою функцию вечно (или, по крайней мере, до перегрузки системы, но когда она произойдет в случае ОС Linux- неизвестно). Функция потока обычно содержит замкнутый цикл, в котором поток пространства ядра по необходимости возобновляет выполнение, исполняет свои обязанности и снова переходит в приостановленное состояние.

В следующих главах более детально будут рассмотрены конкретные примеры потоков пространства ядра.

Завершение процесса

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

Обычно уничтожение процесса происходит тогда, когда процесс вызывает системный вызов

exit
явно или неявно при выходе из главной функции программы (компилятор языка С помещает вызов функции
exit
после возврата из функции
main
). Процесс также может быть завершен непроизвольно. Это происходит, когда процесс получает сигнал или возникает исключительная ситуация, которую процесс не может обработать или проигнорировать. Независимо от того, каким образом процесс завершается, основную массу работы выполняет функция
do_exit,
а именно указанные далее операции.

• Устанавливается флаг

PF_EXITING
в поле
flags
структуры
task struct
.

• Вызывается функция

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

• Если включена возможность учета системных ресурсов, занятых процессами (BSD process accounting), то вызывается функция

acct_process
для записи информации об учете ресурсов, которые использовались процессом.

• Вызывается функция

__exit_mm
для освобождения структуры
mm_struct
, занятой процессом. Если эта структура не используется больше ни одним процессом (другими словами, не является разделяемой), то она освобождается совсем.

• Вызывается функция

exit_sem
. Если процесс находится в очереди ожидания на освобождение семафора подсистемы IPC, то в этой функции процесс удаляется из этой очереди.

• Вызываются функции

__exit_files
,
__exit_fs
,
exit_namespace
и
exit_signals
для уменьшения счетчика ссылок на объекты, которые отвечают файловым дескрипторам, данным по файловой системе, пространству имен и обработчикам сигналов соответственно. Если счетчик ссылок какого- либо объекта достигает значения, равного нулю, то соответствующий объект больше не используется никаким процессом и удаляется.

• Устанавливается код завершения задания, который хранится в поле

exit_code
структуры
task struct
. Значение этого кода передается как аргумент функции
exit
или задается тем механизмом ядра, из-за которого процесс завершается.

• Вызывается функция

exit_notify
, которая отправляет сигналы родительскому процессу завершающегося задания и назначает новый родительский процесс (reparent) для всех порожденных завершающимся заданием процессов, этим процессом становится или какой-либо один поток из группы потоков завершающегося процесса, или процесс
init
. Состояние завершающегося процесса устанавливается в значение
TASK_ZOMBIE
.

• Вызывается функция

schedule
для переключения на новый процесс (см. главу 4, "Планирование выполнения процессов"). Поскольку процесс в состоянии
TASK_ZOMBIE
никогда не планируется на выполнение, этот код является последним, который выполняется завершающимся процессом.

Исходный код функции

do_exit
описан в файле
kernel/exit.c
.

К этому моменту освобождены все объекты, занятые задачей (если они используются только этой задачей). Задача больше не может выполняться (действительно, у нее больше нет адресного пространства, в котором она может выполняться), а кроме того, состояние задачи —

TASK_ZOMBIE
Единственные области памяти, которые теперь занимает процесс, — это стек режима ядра и слябовый объект, соответственно содержащие структуры
thread_info
и
task_struct
.

Поделиться с друзьями: