First, preface
From an application perspective, there are three types of time-related services that the kernel needs to provide:
1, and system time-related services. For example, when you write a record to a database, you need to record the operation time (when and where the month is).
2. Let the process sleep for some time
3, and timer-related services. After a specified period of time has elapsed, kernel to alert the user process
This paper mainly describes the knowledge of user-space interface functions related to time subsystem.
Second, and system time-related services
1, second-level time functions: times and Stime
The time and STIME functions are defined as follows:
#include <time.h>
time_t time (time_t *t);
int stime (time_t *t);
The time function returns the number of seconds to the Linux epoch (the timekeeper module in the kernel holds this value, timekeeper->xtime_sec). Stime is the number of seconds to set the current point in time to the Linux epoch. For Linux kernel, the time-setting process must have cap_sys_time rights, or it will fail.
Linux kernel supports these two functions with system calls to Sys_time and Sys_stime. In fact, after the introduction of higher-precision time-related system calls (for example: Sys_gettimeofday), the above two system calls can be implemented in the user space with the new system to implement timing and stime functions. In kernel, only the __arch_want_sys_time macro is defined, and the system will provide the above two system calls. Of course, the provision of such system calls is mostly intended to be compatible with older application software.
Along with the above interface functions, there is a series of conversion of the current time point to the Linux epoch into interface functions for human reading, such as Asctime, CTime, Gmtime, LocalTime, Mktime, Asctime_r, Ctime_r, Gmtime_r, Localtime_r, these functions are primarily used to convert the time of the time_t type to a break-down or character form.
2, microsecond-level time functions: Gettimeofday and Settimeofday
#include <sys/time.h>
int gettimeofday (struct timeval *tv, struct timezone *tz);
int settimeofday (const struct Timeval *tv, const struct timezone *tz);
These two functions are similar to those of the previous bar seconds, except that the time precision can reach the microsecond level. The Gettimeofday function can obtain the number of seconds from the Linux epoch to the current point in time and the number of microseconds (in the kernel state, this time value is still obtained through the timekeeper module, the specific interface is getnstimeofday64, The time accuracy of the interface is nanosecond, but it doesn't matter if you divide by 1000 to get the microsecond level of precision, Settimeofday is the number of seconds from the Linux epoch to the current point in time and the number of microseconds. Similarly, the time-setting process must have the right to cap_sys_time, or it will fail. The TZ parameter exists for historical reasons, and the kernel does not actually support timezone.
Obviously, the Sys_gettimeofday and sys_settimeofday these two system calls are used to support the above two function functions, it is worth mentioning that: these system calls in the new POSIX standard The Gettimeofday and Settimeofday interface functions are labeled obsolescent and replaced by Clock_gettime and Clock_settime interface functions
3, nanosecond-level time functions: Clock_gettime and Clock_settime
#include <time.h>
int Clock_getres (clockid_t clk_id, struct timespec *res);
int Clock_gettime (clockid_t clk_id, struct timespec *tp);
int Clock_settime (clockid_t clk_id, const struct TIMESPEC *tp);
If this parameter is not clk_id, Clock_gettime and clock_settime are basically not explained, and the concept and gettimeofday and Settimeofday interface functions are completely similar except that the precision is nanosecond. Clock is the meaning of clocks, it records the passage of time. The clock ID is, of course, the ID of the system clock, which is defined as follows:
Clock_realtime
Clock_monotonic
Clock_monotonic_raw
clock_process_cputime_id
clock_thread_cputime_id
Depending on the needs of the application, the kernel maintains several different system clocks. The most familiar of course is clock_realtime this system clock, because it represents the real world of the wall clock (the first two sections of the interface function does not specify the clock ID, actually obtained is the clock_realtime time value). Clock_realtime This system clock allows the user to set it (with Cap_sys_time permissions, of course), which means that the system clock can be modified in user space, resulting in discontinuous time breaks. In addition, the clock can be adjusted by NTP (there is no discontinuity, NTP adjusts the local oscillator and upstream server frequency error).
Just from the name you can see that the clock_monotonic system clock should be monotonically increasing, in addition, the clock is the real world wall clock, but its datum point is not necessarily the Linux epoch (of course, can be), generally will be the system start point of time set to its datum point. The clock is then incremented continuously. In addition to being able to adjust the clock through NTP, any other program does not allow the clock to be set, which also guarantees the monotony of the clock.
The Clock_monotonic_raw features clock_monotonic, in addition to NTP tuning. That is, the clock ID is the CLOCK_MONOTONIC_RAW system clock that is completely based on the local crystal oscillator. cannot be set, nor can it be adjusted to the frequency of crystal oscillator.
When calling the Clock_gettime and Clock_settime interface functions, if the pass clock ID parameter is clock_realtime, then the behavior of the two functions is consistent with the first two subsections, except the NS precision. Read here, I detailed the masses of the masses inevitably ask: why should there be other types of system clock it? Monotonic type of clock is relatively simple, if you set event a after 5 seconds of Action B, then the monotonic type of clock is a good choice, if the use of realtime clock, when the user between event A and action B insert time set operation, Then you set event a after 5 seconds of Action B will not be triggered. In addition, the user needs to understand the system boot time, which requires the clock to use the monotonic type clock. It should be noted that the monotonic type of clock is not the concept of absolute time, mostly calculating the time between two sample points and guaranteeing the monotony of time between sample points. Monotonic_raw is a low-level tool, in general programmers do not operate it, using the monotonic type of clock is sufficient, of course, some advanced applications, such as you want to use another method (not NTP) to adjust the time, then you can use the Monotonic_ Raw out.
Some scenarios use real time clocks (wall clocks) that are inappropriate, such as when we perform performance analysis and statistics for each application in the system. Because of this, kernel provides a process-or thread-based system clock, which is clock_process_cputime_id and clock_thread_cputime_id. When we intend to use a process-or thread-based system clock, we need to get the clock ID first:
#include <time.h>
int Clock_getcpuclockid (pid_t pid, clockid_t *clock_id);
If it is a thread, you need to call the Pthread_getcpuclockid interface function:
#include <pthread.h>
#include <time.h>
int Pthread_getcpuclockid (pthread_t thread, clockid_t *clock_id);
Although the accuracy of this set of function interfaces can reach the NS level, the actual system can achieve what precision is implemented, so the clock_getres is used to obtain the accuracy of the system clock.
4, the system clock adjustment
Setting the system time is a rough way, once the system time has been modified, many processes in the system that rely on absolute time will have a variety of bizarre behaviors. Because of this, the system provides time-synchronized interface functions that allow an external, accurate timing server to constantly remediate the system clock.
(1) Adjtime interface function
int adjtime (const struct timeval *delta, struct timeval *olddelta);
The function can fix the system clock slowly (clock_realtime that) according to the Delta parameter. Olddelta returns the delta that was not yet complete in the last adjustment.
(2) Adjtimex
#include <sys/timex.h>
int Adjtimex (struct timex *buf);
RFC 1305 defines a more complex and powerful time-tuning algorithm, so Linux kernel supports this algorithm through Sys_adjtimex, and its user-space interface function is Adjtimex. Because this algorithm used to be strong, here will not repeat, wait for time, interested to fill the gap here.
The Linux kernel provides SYS_ADJTIMEX system tuning to support the above two interface functions. In addition, a sys_clock_adjtime system tune is provided to support POSIX clock tunning.
Third, process sleep
1, seconds sleep function: Sleep
#include <unistd.h>
unsigned int sleep (unsigned int seconds);
Calling this function causes the current process to Sleep,seconds (based on Clock_realtime) to return to the execution of the program. The return value of the function indicates the time that the process has not entered sleep. For example, if we want to sleep 8 seconds, but because siganl interrupted sleep, just sleep for 5 seconds, then the return value is 3, indicating that 3 seconds have not slept.
2, microsecond-level sleep function: Usleep
#include <unistd.h>
int Usleep (useconds_t usec);
The concept is the same as sleep, but the return value is defined differently. Usleep returns 0 for successful execution, 1 for execution failure, and error code in errno.
3, nanosecond-level sleep function: Nanosleep
#include <time.h>
int nanosleep (const struct TIMESPEC *req, struct timespec *rem);
The Usleep function is already in the past, is not recommended, and is replaced by the Nanosleep function. In req, set the seconds you want to sleep and the nanosecond value, and call the function to let the current process sleep. A return of 0 indicates a successful execution, and a return of 1 indicates that the execution failed with the error code obtained in errno. EINTR indicates that the function was interrupted by signal. The REM parameter is the meaning of remaining time, which means that there is still a lot of sleep to be done.
The Linux kernel does not provide system calls for sleep and usleep, and the implementation of sleep and Usleep is located in C Lib. In some systems, these implementations are signal-dependent, and some systems are implemented using a timer, and for the GNU system, sleep and Usleep and nanosleep functions are all passed through kernel Sys_ Nanosleep system calls are implemented (the underlying is based on Hrtimer).
4. More advanced sleep function: Clock_nanosleep
#include <time.h>
int Clock_nanosleep (clockid_t clock_id, int flags,
const struct TIMESPEC *request,
struct Timespec *remain);
The Clock_nanosleep interface function needs to pass more arguments, and of course it means it is more powerful. CLOCK_ID shows that this interface function can sleep based not only on real time clock, but also on other system clocks. Flag equals 0 or 1, indicating, respectively, whether the time value set by the request parameter is relative time or absolute time.
IV. Services related to timer
1. Alarm function
#include <unistd.h>
unsigned int alarm (unsigned int seconds);
The alarm function is the simplest interface to use a timer. Sends a SIGALRM signal to the process after a specified number of seconds (based on clock_realtime) has elapsed. Of course, the program calling this interface needs to set signal handler.
2. Interval Timer function
#include <sys/time.h>
int Getitimer (int which, struct itimerval *curr_value);
int Setitimer (int which, const struct Itimerval *new_value,
struct Itimerval *old_value);
The Interval timer function behaves like the alarm function, but is more powerful. Each process supports 3 timers, and a different timer defines how to timing and what signal to send to the process, and the which parameter indicates which timer to use:
(1) itimer_real. Based on the Clock_realtime timing, the SIGALRM signal is sent after timeout, just like the alarm function.
(2) itimer_virtual. The SIGVTALRM signal is sent only when the user-space code for the process is executed and timed out.
(3) Itimer_prof. It is only timed when the process executes, whether it is executing user space code or in kernel execution (such as a system call), and sending a sigprof signal after timeout.
The struct itimerval is defined as follows:
struct Itimerval {
struct Timeval it_interval; /* Next Value */
struct Timeval it_value; /* Current Value */
};
Two members indicate the time value of this and the next (how to set after the extended period) respectively. With this definition, the interval timer can implement the one shot type of timer and the periodic timer. For example, current value is set to 5 seconds, next value is set to 3 seconds, and after setting such a timer, the It_value value is continuously decremented until 5 seconds later, and then the It_value value is reloaded (using the value of It_interval). That is equal to 3 seconds, followed by a 3 cycle of constant triggering.
Old_value returns the set value of the last Setitimer function. The Getitimer function gets the state of the current interval timer, where the It_value member can get the time information for the current moment to the next trigger point, and the It_interval member remains the same unless you re-invoke Setitimer reset.
Although the interval timer function is also part of the POSIX standard, in the new POSIX standard, the interval timer interface function is labeled obsolescent instead of the POSIX timer interface function.
3, more advanced, more flexible timer function
The interval timer function described in the previous section still has a functional disadvantage: for example, a process can have only itimer_real, itimer_virtual, and itimer_prof three timer, if one of the timer is set consecutively (for example, ITIMER _real), which causes the first setting to be overwritten by a second setting. In addition, the time-out processing is always signaled, and the signal cannot be modified. When the mask signal processing, although the timer has been extended several times, but signal handler will only be called once, unable to obtain more detailed information. Finally, Interval timer function accuracy is the microsecond level, the accuracy has the further enhancement space. Just because of the shortcomings of the traditional interval timer function, the POSIX standard defines a more advanced, more flexible timer function, which we call the POSIX (interval) timer.
(1) Create a timer
#include <signal.h>
#include <time.h>
int Timer_create (clockid_t clockid, struct sigevent *sevp, timer_t *timerid);
In this interface function, the clock ID is believed to be familiar to everyone, Timerid is a handle to the timer ID returned, just like the file descriptor returned by the Open function. Therefore, to understand this interface function is to focus on understanding the struct sigevent this data structure:
Union sigval {/* Data passed with notification */
int sival_int; /* Integer Value */
void *sival_ptr; /* Pointer Value */
};
typedef struct SIGEVENT {
sigval_t Sigev_value;
int Sigev_signo;
int sigev_notify;
Union {
int _pad[sigev_pad_size];
int _tid;
struct {
void (*_function) (sigval_t);
void *_attribute; /* Really pthread_attr_t */
} _sigev_thread;
} _sigev_un;
} sigevent_t;
Sigev_notify defines how to notify the process after the timer has been extended, which can be set:
(a) Sigev_none. No asynchronous notification is required and the program calls Timer_gettime itself to poll the current state of the timer
(b) sigev_signal. Use asynchronous notification methods such as sinal. The signal sent is defined by Sigev_signo. If the realtime signal is sent, additional data for that signal is defined by Sigev_value.
(c) Sigev_thread. Create a thread to execute the timer extended callback function, _attribute defines the properties of the thread.
(d) sigev_thread_id. The behavior is similar to sigev_signal, but the sent signal is delivered to a specified thread within the process, and this thread is identified by the _tid.
(2) Set timer
#include <time.h>
int Timer_settime (timer_t timerid, int flags, const struct ITIMERSPEC *new_value,
struct Itimerspec * old_value);
int Timer_gettime (timer_t timerid, struct itimerspec *curr_value);
Timerid is the timer created in the previous section through Timer_create. New_value and Old_value These two parameters are similar to the Setitimer function, here is no longer detailed. Flag equals 0 or 1, indicating, respectively, whether the time value set by the New_value parameter is relative or absolute time. If New_value.it_value is a non-0 value, then calling Timer_settime can start the timer. If New_value.it_value is a value of 0, then call Timer_settime can stop the timer.
The Timer_gettime function is similar to Getitimer and can be referenced in the above description.
(3) Delete timer
#include <time.h>
int Timer_delete (timer_t timerid);
There is a delete that is created, Timer_delete is used to delete the specified timer, freeing the resource.
Linux time Subsystem (iii) User space interface function