zoukankan      html  css  js  c++  java
  • 第五章学习笔记

    读书笔记

    本章讨论了定时器和定时器服务﹔介绍了硬件定时器的原理和基于Intel x86的PC中的硬件定时器;讲解了CPU操作和中断处理;描述了Linux中与定时器相关的系统调用、库函数和定时器服务命令;探讨了进程间隔定时器、定时器生成的信号,并通过示例演示了进程间隔定时器。编程项目的目的是要在一个多任务处理系统中实现定时器、定时器中断和间隔定时器。多任务处理系统作为一个Linux进程运行,该系统是 Linux进程内并发任务的一个虚拟CPU。Linux进程的实时模式间隔定时器被设计为定期生成SIGALRM信号,充当虚拟CPU的定时器中断,虚拟CPU使用SIGALRM信号捕捉器作为定时器的中断处理程序。该项目可让读进程通过定时器队列实现任务间隔定时器,还可让读进程使用Linux信号掩码来实现临界区,以防止各项任务和中断处理程序之间出现竞态条件。

    知识点总结

    定时器

    • 硬件定时器
      定时器是由时钟源和可编程计数器组成的硬件设备。时钟源通常是一个晶体振荡器,会产生周期性电信号,以精确的频率驱动计数器。使用一个倒计时值对计数器进行编程,每个时钟信号减1。当计数减为0时,计数器向CPU生成一个定时器中断,将计数值重新加载到计数器中,并重复倒计时。计数器周期称为定时器刻度,是系统的基本计时单元。
    • 个人计算机定时器
      基于Intel x86的个人计算机有数个定时器(Bovet和 Cesati 2005).
      (1)实时时钟(RTC):RTC由一个小型备用电池供电。即使在个人计算机关机时,它也能连续运行。它用于实时提供时间和日期信息。当Linux启动时,它使用RTC更新系统时间变量,以与当前时间保持一致。在所有类Unix系统中,时间变量是一个长整数,包含从1970年1月1日起经过的秒数。
      (2)可编程间隔定时器(PIT)(Wang2015):PIT是与CPU分离的一个硬件定时器。可对它进行编程,以提供以毫秒为单位的定时器刻度。在所有IO设备中,PIT可以最高优先级IRQ0中断。PIT定时器中断由Linux内核的定时器中断处理程序来处理,为系统操作提供基本的定时单元,例如进程调度、进程间隔定时器和其他许多定时事件。
      (3)多核CPU中的本地定时器(Intel1997;Wang2015):在多核CPU中,每个核都是一个独立的处理器,它有自已的本地定时器,由 CPU时钟驱动。
      (4)高分辨率定时器;大多数电脑都有一个时间戳定时器(TSC),由系统时钟驱动。它的内容可通过64位TSC寄存器读取。由于不同系统主板的时钟频率可能不同,TSC不适合作为实时设备,但它可提供纳秒级的定时器分辨率。一些高端个人计算机可能还配备有专用高速定时器,以提供纳秒级定时器分辨率。

    CPU操作

    每个CPU都有一个程序计数器(PC),也称为指令指针(IP),以及一个标志或状态寄存器(SR)、一个堆栈指针(SP)和几个通用寄存器,当PC指向内存中要执行的下一条指令时,SR包含CPU的当前状态,如操作模式、中断掩码和条件码,SP指向当前堆栈栈顶。堆栈是CPU用于特殊操作(如 push、pop调用和返回等)的一个内存区域。CPU操作可通过无限循环进行建模。

    时钟服务函数

    在linux下,常用的获取时间的函数有如下几个:
       asctime, ctime, gmtime, localtime, gettimeofday ,
      mktime, asctime_r, ctime_r, gmtime_r, localtime_r
    1)time() 函数获取当前时间

      SYNOPSIS
             #include <time.h>
     
            time_t time(time_t *t);
      
      DESCRIPTION
            time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00+0000 
            (UTC).8    //此函数会返回从公元1970年1月1日的UTC时间从0时0分0秒算起到现在所经过的秒数。如果t 并非空指针的话,此函数也会将返回值存到t指针所指的内存。
      RETURN VALUE
            On  success,  the value of time in seconds since the Epoch is returned.  On error, ((time_t) -1) is returned, and errno is
            set appropriately.
     ERRORS
            EFAULT t points outside your accessible address space.
         //成功返回秒数,错误则返回(time_t) -1),错误原因存于errno中
    

    (2)localtime_r() localtime()取得当地目前时间和日期
    函数原型如下:

     #include <time.h>
           
         struct tm *localtime(const time_t *timep);
         struct tm *localtime_r(const time_t *timep, struct tm *result);
             
     /*该函数将有time函数获取的值timep转换真实世界所使用的时间日期表示方法,然后将结果由结构tm返回*/
     
     /**需要注意的是localtime函数可以将时间转换本地时间,但是localtime函数不是线程安全的。
    多线程应用里面,应该用localtime_r函数替代localtime函数,因为localtime_r是线程安全的**/
    

    (3)asctime() asctime_r() 将时间和日期以字符串格式返回‘
    函数原型如下:

      #include <time.h>
             
          struct tm *gmtime(const time_t *timep);
          struct tm *gmtime_r(const time_t *timep, struct tm *result);
             
          char *asctime(const struct tm *tm);
          char *asctime_r(const struct tm *tm, char *buf);
             
             
     /**gmtime是把日期和时间转换为格林威治(GMT)时间的函数。将参数time 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回**/
            
     /**asctime 将时间以换为字符串字符串格式返回 **/
    

    (4) ctime(),ctime_r() 将时间和日期以字符串格式表示
    函数原型如下:

     #include <time.h>
            
           char *ctime(const time_t *timep);
           char *ctime_r(const time_t *timep, char *buf);
            
     /**ctime()将参数timep所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果以字符串形态返回**/
    

    (5)mktime() 将时间结构体struct tm的值转化为经过的秒数
    函数原型:

     #include <time.h>
            
         time_t mktime(struct tm *tm);
            /**将时间结构体struct tm的值转化为经过的秒数**/
    

    (6)gettimeofday() 获取当前时间
    函数原型如下:

      #include <sys/time.h>
      
          int gettimeofday(struct timeval *tv, struct timezone *tz);
          
      struct timeval {
                     time_t      tv_sec;     /* seconds (秒)*/
                     suseconds_t tv_usec;    /* microseconds(微秒) */
                 };
      struct timezone {
                    int tz_minuteswest;     /* minutes west of Greenwich */
                    int tz_dsttime;         /* type of DST correction */1          };
     //gettimeofday函数获取当前时间存于tv结构体中,相应的时区信息则存于tz结构体中
     //需要注意的是tz是依赖于系统,不同的系统可能存在获取不到的可能,因此通常设置为NULL   
    

    临界区

    在基本代码系统中,只有一种执行实体,即任务,一次只执行一个任务。某任务在收到切换命令、进入休眠或退出之前,会一直执行下去。此外,任务切换只会发生在操作结束时,而不会发生在任何操作过程中。因此,任务之间没有竞争,因此在基本代码系统中没有临界区。但是,一旦我们将中断引人系统,情况就会改变。有两种类型的实体来执行中断,分别是任务和中断处理程序,它们可能会争夺系统中的同一(共享)数据对象。例如,当某任务请求间隔定时器时,必须将请求作为定时器队列元素输入timerQueue中。当某任务修改timerQueue 时,如果出现定时器中断,它将转移任务以执行中断处理程序,可能会改动同一 timerQueue,造成竞态条件。因此,timerQueue是临界区,必须对它进行保护,以确保它一次只能由一个执行实体访问。同样,当某进程在sleep()函数过程中执行时,可能被转移到执行中断处理程序,即可执行wakeup(),以试图在进程完成休眠操作之前唤醒它,从而导致另一个竟态条件。所以,问题是如何防止任务和中断处理程序相互干扰。

    实践与代码

    time函数,可以获取当前时间

    #include <stdio.h>
    #include <string.h>
    #include <time.h>
    
    int main()
    {
        time_t seconds;
    
        seconds = time((time_t *)NULL);
        printf("%d
    ", seconds);
    
        return 0;
    }
    

    gettimeofday() 获取当前时间:

    #include <stdio.h>
    #include <string.h>
    #include <sys/time.h>
    
    int main()
    {
        struct timeval tv;
    
        gettimeofday(&tv, NULL);
    
        printf("tv_sec: %d
    ", tv.tv_sec);
        printf("tv_usec: %d
    ", tv.tv_usec);
    
        return 0;
    }
    

    测试代码:每间隔1s,倒计时100毫秒触发handler

    #include <signal.h>
    #include <stdio.h>
    #include <sys/time.h>
    #include <time.h>
    int count = 0;
    struct itimerval t;
    time_t start,end ;
    void timer_handler(int sig){
          end =time(NULL);
          printf("timer_handler :  signal %d   count=%d  , diff: %ld 
    ",sig, ++count,end -start);
          start =  end;
          if( count >= 8){
              printf("cancel timer 
    ");
              t.it_value.tv_sec  =  0 ;
              t.it_value.tv_usec    =    0;
              setitimer(ITIMER_VIRTUAL, &t , NULL);
          }
    }
     
    int  main(){
          struct itimerval timer ;
          signal (SIGVTALRM ,timer_handler);
          timer.it_value.tv_sec =  0;
          timer.it_value.tv_usec  = 100000;
          //every 1s afterward
          timer.it_interval.tv_sec = 1;
          timer.it_interval.tv_usec = 0;
          // start a virtual itimer
          start = time(NULL);
          setitimer( ITIMER_VIRTUAL , &timer ,NULL );
          printf("press Ctrl + C  to terminate 
    ");
          while(1);
    }
    

    问题和解决思路

    运行time.c时编译报错,看起来是%d的问题

    错误分析:%d需要另一个类型的整数,应该是长整型或无符号
    粘贴错误指令搜索,发现了解决办法☞
    %md,m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出。
    %ld(%mld 也可),输出长整型数据。
    u格式符,用来输出unsigned型数据,无符号数,以十进制数形式输出。格式:%u,%mu,%lu都可

  • 相关阅读:
    loj#6433. 「PKUSC2018」最大前缀和(状压dp)
    PKUWC2019游记
    10. Regular Expression Matching
    9. Palindrome Number
    8. String to Integer (atoi)
    7. Reverse Integer
    6. ZigZag Conversion
    5. Longest Palindromic Substring
    4. Median of Two Sorted Arrays
    3. Longest Substring Without Repeating Characters
  • 原文地址:https://www.cnblogs.com/20191211yss/p/15499141.html
Copyright © 2011-2022 走看看