/
bvs22
/
draft
Обзор
Документация
Войти
/
bvs22
/
draft
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
kernel/wait_queue
314 строк
15 KB
Batomunkuev Vladimir
1
01 фев 2025, 12:14
01 фев 2025, 12:14
0c256c5
Код
Авторство
О чём код?
### Конспект по Очередям Ожидания (Wait Queues) в Ядре Linux #### Введение Очереди ожидания (wait queues) — это механизм ядра Linux, который позволяет процессам или потокам ждать определенных событий. Это особенно полезно для реализации синхронизации между задачами и управления ресурсами в ядре. В этом конспекте рассмотрим основы создания и использования очередей ожидания в ядре Linux на языке C, а также их API. ### Основные аспекты работы с очередями ожидания 1. **Создание очереди ожидания** - Используйте структуру `wait_queue_head_t` для представления очереди ожидания. - Инициализируйте очередь с помощью функции `init_waitqueue_head()`. 2. **Добавление процессов в очередь** - Используйте функцию `add_wait_queue()` для добавления текущего процесса в очередь. - Используйте функцию `remove_wait_queue()` для удаления процесса из очереди. 3. **Ожидание события** - Используйте функцию `wait_event()` или `wait_event_interruptible()` для блокировки текущего процесса до наступления события. 4. **Уведомление процессов** - Используйте функцию `wake_up()` или `wake_up_interruptible()` для уведомления всех процессов в очереди ожидания. ### Основные API для работы с очередями ожидания 1. **`init_waitqueue_head()`** - Инициализирует очередь ожидания. - Прототип: `void init_waitqueue_head(wait_queue_head_t *q)` - Аргумент: - `q`: Указатель на структуру `wait_queue_head_t`, представляющую очередь ожидания. 2. **`add_wait_queue()`** - Добавляет процесс в очередь ожидания. - Прототип: `void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)` - Аргументы: - `q`: Указатель на структуру `wait_queue_head_t`, представляющую очередь ожидания. - `wait`: Указатель на структуру `wait_queue_t`, представляющую процесс. 3. **`remove_wait_queue()`** - Удаляет процесс из очереди ожидания. - Прототип: `void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)` - Аргументы: - `q`: Указатель на структуру `wait_queue_head_t`, представляющую очередь ожидания. - `wait`: Указатель на структуру `wait_queue_t`, представляющую процесс. 4. **`wait_event()`** - Блокирует текущий процесс до наступления события. - Прототип: `void wait_event(wait_queue_head_t q, condition)` - Аргументы: - `q`: Указатель на структуру `wait_queue_head_t`, представляющую очередь ожидания. - `condition`: Условие, которое должно быть выполнено для продолжения выполнения процесса. 5. **`wait_event_interruptible()`** - Блокирует текущий процесс до наступления события, но позволяет прерываться сигналами. - Прототип: `long wait_event_interruptible(wait_queue_head_t q, condition)` - Возвращает 0, если условие выполнено, или `-ERESTARTSYS`, если процесс был прерван сигналом. 6. **`wake_up()`** - Уведомляет все процессы в очереди ожидания. - Прототип: `void wake_up(wait_queue_head_t *q)` - Аргумент: - `q`: Указатель на структуру `wait_queue_head_t`, представляющую очередь ожидания. 7. **`wake_up_interruptible()`** - Уведомляет все процессы в очереди ожидания, которые могут быть прерваны сигналами. - Прототип: `void wake_up_interruptible(wait_queue_head_t *q)` - Аргумент: - `q`: Указатель на структуру `wait_queue_head_t`, представляющую очередь ожидания. ### Пример: Создание простой очереди ожидания #### Шаг 1: Создание исходного файла Создайте файл `waitqueue_example.c` с содержимым: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/kthread.h> // Для работы с потоками ядра #include <linux/delay.h> // Для задержек #include <linux/wait.h> // Для работы с очередями ожидания MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple example of wait queues in Linux kernel"); MODULE_VERSION("0.1"); static struct task_struct *worker_thread; static wait_queue_head_t wq; static int flag = 0; // Функция, выполняемая в потоке ядра static int worker_thread_fn(void *data) { while (!kthread_should_stop()) { printk(KERN_INFO "Worker thread is waiting...\n"); wait_event(wq, flag != 0); if (flag == 1) { printk(KERN_INFO "Worker thread received event\n"); } else if (flag == 2) { printk(KERN_INFO "Worker thread is stopping\n"); break; } flag = 0; // Сброс флага после обработки } return 0; } static int __init kthread_init(void) { init_waitqueue_head(&wq); // Инициализация очереди ожидания printk(KERN_INFO "Starting worker thread\n"); worker_thread = kthread_run(worker_thread_fn, NULL, "worker_thread"); if (IS_ERR(worker_thread)) { printk(KERN_ALERT "Failed to create the worker thread\n"); return PTR_ERR(worker_thread); } return 0; } static void __exit kthread_exit(void) { printk(KERN_INFO "Stopping worker thread\n"); flag = 2; // Установка флага для завершения потока wake_up(&wq); // Уведомление потока if (worker_thread) kthread_stop(worker_thread); printk(KERN_INFO "Worker thread stopped\n"); } module_init(kthread_init); module_exit(kthread_exit); static int notify_flag(int new_flag) { flag = new_flag; wake_up(&wq); // Уведомление потока return 0; } static int __init notify_init(void) { printk(KERN_INFO "Notify module loaded\n"); notify_flag(1); // Уведомляем поток return 0; } static void __exit notify_exit(void) { printk(KERN_INFO "Notify module unloaded\n"); } module_init(notify_init); module_exit(notify_exit); ``` #### Шаг 2: Создание Makefile Создайте файл `Makefile` рядом с исходным файлом: ```makefile obj-m += waitqueue_example.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ``` #### Шаг 3: Компиляция модуля Выполните команду `make` для компиляции модуля. Будет создан файл `waitqueue_example.ko`. #### Шаг 4: Загрузка и выгрузка модуля - Загрузите модуль командой: `sudo insmod waitqueue_example.ko` - Проверьте логи ядра: `dmesg | tail` - Выгрузите модуль командой: `sudo rmmod waitqueue_example` ### Ключевые элементы работы с очередями ожидания 1. **Инициализация очереди ожидания** - Используйте функцию `init_waitqueue_head()` для инициализации очереди ожидания. 2. **Добавление процесса в очередь** - Используйте функцию `add_wait_queue()` для добавления текущего процесса в очередь. - После добавления процесса можно использовать `wait_event()` или `wait_event_interruptible()` для блокировки процесса до наступления события. 3. **Удаление процесса из очереди** - Используйте функцию `remove_wait_queue()` для удаления процесса из очереди перед завершением его работы. 4. **Ожидание события** - Используйте функцию `wait_event()` для блокировки процесса до наступления события. - Если необходимо разрешить прерывания сигналами, используйте `wait_event_interruptible()`. 5. **Уведомление процессов** - Используйте функцию `wake_up()` для уведомления всех процессов в очереди ожидания. - Если необходимо уведомить только процессы, которые могут быть прерваны сигналами, используйте `wake_up_interruptible()`. ### Пример более сложного использования очередей ожидания #### Шаг 1: Создание исходного файла Создайте файл `complex_waitqueue_example.c` с содержимым: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/kthread.h> #include <linux/delay.h> #include <linux/wait.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A more complex example of wait queues in Linux kernel"); MODULE_VERSION("0.1"); static struct task_struct *producer_thread; static struct task_struct *consumer_thread; static wait_queue_head_t queue; static int data_ready = 0; static int shared_data = 0; // Функция производителя static int producer_fn(void *data) { int i; for (i = 0; i < 5; i++) { msleep(1000); // Имитация длительной операции shared_data = i; printk(KERN_INFO "Producer produced %d\n", shared_data); data_ready = 1; wake_up(&queue); // Уведомление потребителя } return 0; } // Функция потребителя static int consumer_fn(void *data) { while (!kthread_should_stop()) { wait_event(queue, data_ready != 0); if (shared_data >= 0 && shared_data < 5) { printk(KERN_INFO "Consumer consumed %d\n", shared_data); } else { printk(KERN_INFO "Consumer stopping\n"); break; } data_ready = 0; // Сброс флага после обработки } return 0; } static int __init waitqueue_init(void) { init_waitqueue_head(&queue); // Инициализация очереди ожидания printk(KERN_INFO "Starting producer and consumer threads\n"); producer_thread = kthread_run(producer_fn, NULL, "producer_thread"); consumer_thread = kthread_run(consumer_fn, NULL, "consumer_thread"); if (IS_ERR(producer_thread) || IS_ERR(consumer_thread)) { printk(KERN_ALERT "Failed to create threads\n"); if (!IS_ERR(producer_thread)) kthread_stop(producer_thread); if (!IS_ERR(consumer_thread)) kthread_stop(consumer_thread); return -1; } return 0; } static void __exit waitqueue_exit(void) { printk(KERN_INFO "Stopping producer and consumer threads\n"); if (producer_thread) kthread_stop(producer_thread); if (consumer_thread) kthread_stop(consumer_thread); printk(KERN_INFO "Threads stopped\n"); } module_init(waitqueue_init); module_exit(waitqueue_exit); ``` #### Шаг 2: Создание Makefile Используйте тот же Makefile, что и в предыдущем примере. #### Шаг 3: Компиляция модуля Выполните команду `make` для компиляции модуля. Будет создан файл `complex_waitqueue_example.ko`. #### Шаг 4: Загрузка и выгрузка модуля - Загрузите модуль командой: `sudo insmod complex_waitqueue_example.ko` - Проверьте логи ядра: `dmesg | tail` - Выгрузите модуль командой: `sudo rmmod complex_waitqueue_example` ### Обработка ошибок и отладка 1. **Проверка ошибок** - Используйте `printk()` для вывода сообщений в журнал ядра. - Используйте макросы `pr_info()`, `pr_warn()`, `pr_err()` для более удобного вывода. 2. **Отладка** - Используйте `dmesg` для просмотра сообщений ядра. - Используйте `lsmod` для проверки загруженных модулей. - Используйте `modinfo` для получения информации о модуле. ### Дополнительные функции API 1. **`DEFINE_WAIT()` и `prepare_to_wait()`** - Используются для подготовки процесса к ожиданию. - Прототипы: - `DEFINE_WAIT(name)` - `void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)` 2. **`finish_wait()`** - Используется для завершения ожидания. - Прототип: `void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)` ### Заключение Очереди ожидания предоставляют мощный инструмент для синхронизации процессов и потоков в контексте ядра. Они позволяют эффективно управлять ресурсами и обеспечивать надежную работу драйверов и других компонентов ядра. Разработка и управление очередями ожидания требует понимания особенностей ядра и его взаимодействия с пользовательскими процессами. Правильное использование очередей ожидания позволяет избежать блокировок и обеспечить высокую производительность и стабильность системы.