Роберт Лав - Разработка ядра Linux
- Название:Разработка ядра Linux
- Автор:
- Жанр:
- Издательство:Издательский дом Вильямс
- Год:2006
- Город:Москва
- ISBN:5-8459-1085-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Роберт Лав - Разработка ядра Linux краткое содержание
В книге детально рассмотрены основные подсистемы и функции ядер Linux серии 2.6, включая особенности построения, реализации и соответствующие программны интерфейсы. Рассмотренные вопросы включают: планирование выполнения процессов, управление временем и таймеры ядра, интерфейс системных вызовов, особенности адресации и управления памятью, страничный кэш, подсистему VFS, механизмы синхронизации, проблемы переносимости и особенности отладки. Автор книги является разработчиком основных подсистем ядра Linux. Ядро рассматривается как с теоретической, так и с прикладной точек зрения, что может привлечь читателей различными интересами и потребностями.
Книга может быть рекомендована как начинающим, так и опытным разработчикам программного обеспечения, а также в качестве дополнительных учебных материалов.
Разработка ядра Linux - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Рабочие потоки, которые выполняются по умолчанию, называются events/ n
, где n
— номер процессора. Для каждого процессора выполняется один такой поток. Например, в однопроцессорной системе выполняется один поток events/0
. Б двухпроцессорной системе добавляется еще один поток— events/1
. Рабочие потоки, которые выполняются по умолчанию, обрабатывают отложенные действия, которые приходят из разных мест. Многие драйверы, которые работают в режиме ядра, откладывают обработку своих нижних половин с помощью потоков, работающих по умолчанию. Если для драйвера или подсистемы нет строгой необходимости в создании своего собственного потока ядра, то использование потоков, работающих по умолчанию, более предпочтительно.
Тем не менее ничто не запрещает коду ядра создавать собственные потоки. Это может понадобиться, если в рабочем потоке выполняется большое количество вычислительных операций. Операции, критичные к процессорным ресурсам или к высокой производительности, могут получить преимущества от использования отдельного выделенного потока. Это также уменьшает нагрузку на потоки, работающие по умолчанию, и предотвращает нехватку ресурсов для остальных отложенных действий.
Рабочие потоки представлены с помощью следующей структуры workqueue_struct
.
/*
* Внешне видимая абстракция для представления очередей отложенных
действий представляет собой массив очередей для каждого процессора:
*/
struct workqueue_struct {
struct cpu_workqueue_struct cpu_wq[NR_CPUS];
const char* name;
struct list_head list;
};
Эта структура содержит массив структур struct cpu_workqueue_struct
, по одному экземпляру на каждый возможный процессор в системе. Так как рабочий поток существует для каждого процессора в системе, то для каждого рабочего потока, работающего на каждом процессоре машины, существует такая структура.
Структура cpu_workqueue_struct
определена в файле kernel/workqueue.c
и является основной. Эта структура показана ниже.
/*
* Очередь отложенных действий, связанная с процессором:
*/
struct cpu_workqueue_struct {
spinlock_t lock; /* Очередь для защиты данной структуры */
long remove_sequence; /* последний добавленный элемент
(следующий для запуска ) */
long insert_sequence; /* следующий элемент для добавления */
struct list_head worklist; /* список действий */
wait_queue_head_t more_work;
wait_queue_head_t work_done;
struct workqueue_struct *wq; /* соответствующая структура
workqueue_struct */
task_t *thread; /* соответствующий поток */
int run_depth; /* глубина рекурсии функции run_workqueue() */
};
Заметим, что каждый тип рабочих потоков имеет одну, связанную с этим типом структуру workqueue_struct
. Внутри этой структуры имеется по одному экземпляру структуры cpu_workqueue_struct
для каждого рабочего потока и, следовательно, для каждого процессора в системе, так как существует только один рабочий поток каждого типа на каждом процессоре.
Все рабочие потоки реализованы как обычные потоки пространства ядра, которые выполняют функцию worker_thread()
. После начальной инициализации эта функция входит в бесконечный цикл и переходит в состояние ожидания. Когда какие-либо действия ставятся в очередь, поток возвращается к выполнению и выполняет эти действия. Когда в очереди не остается работы, которую нужно выполнять, поток снова возвращается в состояние ожидания. Каждое действие представлено с помощью структуры work_struct
, определенной в файле . Эта структура показана ниже.
struct work_struct {
unsigned long pending; /* ожидает ли это действие на выполнение? */
struct list_head entry; /* связанный список всех действий */
void (*func)(void*) ; /* функция-обработчик */
void *data; /* аргумент функции-обработчика */
void *wq_data; /* для внутреннего использования */
struct timer_list timer; /* таймер, который используется для
очередей отложенных действий с задержками */
};
Эти структуры объединены в связанный список, по одному списку на каждый тип очереди для каждого процессора. Например, для каждого процессора существует список отложенных действий, которые выполняются потоками, работающими по умолчанию. Когда рабочий поток возвращается к выполнению, он начинает выполнять все действия, которые находятся в его списке. После завершения работы рабочий поток удаляет соответствующие структуры work_struct
из списка. Когда список становится пустым, поток переходит в состояние ожидания.
Давайте рассмотрим упрощенную основную часть функции worker_thread()
.
for (;;) {
set_task_state(current, TASK_INTERRUPTIBLE);
add_wait_queue(&cwq->more_work, &wait);
if (list_empty(&cwq->worklist))
schedule();
else
set_task_state(current, TASK_RUNNING);
remove_wait_queue(&cwq->more_work, &wait);
if (!list_empty(&cwq->worklist))
run_workqueue(cwq);
}
Эта функция выполняет следующие действия в бесконечном цикле.
• Поток переводит себя в состояние ожидания (флаг состояния устанавливается в значение TASK_INTERRUPTIBLE
), и текущий поток добавляется в очередь ожидания.
• Если связанный список действий пуст, то поток вызывает функцию schedule()
и переходит в состояние ожидания.
• Если список не пуст, то поток не переходит в состояние ожидания. Вместо этого он устанавливает свое состояние в значение TASK_RUNNING
и удаляет себя из очереди ожидания.
• Если список не пустой, то вызывается функция run_workqueue()
для выполнения отложенных действий.
run_workqueue()
Функция run_workqueue()
в свою очередь выполняет сами отложенные действия, как показано ниже.
while (!list_empty(&cwq->worklist)) {
struct work_struct *work;
void (*f)(void*);
void *data;
work = list_entry(cwq->worklist.next, struct work_struct, entry);
f = work->func;
data = work->data;
list_del_init(cwq->worklist.next);
clear_bit(0, &work->pending);
f(data);
}
Эта функция просматривает в цикле все элементы списка отложенных действий и выполняет для каждого элемента функцию, на которую указывает поле func
соответствующей структуры workqueue_struct
. Последовательность действий следующая.
• Если список не пустой, получить следующий элемент списка.
• Получить указатель на функцию (поле func
), которую необходимо вызвать, и аргумент этой функции (поле data
).
• Удалить полученный элемент из списка и обнулить бит ожидания в структуре элемента.
• Вызвать полученную функцию.
• Повторить указанные действия.
Взаимоотношения между различными, рассмотренными в этом разделе структурами достаточно запутанные. На рис. 7.1 показана диаграмма, которая эти взаимоотношения поясняет.
Читать дальшеИнтервал:
Закладка: