Linux Process Control-waiting for the queue to be detailed __linux

Source: Internet
Author: User

One, what is sleep

What does "sleep" mean for a process? When a process is put into sleep, it is identified as being in a special state and removed from the scheduler's run queue. Until something happens that changes that state, the process will not be scheduled on any CPU and, therefore, will not run. A sleeping process has been shelved on one side of the system, waiting for events to occur later.

LDD3 said very iffy, sleep is "voluntary scheduling", in fact, the current process is set to the state of task_interruptible and so on, and then schedule () out of CPU1, let the scheduler select a process to perform.

For a Linux driver making a process sleep is an easy thing to do. However, there are several rules that must remember to encode sleep in a safe way.

The first of these rules is: You can't sleep when you're running in an atomic context .

An additional related point, of course, is that your process cannot sleep unless you are certain that others, somewhere, will awaken it. the code to do the wake-up work must also be able to find your process to do its job . Ensuring that a wake occurs is an in-depth consideration of your code and for each sleep, knowing exactly what series of events will end that sleep. so that your process can be found, really, through a data structure called wait Queues . A waiting queue is what it sounds like: a list of processes that are waiting for a specific event.


second, how to sleep

In Linux, a waiting queue is managed by a "Wait queue header", a wait_queue_head_t type structure defined in <linux/wait.h>. A waiting queue header can be defined and initialized using:

    Declare_wait_queue_head (name); 

Or dynamically, as follows:

    wait_queue_head_t My_queue;
    Init_waitqueue_head (&my_queue);

1. Simple Sleep

The simplest way to sleep in a Linux kernel is a macro definition called wait_event (with several variants); It combines the details of the process of dealing with sleep and the inspection of processes in the waiting condition. The form of wait_event is:

    Wait_event (queue, condition)
    wait_event_interruptible (queue, condition)
    wait_event_timeout (queue, condition, timeout)
    wait_event_interruptible_timeout (queue, condition, timeout)
    How these things are used. The queue is the queued header, condition is the condition, and if you call wait_event before condition = 0, the current process will hibernate after the wait_event is called.
    So what's the difference between four of them.
    Wait_event: Sets the status of the current process to task_uninterruptible and  then schedule ()
    Wait_event_interruptible:         task_interruptible    , then schedule ()
    Wait_event_timeout:               task_uninterruptible  , then Schedule_timeout ()
    Wait_event_interruptible_timeout:  task_interruptible    , then Schedule_timeout ()
    The difference between task_interruptible and task_uninterruptible is whether its dormancy is interrupted by signals, and other processes send a signal such as kill, and Task_interruptible wakes up to deal with it. But task_uninterruptible not. Schedule (), process scheduling, and schedule_timeout () after scheduling, automatically wake up after a certain time .
    Corresponds to a different process state, using a different wake-up function:
    void Wake_up (wait_queue_head_t *queue);
    void Wake_up_interruptible (wait_queue_head_t *queue);
    Wake up is very interesting, such as you call Wake_up to wake up a use of wait_event and so on, into the hibernation process, wake up, it will determine whether the condition is true, if it is false to continue sleep . As for why this looks like, the back analysis code will understand.
  2. Manual Sleep
    Declare_waitqueue (name, tsk)  creates a wait queue:
        tsk is generally current. This macro defines and initializes a wait queue named name.
Add wait queue headers to/Remove wait queues:
        void Add_wait_queue (wait_queue_head_t *q, wait_queue_t *wait);
        void Add_wait_queue_exclusive (wait_queue_head_t *q, wait_queue_t *wait);
        void Remove_wait_queue (wait_queue_head_t *q, wait_queue_t *wait);

Set process status: Set_current_state (task_interruptible), etc.

Process scheduling:
Schedule () or schedule_timeout ()


Iii. How the kernel realizes

Take wait_event For example, let's look at what the kernel has done.

#define Wait_event (Wq, condition) 					\ Do
{									\
	if (condition)	 						\ break
		;							\
	__wait_event (Wq, condition);					\
} while (0)
#define __wait_event (Wq, condition) 					\ Do
{									\
	define_wait (__wait);						\
									for
	(;;) {							\
		prepare_to_wait (&wq, &__wait, task_uninterruptible);	\
		if (condition)						\ break
			;						\
		Schedule ();						\
	}								\
	finish_wait (&wq, &__wait);					\
} while (0)
#define DEFINE_WAIT (name)						\
	wait_queue_t name = {						\
		. Private	= current,				\
		. Func		= Autoremove_wake_function,		\
		. task_list	= List_head_init ((name). task_list),	\
	}
typedef struct __WAIT_QUEUE wait_queue_t;	
struct __wait_queue {
	unsigned int flags;
#define Wq_flag_exclusive	0x01
	void *private;
	wait_queue_func_t func;
	struct List_head task_list;
For example: After a macro is expanded
__wait_event (Wq, condition);
wait_queue_t __wait = {						\
		. Private	= current,				\
		. Func		= autoremove_wake_function,		\
		. task_list	= List_head_init ((__wait). task_list),	\
	for
	(;;) {							\
		prepare_to_wait (&wq, &__wait, task_uninterruptible);	\
		if (condition)						\ break
			;						\
		Schedule ();						\
	}								\
	finish_wait (&wq, &__wait);
in fact, it defines a wait queue called __wait, which points to the task_struct structure of the current process (which is known as the process), and then calls Prepare_to_wait joins the waiting queue header to the waiting queue. and set the status of the current process to task_uninterruptible. Then, if condition is false, then schedule (), when the process is scheduled, the current process state is not task_running necessarily to be removed "run queue", and will never be scheduled unless it wakes up. If condition is true, then finish_wait will restore the previous work, you continue to execute it, you are satisfied with the conditions, you also sleep fart.

The functional code involved is posted

void Fastcall
prepare_to_wait (wait_queue_head_t *q, wait_queue_t *wait, int state)
{
	unsigned long flags;< C4/>wait->flags &= ~wq_flag_exclusive;
	Spin_lock_irqsave (&q->lock, flags);
	if (List_empty (&wait->task_list))
		__add_wait_queue (q, wait);
	 /* don ' t alter the task state if it's just going to
	 * \ async Wait Queue callback
	/if (IS _sync_wait (Wait))
		set_current_state (state);
	Spin_unlock_irqrestore (&q->lock, flags);
}
void Fastcall finish_wait (wait_queue_head_t *q, wait_queue_t *wait)
{
	unsigned long flags;

	__set_current_state (task_running);

	if (!list_empty_careful (&wait->task_list)) {
		spin_lock_irqsave (&q->lock, flags);
		List_del_init (&wait->task_list);
		Spin_unlock_irqrestore (&q->lock, flags);
	}
Now let's see how to wake up.

#define WAKE_UP (x) __wake_up (x, task_uninterruptible | Task_interruptible, 1, NULL)
void Fastcall __wake_up (wait_queue_head_t *q, unsigned int mode,
			int nr_exclusive, void *key)
{
	unsigned Long flags;

	Spin_lock_irqsave (&q->lock, flags);
	__wake_up_common (q, Mode, nr_exclusive, 0, key);
	Spin_unlock_irqrestore (&q->lock, flags);
}
static void __wake_up_common (wait_queue_head_t *q, unsigned int mode,
			     int nr_exclusive, int sync, void *key)
{
  struct List_head *tmp, *next;

	List_for_each_safe (TMP, Next, &q->task_list) {
		wait_queue_t *curr = list_entry (tmp, wait_queue_t, task_list );
		Unsigned flags = curr->flags;

		if (Curr->func (curr, mode, Sync, key) &&
				(Flags & wq_flag_exclusive) &&!--nr_exclusive) Break
			;
	}
}
The func function, which we specify in the wait queue, is called Autoremove_wake_function
int autoremove_wake_function (wait_queue_t *wait, unsigned mode, int sync, void *key)
{
	int ret = Default_wake_f Unction (wait, mode, sync, key);

	if (ret)
		list_del_init (&wait->task_list);
	return ret;
}
int default_wake_function (wait_queue_t *curr, unsigned mode, int sync,
			  void *key)
{return
	try_to_wake_up ( Curr->private, mode, sync);
The final call to Default_wake_function to wake up the process specified in private in the waiting queue. The wait queue header is then removed from the wait queue. TRY_TO_WAKE_UP, the process state that will wake the process is set to task_running and placed in the run queue.

The interesting thing is:

where schedule () when we hibernate. The TMD is in the For loop.

	for (;;) {							\
		prepare_to_wait (&wq, &__wait, task_uninterruptible);	\
		if (condition)						\ break
			;						\
		Schedule ();						\
	}
Wake up, then began to prepare_to_wait, Judge condition .... It is obvious that condition is true to awaken.
Understand them, and the manual hibernation is clear. Manual hibernation does not have to be judged by what condition.
Declare_waitqueue (name, tsk)
tsk is generally current. This macro defines and initializes a wait queue named name.

#define DECLARE_WAITQUEUE (name, tsk)                    \
    wait_queue_t name = __waitqueue_initializer (name, tsk)
#define __ Waitqueue_initializer (name, tsk) {                \
    . Private    = tsk,                        \
    . Func        = default_wake_function,            \
    . task_list    = {null, null}}
	
void Add_wait_queue (wait_queue_head_t *q, wait_queue_t *wait);
void Add_wait_queue_exclusive (wait_queue_head_t *q, wait_queue_t *wait);
void Remove_wait_queue (wait_queue_head_t *q, wait_queue_t *wait);

void Fastcall Add_wait_queue (wait_queue_head_t *q, wait_queue_t *wait)
{
	unsigned long flags;

	Wait->flags &= ~wq_flag_exclusive;
	Spin_lock_irqsave (&q->lock, flags);
	__add_wait_queue (q, wait);
	Spin_unlock_irqrestore (&q->lock, flags);
}

Set_current_state (task_interruptible);  
Schedule ();
simple and clear:

1. Create wait queue, wait queue header

2. Join the waiting queue header to the waiting queue

3, set the process state of the current process

4, Process scheduling ~









Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.