O Torvalds Linus

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

Потоки ядра

Mar-22-2012 By root

В традиционных Unix – системах некоторые критические задачи делегируются перемежающимся процессам. Сюда входят сброс дисковых кэшей, выгрузка неиспользуемых страниц, обслуживание сетевых соединений и другие задачи. В самом деле, выполнять эти задачи в строго линейной последовательности неэффективно; их функции и процессы конечного пользователя будут иметь лучшее время отклика, если такие действия выполняются в фоновом режиме. Поскольку некоторые системные процессы работают только в режиме ядра, современные операционные системы делегируют их функции потокам ядра, не обремененным ненужным контекстом пользовательского режима. В Linux потоки ядра отличаются от обычных процессов по следующим пунктам:

? потоки ядра работают только в режиме ядра, а обычные процессы выполняются попеременно то в режиме ядра, то в пользовательском режиме;

? поскольку потоки ядра работают только в режиме ядра, они обращаются только к линейным адресам, большим чем page offset; обычные же процессы пользуются всеми четырьмя гигабайтами линейных адресов, будь то в режиме пользователя или в режиме ядра.

Создание потока ядра

Функция kernei thread () создает новый поток ядра. Она принимает в качестве параметров адрес функции ядра, подлежащей выполнению (fn), аргумент, передаваемый этот функции (arg), и набор флагов клона (flags). Фактически она вызывает функцию do_fork () со следующими аргументами.

Флаг clone vm позволяет избежать копирования таблиц страниц вызвавшего процесса. Такое копирование было бы непроизводительной тратой времени и памяти, потому что новый поток ядра все равно не станет обращаться к адресному пространству пользовательского режима. Флаг clone untraced гарантирует, что никакой процесс не сможет отслеживать выполнение нового потока ядра, даже если производилось отслеживание вызвавшего процесса.

Параметр pregs, передаваемый функции do forkO, соответствует адресу в стеке режима ядра, по которому функция copy thread () сможет найти начальные значения регистров процессора для нового потока. Функция kernei thread () строит эту стековую область так, чтобы выполнялось следующее:

? регистры ebx и edx получили от функции copy thread () значения параметров fn и arg соответственно;

? регистр eip получил адрес следующего фрагмента ассемблерного кода:

movl %edx,%eax pushl %edx call %ebx pushl %eax call do_exit

Таким образом, новый поток ядра начнется с выполнения функции fn(arg). Если эта функция завершится, поток ядра сделает системный вызов exit (), передав ему значение, возвращенное функцией fn ().

Процесс О

Предок всех процессов, называемый процессом 0, или холостым процессом, или (по историческим причинам) процессом swapper, представляет собой поток ядра, созданный “с нуля”на этапе инициализации Linux. Этот всеобщий предок использует следующие статически выделяемые структуры (структуры для всех остальных процессов выделяются динамически):

? дескриптор процесса, хранящийся в переменной init task, которая инициализируется макросом init task;

? дескриптор thread info и стек режима ядра, хранящиеся в переменной

init_thread_union И инициализируемые МакрОСОМ INIT_THREAD_INFO;

? таблицы, на которые указывает дескриптор процесса.

Эти таблицы инициализируются, соответственно, следующими макросами.

? главный глобальный каталог страниц ядра, хранящийся в переменной

swapper_pg_dir (СМ. главу 2).

Функция start kernei о инициализирует все структуры данных, необходимые ядру, включает прерывания и создает еще один поток ядра, процесс 1, чаще называемый процессом init.

Этот новый поток ядра имеет идентификатор процесса, равный 1, и использует все соответствующие структуры данных ядра совместно с процессом 0. Будучи выбранным планировщиком, процесс init запускает функцию init ().

Создав процесс init, процесс 0 вызывает функцию cpu idie (), которая, в сущности, сводится к многократному выполнению ассемблерной инструкции hit при включенных прерываниях. Процесс 0 выбирается планировщиком, только если нет других процессов в состоянии task running.

В многопроцессорных системах процесс 0 имеется у каждого процессора. Сразу после включения питания система BIOS включает один процессор, оставив остальные выключенными. Процесс swapper, работающий на процессоре 0, инициализирует структуры данных ядра, а затем включает остальные процессоры и создает дополнительные процессы swapper с помощью функции copy process о, которой он передает ноль в качестве идентификатора нового процесса. Кроме того, ядро записывает в поле сри дескриптора thread info каждого ответвленного процесса соответствующий индекс процессора.

Процесс 1

Поток ядра, созданный процессом 0, вызывает функцию init (), которая завершает инициализацию ядра. Затем функция init () делает системный вызов execve (), чтобы загрузить исполняемую программу init. В результате поток ядра init становится обычным процессом, имеющим собственные структуры данных ядра. Процессор init продолжает существовать вплоть до выключения питания, поскольку он создает и отслеживает все процессы, реализующие внешние слои операционной системы.

Add A Comment