Detailed explanation of delay function in Linux kernel

Keywords: Linux

There are two main ways to implement the delay involved in the kernel: busy waiting or sleep waiting. The former blocks the program and occupies the CPU until the delay time arrives, while the latter suspends the process (sets the process to sleep and releases CPU resources). Therefore, the former is generally used for accurate delay with delay time less than milliseconds, and the latter is used for long delay with delay time more than milliseconds. In order to make full use of CPU resources and make the system have better throughput performance, sleep waiting is usually recommended when the requirements for delay time are not very precise.

1. Busy wait short delay

The kernel provides the following three functions for nanosecond, microsecond and millisecond delays:

void ndelay(unsigned long nsecs); 
void udelay(unsigned long usecs); 
void mdelay(unsigned long msecs); //It is generally not recommended to use the mdelay() function directly, which will unnecessarily consume CPU resources

The implementation principle of the above delay is essentially busy waiting, which performs a certain number of cycles according to the CPU frequency. Its essence is the same as the following code:

void delay(unsigned int time) 
 while (time--); 

2. Busy wait long delay function

An intuitive way to delay in the kernel is to compare the current jiffies with the target jiffies (set to the current jiffies plus the interval jiffies) until the future jiffies reach the target jiffies.

  • Leverage jiffies and time_befor implements a code with a delay of 100 jiffies and 2 seconds:
 /*Delay 100 jiffies*/ 
 unsigned long delay = jiffies + 100; 
 while (time_before(jiffies, delay)); 
 /*Further delay 2s*/ 
 unsigned long delay = jiffies + 2*HZ; 
 while (time_before(jiffies, delay)); 

Where, time_befor() is just a function macro, with a time corresponding to it_ after():

#define time_after(a,b) \ 
 (typecheck(unsigned long, a) && \ 
 typecheck(unsigned long, b) && \ 
 ((long)(b) - (long)(a) < 0)) 

#define time_before(a,b) time_after(b,a) 

3. Short sleep delay

3.1 sleep delay function

The following functions will make the time specified by the sleep parameters of the process calling it. Affected by the system HZ and process scheduling, the accuracy of msleep() similar functions is limited. Msleep(), ssleep() cannot be interrupted, and msleep()_ Interruptible() can be interrupted.

void msleep(unsigned int millisecs); 
unsigned long msleep_interruptible(unsigned int millisecs); 
void ssleep(unsigned int seconds); 

3.2 sleep delay function of schedule class

signed long  schedule_timeout_interruptible(signed long timeout);

signed long  schedule_timeout_uninterruptible(signed long timeout) 

schedule_timeout() enables the current task to sleep the specified jiffies and then be rescheduled for execution. Its implementation principle is to add a timer to the system and wake up the process corresponding to the parameters in the timer processing function. The underlying implementation of sleep class function in the previous section is also implemented by calling it:

void msleep(unsigned int msecs) 
	unsigned long timeout = msecs_to_jiffies(msecs) + 1; 
	while (timeout) 
		timeout = schedule_timeout_uninterruptible(timeout); 
unsigned long msleep_interruptible(unsigned int msecs) 
	unsigned long timeout = msecs_to_jiffies(msecs) + 1; 
	while (timeout && !signal_pending(current)) 
		timeout = schedule_timeout_interruptible(timeout); 
	return jiffies_to_msecs(timeout); //Returns the remaining delay time

signed long _ _sched schedule_timeout_interruptible(signed long timeout) 
	_ _set_current_state(TASK_INTERRUPTIBLE); //Set the process status to TASK_INTERRUPTIBLE
	return schedule_timeout(timeout); 
signed long _ _sched schedule_timeout_uninterruptible(signed long timeout) 
	_ _set_current_state(TASK_UNINTERRUPTIBLE); //Set the process status to TASK_UNINTERRUPTIBLE
	return schedule_timeout(timeout); 

3.3 sleep_on class, the delay function of sleep on the waiting queue

The function can add the current process to the waiting queue to sleep on the waiting queue. When the timeout occurs, the process will wake up (the latter can be interrupted before the timeout):

sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout); 

interruptible_sleep_on_timeout(wait_queue_head_t*q, unsigned long timeout); 

Posted by rednaxel on Mon, 06 Dec 2021 10:47:42 -0800