Роберт Лав - Разработка ядра Linux
- Название:Разработка ядра Linux
- Автор:
- Жанр:
- Издательство:Издательский дом Вильямс
- Год:2006
- Город:Москва
- ISBN:5-8459-1085-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Роберт Лав - Разработка ядра Linux краткое содержание
В книге детально рассмотрены основные подсистемы и функции ядер Linux серии 2.6, включая особенности построения, реализации и соответствующие программны интерфейсы. Рассмотренные вопросы включают: планирование выполнения процессов, управление временем и таймеры ядра, интерфейс системных вызовов, особенности адресации и управления памятью, страничный кэш, подсистему VFS, механизмы синхронизации, проблемы переносимости и особенности отладки. Автор книги является разработчиком основных подсистем ядра Linux. Ядро рассматривается как с теоретической, так и с прикладной точек зрения, что может привлечь читателей различными интересами и потребностями.
Книга может быть рекомендована как начинающим, так и опытным разработчикам программного обеспечения, а также в качестве дополнительных учебных материалов.
Разработка ядра Linux - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
• TASK_RUNNING
— процесс готов к выполнению (runnable). Иными словами, либо процесс выполняется в данный момент, либо находится в одной из очередей процессов, ожидающих на выполнение (эти очереди, runqueue
, обсуждаются в главе 4. "Планирование выполнения процессов").
• TASK_INTERRUPTIBLE
— процесс приостановлен (находится в состоянии ожидания, sleeping ), т.е. заблокирован в ожидании выполнения некоторого условия. Когда это условие выполнится, ядро переведет процесс в состояние TASK_RUNNING
. Процесс также возобновляет выполнение (wake up) преждевременно при получении им сигнала.
• TASK_UNINTERRUPTIBLE
— аналогично TASK_INTERRUPTIBLE
, за исключением того, что процесс не возобновляет выполнение при получении сигнала. Используется в случае, когда процесс должен ожидать беспрерывно или когда ожидается, что некоторое событие может возникать достаточно часто. Так как задача в этом состоянии не отвечает на сигналы, TASK_UNINTERRUPTIBLE
используется менее часто, чем TASK_INTERRUPTIBLE
[13] Именно из-за этого появляются наводящие ужас "неубиваемые" процессы, для которых команда ps(1) показывает значение состояния, равное D . Так как процесс не отвечает на сигналы, ему нельзя послать сигнал SIGKILL . Более того, завершать такой процесс было бы неразумно, так как этот процесс, скорее всего, выполняет какую-либо важную операцию и может удерживать семафор.
.
• TASK_ZOMBIE
— процесс завершен, однако порождающий его процесс еще не вызвал системный вызов wait4()
. Дескриптор такого процесса должен оставаться доступным на случай, если родительскому процессу потребуется доступ к этому дескриптору. Когда родительский процесс вызывает функцию wait4()
, то такой дескриптор освобождается.
• TASK_STOPPED
— выполнение процесса остановлено. Задача не выполняется и не имеет право выполняться. Такое может случиться, если задача получает какой-либо из сигналов SIGSTOP
, SIGTSTP
, SIGTTIN
или SIGTTOU
, а также если сигнал приходит в тот момент, когда процесс находится в состоянии отладки.
Манипулирование текущим состоянием процесса
Исполняемому коду ядра часто необходимо изменять состояние процесса. Наиболее предпочтительно для этого использовать функцию
set_task state(task, state);
/* установить задание 'task' в состояние 'state' */
которая устанавливает указанное состояние для указанной задачи. Если применимо, то эта функция также пытается применить барьер памяти ( memory barrier ), чтобы гарантировать доступность установленного состояния для всех процессоров (необходимо только для SMP-систем). В других случаях это эквивалентно выражению:
task->state = state;
Вызов set_current_state(state)
является синонимом к вызову set_task_state(current, state)
.
Контекст процесса
Одна из наиболее важных частей процесса— это исполняемый программный код. Этот код считывается из выполняемого файла ( executable ) и выполняется в адресном пространстве процесса. Обычно выполнение программы осуществляется в пространстве пользователя . Когда программа выполняет системный вызов (см. главу 5, "Системные вызовы") или возникает исключительная ситуация, то программа входит в пространство ядра .
С этого момента говорят, что ядро "выполняется от имени процесса" и делает это в контексте процесса . В контексте процесса макрос current
является действительным [14] Отличным от контекста процесса является контекст прерывания, описанный в главе 6, "Прерывания и обработка прерываний". В контексте прерывания система работает не от имени процесса, а выполняет обработчик прерывания. С обработчиком прерывании не связан ни один процесс, поэтому и контекст процесса отсутствует.
. При выходе из режима ядра процесс продолжает выполнение в пространстве пользователя, если в это время не появляется готовый к выполнению более приоритетный процесс. В таком случае активизируется планировщик, который выбирает для выполнения более приоритетный процесс.
Системные вызовы и обработчики исключительных ситуаций являются строго определенными интерфейсами ядра. Процесс может начать выполнение в пространстве ядра только посредством одного из этих интерфейсов — любые обращения к ядру возможны только через эти интерфейсы.
Дерево семейства процессов
В операционной системе Linux существует четкая иерархия процессов. Все процессы являются потомками процесса init
, значение идентификатора PID
для которого равно 1. Ядро запускает процесс init
на последнем шаге процедуры загрузки системы. Процесс init
, в свою очередь, читает системные файлы сценариев начальной загрузки ( initscripts ) и выполняет другие программы, что в конце концов завершает процедуру загрузки системы.
Каждый процесс в системе имеет всего один порождающий процесс. Кроме того, каждый процесс может иметь один или более порожденных процессов. Процессы, которые порождены одним и тем же родительским процессом, называются родственными ( siblings ). Информация о взаимосвязи между процессами хранится в дескрипторе процесса. Каждая структура task_struct
содержит указатель на структуру task_struct
родительского процесса, который называется parent, эта структура также имеет список порожденных процессов, который называется children
. Следовательно, если известен текущий процесс ( current
), то для него можно определить дескриптор родительского процесса с помощью выражения:
struct task_struct *task = current->parent;
Аналогично можно выполнить цикл по процессам, порожденным от текущего процесса, с помощью кода:
struct task_struct *task;
struct list_head *list;
list_for_each(list, ¤t->children) {
task = list_entry(list, struct task_struct, sibling);
/* переменная task теперь указывает на один из процессов,
порожденных текущим процессом */
}
Дескриптор процесса init
— это статически выделенная структура данных с именем init_task
. Хороший пример использования связей между всеми процессами — это приведенный ниже код, который всегда выполняется успешно.
struct task_struct *task;
for (task = current; task != $init_task; task = task->parent)
;
/* переменная task теперь указывает на процесс init */
Конечно, проходя по иерархии процессов, можно перейти от одного процесса системы к другому. Иногда, однако, желательно выполнить цикл по всем процессам системы. Такая задача решается очень просто, так как список задач — это двухсвязный список. Для того чтобы получить указатель на следующее задание из этого списка, имея действительный указатель на дескриптор какого-либо процесса, можно использовать показанный ниже код:
Читать дальшеИнтервал:
Закладка: