From: http://hi.baidu.com/wzbob/blog/item/dec68f8255391690f703a66f.html
System clock hardware and Linux time representation process interval timer itimer
7. Process interval timer itimer
Interval
Timer (itimer) refers to the timer using the "interval" value (interval) as the timing method. When the timer starts, the interval value will decrease. When
When the interval value is reduced to 0, we will say that the interval timer expires. Compared with the kernel dynamic timer mentioned in the previous section, the biggest difference between the two is that the timer timing method is different. The kernel timer is
Its expiration time expires value is used for timing. When the global variable jiffies value is greater than or equal to the expires value of the kernel dynamic timer, we say that the kernel timer expires. The interval is fixed.
The timer is actually timing through a decreasing counter. Although the two timers are different, they are also related to each other. Assume that the interval timer counter is reduced for every clock cycle.
1. In this case, the interval timer is actually the kernel dynamic timer (we will see that the real interval timer of the process is implemented through the kernel timer ).
The interval timer is mainly applied to user processes. Each Linux Process has three interrelated intervals timers. Their respective interval counters are defined in the task_struct structure of the process, as shown below (include/Linux/sched. h ):
Struct task_struct {
......
Unsigned long it_real_value, it_prof_value, it_assist_value;
Unsigned long it_real_incr, it_prof_incr, it_assist_incr;
Struct timer_list real_timer;
......
}
(1) Real interval timer (itimer_real): After the interval timer is started, every tick of the timer reduces the interval counter by 1 regardless of whether the process is running or not. When the value is reduced to 0
The process sends a sigalrm signal. It_real_incr, a member of the structure task_struct, indicates the initial value of the interval counter of the real interval timer, and the member
It_real_value indicates the current value of the interval counter of the real interval timer. Because the interval timer is essentially the same as the kernel timer in the previous section, Linux is actually
The real_timer kernel dynamic timer embedded in the task_struct structure is used to implement the real interval timer itimer_real.
(2) virtual interval timer itimer_virtual: Also known as the user State interval timer of the process. It_virt_incr and
It_interval _value indicates the initial value and current value of the interval counter of the virtual interval timer, both of which are measured in the number of tick times. When the virtual interval timer starts, only when the process
When running in user mode, the current it_virt_value of the interval counter can be reduced by 1 by a single tick of the clock. When the value is reduced to 0, the kernel sends the sigvtalrm signal (Virtual alarm clock) to the process.
Signal), and reset it_1__value to the initial value it_1__incr. For details, see the implementation of the do_it_virt () function in section 4.3.
(3) Prof interval timer itimer_prof: it_prof_value and it_prof_incr members in the task_struct STRUCTURE OF THE PROCESS
The current value and initial value of the interval counter of the prof interval timer (in the unit of clock tick ). When a process's prof interval timer starts, as long as the process is running, no matter
Run the command in the user or core State. Every tick of the clock reduces the it_prof_value of the interval counter by 1. When the value is reduced to 0, the kernel sends the sigprof signal to the process and
It_prof_value is reset to the initial value it_prof_incr. For details, see the do_it_prof () function in section 4.3.
Linux defines the index identifier for the preceding three process interval timers in the include/Linux/time. h header file, as shown below:
# Define itimer_real 0
# Define itimer_virtual 1
# Define itimer_prof 2
7.1 Data Structure itimerval
Although the interval counter of the interval timer in the kernel is measured in the unit of the number of times the clock is ticking, it is obviously inconvenient for users to specify the initial values of the interval counter of the interval timer in the unit of clock ticking, because
The unit of time used by the user is second, millisecond, or microsecond. Therefore, Linux defines the data structure itimerval to allow users to specify the interval value of the timer in seconds or microseconds. The definition is as follows:
(Include/Linux/time. h ):
Struct itimerval {
Struct timeval it_interval;/* timer interval */
Struct timeval it_value;/* Current Value */
};
The it_interval Member indicates the initial value of the interval counter, while the it_value Member indicates the current value of the interval counter. The two members are all variables of the timeval structure type, so their accuracy can reach microseconds.
L mutual conversion between timeval and jiffies
Because the internal representation of the interval counter of the interval timer is different from that of the external representation, it is necessary to implement the timeval structure in microseconds and
Mutual conversion between jiffies. Therefore, Linux implements two functions in kernel/itimer. C to implement mutual conversion-tvtojiffies () function
Number and jiffiestotv () functions. Their source code is as follows:
Static unsigned long tvtojiffies (struct timeval * value)
{
Unsigned long sec = (unsigned) Value-> TV _sec;
Unsigned long USEC = (unsigned) Value-> TV _usec;
If (SEC> (ulong_max/Hz ))
Return ulong_max;
USEC + = 1000000/Hz-1;
USEC // = 1000000/Hz;
Return Hz * sec + USEC;
}
Static void jiffiestotv (unsigned long jiffies, struct timeval * value)
{
Value-> TV _usec = (jiffies % Hz) * (1000000/Hz );
Value-> TV _sec = jiffies/Hz;
}
7.2 underlying operating mechanism of the real interval timer itimer_real
The underlying operating mechanisms of the interval timer itimer_virt and itimer_prof are implemented through the do_it_virt () function and the do_it_prof () function respectively, so we will not repeat them here (see section 4.3 ).
Because the interval timer itimer_real is essentially no different from the kernel dynamic timer. Therefore, the kernel uses the kernel dynamic timer to implement the itimer_real interval timer of the process.
. Therefore, the task_struct structure sets up a member variable real_timer of the timer_list structure type. Dynamic timer for real_timer
The function pointer function is always set to point to the function it_real_fn () by the initialization macro init_task of the task_struct structure (). As shown below
(Include/Linux/sched. h ):
# Define init_task (TSK )/
......
Real_timer :{
Function: it_real_fn/
}/
......
}
The real_timer linked list element list and data members are always initialized by the process when they are created as null and the task_struct structure of the process, as shown in the following (kernel/fork. C ):
Int do_fork (......)
{
......
P-> it_real_value = p-> it_1__value = p-> it_prof_value = 0;
P-> it_real_incr = p-> it_1__incr = p-> it_prof_incr = 0;
Init_timer (& P-> real_timer );
P-> real_timer.data = (unsigned long) P;
......
}
When you use the setitimer () system call to set the itimer_real interval timer of the process, it_real_incr is set to a non-zero value, so the system calls
Set the real_timer.expires value, and then add the real_timer timer of the process to the kernel dynamic timer linked list.
The itimer_real interval timer is started. When the real_timer timer expires, its associated function it_real_fn () will be executed. Note! All processes
The function pointer of the real_timer timer points to the same function of it_real_fn (). Therefore, the it_real_fn () function must use its parameters.
Identify which process it is, so it will unsigned
The long type parameter P is interpreted as the address of the task_struct structure of the process. The source code of this function is as follows (kernel/itimer. C ):
Void it_real_fn (unsigned long _ data)
{
Struct task_struct * P = (struct task_struct *) _ data;
Unsigned long interval;
Send_sig (sigalrm, P, 1 );
Interval = p-> it_real_incr;
If (interval ){
If (interval> (unsigned long) long_max)
Interval = long_max;
P-> real_timer.expires = jiffies + interval;
Add_timer (& P-> real_timer );
}
}
The execution process of the it_real_fn () function is roughly as follows:
(1) first, the parameter P is interpreted as a pointer to the task_struct structure type of the process through forced type conversion.
(2) Send a sigalrm signal to the process.
(3) Enable the real_timer timer when the it_real_incr of the process is not 0. First, calculate the expires value of the real_timer timer
(Jiffies + it_real_incr ). Then, call the add_timer () function to add real_timer to the kernel dynamic timer linked list.
7.3 itimer timer system call
There are two syscall related to the itimer Timer: getitimer () and setitimer (). Getitimer () is used to query the three
Interval timer information, while setitimer () is used to set the three interval timers of the calling process. The two syscall files are now the kernel/itimer. c file.
.
7.3.1 Implementation of getitimer () system call
The sys_getitimer () function has two parameters: (1) which, which specifies the interval timer of the calling process. Its value can be itimer_real,
Either itimer_virt or itimer_prof. (2) value pointer, pointing to an itimerval structure in the user space, used to receive query results. This letter
The source code is as follows:
/* SMP: Only we modify our itimer values .*/
Asmlinkage long sys_getimer (INT which, struct itimerval * value)
{
Int error =-efault;
Struct itimerval get_buffer;
If (value ){
Error = do_getimer (which, & get_buffer );
If (! Error &&
Copy_to_user (value, & get_buffer, sizeof (get_buffer )))
Error =-efault;
}
Return Error;
}
Apparently, the sys_getitimer () function mainly queries the interval timer information of the current process through the do_getitimer () function, and stores the query results in the kernel space structure.
Variable get_buffer. Then, call the copy_to_usr () macro to copy the results in get_buffer to the user space buffer.
The source code of the function do_getimer () is as follows (kernel/itimer. C ):
Int do_getitimer (INT which, struct itimerval * value)
{
Register unsigned long Val, interval;
Switch (which ){
Case itimer_real:
Interval = Current-> it_real_incr;
Val = 0;
/*
* Fixme! This needs to be atomic, in case the kernel timer happens!
*/
If (timer_pending (& Current-> real_timer )){
Val = Current-> real_timer.expires-jiffies;
/* Look out for negative/Zero itimer ..*/
If (long) Val <= 0)
Val = 1;
}
Break;
Case itimer_virtual:
Val = Current-> it_1__value;
Interval = Current-> it_assist_incr;
Break;
Case itimer_prof:
Val = Current-> it_prof_value;
Interval = Current-> it_prof_incr;
Break;
Default:
Return (-einval );
}
Jiffiestotv (Val, & Value-> it_value );
Jiffiestotv (interval, & Value-> it_interval );
Return 0;
}
The query process is as follows:
(1) first, use the local variables Val and interval to indicate the current value and initial value of the interval counter of the timer to be queried.
(2) If which = itimer_real, The itimer_real interval timer of the current process is queried. Therefore
Current-> it_real_incr, get the initial value of the interval counter of the itimer_real interval timer, and save it to the interval local variable.
. For the current value of the interval counter, because the ititmer_real interval timer is implemented through the real_timer kernel dynamic timer, it cannot pass
Current-> it_real_value to obtain the current value of the interval counter of the itimer_real interval timer, which must be obtained through real_timer
To this value. Therefore, use the timer_pending () function to determine whether current-> real_timer has been started. If not started, it indicates
The itimer_real interval timer is not started, so the current value of the interval counter must be 0. Therefore, you can simply set the Val variable to 0. If the interval counter is already started
The value should be equal to (timer_real.expires-jiffies ).
(3) If which = itimer_virt, The itimer_virt interval timer of the current process is queried. Therefore, the initial values of the counter it_virt_incr and the current value it_virt_value are saved to the local variables interval and Val respectively.
(4) If which = itimer_prof, The itimer_prof interval timer of the current process is queried. Therefore, the initial values of the counter it_prof_incr and the current value it_prof_value are saved to the local variables interval and Val respectively.
(5) Finally, convert Val and interval to time values in timeval format by using the conversion function jiffiestotv () and save them to value-> it_value and value-> it_interval, return the query result.
7.3.2 setitimer () System Call implementation
The sys_setitimer () function not only sets the timer at the specified interval of the calling process, but also returns the original information of the timer at the interval. It has three parameters: (1) which, meaning and
The parameters in sys_getitimer () are the same. (2) enter the parameter value, pointing to an itimerval structure in the user space, containing the new value to be set. (3) Output Parameters
Ovalue refers to an itimerval structure in the user space, which is used to receive the original information of the interval timer.
The source code of this function is as follows (kernel/itimer. C ):
/* SMP: again, only we play with our itimers, and signals are SMP safe
* Now so that is not an issue at all anymore.
*/
Asmlinkage long sys_setimer (INT which, struct itimerval * value,
Struct itimerval * ovalue)
{
Struct itimerval set_buffer, get_buffer;
Int error;
If (value ){
If (copy_from_user (& set_buffer, value, sizeof (set_buffer )))
Return-efault;
} Else
Memset (char *) & set_buffer, 0, sizeof (set_buffer ));
Error = do_setitimer (which, & set_buffer, ovalue? & Get_buffer: 0 );
If (error |! Ovalue)
Return Error;
If (copy_to_user (ovalue, & get_buffer, sizeof (get_buffer )))
Return-efault;
Return 0;
}
Note the function as follows:
(1) When the input parameter value is not null, call the copy_from_user () macro to copy the information to be set in the user space to the set_buffer structure variable in the kernel space. If the value pointer is null, set all the set_buffer Structure Variables to 0.
(2) Call the do_setitimer () function to complete the actual setting operation. If the output parameter ovalue pointer is valid, the address of the Inner-core variable get_buffer is used
The third call parameter of the do_setitimer () function. When the do_setitimer () function returns, the get_buffer structure variable contains the current process.
The original information of the specified interval timer. The do_setitimer () function returns a value of 0, indicating success. A value other than 0 indicates failure.
(3) When the do_setitimer () function returns a non-zero value, or the ovalue pointer is null (the original information of the interval timer is not required ), the function can be directly returned.
(4) If the ovalue pointer is not null, call the copy_to_user () macro to copy the value of the get_buffer () structure variable to the user space pointed to by the ovalue, this allows you to obtain the original information value of the specified interval timer.
The source code of the do_setitimer () function is as follows (kernel/itimer. C ):
Int do_setitimer (INT which, struct itimerval * value, struct itimerval * ovalue)
{
Register unsigned long I, j;
Int K;
I = tvtojiffies (& Value-> it_interval );
J = tvtojiffies (& Value-> it_value );
If (ovalue & (k = do_getitimer (which, ovalue) <0)
Return K;
Switch (which ){
Case itimer_real:
Del_timer_sync (& Current-> real_timer );
Current-> it_real_value = J;
Current-> it_real_incr = I;
If (! J)
Break;
If (j> (unsigned long) long_max)
J = long_max;
I = J + jiffies;
Current-> real_timer.expires = I;
Add_timer (& Current-> real_timer );
Break;
Case itimer_virtual:
If (j)
J ++;
Current-> it_1__value = J;
Current-> it_1__incr = I;
Break;
Case itimer_prof:
If (j)
J ++;
Current-> it_prof_value = J;
Current-> it_prof_incr = I;
Break;
Default:
Return-einval;
}
Return 0;
}
Note the function as follows:
(1) Call the tvtojiffies () function to convert the initial value and current value in timeval format to the time value in the unit of clock tick. And stored in local variables I and j respectively.
(2) If the ovalue pointer is not null, call the do_getitimer () function to query the original information of the timer at the specified interval. If the do_getitimer () function returns a negative value, an error occurs. Therefore, an error value is returned directly. Otherwise, continue to the next step to set the specified interval timer.
(3) If which = ititmer_real, it indicates setting the itimer_real interval timer. (A) call the del_timer_sync () function (this function
In a single CPU system, the del_timer () function deletes the real_timer timer of the current process from the kernel dynamic timer linked list. (B) Change
It_real_incr and it_real_value are set to local variables I and j respectively. (C) If J = 0, it means you do not have to start the real_timer timer. Therefore
Exit switch... Case control structure, but return directly. (D) Set the expires member of real_timer to (jiffies + current value)
J), and then call the add_timer () function to add the real_timer timer of the current process to the kernel dynamic timer linked list to start the timer.
(4) If which = itimer_virt, you can simply update it_1__incr and it_1__value with the values of the local variables I and J.
(5) If which = itimer_prof, you can simply update it_prof_incr and it_prof_value by using the values of the local variables I and J.
(6) Finally, return 0 to indicate success.
7.3.3 alarm system call
The system calls alarm to receive a sigalrm signal after the specified interval of seconds. It only has one parameter seconds, which specifies the timing interval in seconds. The source code of the function sys_alarm () is as follows (kernel/Timer. C ):
/*
* For backwards compatibility? This can be done in libc so alpha
* And all newer ports shouldn't need it.
*/
Asmlinkage unsigned long sys_alarm (unsigned int seconds)
{
Struct itimerval it_new, it_old;
Unsigned int oldalarm;
It_new.it_interval. TV _sec = it_new.it_interval. TV _usec = 0;
It_new.it_value. TV _sec = seconds;
It_new.it_value. TV _usec = 0;
Do_setitimer (itimer_real, & it_new, & it_old );
Oldalarm = it_old.it_value. TV _sec;
/* Ehhh .. we can't return 0 if we have an alarm pending ..*/
/* And we 'd better return too much than too little anyway */
If (it_old.it_value. TV _usec)
Oldalarm ++;
Return oldalarm;
}
This system call is actually the itimer_real interval timer for starting the process. Therefore, it can be fully implemented in user space C function libraries (such as libc and glibc. But
To ensure the backward compatibility of this kernel, the kernel of version 2.4.0 still puts this syscall in the kernel space for implementation. The implementation process of the function sys_alarm () is as follows:
(1) construct an itimerval Structure Variable it_new based on the value of seconds. Note! Because the itimer_real interval timer started by alarm is one-time instead of repeating, The it_interval member in the it_new variable must be set to 0.
(2) Call the do_setitimer () function to start the itimer_real timer of the current process with the newly constructed timer it_new, and save the original timer interval of the timer to the local variable it_old.
(3) the return value oldalarm indicates the original timer interval value of the itimer_real interval timer in seconds. Therefore, assign it_old.it_value. TV _sec
Add the value of oldalarm to 1 (that is, Supplement 1 second in less than 1 second) When it_old.it_value. TV _usec is not 0 ).