• 《信息安全系统设计与实现》学习笔记8


    一、学习笔记

    1.硬件定时器

          使用一个倒计时值对计数器进行编程,每个时钟信号减1。当计数减为0时,计数器向CPU生成一个定时器中断,将计数值重新加载到计数器中,并重复倒计时。计数器周期称为定时器刻度,是系统的基本计时单元。

    2.个人计算机定时器

    基于Intel x86的个人计算机有数个定时器:

    • 实时时钟(RTC)
    • 可编程间隔定时器(PIT)
    • 多核CPU中的本地定时器
    • 高分辨率定时器

    3.CPU操作

    每个CPU都有:

    • 一个程序计数器(PC),也称为指令指针(IP)
    • 一个标志或状态寄存器(SR)
    • 一个堆栈指针(SP)
    • 几个通用寄存器

          当PC指向内存中要执行的下一条指令时,SR包含CPU的当前状态,SP指向当前堆栈栈顶。堆栈是CPU用于特殊操作(如push、pop调用和返回等)的一个内存区域。

          由于无效地址、非法指令、越权等原因,可能会出现一个错误状态,称为异常或陷阱。当CPU遇到异常时,它会根据内存中预先安装的指针来执行软件中的异常处理程序。在每条指令执行结束时,CPU会检查挂起的中断。中断是I/O设备或协处理器发送 给CPU的外部信号,请求CPU服务。如果有挂起的中断请求,但是CPU未处于接受中断 的状态,即它的状态寄存器已经屏蔽了中断,CPU会忽略中断请求,继续执行下一条指令。 否则,它将直接执行中断处理。在中断处理结束时,它将恢复指令的正常执行。

          中断处理和异常处理都在操作系统内核中进行。在大多数情况下,用户级程序无法访问它们。

    4.中断处理

          外部设备(如定时器)的中断被馈送到中断控制器的预定义输入行,按优先级对中断输入排序,并将具有最高优先级的中断作为中断请求(IRQ)路由到CPU。在每条指令执行结束时,如果CPU未处于接受中断的状态,即在CPU的状态寄存器中屏蔽了中断.它将忽略中断请求,使其处于挂起状态,并继续执行下一条指令。如果CPU处于接受中断状态,即中断未被屏蔽,那么CPU将会转移它正常的执行顺序来进行中断处理。对于每个中断,可以编程中断控制器以生成一个唯一编号,叫作中断向量,标识中断源。在获取中断向量号后,CPU用它作为内存中中断向量表中的条目索引,条目包含一个指向中断处理程序入口地址的指针来实际处理中断。当中断处理结束时,CPU恢复指令的正常执行。

    5.时钟服务函数

    (1)gettimeofday-settimeofday


          gettimeofday()函数用于返回当前时间(当前秒的秒和微秒)。settimeofday()函数用于设置当前时间。在Unix/Linux中,时间表示自1970年1月1日00:00:00起经过的秒数。它可以通过库函数ctime(&time)转换为日历形式。

    (2)time系统调用


          以秒为单位返回当前时间。如果参数t不是NULL,还会将时间存储在t指向的内存中。 time系统调用具有一定的局限性,只提供以秒为单位的分辨率,而不是以微秒为单位。

    (3)times系统调用


          可用于获取某进程的具体执行时间。它将进程时间存储在struct tms buf中

    (4)time和data命令

    • date:打印或设置系统日期和时间。
    • time:报告进程在用户模式和系统模式下的执行时间和总时间。
    • hwclock:查询并设置硬件时钟(RTC),也可以通过BIOS来完成。

    6.间隔定时器

          Linux为每个进程提供了三种不同类型的间隔计时器,可用作进程计时的虚拟时钟。间隔定时器由setitimer()系统调用创建。getitimer()系统调用返回间隔定时器的状态。

          各间隔定时器在参数which指定的不同时间域中工作。当间隔定时器定时到期时,会向进程发送一个信号,并将定时器重置为指定的间隔值(如果是非零数)。一个信号就是发送给某个进程进行处理的一个数字(1到31)。有3类间隔定时器,分别是:

    • ITIMER_REAL:实时减少,在到期时生成一个SIGALRM(14)信号。
    • IT1MER_V1RTUAL;仅当进程在用户模式下执行时减少,在到期时生成一个 SIGVTALRM(26)信号。
    • ITIMER_PROF:当进程正在用户模式和系统(内核)模式下执行时减少。这类间隔 定时器与IT1MER_VIRTUAL结合使用,通常用于分析应用程序在用户模式和内核模式下花费的时间。它在到期时生成一个SIGPROF(27)信号。

    7.定时器中断

          整个基本系统在一个虚拟CPU上运行,它是一个Linux进程。定时器向Linux进程发 出的信号可看作是对基本系统虚拟CPU的中断。

    8.临界区

          在基本代码系统中,只有一种执行实体,即任务,一次只执行一个任务。某任务在收 到切换命令、进入休眠或退出之前,会一直执行下去。此外.任务切换只会发生在操作结束时,而不会发生在任何操作过程中。因此,任务之间没有竞争,因此在基本代码系统中没有临界区.

    二、问题及解决办法

    1.我想知道Linux有哪些定时器?它们的不同与用法,于是查了资料

    sleep,usleep和nanosleep
          sleep()和nanosleep()都是使进程睡眠一段时间后被唤醒,但是二者的实现完全不同。Linux中并没有提供系统调用sleep(),sleep()是在库函数中实现的,它是通过调用alarm()来设定报警时间,调用sigsuspend()将进程挂起在信号SIGALARM上,sleep()只能精确到秒级上。nanosleep()则是Linux中的系统调用,它是使用定时器来实现的,该调用使调用进程睡眠,并往定时器队列上加入一个timer_list型定时器,time_list结构里包括唤醒时间以及唤醒后执行的函数,通过nanosleep()加入的定时器的执行函数仅仅完成唤醒当前进程的功能。系统通过一定的机制定时检查这些队列(比如通过系统调用陷入核心后,从核心返回用户态前,要检查当前进程的时间片是否已经耗尽,如果是则调用schedule()函数重新调度,该函数中就会检查定时器队列,另外慢中断返回前也会做此检查),如果定时时间已超过,则执行定时器指定的函数唤醒调用进程。当然,由于系统时间片可能丢失,所以nanosleep()精度也不是很高。

    信号量SIGALRM + alarm()
          alarm方式虽然很好,但这种方式的精度能达到1秒,是无法低于1秒的精度。其中利用了*nix系统的信号量机制,首先注册信号量SIGALRM处理函数,调用alarm(),设置定时长度

    RTC机制
          RTC机制利用系统硬件提供的Real Time Clock机制,通过读取RTC硬件/dev/rtc,通过ioctl()设置RTC频率,这种方式比较方便,利用了系统硬件提供的RTC,精度可调,而且非常高.该种方式要求系统有RTC设备

    select()
          能精确到1us,目前精确定时的最流行方案。通过使用select(),来设置定时器;原理利用select()方法的第5个参数,第一个参数设置为0,三个文件描述符集都设置为NULL,第5个参数为时间结构体

    2.运行书上代码

    (1)setitimer

    #include <signal.h>
    #include <stdio.h>
    #include <sys/time.h>
    
    int count = 0;
    struct itimerval t;
    
    void timer_handler(int sig)
    {
        printf("timer_handler: signal=%d count=%d
    ", sig, ++count);
        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;
        timer.it_interval.tv_sec = 1;
        timer.it_interval.tv_usec = 0;
        setitimer(ITIMER_VIRTUAL,&timer, NULL);
        printf("looping:enter Control-C to Terminate
    ");
        while(1);
    }
    

    运行结果

    (2)gettimeofday

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    
    struct timeval t;
    
    int main()
    {
        gettimeofday(&t, NULL);
        printf("sec=%ld usec=%d
    ", t.tv_sec, t.tv_usec);
        printf((char *)ctime(&t.tv_sec));
    }
    

    运行结果出错了

    上网查了原因 https://blog.csdn.net/weixin_40877924/article/details/108762118
    也可直接在源代码上加头文件time.h
    然后我换了一个代码

    #include <sys/time.h>
    #include <stdio.h>
    #include <unistd.h>
    
    void foo()
    {
            int i = 0;
            for (; i <= 10000; i++);
    }
    
    int main(int argc, char *argv[])
    {
            struct timeval start;
            struct timeval end;
            float timeuse;
            gettimeofday(&start, NULL);
            printf("start time is %d:%d
    ", start.tv_sec, start.tv_usec);
            foo();
            gettimeofday(&end, NULL);
            printf("end time is %d:%d
    ", end.tv_sec, end.tv_usec);
            timeuse = 1000000 * (end.tv_sec - start.tv_sec) + ( end.tv_usec - start.tv_usec );
            printf("timeuse1 is %f 
    ", timeuse);
            timeuse /= 1000000;
            printf("timeuse2 is %f 
    ", timeuse);
            printf("use time is %f
    ", timeuse);
            return 0;
    }
    

    运动结果

    三、学习感悟

          在自学的过程中,经常会遇到很多问题,甚至是运行和书上一模一样的代码,都会出现问题,这时就要及时查资料,寻找解决办法。但是运行代码不是出结果了就结束了,还要尽量看懂它,这样才会有所收获。

  • 相关阅读:
    了解自我
    IT技能栈
    客户端的工作不仅仅只是看起来那么简单
    .NET 基础 一步步 一幕幕[XML基础操作]
    .NET 基础 一步步 一幕幕[Winform应用程序]
    .NET 基础 一步步 一幕幕[面向对象之堆、栈、引用类型、值类型]
    .NET 基础 一步步 一幕幕[面向对象之new、this关键字]
    .NET 基础 一步步 一幕幕[面向对象之静态、非静态]
    .NET 基础 一步步 一幕幕[面向对象之方法、方法的重载、方法的重写、方法的递归]
    .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]
  • 原文地址:https://www.cnblogs.com/ffffatal/p/15510559.html
Copyright © 2020-2023  润新知