• Examples


    本文章摘自下面的网友:

    http://blog.sina.com.cn/s/blog_6e5b342e0100m87d.html

    一、内核中如何记录时间

    任何程序都需要时间控制,其主要目的是:

    • 测量时间流逝和比较时间
    • 知道当前时间
    • 指定时间量的延时操作

    为达到这个目的,应用程序使用日历时间(年月日时分秒)或者自1970年1月1日零时零分零秒到当前的秒数来度量时间的流逝,但内核中需要更加有精度的时间度量,因此内核使用时钟嘀嗒来记录时间。时钟中断发生后内核内部时间计数器增加1(即:增加1个时钟嘀嗒),系统引导时为0,当前值为自上次系统引导以来的时钟滴答数,程序可通过内核定义的全局变量jiffies_64或jiffies来访问。真实硬件上每秒的嘀嗒数从 50 到 1200 不等 ,x86上默认为1000,而s3c2440上默认为200,出于统一编程接口的考虑,内核定义了一个宏HZ,它表示1秒钟的嘀嗒数,可供程序使用。

    Jiffies 和 jiffies_64是 unsigned long 类型只读变量,其用法如下:

    • #include <linux/jiffies.h>
    • unsigned long j, stamp_1, stamp_half, stamp_n;
    • j = jiffies;
    • stamp_1 = j + HZ;
    • stamp_half = j + HZ/2;
    • stamp_n = j + n * HZ / 1000;

    注意:32-位 平台上当 HZ 是 1000 时, 计数器只是每 50 天溢出一次, 必要时你的代码应当准备处理这个事件

    比较2个时间的大小,常用内核的如下宏定义:

    • #include <linux/jiffies.h>
    • int time_after(unsigned long a, unsigned long b);
    • int time_before(unsigned long a, unsigned long b);
    • int time_after_eq(unsigned long a, unsigned long b);
    • int time_before_eq(unsigned long a, unsigned long b);

    它们分别在时间a在时间b之后、之前、之后或相等、之前或相等的时候为真,反之为假

      • 求 2 个 jiffies 实例之间的差:
        • diff = (long)t2 - (long)t1;.
      • 你可以转换一个 jiffies 差为毫秒, 一般地通过:
        • msec = diff * 1000 / HZ;
      • jiffies与日历时间的转换函数:
        • #include <linux/time.h>
        • unsigned long timespec_to_jiffies(struct timespec *value);
        • void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);
        • unsigned long timeval_to_jiffies(struct timeval *value);
        • void jiffies_to_timeval_r(unsigned long jiffies, struct *timeval)

    二、内核定时器

    1、概述

    • 无论何时你需要调度一个动作以后发生, 就可以使用内核定时器,如:当硬件无法发出中断时, 可通过使用内核定时器,以定期的间隔检查一个设备的状态。
    • 一个内核定时器是一个数据结构, 它指导内核在一个用户定义的时间,使用一个用户定义的参数,执行一个用户定义的函数
    • 由内核线程——软中断(ksoftirqd/0)调度执行

        一个cpu,一个ksoftirqd

        ksoftirqd属于atomic context

        ksoftirqd运行时,不禁用irq

    2、定时器 API

    #include <linux/timer.h>
    
    struct timer_list {
        unsigned long expires;
        void (*function)(unsigned long);
        unsigned long data;
        其它字段
    };
      静态初始化定时器结构:
                   struct timer_list timerval = TIMER_INITIALIZER(_function,_expires,_data)
      动态初始化定时器结构:
                   setup_timer(struct timer_list *timer,_function,_data);   //初始化function和data后,调用init_timer
                   init_timer(struct timer_list *timer);
                   timer.expires = jiffies+100;或者:timer.expires = jiffies + HZ/10(手动指定触发时间)
      将已经初始化的定时器加入系统定时器链表:
                  void add_timer(struct timer_list *timer);
                   注:定时器执行后,会自动退出系统定时器链表,如需再次执行,则需要更新expires后,再次加入系统定时器链表
      更新一个定时器的超时时间,同时加入系统链表
                  int mod_timer(struct timer_list *timer,unsigned long expires)
      删除定时器,退出系统定时器链表
                 int del_timer(struct timer_list *timer)
      三、如何在内核中实现延时
        设备驱动常常需要延后一段时间执行一个特定片段的代码, 以便允许硬件完成某个任务。延时一般区分为短延时和长延时
            1、短延时:当一个设备驱动需要等待硬件的反应时间, 涉及到的延时常常是最多几个毫秒 。此种延时就是短延时,一般采用忙等待。(忙等待我个人理解是此时处理器依然在本进程中)
            相关函数如下:
    #include <linux/delay.h>
    void ndelay(unsigned long nsecs);
    void udelay(unsigned long usecs);
    void mdelay(unsigned long msecs);

    2、长延时:

    如果需要延后较长时间,就可以采用长延时。长延时可分为忙等待和让出CPU两种方式。

    1)、忙等待:

      unsigned long j1 = jiffies + 2*HZ;
          while (time_before(jiffies, j1))

          cpu_relax();

      cpu_relex 的调用使用了一个特定于体系的方式,你此时没有用处理器做事情,比较浪费处理器的资源

    2)、让出处理器

      unsigned long j1 = jiffies + 3600*HZ;

      while (time_before(jiffies, j))

      {

        set_current_state(TASK_INTERRUPTIBLE);

        schedule_timeout(30*HZ);

      }

    3)此外,如果你的驱动使用一个等待队列来等待某些其他事件,但是你也想确保它在一个确定时间段内运行能够运行,而不是永久等待,那么可以使用超时

      

    #include <linux/wait.h>
    long wait_event_timeout(wait_queue_head_t q, condition, long timeout);
    long wait_event_interruptible_timeout(wait_queue_head_t q, condition, long timeout);
    #include <linux/module.h>
    #include <linux/timer.h>
    #include <linux/jiffies.h>
     
    struct timer_list mytimer;
     
    static void myfunc(unsigned long data)
    {
            printk("%s/n", (char *)data);
            mod_timer(&mytimer, jiffies + 2*HZ);
    }
     
    static int __init mytimer_init(void)
    {
            setup_timer(&mytimer, myfunc, (unsigned long)"Hello, world!");
            mytimer.expires = jiffies + HZ;
            add_timer(&mytimer);
     
            return 0;
    }
     
    static void __exit mytimer_exit(void)
    {
            del_timer(&mytimer);
    }
     
    module_init(mytimer_init);
    module_exit(mytimer_exit);

    后面会编写一个linux模块化驱动编程的实例和延时。

  • 相关阅读:
    零零碎碎
    MFC入门--显示静态图片及调用本地软件
    Python版本OpenCV安装配置及简单实例
    用星星画菱形--Java
    pycharm IDE在导入自定义模块时提示有错,但实际没错
    Cmd使用方式--命令行运行程序
    cv2 & PIL(pillow)显示图像
    C++命令行多文件编译(g++)
    MNIST多图显示--Python练习
    visual studio 2017--括号自动补全
  • 原文地址:https://www.cnblogs.com/hjj801006/p/4551824.html
Copyright © 2020-2023  润新知