On the use of several timing functions of linux

  • 2020-05-12 06:39:10
  • OfStack

In the process of program development, we need to use 1 timer from time to time. Usually, if the time accuracy is not high, we can use sleep and uslepp to make the process sleep for a period of time to achieve timing.

The former is in seconds (s), while the latter is subtle (us). But sometimes we don't want the process to sleep where it's blocked, so we need the process to execute normally, and then do something when it gets to a certain time,

Under linux, we use alarm function and setitimer function to realize the timing function.

The following is a detailed analysis of the two functions:

(1) alarm function

Also known as the alarm function, alarm can set a timer in a process and send an SIGALRM signal to the process when the timer specified a time.

The prototype of alarm function is as follows:


unsigned int alarm ( unsigned int seconds);
//seconds  Is the specified number of seconds 

Required header file
# include < unistd.h >

The function prototype
unsigned int alarm (unsigned int seconds)

Function parameters
seconds: number of seconds specified

Function return value
Success: if the process has already set the alarm time before calling this alarm (), return the remaining time of the last alarm time, otherwise return 0.
Error: 1

Here's a simple example of the alarm () function:


void sigalrm_fn(int sig)  
 
{ 
  printf("alarm!\n"); 
  alarm(2); 
  return; 
} 
 
int main(void) 
 
{ 
  signal(SIGALRM, sigalrm_fn); // The following function must be a band int Parameters of the 
  alarm(1); 
  while(1)  
  pause(); 
 
}

(2) setitimer () function

Under linux, alarm() and signal() are fine if the timing requirements are not precise, but setitimer functions are needed if you want to achieve more precise timing.

setitimer() is API for Linux, not Standard Library for C. setitimer() has two functions.

Linux has three internal timers for each task:

ITIMER_REAL: a real-time timer that counts no matter what mode the process is running in (even when the process is suspended). Arrive regularly and send an SIGALRM signal to the process.

ITIMER_VIRTUAL: this is not a real-time timer, when the process is in user mode (that is, when the program is executing) to calculate the time for the process to execute. Sends an SIGVTALRM signal to the process upon scheduled arrival.

ITIMER_PROF: the process counts both in user mode (when the program is executed) and in core mode (when the process is scheduled). Timing arrival generates SIGPROF signal. ITIMER_PROF records more time for process scheduling than ITIMER_VIRTUAL.

The timer is given an initial value in initialization, which decreases with time and sends a signal after it reaches 0, and at the same time restores the initial value. In a task, we can have one or all three timers, but only one timer of the same type can be used at the same time.

The setitimer function prototype is as follows:


#include <sys/time.h>

    int setitimer(int which, const struct itimerval *new_value,
           struct itimerval *old_value);

 Timer values are defined by the following structures:

      struct itimerval {
        struct timeval it_interval; /* next value */
        struct timeval it_value;  /* current value */
      };

      struct timeval {
        time_t   tv_sec;     /* seconds */
        suseconds_t tv_usec;    /* microseconds */
      };

it_interval is used to specify how often the task is executed, and it_value is used to save the current time until the task is executed. For example, if you specify that it_interval is 2 seconds (microseconds are 0), we set it_value to be 2 seconds (microseconds are 0) at the beginning. After 1 second, it_value will be reduced by 1 to be 1. After 1 second, it_value will be reduced by 1 to be 0. And the system automatically reset the time of it_value to the value of it_interval, that is, 2 seconds, and count again

Here's a simple example of setitimer:


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>

void test_func()
{
  static count = 0;

  printf("count is %d\n", count++);
}

void init_sigaction()
{
  struct sigaction act;
     
  act.sa_handler = test_func; // Sets the function that processes the signal 
  act.sa_flags = 0;

  sigemptyset(&act.sa_mask);
  sigaction(SIGPROF, &act, NULL);// Time to send SIGROF signal 
}

void init_time()
{
  struct itimerval val;
     
  val.it_value.tv_sec = 1; //1 Enable the timer in seconds 
  val.it_value.tv_usec = 0;

  val.it_interval = val.it_value; // The timer interval is zero 1s

  setitimer(ITIMER_PROF, &val, NULL);
}

int main(int argc, char **argv)
{

  init_sigaction();
  init_time();

  while(1);

  return 0;
}

It can be seen that each 1 second outputs a value of count:

Here are the results:

[root@localhost 5th]# ./test
count is 0
count is 1
count is 2
count is 3
count is 4
count is 5
count is 6
count is 7
count is 8
count is 9

Appendix:

signal

1. The header files
#include < signal.h >

Function of 2.
Set the corresponding action of a 1 signal

3. Function prototype
void (*signal(int signum,void(* handler)(int)))(int);

Break it down:

typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);

The first parameter is the target signal. The func parameter is a pointer to a function that processes the signal. This processing signal function takes one int parameter and should return void.
The func parameter can also be set to the following values:
SIG_IGN: if the func parameter is set to SIG_IGN, the signal will be ignored.
SIG_DFL: if the func parameter is set to SIG_DFL, the signal is processed as determined.

4. Possible types of sig signals

1) #define SIGHUP 1 /* hangup */

SIGHUP is a very common signal used by Unix system administrators. Many background processes will re-read their configuration files after receiving the signal. However, the actual function of this signal is to notify the process that its control terminal is disconnected. The default behavior is to terminate the process.

2) #define SIGINT 2 /* interrupt */

For Unix users, SIGINT is another commonly used signal. Many combinations of shell's CTRL-C make this signal well known. The official name of the signal is the interrupt signal. The default behavior is to terminate the process.

3) #define SIGQUIT 3 /* quit */

The SIGQUIT signal is used to receive the CTRL-/ combination of shell. In addition, it is used to tell the process to exit. This is a common signal used to tell an application to shut down gracefully. The default behavior is to terminate the process and create a core dump.

4) #define SIGILL 4 /* illegal instr. (not reset when caught) */

If the executing process contains illegal instructions, the operating system will send an SIGILL signal to the process. If your program USES threads, or pointer functions, then try to capture this signal to aid debugging if possible. [color=Red] [color=Red] "If your makes use of threads, or pointer functions, try this in debugging." The middle two are use of use of. I don't know whether it is a typesetting flaw in the original book or whether I really don't understand its meaning. In addition, I often hear of functions pointer, for pointer functions, google 1, it should be the thing inside fortran, anyway, really do not know, please know the exact meaning of the brothers. [/color]) the default behavior is to terminate the process and create a core dump.

5) #define SIGTRAP 5 /* trace trap (not reset when caught) */

SIGTRAP this signal is defined by the POSIX standard for debugging purposes. When the debugged process receives the signal, it means that it has reached a debug breakpoint. Once this signal is delivered, the debugged process will stop and its parent will be notified. The default behavior is to terminate the process and create a core dump.

6) #define SIGABRT 6 /* abort() */

SIGABRT provides a way to create a core dump at the same time as a process terminates abnormally (abort). However, if the signal is captured and the signal processing handle is not returned, the process does not terminate. The default behavior is to terminate the process and create a core dump.

7) #define SIGFPE 8 /* floating point exception */

When a process has a floating point error, the SIGFPE signal is sent to the process. For programs that deal with complex mathematical operations, 1 will recommend you capture the signal. The default behavior is to terminate the process and create a core dump.

8) #define SIGKILL 9 /* kill (cannot be caught or ignored) */

SIGKILL is the most difficult of these signals to deal with. As you can see in the comment next to it, this signal cannot be captured or ignored. Once the signal is delivered to a process, the process terminates. However, there are a few rare cases where SIGKILL does not terminate the process. These rare situations occur when handling one "non-interrupt operation" (such as disk I/O). While this is rare, once it happens, it can cause a process deadlock. The only way to end the process is to restart it. The default behavior is to terminate the process.

9) #define SIGBUS 10 /* bus error */

As its name implies, CPU generates an SIGBUS signal when it detects an error on the data bus. This signal is generated when a program attempts to access an incorrectly aligned memory address. The default behavior is to terminate the process and create a core dump.

10) #define SIGSEGV 11 /* segmentation violation */

SIGSEGV is another C/C++ signal familiar to programmers. When the program does not have access to a protected memory address, or to an invalid virtual memory address (dirty pointer, dirty pointers). About wild pointer, can see http: / / en wikipedia. org/wiki/Wild_pointer explanation. And that will generate this signal. The default behavior is to terminate the process and create a core dump.

11) #define SIGSYS 12 /* non-existent system call invoked */

The SIGSYS signal is delivered when the process makes a nonexistent system call. The operating system delivers the signal and the process is terminated. The default behavior is to terminate the process and create a core dump.

12) #define SIGPIPE 13 /* write on a pipe with no one to read it */

A pipe ACTS like a telephone 1, allowing communication between processes. If the process tries to write to the pipe, and there is no response on the other side of the pipe, the operating system delivers the SIGPIPE signal to the offending process (in this case, the process that intends to write). The default behavior is to terminate the process.

13) #define SIGALRM 14 /* alarm clock */

When the timer of the process expires, the SIGALRM signal is delivered (delivered) to the process. These timers are mentioned later in this chapter
setitimer and alarm call Settings. The default behavior is to terminate the process.

14) #define SIGTERM 15 /* software termination signal from kill */

The SIGTERM signal is sent to the process to notify it that it is time to terminate and to do some cleanup before it terminates. The SIGTERM signal is the default signal sent by Unix's kill command and is also the default signal sent to a process when the operating system is down. The default behavior is to terminate the process.

15) #define SIGURG 16 /* urgent condition on IO channel */

When something happens on a socket that the process has opened, SIGURG is sent to the process. If the process does not capture the signal, it is discarded. The default behavior is to discard this signal.

16) #define SIGSTOP 17 /* sendable stop signal not from tty */

This signal cannot be captured or ignored. Once a process receives an SIGSTOP signal, it immediately stops (stop) until another SIGCONT is received
The signal stops. The default behavior is to stop the process until an SIGCONT signal is received.

17) #define SIGTSTP 18 /* stop signal from tty */

SIGSTP is similar to SIGSTOP except that the SIGSTP signal can be captured or ignored. When shell receives CTRL-Z from the keyboard, it delivers the (deliver) signal to the process. The default behavior is to stop the process until an SIGCONT signal is received.

18) #define SIGCONT 19 /* continue a stopped process */

SIGCONT is also an interesting signal. As mentioned earlier, this signal is used to tell a process to resume running when it stops. The interesting thing about this signal is that it can't be ignored or blocked, but it can be caught. This makes sense: because the process probably doesn't want to ignore or block the SIGCONT signal, what happens if the process receives SIGSTOP or SIGSTP? The default behavior is to discard the signal.

19) #define SIGCHLD 20 /* to parent on child stop or exit */

SIGCHLD was introduced by Berkeley Unix and has a better interface than the implementation on SRV 4 Unix. If the signal is a non-traceability process (not a retroactive process), then BSD's SIGCHID signal implementation is better. In the implementation of system V Unix, if the process requests to capture the signal, the operating system checks to see if there are any unfinished child processes (which are children that have exterminated exit) and is waiting for the parent process calling wait to collect their state. If the child process exits with some termination information attached (terminating information), the signal processing handle is called. So, simply asking for this signal to be captured will cause the signal processing handle to be called, which is a rather messy situation. Once the state of a child of a process changes, the SIGCHLD signal is sent to that process. As I mentioned in the previous section, the parent process can fork out of the child process, but there is no need to wait for the child process to exit. 1 this is generally not a good idea, because then the 1 exit process might become a zombie process. However, if the parent process captures the SIGCHLD signal, it can use one of the wait series calls to collect the child process state, or to determine what is going on. When an SIGSTOP,SIGSTP, or SIGCONF signal is sent to a child process, the SIGCHLD signal is also sent to the parent process. The default behavior is to discard the signal.

20) #define SIGTTIN 21 /* to readers pgrp upon background tty read */

When a background process attempts a read, the SIGTTIN signal is sent to the process. The process will block until it receives the SIGCONT signal. The default behavior is to stop the process until the SIGCONT signal is received.

21) #define SIGTTOU 22 /* like TTIN if (tp- > t_local<OSTOP) */

The SIGTTOU signal is similar to the SIGTTIN signal, except that the SIGTTOU signal is generated when a background process tries to write to an tty with the TOSTOP property set. However, if tty does not set this property, SIGTTOU will not be sent. The default behavior is to stop the process until the SIGCONT signal is received.

22) #define SIGIO 23 /* input/output possible signal */

If the process has an I/O operation on a file descriptor, the SIGIO signal will be sent to the process. The process can be set through the fcntl call. The default behavior is to discard the signal.

23) #define SIGXCPU 24 /* exceeded CPU time limit */

If a process exceeds the CPU limit (CPU limit) that it can use, the SIGXCPU signal is sent to it. This restriction can be set using the setrlimit Settings discussed later. The default behavior is to terminate the process.

24) #define SIGXFSZ 25 /* exceeded file size limit */

If a process exceeds the file size limit it can use, the SIGXFSZ signal is sent to it. We'll talk more about this signal later. The default behavior is to terminate the process.

25) #define SIGVTALRM 26 /* virtual time alarm */

If a process exceeds its virtual timer count, the SIGVTALRM signal is sent to it. The default behavior is to terminate the process.

26) #define SIGPROF 27 /* profiling time alarm */

When a timer is set, SIGPROF is another signal that will be sent to the process. The default behavior is to terminate the process.

27) #define SIGWINCH 28 /* window size changes */

The SIGWINCH signal is sent to the process when it adjusts its terminal rows or columns (such as increasing the size of your xterm). The default behavior is to discard the signal.

28) #define SIGUSR1 29 /* user defined signal 1 */

29) #define SIGUSR2 30 /* user defined signal 2 */

Two signals, SIGUSR1 and SIGUSR2, are designed to be user-specific. They can be programmed to do whatever you need. In other words, the operating system has no behavior associated with the two signals. The default behavior is to terminate the process. There is a contradiction between the two sentences.

Example 5.

5.1. Implementation 1 of Ctrl+C under Linux

Common practices under Linux:


signal(SIGINT, sigfunc); //  Set the signal 
void sigfunc(int signo)
  {
   ... // Processing of signal related operations 
  }

Below is the implementation of Ctrl+C under Windows under Linux


#include <stdio.h>
  #include <windows.h>
  static is_loop = 1;
  //  Capture console  Ctrl+C  Function of an event 
  BOOL CtrlHandler( DWORD fdwCtrlType )
  {
   switch (fdwCtrlType)
   {
   /* Handle the CTRL-C signal. */
   case CTRL_C_EVENT:
     printf("CTRL_C_EVENT \n");
     break;
   case CTRL_CLOSE_EVENT:
     printf("CTRL_CLOSE_EVENT \n");
     break;
   case CTRL_BREAK_EVENT:
     printf("CTRL_BREAK_EVENT \n");
     break;
   case CTRL_LOGOFF_EVENT:
     printf("CTRL_LOGOFF_EVENT \n");
     break;
   case CTRL_SHUTDOWN_EVENT:
     printf("CTRL_SHUTDOWN_EVENT \n");
     break;
   default:
     return FALSE;
   }
   is_loop = 0;
   return (TRUE);
  }

  int main(int argc, char *argv[])
  {
   printf("Set Console Ctrl Handler\n");
   SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
   while (is_loop);
   return 0;
  }

5.2. Implementation 2 of Ctrl+C under Windows


#include <stdio.h>
  #include <windows.h>
  #define CONTRL_C_HANDLE() signal(3, exit)
  int main(int argc, char *argv[])
  {
   printf("Set Console Ctrl Handler\n");
   CONTRL_C_HANDLE();
   while (1);
   system("PAUSE");
   return 0;
  }

Related articles: