• Linux定时器


    setitimer函数实现定时器

    分析:实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理函数来处理产生的定时信号,从而实现定时器。

    itimerval结构体

    先看itimerval的结构体

    struct itimerval
    {
           /* Value to put into `it_value' when the timer expires. */
           //启动定时器后,每次触发定时器周期时间值
           struct timeval it_interval;
           /* Time to the next timer expiration. */
           //程序跑到这之后,多久启动定时器
           struct timeval it_value;
    };
    
    struct timeval
    {
           __time_t tv_sec; 		/* Seconds. */
           __suseconds_t tv_usec; 	/* Microseconds. */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    setitimer函数设置定时方式

    setitimer()将value指向的结构体设为计时器的当前值,如果第三个参数oldvalue不是NULL(一般都设为NULL),将返回计时器原有值。

    int setitimer (__itimer_which_t __which,
                   const struct itimerval *__restrict __new,
                   struct itimerval *__restrict __old)
    
    • 1
    • 2
    • 3

    which:三种类型

    • ITIMER_REAL //内核宏定义为0,计时器的值实时递减,减完则发送信号SIGALRM(常用)。
    • ITIMER_VIRTUAL //内核宏定义为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM(了解)。
    • ITIMER_PROF //内核宏定义为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF(了解)。

    settimer工作机制

    • 先对it_value倒计时,当it_value为零时触发信号。然后重置为it_interval。继续对it_value倒计时。一直这样循环下去。

    • 基于此机制。setitimer既能够用来延时运行,也可定时运行。

    • 如果it_value为0,不会触发信号,所以要能触发信号,it_value得大于0;

    • 如果it_interval为0,仅仅会延时。不会定时(也就是说仅仅会触发一次信号)。

    • old_value參数,通经常使用不上。设置为NULL,它是用来存储上一次setitimer调用时设置的new_value值。

    signal信号处理

    这边需要捕获对应的信号并进行逻辑处理 ,这个就像单片机的中断处理函数。

    signal(SIGALRM,signal_handler);
    
    返回说明:
    成功执行时,返回0。失败返回-1
    
    • 1
    • 2
    • 3
    • 4

    实现代码(1s后开启定时器,然后每隔1s打印hello)

    /*该代码实现的功能是: 1s后开启定时器,然后每隔1s向终端打印hello*/
    #include 
    #include 
    #include 
    #include 
    static int i;
    
    void signal_handler(int signum)
    {
          i++;
          if(i == 2000){//0.5ms溢出一次,一共溢出了2000次,就是1s
              printf("hello\n");
              i = 0;
          }
    }
    
    int main()
    {
          struct itimerval itv;
          //1、设定定时时间:0.5ms
          itv.it_interval.tv_sec = 0;
          itv.it_interval.tv_usec = 500;
          //2、多久启动定时器:1s后启动
          itv.it_value.tv_sec = 1;
          itv.it_value.tv_usec = 0;
          //3、设定定时方式
          if( -1 == setitimer(ITIMER_REAL, &itv, NULL)){
                perror("error");
                exit(-1);
          }
          //4、信号处理
          signal(SIGALRM,signal_handler);
          while(1);
          return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述

    timer_create函数实现定时器(可以传参,可以删除定时器)

    参考:linux定时器

    timer_create创建定时器函数(Sigevent结构体)

    int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
    
    • 1

    成功创建返回0,失败返回-1,并更新错误码。参数中timerid 是创建成功的timer id,另两个参数则相对复杂:

    参数 clock_id :(可选值如下)

    CLOCK_REALTIME: 			系统保存的时间,比如当前是1010分,我们起了一个10min的定时器,5min后,我们将系统时间修改成1010分,定时器还会再过10min到时。
    CLOCK_MONOTONIC:			Timer严格按照设定的时间定时,无法通过修改时间改变;
    CLOCK_PROCESS_CPUTIME_ID:	计时器只记录当前进程所实际花费的时间;比如当前进程只能获得50%的 CPU 时间,为了让进程真正地运行 10 分钟,到1030 分Timer才 到期。
    CLOCK_THREAD_CPUTIME_ID:	以线程为计时实体,当前进程中的某个线程真正地运行了一定时间才触发 Timer
    
    • 1
    • 2
    • 3
    • 4

    Sigevent结构体

    struct sigevent {
            int sigev_notify;                    
            int sigev_signo;            
            union sigval sigev_value;     
            void (*sigev_notify_function) (union sigval); 
            void *sigev_notify_attributes;          
            pid_t sigev_notify_thread_id;  
     };
    SIGEV_NONE: 	到期时不产生通知;
    SIGEV_SIGNAL:	到期时将给进程投递一个信号sigev_signo可以用来指定使用什么信号;
    SIGEV_THREAD:	定时器到期时将启动新的线程进行处理,此种情况下需要设置 sigev_notify_function。
    				当 Timer 到期时,将使用该函数作为入口启动一个线程来处理信号;sigev_value保存了传入 sigev_notify_funct的参数。
    sigev_notify_attributes:
    				如果非空,则应该是一个指向 pthread_attr_t 的指针,用来设置线程的属性
    				(比如 stack 大小,detach 状态等);
    SIGEV_THREAD_ID:到期时将向指定线程发送信号,通常和 SIGEV_SIGNAL 联合使用,这样当 
    				Timer 到期时,系统会向由 sigev_notify_thread_id 指定的线程发
    				送信号,否则可能进程中的任意线程都可能收到该信号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    timer_settime启动定时器函数(itimespec结构体)

    itimespec结构体

    int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value,
                      struct itimerspec * old_value);
     
    struct itimespec{
        struct timespec it_interval;
        struct timespec it_value;  
    };
    
    struct timespec{
        time_t tv_sec;    	//s
        long tv_nsec;    	//ns
    };
    
    参数 timerid: 是调用timer_create函数成功创建的timerid
    参数 flags: flags取值只有2: 0 和 TIMER_ABSTIME。
    	当 flags 为 0, new_value->it_value 表示希望timer首次到期时的时间与启动timer的时间间隔(例如,希望timer在2秒后到期);
    	当flags为 TIMER_ABSTIME 时, new_value->it_value 表示希望timer首次到期的绝对时间(例如希望timer在01:23:45到期);
    	如果new_value->it_value设定的绝对时间早于当前的绝对时间, 那么timer会立即到期;
    	如果时钟 CLOCK_REALTIME 被调整了,那么timer的首次过期时间也会适当调整。
    参数 new_value : 配置定时器的时间参数
    参数 * old_value : 获取上一次配置的定时器参数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    timer_delete删除定时器函数

    int timer_delete (timer_t timerid);
    
    • 1

    实现代码()

    gcc 1.c -lrt//编译链接库   实时库(real time):shm_open系列
    
    • 1
    #include 
    #include 
    #include 
    #include 
    
    
    void tm_timeout(union sigval value)
    {										//传递参数
    	printf("create time sifval:%X\n", value.sival_int);
    }
    
    void timeout1(int sig)
    {
    	time_t times=0;			 //获取到当前时间
    	printf("tick time:%ld\n", time(&times) );	
    }
    
    
    void create_time_by_thread(void)
    {
    	static timer_t times;	//time id
    	
    	struct itimerspec ts;	//用于配置定时器时间  
    	struct sigevent evp;
    	
    	evp.sigev_notify            = SIGEV_THREAD;	//定时器到期时将启动新的线程进行处理
        evp.sigev_notify_function   = tm_timeout;	//线程函数地址
    	evp.sigev_value.sival_int   = 0xFEDC;		//传递参数给tm_timeout函数
        evp.sigev_notify_attributes = NULL;
    	
    	timer_create(CLOCK_REALTIME, &evp, &times);
    	
    	if(1)
    	{
    		//第一次调用后,每次调用间隔时间,若不需要周期性启动定时器可将下面两个参数设置值为0
    		ts.it_interval.tv_sec = 3;
    		ts.it_interval.tv_nsec = 0;		
    		//第一次调用时间,即程序跑到这多久后启动定时器
    		ts.it_value.tv_sec =7;
    		ts.it_value.tv_nsec = 0;	
    		
    		if(0 != timer_settime(times, 0, &ts, NULL))	//3s调用一次tm_timeout
    		{
    			printf("create timer to start failed\n");
    		}
    	}
    	else
    	{
    		struct timespec   now;										//获取linux系统时间 
    		clock_gettime(CLOCK_REALTIME, &now);
    		ts.it_value.tv_sec =now.tv_sec + 7;
    		ts.it_value.tv_nsec = now.tv_nsec + 0;	
    		
    		if(0 != timer_settime(times, TIMER_ABSTIME, &ts, NULL))		//绝对时间启动定时器
    		{
    			printf("create timer to start failed\n");
    		}
    	}
    }
    
    void create_timer_by_signal(void)
    {
    	static timer_t times;
    	
    	struct itimerspec 	ts;	//用于配置定时器时间
        struct timespec   	now;//获取linux系统时间
    	struct sigevent 	evp;
    	
    	evp.sigev_notify            = SIGEV_SIGNAL;
    	evp.sigev_signo 			= SIGUSR1;
    	evp.sigev_value.sival_ptr	= ×
        evp.sigev_notify_attributes = NULL;
    	timer_create(CLOCK_REALTIME, &evp, &times);
    	
    	/*第一次调用后,每次调用间隔时间*/
    	ts.it_interval.tv_sec = 3;
    	ts.it_interval.tv_nsec = 0;	
    	/*第一次调用时间,即程序跑到这多久后启动定时器*/
    	ts.it_value.tv_sec =5;
    	ts.it_value.tv_nsec = 0;	
    	
    	signal(SIGUSR1, timeout1);
    	
    	timer_settime(times, 0, &ts, NULL);	
    }
    
    int main(int argc, char *argv[])
    {	
    	create_time_by_thread();
    	create_timer_by_signal();	
    	while(1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    在这里插入图片描述

  • 相关阅读:
    about linux的一部分学习笔记
    牛客网《剑指offer》专栏刷题练习之掌握动态规划思想
    Embind进阶用法(vector)
    【服务器存储数据恢复】HP-Lefthand存储数据恢复案例
    SpringIOC之support模块SimpleThreadScope
    【SSL 1535】小罐跳舞(构造)
    android Handler
    虚假姓名生成器
    【坑】Spring Boot整合MyBatis,一级缓存失效
    vue 做一个树图
  • 原文地址:https://blog.csdn.net/zhuguanlin121/article/details/127836840