Several important terms
- Hz: The system timer frequency Hz is used to define the number of clock interruptions generated by the system timer every one second.
- TICK: the reciprocal of Hz. The interval between two clock interruptions by the system timer.
- Xtime: record the wall time value, that is, the UTC time. It is a struct timeval structure and is read in the user space through gettimeofday.
- Jiffies: record the number of tick requests since the system was started. It is defined as unsigned long volatile _ jiffy_data jiffies;
- RTC: Real-time clock, which is a hardware clock used to persistently store system time.
Hz
Hz is statically compiled into the kernel. Its definition is as follows:
/// Usr/include/ASM-generic/Param. h file
# Ifdef _ KERNEL __
# Define Hz config_hz/* internal kernel timer frequency */
# Define user_hz 100/* Some user interfaces are */
# Define clocks_per_sec (user_hz)/* in "ticks" like times ()*/
# Endif
# Ifndef Hz
# Define hz100
# Endif
In kernels earlier than 2.6, if the Hz value in the kernel is changed, someProgramResult. Because the kernel exports this value to the user space in the form of the number of beats/Second, the application depends on this specific Hz value. If the defined Hz value is changed in the kernel, the constant relationship of the user space is broken-the user space does not know the new Hz value.
The kernel changes all exported jiffies values. The kernel defines user_hz to represent the Hz value seen by the user space. In the X86 architecture, because the Hz value has always been 100, the user_hz value is defined as 100. The kernel can use the macro jiffies_to_clock_t () to convert a cycle count represented by Hz to a one represented by user_hz:
Start = jiffies;
// Doing some jobs
Total_time = jiffies-start;
Ticks = jiffies_to_clock_t (total_time );
The jiffies_to_clock_t () function is defined as follows:
/*
* Convert jiffies/jiffies_64 to clock_t and back.
*/
Clock_t jiffies_to_clock_t (unsigned long X)
{
# If (tick_nsec % (nsec_per_sec/user_hz) = 0
# If Hz <user_hz
Return x * (user_hz/Hz );
# Else
Return x/(Hz/user_hz );
# Endif
# Else
Return div_u64 (u64) x * tick_nsec, nsec_per_sec/user_hz );
# Endif
}
Jiffies
Jiffies records the number of ticks that have elapsed since the system was started. The value is 0 when the system is started, and 1 is added each time the clock is interrupted.
The system boot time can be calculated based on jiffies: jiffies/Hz seconds
Jiffies is defined as unsigned long volatile _ jiffy_data jiffies;
Due to the possibility of jiffies overflow, the kernel defines a set of auxiliary functions to process the comparison operations of jiffies. It is best to use these auxiliary functions when operating jiffies:
/*
* These inlines deal with timer wrapping correctly. You are
* Strongly encouraged to use them
* 1. Because people otherwise forget
* 2. Because if the timer wrap changes in future you won't have
* Alter your driver code.
*
* Time_after (a, B) returns true if the time a is after time B.
*
* Do this with "<0" and "> = 0" to only test the sign of the result.
* Good compiler wowould generate better code (and a really good Compiler
* Wouldn't care). GCC is currently neither.
*/
# Define time_after (a, B )\
(Typecheck (unsigned long, )&&\
Typecheck (unsigned long, B )&&\
(Long) (B)-(long) (a) <0 ))
# Define time_before (a, B) time_after (B,)
# Define time_after_eq (a, B )\
(Typecheck (unsigned long, )&&\
Typecheck (unsigned long, B )&&\
(Long) (A)-(long) (B)> = 0 ))
# Define time_before_eq (a, B) time_after_eq (B,)
Real-time clock rtc
A Real-time clock is a hardware clock used to store the system time for a long time. After the system is shut down, it depends on the micro-battery on the motherboard to maintain the timing;
When the system starts, the kernel initializes the wall time by reading RTC and stores it in the xtime variable. This is the main function of RTC;
After you modify the time, you can use hwclock-W to save it to RTC.
System Timer
Each pc has a pit, which generates a periodic clock interruption signal through irq0 and serves as the system timer. When a clock interruption occurs, the clock interruption handler is automatically called.
The clock interrupt processing program is divided into two parts: the architecture and the architecture. The related part is registered to the kernel as the interrupt handler of the system timer, so that it can run properly when a clock interruption occurs. The task is as follows:
1. Obtain the xtime_lock to protect access to jiffies_64 and wall time xtime.
2. respond or reset the system clock as needed.
3. Use the wall time periodically to update the real-time clock.
4. Call the time routines unrelated to the architecture: do_timer ().
The interrupted service program calls the architecture-independent routine do_timer () to execute the following tasks:
1. Add 1 to the jiffies_64 variable.
2. Update the statistical value of resource consumption, such as the system time and user time consumed by the current process.
3. Execute the expired dynamic timer.
4. Execute the scheduler_tick () function.
5. Update the wall time, which is stored in the xtime variable.
6. Calculate the average load value.
Xtime
Record the wall time value, that is, the UTC time, which is a struct timeval structure.
The xtime_lock lock is required to read and write the xtime variable in the kernel space. This lock is a sequential lock (seqlock ).
In the user space, the system calls sys_gettimeofday () to read the xtime through gettimeofday ().
Dynamic Timer
The dynamic timer is not periodically executed. It is destroyed after timeout. The definer is represented by time_list defined in Linux/Timer. H, as follows:
Struct timer_list {
/*
* All fields that change during normal runtime grouped to
* Same cacheline
*/
Struct list_head entry;
Unsigned long expires;
Struct tvec_base * base;
Void (* function) (unsigned long );
Unsigned long data;
Int slack;
}
The kernel provides a set of functions to simplify timer management. All these interfaces are declared in the file Linux/Timer. H. Most interfaces are implemented in the file kernel/Timer. C. With these interfaces, what we need to do is simple:
1. Create a Timer: struct timer_list my_timer;
2. initialize the Timer: init_timer (& my_timer );
3. Set the timer as needed:
My_timer.expires = jiffies + delay;
My_timer.data = 0;
My_timer.function = my_function;
4. Activate the Timer: add_timer (& my_timer );
After the preceding steps, the timer can start to work. However, in general, the timer will be executed immediately after the time-out, but it may also be postponed until the next time to run, so it cannot be used for hard real-time.
If you modify the timer, use mod_timer (& my_timer, jiffies + new_delay) to modify the activated timer time. It can also operate on the timer that has been initialized but has not been activated. If the timer is not activated, mod_timer will activate it. If the timer is not activated during the call, the function returns 0; otherwise, 1 is returned. However, in either case, the timer is activated and a new scheduled value is set once it is returned from the mod_timer function.
Of course, you can also delete the timer before the timeout: del_timer (& my_timer); also note that the timer interruption on the multi-processor may already be running on other machines, this requires you to wait until all timer handlers that may run on Other Processors exit and then delete the timer. This requires using the del_timer_sync () function to perform the deletion. This function parameter is the same as the preceding one, but cannot be used in the interrupt context. The timer is independent from the currentCodeThis means that there may be competition conditions, so we must be very careful. In this sense, deleting the latter is safer than deleting the former.
Delayed execution
In addition to the timer or lower half mechanism, kernel code (especially drivers) also provides many latency methods to handle various latency requests.
- Busy waiting (also called Busy cycle): usually the most unsatisfactory method, because the processor is occupied in vain and cannot do other things. This method can be used only when the latency is an integer multiple of the cycle or the accuracy requirement is not high. It is quite easy to implement, that is, rotating continuously in the loop until the expected number of clock beats is exhausted. For example:
Unsigned long delay = jiffies + 10; // 10 beats
While (time_before (jiffies, delay ));
The disadvantage is obvious. A better way is to allow the kernel to re-schedule other tasks while the code is waiting, as shown below:
Unsigned long delay = jiffies + 10; // 10 beats
While (time_before (jiffies, delay ))
Cond_resched ();
The cond_resched () function schedules a new program to run, but it takes effect only after the need_resched flag is set. In other words, there are more important tasks in the system that need to be run. Since this method needs to call the scheduler, it cannot be used in the interrupt context-it can only be used in the process context. In fact, all the latency methods are used in the context of the process, because the interrupt handler should be executed as quickly as possible. In addition, delayed execution should not occur when a lock is held or interrupted.
- Udelay mlelay
As for those that require short latency (shorter than the clock cycle) and require precise latency, this often happens when it is synchronized with hardware, that is to say, it takes a short wait for the completion of an action-the wait time is usually less than 1 ms, so it is impossible to use the latency Method Based on jiffies as in the previous example. In this case, you can use the two functions defined in Linux/delay. H. They are not used. These two functions can process latency in microseconds and milliseconds, as shown below:
Void udelay (unsigned long usecs );
Void mdelay (unsigned long msecs );
The former relies on execution cycles to achieve latency, while the mdelay () function is implemented through the udelay () function. Because the kernel knows how many cycles a processor can execute in one second, the udelay () function only needs to be based on the proportion of the specified delay time in one second, you can determine the number of cycles that need to be performed to achieve the required delay. The udelay () function can only be executed when the required latency is short, while a long delay in High-Speed machines may cause overflow, do not try to use this function when the latency exceeds 1 ms. These two functions are actually the same as waiting for a while. If not necessary, do not use them.
- Schedule_timeout
The more ideal method for delayed execution is to use the schedule_timeout () function, which will make the task to be delayed to sleep to the specified delay time and then run again. However, this method does not guarantee that the sleep time is exactly the same as the specified delay time-it is only possible that the sleep time is close to the specified delay time. After the specified time expires, the kernel wakes up the delayed task and puts it back into the running queue, as shown below:
Set_current_state (task_interruptible );
Schedule_timeout (S * Hz );
The only parameter is the relative delay time, in the unit of jiffies. In the previous example, the corresponding task is pushed into the interrupted sleep queue for S-seconds. Before calling the schedule_timeout function, you must set the task to be interrupted or not interrupted. Otherwise, the task will not sleep. This function needs to call the scheduler, so the code that calls it must be able to sleep. In short, the calling code must be in the context of the process and cannot hold a lock.
Finally, waiting for a task on the queue may be waiting for a specific event and waiting for a specific time to expire-it depends on who is coming faster. In this case, the code can simply use the scedule_timeout () function to replace the schedule () function. In this way, the task will be awakened when the specified time expires. Of course, the code needs to check the cause of wakeup-either the event or the delay expires or the signal is received-and then perform the corresponding operation.
References:
Http://blog.csdn.net/zhandoushi1982/article/details/5536210
Http://blog.csdn.net/qinzhonghello/article/details/3588224
Linux Kernel Development