Home > Article > System Tutorial > Master teaches you how to set up the sleep and wake-up of Linux processes
Введение | В Linux процессы, которые ждут только процессорного времени, называются готовыми процессами. Они помещаются в очередь выполнения, а флаг состояния готового процесса — TASK_RUNNING. Как только временной интервал запущенного процесса будет исчерпан, планировщик ядра Linux лишит процесс контроля над ЦП и выберет для запуска подходящий процесс из очереди выполнения. |
Конечно, процесс также может активно освобождать контроль над процессором. Функция Schedule() — это функция планирования, которая может активно вызываться процессом для планирования использования ЦП другими процессами. Как только процесс, который добровольно отказывается от ЦП, перепланирован, чтобы занять ЦП, он начнет выполнение с того места, где он остановился в последний раз, то есть он начнет выполнение со следующей строки кода, которая вызывает Schedule().
Иногда процессу необходимо дождаться определенного события, например инициализации устройства, завершения операции ввода-вывода или истечения срока действия таймера. В этом случае процесс необходимо удалить из очереди выполнения и добавить в очередь ожидания.В это время процесс переходит в состояние сна.
Один из них — прерываемый режим сна, его флаг состояния — TASK_INTERRUPTIBLE;
Другой — это состояние непрерывного сна, и его флаг состояния — TASK_UNINTERRUPTIBLE. Процесс в состоянии прерываемого сна будет спать до тех пор, пока не станет истинным определенное условие. Например, условиями для пробуждения процесса могут быть генерация аппаратного прерывания, освобождение системных ресурсов, которых ожидает процесс, или доставка сигнала. Состояние непрерывного сна похоже на состояние прерываемого сна, но имеет одно исключение: процесс, доставляющий сигнал в это состояние сна, не может изменить свое состояние, то есть не реагирует на сигнал о пробуждении. Состояние непрерывного сна обычно используется реже, но оно все же очень полезно в некоторых конкретных ситуациях.Например, процесс должен ждать и не может быть прерван до тех пор, пока не произойдет определенное событие.
В современных операционных системах Linux процессы обычно переходят в состояние сна путем вызова метода Schedule(). Следующий код выполняет
показывает, как перевести запущенный процесс в спящий режим.
Sleep_task = текущий;
set_current_state(TASK_INTERRUPTIBLE);
расписание();
func1();
/* Остальная часть кода... */
В первом операторе программа сохраняет указатель структуры процесса Sleep_task, текущий — макрос, указывающий на исполняемый процесс
структура процесса. set_current_state() изменяет состояние процесса с состояния выполнения TASK_RUNNING на состояние сна
TASK_INTERRUPTIBLE. Если функция Schedule() запланирована процессом со статусом TASK_RUNNING, то функция Schedule() запланирует занятие ЦП другим процессом; если функция Schedule() запланирована процессом со статусом TASK_INTERRUPTIBLE или TASK_UNINTERRUPTIBLE, то необходимо выполнить дополнительный шаг Выполняется: выполняющийся в данный момент процесс будет удален из очереди выполнения до того, как будет запланирован другой процесс, что приведет к переходу работающего процесса в спящий режим, поскольку он больше не находится в очереди выполнения.
Мы можем использовать следующую функцию, чтобы разбудить процесс, который только что заснул.
пробуждение_up_process(спящая_задача);
После вызоваake_up_process() статус спящего процесса будет установлен на TASK_RUNNING, а планировщик
Он будет добавлен в очередь выполнения. Разумеется, этот процесс можно будет запустить только в следующий раз, когда он будет запланирован планировщиком.
Почти во всех случаях процесс переходит в спящий режим после проверки определенных условий и обнаружения, что они не выполняются. Но иногда
Однако процесс перейдет в спящий режим после того, как условие принятия решения станет истинным. В этом случае процесс будет спать бесконечно. Это так называемая проблема недействительного пробуждения. В операционной системе, когда несколько процессов пытаются выполнить некоторую обработку общих данных, а конечный результат зависит от порядка запуска процессов, возникает состояние гонки. Это типичная проблема в операционной системе. вверх Именно из-за конкурентных условий.
Предположим, есть два процесса A и B. Процесс A обрабатывает связанный список. Ему необходимо проверить, пуст ли связанный список. Если он не пуст, обработать ссылку
Данные в таблице подвергаются некоторым операциям, а процесс B также добавляет узлы в связанный список. Когда связанный список пуст из-за отсутствия данных для работы, процесс A переходит в спящий режим. Когда процесс B добавляет узел в связанный список, он пробуждает процесс A. Код следующий:
Процесс:
1 spin_lock(&list_lock);
2 if(list_empty(&list_head)) {
3 spin_unlock(&list_lock);
4 set_current_state(TASK_INTERRUPTIBLE);
5 расписание();
6 spin_lock(&list_lock);
7}
8
9 /* Остальная часть кода... */
10 spin_unlock(&list_lock);
Процесс Б:
100 spin_lock(&list_lock);
101 list_add_tail(&list_head, new_node);
102 spin_unlock(&list_lock);
103 Wake_up_process(process_task);
Здесь будет проблема: если процесс A выполняется после строки 3 и перед строкой 4, процесс B запланирован другим процессором
введен в эксплуатацию. В течение этого интервала времени процесс B выполнил все свои инструкции, поэтому он пытается разбудить процесс A. В это время процесс A еще не перешел в спящий режим, поэтому операция пробуждения недействительна. После этого процесс A продолжает выполняться. Он ошибочно подумает, что связанный список в это время еще пуст, поэтому устанавливает его статус в TASK_INTERRUPTIBLE, а затем вызывает Schedule(), чтобы перейти в спящий режим. Поскольку процесс B пропустил вызов пробуждения, он будет спать неопределенное время. Это проблема недопустимого пробуждения, поскольку процесс A все еще спит, даже если в связанном списке есть данные, которые необходимо обработать.
Как избежать проблем с неверным пробуждением? Мы обнаружили, что недействительные пробуждения в основном происходят после проверки условий и перехода процесса в состояние сна
Перед состоянием функция Wake_up_process() процесса B изначально предоставляла возможность установить состояние процесса A на TASK_RUNNING. К сожалению, состояние процесса A в это время все еще было TASK_RUNNING, поэтому Wake_up_process() изменил состояние процесса A из спящего режима. состояние в рабочее состояние.Попытки не дали желаемого эффекта. Чтобы решить эту проблему, необходимо использовать механизм гарантии, чтобы определение того, что связанный список пуст, и перевод состояния процесса в спящее состояние стало неотъемлемым шагом. ( ) может играть роль пробуждения процесса, находящегося в спящем состоянии.
Найдя причину, измените структуру кода процесса A, чтобы избежать проблемы недопустимого пробуждения в приведенном выше примере.
Процесс:
1 set_current_state(TASK_INTERRUPTIBLE);
2 spin_lock(&list_lock);
3 if(list_empty(&list_head)) {
4 spin_unlock(&list_lock);
5 расписание();
6 spin_lock(&list_lock);
7}
8 set_current_state(TASK_RUNNING);
9
10 /* Остальная часть кода... */
11 spin_unlock(&list_lock);
Как видите, этот код устанавливает текущее состояние процесса выполнения на TASK_INTERRUPTIBLE перед проверкой условий и устанавливает себя в состояние TASK_RUNNING, когда связанный список не пуст. Таким образом, если процесс B находится в процессе A, проверка процесса
После вызова Wake_up_process() после того, как связанный список пуст, статус процесса A автоматически изменится с исходного TASK_INTERRUPTIBLE
Он становится TASK_RUNNING.После этого, даже если процесс снова вызовет Schedule(), так как его текущий статус TASK_RUNNING, он все равно не будет удален из очереди выполнения, поэтому он не перейдет в сон по ошибке, и конечно, проблема недействительного пробуждения можно избежать.
In the Linux operating system, the stability of the kernel is crucial. In order to avoid invalid wake-up problems in the Linux operating system kernel,
The Linux kernel should use operations similar to the following when it needs to sleep a process:
/* ‘q’ is the waiting queue we want to sleep in */
DECLARE_WAITQUEUE(wait,current);
add_wait_queue(q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
/* or TASK_INTERRUPTIBLE */
while(!condition) /* ‘condition’ is the waiting condition*/
schedule();
set_current_state(TASK_RUNNING);
remove_wait_queue(q, &wait);
The above operation allows the process to safely add itself to a waiting queue for sleep through the following series of steps: first adjust
Use DECLARE_WAITQUEUE () to create a waiting queue item, then call add_wait_queue() to add yourself to the waiting queue, and set the status of the process to TASK_INTERRUPTIBLE or TASK_INTERRUPTIBLE. Then the loop checks whether the condition is true: if it is, there is no need to sleep, if the condition is not true, schedule() is called. When the conditions checked by the process are met, the process sets itself to TASK_RUNNING and calls remove_wait_queue() to remove itself from the waiting queue.
As you can see from the above, the Linux kernel code maintainer also sets the state of the process to sleep state before the process checks the conditions,
Then the loop checks the condition. If the condition has been met before the process starts sleeping, then the loop will exit and use set_current_state() to set its state to ready. This also ensures that the process will not have the tendency to enter sleep by mistake, and of course it will not cause an error. Invalid wakeup problem.
Let us use an example in the Linux kernel to see how the Linux kernel avoids invalid sleep. This code comes from the Linux 2.6 kernel (linux-2.6.11/kernel/sched.c: 4254):
4253 /* Wait for kthread_stop */
4254 set_current_state(TASK_INTERRUPTIBLE);
4255 while (!kthread_should_stop()) {
4256 schedule();
4257 set_current_state(TASK_INTERRUPTIBLE);
4258 }
4259 __set_current_state(TASK_RUNNING);
4260 return 0;
The above codes belong to the migration service thread migration_thread. This thread continuously checks kthread_should_stop(),
It cannot exit the loop until kthread_should_stop() returns 1, which means that the process will sleep as long as kthread_should_stop() returns 0. We can see from the code that the check kthread_should_stop() is indeed executed after the process status is set to TASK_INTERRUPTIBLE. Therefore, if another process attempts to wake it up after the condition check but before schedule(), the wake-up operation of the process will not be invalidated.
Through the above discussion, we can find that the key to avoid invalid wake-up of the process in Linux is to
The status is set to TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE, and if the checked conditions are met, it should
Reset its status to TASK_RUNNING. In this way, no matter whether the waiting conditions of the process are met or not, the process will not enter the sleep state by mistake because it is removed from the ready queue, thereby avoiding the problem of invalid wake-up.
The above is the detailed content of Master teaches you how to set up the sleep and wake-up of Linux processes. For more information, please follow other related articles on the PHP Chinese website!