Linux Time Subsystem VII: Application of Timers--msleep (), Hrtimer_nanosleep ()

Source: Internet
Author: User
Tags goto

We have introduced the implementation principle of low-resolution timers and high-precision timers in the previous chapters, and the kernel has provided some APIs for delay or scheduling in the time subsystem to facilitate other subsystems, such as msleep,hrtimer_nanosleep, etc. These APIs are based on low-resolution timers or high-precision timers, and this chapter discusses how these handy, easy-to-use APIs can be used to perform the required functions using a timer system.

/*****************************************************************************************************/
Statement: The content of this blog by Http://blog.csdn.NET/droidphone Original, reproduced please indicate the source, thank you!
/*****************************************************************************************************/

1. Msleep

Msleep believe everyone has used it, it could be one of the most widely used delay functions in the kernel, which will cause the current process to be dispatched and give up the CPU for a period of time, because it cannot be used for interrupt contexts, only in the context of a process. To use the delay function in an interrupt context, use a mdelay version that blocks the CPU. The function prototypes for Msleep are as follows:

[CPP] view plain copy

    1. void msleep (unsigned int msecs)

The time delay is specified by the parameter msecs, in milliseconds, in fact, the Msleep implementation is based on a low-resolution timer, so the actual accuracy of the msleep can only be 1/hz level. The kernel also provides another more similar delay function msleep_interruptible:

[CPP] view plain copy

    1. unsigned long msleep_interruptible (unsigned int msecs)

The delay units are the same number of milliseconds, and their differences are as follows:

Function
Delay Unit
return value
Whether it can be interrupted by a signal

Msleep
Milliseconds
No
Whether

Msleep_interruptible
Milliseconds
Number of milliseconds not completed
Is

The main difference is that Msleep will ensure that the required delay must be executed, while the msleep_interruptible can be interrupted by a signal interrupt when the delay is reached, and the remaining delay number is returned by the return value. Two functions The final code will reach the Schedule_timeout function, and their invocation sequence is as follows:

Figure 1.1 Call sequences for two delay functions

Let's take a look at the implementation of the Schedule_timeout function, the function first deal with two special cases, one is the incoming delay jiffies number is a negative, then print a warning message, and then immediately return, the other is the delay jiffies number is Max_schedule_ TIMEOUT, which indicates that the schedule needs to be delayed and executed directly:

[CPP] view plain copy

  1. Signed Long __sched Schedule_timeout (signed long timeout)
  2. {
  3. struct Timer_list timer;
  4. unsigned long expire;
  5. Switch (timeout)
  6. {
  7. Case Max_schedule_timeout:
  8. Schedule ();
  9. Goto out;
  10. Default
  11. if (Timeout < 0) {
  12. PRINTK (kern_err "Schedule_timeout:wrong timeout"
  13. "Value%lx\n", timeout);
  14. Dump_stack ();
  15. Current->state = task_running;
  16. Goto out;
  17. }
  18. }

Then calculate the expiration of the number of jiffies, and build a low-resolution timer on the stack, set the expiry time to the timer, after starting the timer, through the schedule to the current process to dispatch the CPU's running queue:

[CPP] view plain copy

    1. expire = timeout + jiffies;
    2. Setup_timer_on_stack (&timer, Process_timeout, (unsigned long) current);
    3. __mod_timer (&timer, expire, false, timer_not_pinned);
    4. Schedule ();

By this time, the process has been dispatched and how does it go back to execution? We see that the expiration callback function of the timer is process_timeout, and the parameter is the task_struct pointer of the current process to see its implementation:

[CPP] view plain copy

    1. static void Process_timeout (unsigned long __data)
    2. {
    3. Wake_up_process (struct task_struct *) __data);
    4. }

Oh, yes. Once the timer expires, the process is woken up and continues to execute:

[CPP] view plain copy

    1. Del_singleshot_timer_sync (&timer);
    2. /* Remove the timer from the object Tracker */
    3. Destroy_timer_on_stack (&timer);
    4. Timeout = expire-jiffies;
    5. Out
    6. Return Timeout < 0? 0:timeout;
    7. }

Schedule returns, indicating either that the timer expires, or because other times cause the process to wake up, the function is to delete the timer established on the stack, return the remaining outstanding jiffies number.

Having said the key schedule_timeout function, let's see how Msleep is implemented:

[CPP] view plain copy

    1. Signed Long __sched schedule_timeout_uninterruptible (signed long timeout)
    2. {
    3. __set_current_state (task_uninterruptible);
    4. return Schedule_timeout (timeout);
    5. }
    6. void msleep (unsigned int msecs)
    7. {
    8. unsigned long timeout = msecs_to_jiffies (msecs) + 1;
    9. while (timeout)
    10. Timeout = schedule_timeout_uninterruptible (timeout);
    11. }

Msleep first converts milliseconds to jiffies number, through a while loop to ensure that all delay is executed, the delay operation through the schedule_timeout_uninterruptible function, it is simply to change the state of the process to Task_ After uninterruptible, call the above schedule_timeout to complete the specific delay operation, task_uninterruptible state to ensure that the msleep will not be awakened by the signal, it means that during the Msleep, The process cannot be killed.

Look at the implementation of Msleep_interruptible:

[CPP] view plain copy

    1. signed Long __sched schedule_timeout_interruptible (signed long timeout)  
    2. &nbs p;   __set_current_state (task_interruptible); 
    3. return schedule_timeout (timeout); 
    4. unsigned long msleep_interruptible (unsigned int msecs)  
    5. {  /li>
    6.     unsigned long timeout = msecs_to_jiffies (msecs) + 1; 
    7. while (time Out &&!signal_pending (current)  
    8.         timeout = Schedule_timeout_interruptible (timeout); 
    9. return jiffies_to_msecs (timeout); 
    10. }  /li>

Msleep_interruptible through schedule_timeout_interruptible relay, the only difference between schedule_timeout_interruptible is to set the status of the process to Task_ Interruptible, indicating that there is a signal notification during the delay period, the while loop terminates immediately, and the remaining jiffies number is converted to milliseconds to return. In fact, you can also use schedule_timeout_interruptible or schedule_timeout_uninterruptible to construct your own delay function, while the kernel also provides another similar function, without my explanation, Look at the code to know its purpose:

[CPP] view plain copy

    1. Signed Long __sched schedule_timeout_killable (signed long timeout)
    2. {
    3. __set_current_state (task_killable);
    4. return Schedule_timeout (timeout);
    5. }
2. Hrtimer_nanosleep

The Msleep function discussed in the first section is based on the time wheel timing system, only provides millisecond precision, in fact, its accuracy depends on the Hz configuration value, if Hz is less than 1000, it can not even achieve the millisecond accuracy, to get more accurate delay, we naturally think of the use of high-precision timer to achieve. Yes, Linux provides a api:nanosleep for user space, it can provide nanosecond delay accuracy, the user space function corresponding to the kernel implementation is sys_nanosleep, its work by the high-precision Timer system Hrtimer_nanosleep function implementation , the final majority of the work is done by Do_nanosleep. The calling procedure looks like this:

Figure 2.1 Call procedure for Nanosleep

Similar to the implementation of Msleep, the Hrtimer_nanosleep function first creates a high-precision timer in the stack, sets its expiry time, and then completes the final delay through Do_nanosleep, and the current process exits after suspending the corresponding delay time Do_ The Nanosleep function destroys the timer in the stack and returns a value of 0 to indicate the success of the execution. However, if this occurs, the last part of Hrtimer_nanosleep do_nanosleep the remaining delay time into the restart_block of the process, and returns the Erestart_ when the required delay quantity is not reached for other reasons. Restartblock the error code, the system or user space can decide whether to recall the nanosleep in order to continue the execution of the remaining delay, depending on the return value. Here is the code for Hrtimer_nanosleep:

[CPP] view plain copy

  1. Long Hrtimer_nanosleep (struct timespec *rqtp, struct timespec __user *rmtp,
  2. const enum Hrtimer_mode mode, const clockid_t clockid)
  3. {
  4. struct Restart_block *restart;
  5. struct Hrtimer_sleeper t;
  6. int ret = 0;
  7. unsigned long slack;
  8. Slack = current->timer_slack_ns;
  9. if (Rt_task (current))
  10. Slack = 0;
  11. Hrtimer_init_on_stack (&t.timer, clockid, mode);
  12. Hrtimer_set_expires_range_ns (&t.timer, Timespec_to_ktime (*RQTP), slack);
  13. if (Do_nanosleep (&t, mode))
  14. Goto out;
  15. /* Absolute timers do not update the RMTP value and restart: */
  16. if (mode = = Hrtimer_mode_abs) {
  17. ret =-erestartnohand;
  18. Goto out;
  19. }
  20. if (RMTP) {
  21. ret = UPDATE_RMTP (&t.timer, RMTP);
  22. if (ret <= 0)
  23. Goto out;
  24. }
  25. Restart =¤t_thread_info ()->restart_block;
  26. RESTART->FN = Hrtimer_nanosleep_restart;
  27. Restart->nanosleep.clockid = t.timer.base->clockid;
  28. RESTART->NANOSLEEP.RMTP = RMTP;
  29. Restart->nanosleep.expires = hrtimer_get_expires_tv64 (&t.timer);
  30. ret =-erestart_restartblock;
  31. Out
  32. Destroy_hrtimer_on_stack (&t.timer);
  33. return ret;
  34. }

Then we look at the implementation of Do_nanosleep code, it first through the Hrtimer_init_sleeper function, the timer callback function set to Hrtimer_wakeup, the current process task_struct structure pointer is saved in Hrtimer In the _sleeper structure's task field:

[CPP] view plain copy

    1. void Hrtimer_init_sleeper (struct hrtimer_sleeper *sl, struct task_struct *task)
    2. {
    3. Sl->timer.function = Hrtimer_wakeup;
    4. Sl->task = task;
    5. }
    6. EXPORT_SYMBOL_GPL (Hrtimer_init_sleeper);
    7. static int __sched do_nanosleep (struct hrtimer_sleeper *t, enum Hrtimer_mode mode)
    8. {
    9. Hrtimer_init_sleeper (T, current);

Then, within a do/while loop: Start the timer, suspend the current process, wait for the timer or other event to wake up the process. Here the loop body implementation is very strange, it uses the Hrtimer_active function indirectly to determine whether the timer expires, if Hrtimer_active returns False, indicating that the timer has expired, and then put Hrtimer_ The task field of the sleeper structure is set to NULL, which results in the end of the loop body, and the other end condition is that the current process has received a signal event, so Do_nanosleep returns True if the timer expires, otherwise returns false, The above-mentioned hrtimer_nanosleep is the use of this feature to determine its return value. The following is the code for the Do_nanosleep loop body:

[CPP] view plain copy

    1. do { 
    2.         set_current_state (task_interruptible);  
    3.         hrtimer_start_expires (&t->timer, mode); 
    4. if (!hrtimer_active (&t->timer))  
    5.         & nbsp;   t->task = null; 
    6. if (likely (T->task))  
    7. &NBSP;&NBSP;&NB sp;         schedule (); 
    8.          Hrtimer_cancel (&t->timer); 
    9.         mode = hrtimer_mode_abs; 
    10.    } while (T->task &&!signal_pending (cur Rent); 
    11.     __set_current_state (task_running); 
    12. ret Urn T->task = = null; 

In addition to the Hrtimer_nanosleep, the high-precision timer system also provides several APIs for the delay/suspend process:

    • Schedule_hrtimeout enables the current process to hibernate the specified time, using the clock_monotonic timing system;
    • Schedule_hrtimeout_range enables the current process to hibernate a specified time range, using the clock_monotonic timing system;
    • Schedule_hrtimeout_range_clock enables the current process to hibernate a specified time range, and can specify the timing system by itself;
    • Usleep_range enables the current process to hibernate a specified number of subtleties, using the clock_monotonic timing system;
The invocation relationships between them are as follows:

Figure 2.2 Schedule_hrtimeout_xxxx Series functions

Eventually, all implementations go into the Schedule_hrtimeout_range_clock function. It is important to note that before calling the Schedule_hrtimeout_xxxx series function, it is best to use the Set_current_state function to set the state of the process before the function returns and the state of the city is set to task_running again. If you set the status to task_uninterruptible beforehand, they will ensure that the required delay time has elapsed before the function returns, and if the state is set to task_interruptible in advance, It is possible to wake the process from another signal when it has not yet expired, causing the function to return. The function of the main realization of the function Schedule_hrtimeout_range_clock and the previous Do_nanosleep function implementation principle is basically consistent. You can refer to the kernel code yourself, which is located at: kernel/hrtimer.c.

Linux Time Subsystem VII: Application of Timers--msleep (), Hrtimer_nanosleep ()

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.