About Linux application-tier timer __linux

Source: Internet
Author: User
Tags error handling strlen time interval usleep

The purpose of using a timer is to perform a task periodically, or to perform a task at a specified time. In order to achieve this goal, there are generally two common and more effective methods. One is using the three timer inside Linux, and the other is sleeping or usleep the process to sleep for a while; in fact, there is another way, it is to use Gettimeofday, difftime and so on themselves to calculate the time interval, and then time to do a task, But this method is inefficient, so it is not commonly used.

1, Alarm
If not very precise, use alarm () and signal () is enough.

unsigned int alarm (unsigned int seconds)

Specifically for the SIGALRM signal, after a specified time seconds seconds, the process itself will be sent SIGALRM signal, also known as the alarm clock time. After a process calls alarm, any previous alarm () calls are invalidated. If the parameter seconds is zero, then no alarm time is included in the process. If the alarm time is set in the process before alarm () is invoked, the time remaining for the last alarm time is returned, or 0 is returned.
Example:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void sigalrm_fn (int sig)
{
    printf ("alarm!\n");
    Alarm (2);
    return;
}

int main (void)
{
    signal (SIGALRM, SIGALRM_FN);
    Alarm (2);

    while (1) pause ();
}

2, Setitimer

int Setitimer (int which, const struct itimerval *value, struct itimerval));
int Getitimer (int which, struct itimerval *value);

Strcut timeval
{
   long tv_sec;/* sec/
   long tv_usec;/* microsecond/
};

struct Itimerval
{
   struct timeval it_interval;/* time interval/struct timeval it_value
   ;//Current Time count
/};

Setitimer () is more powerful than alarm () and supports 3 kinds of timers:

itimer_real: Give a specified interval, reduce the count by the actual time, and emit a SIGALRM signal when the interval is 0.
itimer_virtual: Given a time interval, the count is reduced when the process executes, and the SIGVTALRM signal is emitted when the time interval is 0.
itimer_prof: Given a time interval, when the process is executing or the system is scheduling for the process, reduce the count, time to send out the sigprof signal.

Setitimer () The first parameter which specifies the timer type (one of the above three); The second parameter is an instance of the structure itimerval, and the third parameter is not processed.
Here is a simple demonstration of the Setitimer call, in which a sigalrm is emitted every second, emitting a sigvtalrm signal every 0.5 seconds::

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <

time.h> #include <sys/time.h> int sec;
       void Sigroutine (int signo) {switch (signo) {case sigalrm:printf ("Catch a signal--SIGALRM \ n");
       Signal (SIGALRM, sigroutine);
   Break
       Case sigvtalrm:printf ("Catch a signal--SIGVTALRM \ n");
       Signal (SIGVTALRM, sigroutine);
   Break
} return;

   int main () {struct Itimerval value, Ovalue, value2;
   SEC = 5;
   printf ("Process ID is%d", getpid ());
   Signal (SIGALRM, sigroutine);
   Signal (SIGVTALRM, sigroutine);
   Value.it_value.tv_sec = 1;
   value.it_value.tv_usec = 0;
   Value.it_interval.tv_sec = 1;
   value.it_interval.tv_usec = 0;
   Setitimer (Itimer_real, &value, &ovalue);
   value2.it_value.tv_sec = 0;
   Value2.it_value.tv_usec = 500000;
   value2.it_interval.tv_sec = 0;
   Value2.it_interval.tv_usec = 500000; Setitimer (Itimer_virtual, &vAlue2, &ovalue); for (;;);}

The results of this example are as follows:

localhost:~$./timer_test
Process ID is 579
catch a signal–sigvtalrm
catch a signal–sigalrm
catch a Si Gnal–sigvtalrm
Catch a signal–sigvtalrm
catch a signal–sigalrm
catch a Signal–gvtalrm

Note: The Linux signaling mechanism is essentially inherited from Unix systems. The signaling mechanisms in early UNIX systems were simple and primitive, and later exposed some problems in practice, so the signals built on the early mechanisms were called "unreliable signals", with a signal value less than sigrtmin (Red hat 7.2, sigrtmin=32,sigrtmax= 63 signals are unreliable signals. This is the source of "unreliable signals". The main problem is that the response to the signal is set to the default action each time the process processes the signal. In some cases, it will result in error handling of the signal, so if the user does not want to do so, then call signal () again at the end of the signal processing function and reinstall the signal.

3, with sleep and usleep implementation of scheduled Tasks

#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

static char msg[] = "I received a msg.\n";
int Len;

void show_msg (int signo)
{
    write (Stderr_fileno, MSG, len);
}

int main ()
{
    struct sigaction act;
    Union Sigval TSval;
    Act.sa_handler = show_msg;
    act.sa_flags = 0;
    Sigemptyset (&act.sa_mask);
    Sigaction (M, &act, NULL);
    Len = strlen (msg);
    while (1)
    {
        Morpheus (2);/* Sleep 2 seconds * * *
        send a signal to the main process, in fact, you send yourself a signal * *
        Sigqueue (Getpid (), tsval);
    } return
    0;
}

See, this is much simpler than the above, and you use a stopwatch test, time is accurate, specify 2 seconds to give you output a string. So, if you only do regular timing and have time to perform a task, this is the easiest way to do it.

4, through their own calculation of time difference method to timing

#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

static char msg[] = "I received a msg.\n";
int Len;
Static time_t lasttime;

void show_msg (int signo)
{
    write (Stderr_fileno, MSG, len);
}

int main ()
{
    struct sigaction act;
    Union Sigval TSval;
    Act.sa_handler = show_msg;
    act.sa_flags = 0;
    Sigemptyset (&act.sa_mask);
    Sigaction (M, &act, NULL);
    Len = strlen (msg);
    Time (&lasttime);
    while (1)
    {
        time_t nowtime;
        /* Get Current time * *
        (&nowtime);
        /* And the last time to compare, if greater than or equal to 2 seconds, then immediately send
        a signal/if (Nowtime-lasttime >= 2)
        {/
            * to the main process to send signals, in fact, to their own signal to themselves * *
            Sigqueue (Getpid (), M, tsval);
            Lasttime = Nowtime;
        }
    return 0;
}

This differs from the above in that it is the manual calculation of the time difference, if you want more accurate calculation time difference, you can change the time function to Gettimeofday, this can be accurate to subtle.

5. Use Select to provide precise timing and hibernation

int select (int n, fd_set *readfds, Fd_set *writefds, Fd_set *exceptfds, struct timeval);

N refers to the range of file descriptors that are monitored, usually set to the Fd+1;readfds,writefds and Exceptfds of the selected select, respectively, the read, write, and exception file descriptor sets; timeout is the timeout time.

The macros that you might use for file descriptor set operations are:

    FD_CLR (int fd, fd_set *set);   Clear FD
    fd_isset (int fd, fd_set *set);//test FD set
    fd_set (int fd, fd_set *set);   Set FD
    Fd_zero (fd_set *set);          

We don't use these macros at this point because we don't care about the state of the file descriptor, we're concerned about the select timeout. So we need to set the Readfds,writefds and Exceptfds to NULL, just specify the timeout time. As for n we can not care, so you can set it to any non-negative value. The implementation code is as follows:

int Mssleep (Long ms) 
 {
    struct timeval TV;
    tv.tv_sec = 0;
    Tv.tv_usec = ms;

    Return Select (0, NULL, NULL, NULL, &TV);
 }

How, is not very simple. Both Setitimer and select can achieve precise hibernation of the process, and a simple implementation based on select is given here. I do not recommend using Setitimer because the Linux system provides a limited number of timer (3 different types of timer per process), and Setitimer does not implement a select simple.

6. High Precision hardware interrupt timer Hrtimer

The "High resolution Timer support" needs to be turned on in kernel, and the initialization of Hrtimer in the driver is as follows:

Hrtimer_init (&m_timer, Clock_monotonic, hrtimer_mode_rel_pinned);
M_timer.function = Vibrator_timer_func;
Hrtimer_start (&m_timer, Ktime_set (0, 62500), hrtimer_mode_rel_pinned);

The timer function Vibrator_timer_func as follows:

static enum Hrtimer_restart vibrator_timer_func (struct Hrtimer *timer)
{
  Gpio_set_value (gpio_test, 1);
  Gpio_set_value (gpio_test, 0);
  Hrtimer_forward_now (&m_timer,ktime_set (0, 62500));
  return hrtimer_restart;
}

Where gpio_test is the output pin, in order to facilitate the output view. But with the oscilloscope to look at the pin waveform, found that although the set period of 62.5us, but the output is always about 72us, and occasionally there will be two waveforms close (that is, the cycle suddenly changed to 10US below). I will set the cycle to 40US, there will be 72us and 10us often alternating, can not achieve accurate 40US waveform, if set to 100US, then the waveform is 100US, and seemingly did not see the following cycle of 10US to appear.

7. High Precision timer Posix_timer

The most powerful timer interface comes from the POSIX clock series, whose creation, initialization, and deletion of a timer's actions are divided into three different functions: Timer_create () (Create timer), Timer_settime (initialization timer), and Timer_delete () (Destroy it).

To create a timer:

int Timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)

A process can create a specific timer by calling Timer_create (), which is itself per process and not inherited when fork. CLOCK_ID describes which clock the timer is based on, and *timerid loads the ID of the timer being created. The function creates a timer and puts his ID in the position Timerid points to. The parameter EVP specifies the asynchronous notification to be generated for the timer to expire. If EVP is NULL, then the timer expires will produce the default signal, for Clock_realtimer, the default signal is SIGALRM. If you want to generate a signal other than the default, the program must set the Evp->sigev_signo to the desired letter number. The member evp->sigev_notify in the struct sigevent structure describes the actions that should be taken when the timer expires. Typically, the value of this member is sigev_signal, which indicates that a signal is generated when the timer expires. The program can set member evp->sigev_notify to Sigev_none to prevent the timer from generating a signal when it expires.

If several timers produce the same signal, the handler can use Evp->sigev_value to distinguish which timer generated the signal. To do this, the program must use the marker Sa_siginfo in the member sa_flags of the struct sigaction when it installs the handler for the signal.

CLOCK_ID values are as follows:
Clock_realtime:systemwide Realtime CLOCK.
Clock_monotonic:represents monotonic time. cannot be set.
Clock_process_cputime_id:high resolution Per-process Timer.
clock_thread_cputime_id:thread-specific timer.
Clock_realtime_hr:high resolution version of Clock_realtime.
Clock_monotonic_hr:high resolution version of Clock_monotonic.

struct sigevent
{
int sigev_notify;//notification type
int sigev_signo;//signal number
Union sigval< C4/>sigev_value; Signal value
void (*sigev_notify_function) (Union sigval);
pthread_attr_t *sigev_notify_attributes;
}
Union sigval
{
int sival_int;//integer value
void *sival_ptr;//pointer value
}

Customize the behavior of the timer after expiration by setting the evp->sigev_notify to the following values:
Sigev_none: Do nothing and only provide timeout information through timer_gettime and Timer_getoverrun queries.
Sigev_signal: When the timer expires, the kernel transmits the signal specified by Sigev_signo to the process. In the signal processing program, the Si_value will be set to Sigev_value.
Sigev_thread: When the timer expires, the kernel creates a thread for the thread attribute (within this process), sigev_notification_attributes it executes sigev_notify_function, and passes in the SIGEV_ Value as a parameter.

Start a timer:
The timer created by Timer_create () is not started. To associate it to an expiration time and to start the clock cycle, you can use Timer_settime ().

int Timer_settime (timer_t timerid, int flags, const struct ITIMERSPEC *value, struct itimerspect);

struct itimespec{
    struct timespec it_interval; 
    struct Timespec it_value;   

Like SetTimer (), It_value is used to specify the current timer expiration time. When the timer expires, the It_value value is updated to the It_interval value. If the value of It_interval is 0, the timer is not a time interval timer, and once the it_value expires it will return to the state of not being started. The TIMESPEC structure provides a nanosecond-level resolution:

struct timespec{
    time_t tv_sec;
    Long tv_nsec;  
};

If the value of flags is Timer_abstime, the time value specified by value is interpreted as absolute value (the default interpretation of this value is relative to the current time). This modified behavior avoids obtaining the current time, calculates the relative difference between "that time" and "expected future time", and creates competitive conditions during the startup timer.
If the value of the ovalue is not NULL, the previous timer expiration is deposited into the itimerspec that it provides. If the timer is previously in an open state, the members of this structure will all be set to 0.

To obtain the remaining time of an active timer:

int Timer_gettime (timer_t timerid,struct itimerspec *value);

To obtain a timer's overrun run times:
It is possible that a timer expires and the signal generated at the time the same timer expires is still in a pending state. In this case, one of the signals may be lost. This is the timer overrun. The program can determine the number of times a particular timer has such a limit by calling Timer_getoverrun. The timer overrun can only occur on the signal generated by the same timer. The signals generated by multiple timers, even those using the same clock and signal, will be queued without loss.

int Timer_getoverrun (timer_t timerid);

When executed successfully, Timer_getoverrun () returns the number of timer expiration times that have occurred between the timer's initial expiration and the notification process (for example, through the signal) timer has expired. For example, in our previous example, a 1ms timer ran 10ms, and the call returned 9. This call returns Delaytimer_max if the number of times the overrun runs is equal to or greater than Delaytimer_max.
When execution fails, the function returns-1 and the errno setting is Einval, and the only error condition indicates that the Timerid specified an invalid timer.

To delete a timer:

int Timer_delete (timer_t timerid);

A successful Timer_delete () call destroys the timer associated with the Timerid and returns 0. When execution fails, the call returns-1 and the errno setting is Einval, and the only error condition represents that Timerid is not a valid timer.

Example 1:

void  handle ()
{
 time_t t;
 Char p[32];
 Time (&t);
 Strftime (P, sizeof (p), "%T", LocalTime (&t));
 printf ("Time is%s\n", p);
}

int main ()
{
 struct sigevent EVP;
 struct ITIMERSPEC ts;
 timer_t timer;
 int ret;
 Evp.sigev_value.sival_ptr = &timer;
 Evp.sigev_notify = sigev_signal;
 Evp.sigev_signo = SIGUSR1;
 Signal (SIGUSR1, handle);
 ret = Timer_create (Clock_realtime, &EVP, &timer);
 if (ret)
  perror ("Timer_create");
 Ts.it_interval.tv_sec = 1;
 ts.it_interval.tv_nsec = 0;
 Ts.it_value.tv_sec = 3;
 ts.it_value.tv_nsec = 0;
 ret = timer_settime (timer, 0, &ts, NULL);
 if (ret)
  perror ("Timer_settime");
 while (1);
}

Example 2:

void handle (Union Sigval v) {time_t t;
 Char p[32];
 Time (&t);
 Strftime (P, sizeof (p), "%T", LocalTime (&t));
 printf ("%s thread%lu, val =%d, signal captured.\n", p, Pthread_self (), v.sival_int);
Return
 int main () {struct sigevent EVP;
 struct ITIMERSPEC ts;
 timer_t timer;
 int ret;
 memset (&EVP, 0, sizeof (EVP));
 Evp.sigev_value.sival_ptr = &timer;
 Evp.sigev_notify = Sigev_thread;
 Evp.sigev_notify_function = handle;   Evp.sigev_value.sival_int = 3;
 As a parameter of handle () ret = Timer_create (Clock_realtime, &EVP, &timer);
 if (ret) perror ("Timer_create");
 Ts.it_interval.tv_sec = 1;
 ts.it_interval.tv_nsec = 0;
 Ts.it_value.tv_sec = 3;
 ts.it_value.tv_nsec = 0;
 ret = timer_settime (timer, Timer_abstime, &ts, NULL);
 if (ret) perror ("Timer_settime");
while (1); }
Related Article

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.