精华内容
下载资源
问答
  • linux内核时间管理(一) : 时间概念和延迟操作

    内核中的时间概念

    HZ:
    Linux 核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来定义每一秒有几次timer interrupts。举例来说,HZ为1000,代表每秒有1000次timer interrupts

    jiffies:
    全局变量jiffies用于记录系统启动以来产生的节拍的总数。
    启动时,jiffies初始化为0,此后每次时钟中断处理程序都会增加该变量的值。
    linux提供了4个宏来比较节拍计数

    #include <linux/jiffies.h> 
    #define time_after(a, b)       // b >  a
    #define time_before(a, b)      // b <  a
    #define time_after_eq(a, b)    // b >= a
    #define time_before_eq(a, b)   // b <= a

    时间获取

    驱动程序中一般不需要知道墙钟时间(也就是年月日的时间)。但驱动可能需要处理绝对时间。
    为此,内核提供了两个结构体,都定义在

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

    (2)采用秒和纳秒值保存时间。

    struct timespec { 
          time_t  tv_sec; /* seconds */ 
          long tv_nsec; /* nanoseconds */ 
     }; 

    (3)用do_gettimeofday()用于获得timeval

    #include <linux/time.h> 
    void do_gettimeofday(struct timeval *tv); 

    (4) current_kernel_time() 用于获得timespec

    #include <linux/time.h> 
    struct timespec current_kernel_time(void);

    延迟操作

    1.长延迟
    (1)忙等待
    如果对延迟的精确度要求不高,最简单的方法是实现一个监视jiffies计时器的循环。

    unsigned long delay = jiffies + 5*HZ;
    while(time_before(jiffies, delay))
          cpu_relax();

    (2)超时

    #include <linux/sched.h> 
    signed long schedule_timeout(signed long timeout); 

    timeout是用jiffies表示的延迟时间,正常值返回0.
    schedule_timeout在使用前需要设置当前进程状态。

    set_current_state(TASK_INTERRUPTIBLE); 
    schedule_timeout(2*HZ); /* 睡2秒 */  

    进程经过2秒后会被唤醒。
    第一行调用set_current_state已设置当前进程状态,调度器只有在超时到期且其状态为TASK_RUNNING时才会运行这个进程。如果不希望被用户空间打断,可以将进程状态设置为TASK_UNINTERRUPTIBLE。

    (3)让出处理器

    while(time_before(jiffies, j1)) 
          schedule(); 

    在等待期间可以让出处理器,减少CPU的负担。

    2.短延迟
    对于那些最多几十个毫秒的延迟,不需要依赖时间滴答

    #include <Linux/delay.h>  
    void ndelay(unsigned long nsecs); /*延迟纳秒 */ 
    void udelay(unsigned long usecs); /*延迟微秒 */ 
    void mdelay(unsigned long msecs); /*延迟毫秒 */ 

    这三个延迟函数均是忙等待函数,在延迟过程中无法运行其他任务。 它们的实现使用了软件循环。

    实现毫秒级(或者更长)延迟还有一种方法,这种方法不涉及忙等待

    #include <Linux/delay.h>  
    void msleep(unsigned int millisecs); /*休眠毫秒 */ 
    
    void ssleep(unsigned int seconds); /*休眠秒 */ 
    unsigned long msleep_interruptible(unsigned int millisecs);/*休眠毫秒,中断可以唤醒*/ 
    展开全文
  • 内核时间子系统的配置

    千次阅读 2016-06-02 13:26:30
    内核中的时间子系统主要向上层提供两种功能,一个就是时间功能,另一个就是timer定时器功能。时间功能: 时间功能是由内核中的timekeeper模块来负责维护,该模块需要clocksource的支持。内核中的模块划分为如下: ...

    内核中的时间子系统主要向上层提供两种功能,一个就是时间功能,另一个就是timer定时器功能。

    时间功能:
    时间功能是由内核中的timekeeper模块来负责维护,该模块需要clocksource的支持。内核中的模块划分为如下:
    Timekeeper—>clocksource—>hardware

    定时器功能:
    从原理上说,定时器需要有一个定期到来的tick来驱动它运行,每个tick到来时检查一下定时器是否到时,由此来实现定时器的功能。所以定时器的实现可以分为tick device设备和基于tick device的上层timer定时器实现。在内核中的模块划分如下所示:
    timer—>tick device—>clockevent—>hardware

    基于tick device的timer定时器还分为两种类型,一种是低精度timer,一种是高精度timer。
    低精度timer是旧的内核中的实现,一直延续至今,也就是我们熟悉的基于jiffies系统滴答来实现的,旧的内核中只有这种定时器实现。
    高精度timer是为了满足新的需求而诞生的hrtimer。高精度基本可以满足任何情况下的需求了,但是由于历史原因低精度timer不能直接从内核中删除,必须保持兼容性。
    一个CPU内可能有多个local clock硬件,但是只会有一个被设置为该CPU的tick设备,每个CPU有且只有一个tick设备。

    内核配置

    1、通用clock source和clock event的内核配置

    (1)clock event是为tick设备提供支持的模块,它分为了新版和旧版两种架构。新架构采用的是通用模型,旧架构采用的是平台相关的实现。
    相关的配置项为:CONFIG_GENERIC_CLOCKEVENTS和CONFIG_GENERIC_CLOCKEVENTS_BUILD。
    如果配置了这两个选项,就会使用新的时间子系统的构架,如果不配置,将使用旧的时间子系统架构。 这一项的配置一般是在arch中选择的。比如arch/arm/Kconfig中,而其余的需要根据情况在menuconfig中配置,对应的Kconfig为kernel/kernel/time/Kconfig。

    arch/arm/Kconfig
    config ARCH_OMAP1
        bool "TI OMAP1"
        depends on MMU
        select ARCH_HAS_CPUFREQ
        select ARCH_HAS_HOLES_MEMORYMODEL
        select ARCH_OMAP
        select ARCH_REQUIRE_GPIOLIB
        select CLKDEV_LOOKUP
        select CLKSRC_MMIO
        select GENERIC_CLOCKEVENTS---选择通用时间框架
        select GENERIC_IRQ_CHIP
        select HAVE_CLK
        select HAVE_IDE
        select IRQ_DOMAIN
        select NEED_MACH_IO_H if PCCARD
        select NEED_MACH_MEMORY_H
        help
          Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx)
    

    如上所示,定义ARCH_OMAP1的时候就已经select GENERIC_CLOCKEVENTS作为时间子系统的框架。

    (2)clocksource是为timekeeper提供支持的模块,目前的内核都是使用新的通用框架。不过为了兼容性考虑,kernel也保留了CONFIG_ARCH_USES_GETTIMEOFFSET这个配置项.

    2、tick device的配置

    如果选择了新的时间子系统的软件架构(配置了CONFIG_GENERIC_CLOCKEVENTS),那么内核会打开Timers subsystem的配置选项,主要是和tick以及高精度timer配置相关。

    tick相关的配置:

    CONFIG_HZ_PERIODIC
    

    无论何时,都启用周期性的tick,即便是在系统idle的时候。

    CONFIG_NO_HZ_IDLE
    

    Idle dynticks system (tickless idle)。在系统idle的时候,停掉tick。使能该选项会自动使能CONFIG_NO_HZ_COMMON选项。

    CONFIG_NO_HZ_FULL
    

    Full dynticks system (tickless)。即便在非idle的状态下,也就是说cpu上还运行在task时,也可能会停掉tick,这个选项和实时应用相关。使能该选项会自动使能CONFIG_NO_HZ_COMMON选项。

    上面的三个选项只能是配置其一,上面描述的是新的内核配置方法。对于旧的内核,只有CONFIG_NO_HZ一个配置项,用来配置idle dynticks system(非idle时有周期性tick,idle时无周期性tick),为了兼容旧的系统,新的内核仍然支持了这个选项。

    除此之外还有一个用来配置tick模式的选项:

    CONFIG_TICK_ONESHOT
    

    如果配置了这个选项,就决定了系统中所有的tick设备都是oneshot mode,否则就是periodic mode。
    当配置了CONFIG_NO_HZ_COMMON或者CONFIG_HIGH_RES_TIMERS的时候,这个选项就会一起被配置上,也就是说采用了dynticks或者hrtimer的系统中,它的tick设备模式必须是oneshot类型的。

    3、timer模块的配置

    CONFIG_HIGH_RES_TIMERS
    

    和高精度timer相关的配置只有一个的配置项。如果配置了高精度hrtimer,那么就会自动配置上CONFIG_TICK_ONESHOT,表示系统只支持one-shot类型的tick device。

    4、 time配置示例

    选择一个时间子系统的构架:
    (1)新的通用时间子系统软件框架(配置了CONFIG_GENERIC_CLOCKEVENTS)
    (2)传统时间子系统软件框架(不配置CONFIG_GENERIC_CLOCKEVENTS,配置CONFIG_ARCH_USES_GETTIMEOFFSET)
    除非维护一个旧的系统,否则建议用新的通用时间子系统框架了,这时候可能的配置包括如下几种情况:
    (1)使用低精度timer和不停的tick(HZ_PERIODIC)
    这种是旧的处理方式
    (2)使用低精度timer和dynamic tick(CONFIG_NO_HZ_IDLE)
    (3)使用高精度timer和不停的tick(HZ_PERIODIC)
    (4)使用高精度timer和dynamic tick(CONFIG_NO_HZ_IDLE)
    这种是时下较流行的配置
    注:上面描述的dynamic tick指的是Idle dynticks system。我们不考虑Full dynticks system的情况。
    采用了dynticks或者hrtimer的系统中,它的tick设备模式必须是oneshot类型的,也就是说上面4种情况,只有第1种情况下的tick 是周期模式的,其他三种都是oneshot mode。

    展开全文
  • Linux内核时间获取

    千次阅读 2015-05-25 15:55:36
    内核一般通过jiffies值来获取当前时间。尽管该数值表示的是自上次系统启动到当前的时间间隔,但因为驱动程序的生命期只限于系统的运行期 (uptime),所以也是可行的。驱动程序利用jiffies的当前值来计算不同事件间的...
    内核一般通过jiffies值来获取当前时间。尽管该数值表示的是自上次系统启动到当前的时间间隔,但因为驱动程序的生命期只限于系统的运行期 (uptime),所以也是可行的。驱动程序利用jiffies的当前值来计算不同事件间的时间间隔。 硬件给内核提供一个系统定时器用以计算和管理时间,内核通过编程预设系统定时器的频率,即节拍率(tick
     rate),每一个周期称作一个tick(节拍)。Linux内核从2.5版内核开始把频率从100调高到1000(当然带来了很多优点,
    也有一些缺点)。jiffies是内核中的一个全局变量,用来记录自系统启动一来产生的节拍数。譬如,如果计算系统运行了多长时间,可以用 jiffies/tick rate 来计算。

    jiffies定义在文件<linux/jiffies.h> 

    如果您需要更精确的时间来测量或者记录某些事情的话,内核中有个xtime全局变量,类型是struct timespec {time_t tv_sec; long tv_nsec;}按照这个数据结构,它是ns级的。

    而且还有一个current_kernel_time函数,通过它就可以获取xtime的值。但是xtime是在时钟中断里更新的,而一个tick往往是 10ms或者100ms,它只能保证在时钟中断ISR调用时刻,它返回的值是精确到ns级,并不能保证任何一个调用这个函数的时刻都能这样,原因是xtime的更新速度比它差几个数量级。

    如果需要精确到微妙级别,可以使用do_gettimeofday函数。该函数并不返回今天是本周的星期几或类似的信息;它是用微秒值来填充一个指向struct timeval的指针变量。相应的原型如下:

    #include <linux/time.h>

    void do_gettimeofday(struct timeval *tv);

    源码中声明的do_gettimeofday在Alpha和Sparc之外的体系结构上有“接近微秒级的分辨率” ,在Alpha和Sparc上和jiffies值的分辨率一样。Sparc的移植版本在2.1.34版的内核中升级了,可以支持更细粒度的时间度量。

    void do_gettimeofday(struct timeval *tv)
    {
     unsigned long flags;
     unsigned long seq;
     unsigned long nsec, sec, lost;

     do {
      seq = read_seqbegin_irqsave(&xtime_lock, flags);
      usec = system_timer->offset();

      lost = jiffies - wall_jiffies;
      if (lost)
      usec += lost * USECS_PER_JIFFY;

      sec = xtime.tv_sec;
      nsec += xtime.tv_nsec;
     } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));

     /* usec may have gone up a lot: be safe */
     while (nsec >= 1000000000) {
      nsec -= 1000000000;
      sec++;
     }

     tv->tv_sec = sec;
     tv->tv_usec = usec;
    }


    中可以发现,我们只要稍微的修改一下代码就可以达到纳秒的精确度了

    void do_gettimeofday_nsec(struct timespec *tv)
    {
     unsigned long flags;
     unsigned long seq;
     unsigned long usec, sec, lost;

     do {
      seq = read_seqbegin_irqsave(&xtime_lock, flags);
      usec = system_timer->offset();

      lost = jiffies - wall_jiffies;
      if (lost)
      usec += lost * USECS_PER_JIFFY;

      sec = xtime.tv_sec;
      usec += xtime.tv_nsec / 1000;
     } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));

     /* usec may have gone up a lot: be safe */
     while (usec >= 1000000) {
      usec -= 1000000;
      sec++;
     }

     tv->tv_sec = sec;
     tv->tv_usec = usec;
    }

    注意这里使用了不同的时间结构。



    获得时间

    内核通过xtime变量保存墙上时间,该变量是timespec类型的,在linux/time.h中定义如下:

    1       struct timespec {

    2           __kernel_time_t tv_sec;                

    3           long tv_nsec;               

    4        };

    其中,tv_sec是以秒为单位时间,它保存着从1970年7月1日以来经过的时间,而tv_nsec记录自上一秒开始经过的纳秒数。

    在最新的内核中,xtime未导出因此不能在内核模块中使用。不过内核提供了内核函数current_kernel_time()来获取当前时间,该函数返回timespec类型的时间。


    展开全文
  • Linux内核时间管理

    千次阅读 2013-03-16 14:42:01
    (1)内核中的时间概念  时间管理在linux内核中占有非常重要的作用。  相对于事件驱动而言,内核中有大量函数是基于时间驱动的。  有些函数是周期执行的,比如每10毫秒刷新一次屏幕;  有些函数是推后一定...
    (1)内核中的时间概念 
        时间管理在linux内核中占有非常重要的作用。
        相对于事件驱动而言,内核中有大量函数是基于时间驱动的。
        有些函数是周期执行的,比如每10毫秒刷新一次屏幕;
        有些函数是推后一定时间执行的,比如内核在500毫秒后执行某项任务。
        要区分:
        *绝对时间和相对时间
        *周期性产生的事件和推迟执行的事件
        周期性事件是由系统系统定时器驱动的
     
    (2)HZ值
        内核必须在硬件定时器的帮助下才能计算和管理时间。
        定时器产生中断的频率称为节拍率(tick rate)。
        在内核中指定了一个变量HZ,内核初始化的时候会根据这个值确定定时器的节拍率。
        HZ定义在<asm/param.h>,在i386平台上,目前采用的HZ值是1000。
        也就是时钟中断每秒发生1000次,周期为1毫秒。即:
        #define HZ 1000
     
        注意!HZ不是个固定不变的值,它是可以更改的,可以在内核源代码配置的时候输入。
        不同的体系结构其HZ值是不一样的,比如arm就采用100。
        如果在驱动中要使用系统的中断频率,直接使用HZ,而不要用100或1000
     
     
        a.理想的HZ值
            i386的HZ值一直采用100,直到2.5版后才改为1000。
            提高节拍率意味着时钟中断产生的更加频繁,中断处理程序也会更频繁地执行。
     
            带来的好处有:
            *内核定时器能够以更高的频率和更高的准确度运行
            *依赖定时器执行的系统调用,比如poll()和select(),运行的精度更高
            *提高进程抢占的准确度
            (缩短了调度延时,如果进程还剩2ms时间片,在10ms的调度周期下,进程会多运行8ms。
            由于耽误了抢占,对于一些对时间要求严格的任务会产生影响)
     
            坏处有:
            *节拍率要高,系统负担越重。
            中断处理程序将占用更多的处理器时间。
     
     (3)jiffies
        全局变量jiffies用于记录系统启动以来产生的节拍的总数。
        启动时,jiffies初始化为0,此后每次时钟中断处理程序都会增加该变量的值。
        这样,系统启动后的运行时间就是jiffies/HZ秒
     
        jiffies定义于<linux/jiffies.h>中:
        extern unsigned long volatile jiffies;
     
        jiffies变量总是为unsigned long型。
        因此在32位体系结构上是32位,而在64位体系上是64位。
        对于32位的jiffies,如果HZ为1000,49.7天后会溢出。
        虽然溢出的情况不常见,但程序在检测超时时仍然可能因为回绕而导致错误。
        linux提供了4个宏来比较节拍计数,它们能正确地处理节拍计数回绕。
     
        #include <linux/jiffies.h>
        #define time_after(unknown, known)       // unknow >  known
        #define time_before(unknown, known)      // unknow <  known
        #define time_after_eq(unknown, known)    // unknow >= known
        #define time_before_eq(unknown, known)   // unknow <= known
     
        unknown通常是指jiffies,known是需要对比的值(常常是一个jiffies加减后计算出的相对值)
     
        例:
        unsigned long timeout = jiffies + HZ/2; /* 0.5秒后超时 */
        ...
        if(time_before(jiffies, timeout)){
            /* 没有超时,很好 */
        }else{
            /* 超时了,发生错误 */
        
        time_before可以理解为如果在超时(timeout)之前(before)完成
     
     
        *系统中还声明了一个64位的值jiffies_64,在64位系统中jiffies_64和jiffies是一个值。
        可以通过get_jiffies_64()获得这个值。
     
        *使用
        u64 j2;
            j2 = get_jiffies_64();
     
     (4)获得当前时间
        驱动程序中一般不需要知道墙钟时间(也就是年月日的时间)。但驱动可能需要处理绝对时间。
        为此,内核提供了两个结构体,都定义在<linux/time.h>:
        a.struct timeval {
          time_t tv_sec; /* seconds */
          suseconds_t tv_usec; /* microseconds */
        };
        较老,但很流行。采用秒和毫秒值,保存了1970年1月1日0点以来的秒数
     
        b.struct timespec {
          time_t  tv_sec; /* seconds */
          long tv_nsec; /* nanoseconds */
        };
        较新,采用秒和纳秒值保存时间。
     
        c.do_gettimeofday()
            该函数用通常的秒或微秒来填充一个指向struct timeval的指针变量,原型如下:
            #include <linux/time.h>
            void do_gettimeofday(struct timeval *tv);
     
        d.current_kernel_time()
            该函数可用于获得timespec
            #include <linux/time.h>

            struct timespec current_kernel_time(void);


    确定时间的延迟执行
        设备驱动程序经常需要将某些特定代码延迟一段时间后执行,通常是为了让硬件能完成某些任务。
        长于定时器周期(也称为时钟嘀嗒)的延迟可以通过使用系统时钟完成,而非常短的延时则通过软件循环的方式完成
          
     
    (1)短延时
        对于那些最多几十个毫秒的延迟,无法借助系统定时器。
        系统通过软件循环提供了下面的延迟函数:
        #include <linux/delay.h>  
        /* 实际在<asm/delay.h> */
        void ndelay(unsigned long nsecs); /*延迟纳秒 */
        void udelay(unsigned long usecs); /*延迟微秒 */
        void mdelay(unsigned long msecs); /*延迟毫秒 */
     
        这三个延迟函数均是忙等待函数,在延迟过程中无法运行其他任务。
     
    (2)长延时
        a.在延迟到期前让出处理器
            while(time_before(jiffies, j1))
                schedule();
            在等待期间可以让出处理器,但系统无法进入空闲模式(因为这个进程始终在进行调度),不利于省电。
     
        b.超时函数
            #include <linux/sched.h>
            signed long schedule_timeout(signed long timeout);
     
        使用方式:
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(2*HZ); /* 睡2秒 */
        进程经过2秒后会被唤醒。如果不希望被用户空间打断,可以将进程状态设置为TASK_UNINTERRUPTIBLE。
     

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/time.h>
    #include <linux/sched.h>
    #include <linux/delay.h>
    
    static int __init test_init(void)
    {
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(5 * HZ);
        printk(KERN_INFO "Hello Micky\n");
        return 0;
    }
    
    static void __exit test_exit(void)
    {
    }
    
    module_init(test_init);
    module_exit(test_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Micky Liu");
    MODULE_DESCRIPTION("Test for delay");
    


    (3)等待队列
        使用等待队列也可以实现长延迟。
        在延迟期间,当前进程在等待队列中睡眠。
        进程在睡眠时,需要根据所等待的事件链接到某一个等待队列。
     
        a.声明等待队列
            等待队列实际上就是一个进程链表,链表中包含了等待某个特定事件的所有进程。
            #include <linux/wait.h>
            struct __wait_queue_head {
                spinlock_t lock;
                struct list_head task_list;
            };
            typedef struct __wait_queue_head wait_queue_head_t;
     
            要想把进程加入等待队列,驱动首先要在模块中声明一个等待队列头,并将它初始化。
     
            静态初始化
                DECLARE_WAIT_QUEUE_HEAD(name);
     
            动态初始化
                wait_queue_head_t my_queue;
                init_waitqueue_head(&my_queue);
     
        b.等待函数
            进程通过调用下面函数可以在某个等待队列中休眠固定的时间:
            #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);
            调用这两个函数后,进程会在给定的等待队列q上休眠,但会在超时(timeout)到期时返回。
            如果超时到期,则返回0,如果进程被其他事件唤醒,则返回剩余的时间数。
            如果没有等待条件,则将condition设为0
     
            使用方式:
            wait_queue_head_t wait;
            init_waitqueue_head(&wait);
            wait_event_interruptible_timeout(wait, 0, 2*HZ);  
            /*当前进程在等待队列wait中睡2秒 */
     
     
     
    (4)内核定时器
        还有一种将任务延迟执行的方法是采用内核定时器。
        与前面几种延迟方法不同,内核定时器并不会阻塞当前进程,
        启动一个内核定时器只是声明了要在未来的某个时刻执行一项任务,当前进程仍然继续执行。
        不要用定时器完成硬实时任务
     
        定时器由结构timer_list表示,定义在<linux/timer.h>
        struct timer_list{
            struct list_head entry; /* 定时器链表 */
            unsigned long expires; /* 以jiffies为单位的定时值 */
            spinlock_t lock;
            void(*function)(unsigned long); /* 定时器处理函数 */
            unsigned long data;  /* 传给定时器处理函数的参数 */
        }
     
        内核在<linux/timer.h>中提供了一系列管理定时器的接口。
     
        a.创建定时器
            struct timer_list my_timer;
     
        b.初始化定时器
            init_timer(&my_timer);
            /* 填充数据结构 */
            my_timer.expires = jiffies + delay;
            my_timer.data = 0;
            my_timer.function = my_function; /*定时器到期时调用的函数*/
     
        c.定时器的执行函数
            超时处理函数的原型如下:
            void my_timer_function(unsigned long data);
            可以利用data参数用一个处理函数处理多个定时器。可以将data设为0
     
        d.激活定时器
            add_timer(&my_timer);
            定时器一旦激活就开始运行。
     
        e.更改已激活的定时器的超时时间
            mod_timer(&my_timer, jiffies+ney_delay);
            可以用于那些已经初始化但还没激活的定时器,
            如果调用时定时器未被激活则返回0,否则返回1。
            一旦mod_timer返回,定时器将被激活。
     
        f.删除定时器
            del_timer(&my_timer);
            被激活或未被激活的定时器都可以使用,如果调用时定时器未被激活则返回0,否则返回1。
            不需要为已经超时的定时器调用,它们被自动删除
     
        g.同步删除
        del_time_sync(&my_timer);
        在smp系统中,确保返回时,所有的定时器处理函数都退出。不能在中断上下文使用。
        
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/time.h>
    #include <linux/sched.h>
    #include <linux/delay.h>
    #include <linux/timer.h>
    
    struct timer_list my_timer;
    
    static void timer_handler(unsigned long arg)
    {
        printk(KERN_INFO "%s %d Hello Micky! arg=%lu\n",__func__, __LINE__, arg );
    }
    
    static int __init test_init(void)
    {
        init_timer(&my_timer);
    
        my_timer.expires = jiffies + 5 * HZ;
        my_timer.function = timer_handler;
        my_timer.data = 10;
        add_timer(&my_timer);
    
        return 0;
    }
    
    static void __exit test_exit(void)
    {
        del_timer(&my_timer);
    }
    
    module_init(test_init);
    module_exit(test_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Micky Liu");
    MODULE_DESCRIPTION("Test for timer");
    


    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/time.h>
    #include <linux/sched.h>
    #include <linux/delay.h>
    #include <linux/timer.h>
    
    struct timer_list my_timer;
    
    static void timer_handler(unsigned long arg)
    {
        printk(KERN_INFO "%s %d Hello Micky! arg=%lu\n",__func__, __LINE__, arg );
    }
    
    static int __init test_init(void)
    {
        init_timer(&my_timer);
    
        //my_timer.expires = jiffies + 5 * HZ;
        my_timer.function = timer_handler;
        my_timer.data = 10; 
        //add_timer(&my_timer);
        mod_timer(&my_timer, jiffies + 5 * HZ);
    
        return 0;
    }
    
    static void __exit test_exit(void)
    {
        del_timer(&my_timer);
    }
    
    module_init(test_init);
    module_exit(test_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Micky Liu");
    MODULE_DESCRIPTION("Test for timer");



     
    不确定时间的延迟执行
    (1)什么是不确定时间的延迟
        前面介绍的是确定时间的延迟执行,但在写驱动的过程中经常遇到这种情况:
        用户空间程序调用read函数从设备读数据,但设备中当前没有产生数据。
        此时,驱动的read函数默认的操作是进入休眠,一直等待到设备中有了数据为止。
     
        这种等待就是不定时的延迟,通常采用休眠机制来实现。
     
     
    (2)休眠
        休眠是基于等待队列实现的,前面我们已经介绍过wait_event系列函数,
        但现在我们将不会有确定的休眠时间。
     
        当进程被置入休眠时,会被标记为特殊状态并从调度器的运行队列中移走。
        直到某些事件发生后,如设备接收到数据,则将进程重新设为运行态并进入运行队列进行调度。
        休眠函数的头文件是<linux/wait.h>,具体的实现函数在kernel/wait.c中。
     
        a.休眠的规则
            *永远不要在原子上下文中休眠
            *当被唤醒时,我们无法知道睡眠了多少时间,也不知道醒来后是否获得了我们需要的资源
            *除非知道有其他进程会在其他地方唤醒我们,否则进程不能休眠
     
        b.等待队列的初始化
            见前文
     
        c.休眠函数
            linux最简单的睡眠方式为wait_event宏。该宏在实现休眠的同时,检查进程等待的条件。
     
            A. void wait_event(
                  wait_queue_head_t q,  
                  int condition);
     
            B. int wait_event_interruptible(wait_queue_head_t q, int condition);
                q: 是等待队列头,注意是采用值传递。
                condition: 任意一个布尔表达式,在条件为真之前,进程会保持休眠。
                注意!进程需要通过唤醒函数才可能被唤醒,此时需要检测条件。
                如果条件满足,则被唤醒的进程真正醒来;
                如果条件不满足,则进程继续睡眠。
     
     
        d.唤醒函数
            当我们的进程睡眠后,需要由其他的某个执行线程(可能是另一个进程或中断处理例程)唤醒。
            唤醒函数:
                #include <linux/wait.h>
                1. void wake_up(
                    wait_queue_head_t *queue);
     
                2. void wake_up_interruptible(
                    wait_queue_head_t *queue);
     
            wake_up会唤醒等待在给定queue上的所有进程。
            而wake_up_interruptible唤醒那些执行可中断休眠的进程。
            实践中,约定做法是在使用wait_event时使用wake_up,而使用wait_event_interruptible时使用wake_up_interruptible。
     


    展开全文
  • linux 内核时间打印

    千次阅读 2018-04-13 10:37:13
    struct timespec ts; ts = current_kernel_time(); printk(KERN_ALERT "i=%d, channel=%d, %ld %ld\n", i, channel, ts.tv_sec, ts.tv_nsec);.../*获取时间*/ do_gettimeofday(&amp;tv); printk(KERN_...
  • linux 内核 时间 获取

    千次阅读 2012-10-29 16:28:23
    还是有的时候需要在Linux内核中获得当前系统时间的,Linux内核中获取当前时间的方法有2种: 1、使用rtc设备,这个时钟可以用于各种模式 2、借鉴系统调用adjtimex 这里使用第二种方式 系统调用adjtimex
  • 表示时间的结构 在内核当中有几个不同的结构用于表示时间
  • Linux内核时间管理子系统——时钟源

    千次阅读 2014-08-27 22:17:13
    在Linux内核中有两种不同的clock设备,一种是clock source设备,另一种是clock event设备。
  • 有了这个Linux内核版本发布时间表(0.00到3.19,当然没有包含全部的版本), 大家就可以看看自己用的版本是何时发布的了!做内核维护查看相关patch log时大致做个参考。 版本号 时间 发展史 0.00 1991....
  • 如何在内核态下获取时间

    千次阅读 2012-12-19 17:09:04
    1. 直接使用内核变量jiffies,通用jiffies的值相减可以获得内核时间,但是此分辨率一般为10ms(可参考内核宏HZ)级. 因为jiffies的值. 2. do_gettimeofday()函数,其用法和系统调用gettimeofday相同. 他可以得到us级的...
  • Linux内核启动显示时间信息

    千次阅读 2017-05-13 16:31:02
    它在打印内核启动信息之前都会先打印一个时间信息出来! 下面来看看怎么配置Linux内核: 首先在Linux内核文件下,执行make menuconfig 命令,进入配置界面。 点击Kernel hacking选项,选择Show timing
  • Linux内核发布时间

    千次阅读 2017-12-14 14:13:15
    有了这个Linux内核版本发布时间表(0.00到3.19,当然没有包含全部的版本),大家就可以看看自己用的版本是何时发布的了!做内核维护查看相关patch log时大致做个参考。 版本号 时间 发展史 0.001991.2-4两个进程分别...
  • Linux内核中的时间函数

    千次阅读 2019-08-05 16:52:08
    ktime时间函数 基于ktime_t格式的时间 ktime_t ktime_get(void); 获取基于CLOCK_MONOTONIC的当前时间,返回ktime_t格式的数据类型。 ktime_t ktime_get_boottime(void); 获取基于CLOCK_BOOTTIME的当前时间,返回...
  • linux内核获取当前系统时间

    千次阅读 2018-10-28 17:38:12
    获取linux内核的系统时间操作方法网上有,大多千篇一律,照着网上的做法,就会产生错误,首先是获取linux内核的系统时间: 参考: http://lhsblog01.blog.163.com/blog/static/10200451920101192410981/ 在这样一...
  • 下面的函数get_time_str实现了在内核中获取本地时间的功能。 他首先获取utc时间,然后根据系统的时区timezone转换成本地时间, 最后将时间以“2014-11-02 21:14:08”的字符串的形式输出到output缓冲区中。 函数的...
  • 内核函数时间获得

    千次阅读 2018-07-25 13:26:59
    功能:此函数用于返回当前内核时间,该时间是距离1970开始的秒和纳秒 返回值: timespec结构体包含秒和纳秒 头文件: #include void getnstimeofday(struct timespec *tv) 功能:此函数用于返回当前内核时间,该时间...
  • linux内核计算时间差以及jiffies溢出

    千次阅读 2014-06-01 00:42:52
    linux内核
  • Linux内核版本发布时间整理

    千次阅读 2015-05-30 23:39:49
    有了这个Linux内核版本发布时间表(0.00到3.19,当然没有包含全部的版本), 大家就可以看看自己用的版本是何时发布的了! 或许有同学会大吃一惊,哇(⊙0⊙)我用的版本10年前就有了啊! 发展史我也没写全,有时间和...
  • 查看内核构建的时间

    千次阅读 2012-11-07 18:37:40
    有时候,忘了现在运行的内核是不是...这时可dmesg查看开头banner,日志中有内核构建的时间。其实这个信息在内核里存储了,cat /proc/version或者 cat /proc/sys/kernel/version就可以看到。
  • jiffies:内核中的时间观念

    千次阅读 2016-09-28 21:13:14
    jiffies在内核中是一个全局变量,声明包含在#include 中, ...每发生一次中断的时间间隔为10ms 注意:不同的环境HZ的值都是可能不一样的,我曾经遇到过一个TI的开发板,他的jiffies比较特殊一点,
  • Linux内核中获取当前时间

    万次阅读 2011-06-17 21:10:00
    7.2. 获知当前时间内核代码能一直获取一个当前时间的表示, 通过查看 jifies 的值. 常常地, 这个值只代表从最后一次启动以来的时间, 这个事实对驱动来说无关, 因为它的生命周期受限于系统的 uptime. 如所示, 驱动...
  • 内核启动时间优化一例

    千次阅读 2010-05-15 13:26:00
    内核启动时间优化一例作者: 宋立新Email:zjujoe@yahoo.com前言 为了提高开机时间,我们需要优化的部分有:1) bootloader 启动速度2) Linux 内核启动速度3) 文件系统启动速度4) Nand (假设你的 Storage ...
  • 2.6.36 内核模块时间同步函数汇总

    千次阅读 2011-07-14 14:50:50
    2.6.36 内核模块调用使用函数汇总:在linux内核模块中能用到的函数比用户空间多,但是它的函数往往被用得很少。当然在内核中叶可以调用用户空间的函数只要直接调用系统调用的封装函数就行,如下:如调用long ...
  • Linux 内核定时器

    千次阅读 2012-05-18 15:00:46
    定时器,有时也称为动态定时器或内核定时器,是管理内核时间的基础 内核经常要推迟执行一些代码,如下半部机制就是为了将工作推后执行 时钟中断由系统的定时硬件以周期性的时间间隔产生,这个间隔(即频率)由内核...
  • 操作系统内核可能是微内核,也可能是单内核(后者有时称之为宏内核Macrokernel)。按照类似封装的形式,这些术语定义如下: 单内核:也称为宏内核。将内核从整体上作为一个大过程实现,并同时运行在一个...
  • 内核与微内核、Linux内核与Unix内核的区别

    万次阅读 多人点赞 2016-05-25 10:06:42
    操作系统内核可能是微内核,也可能是单内核(后者有时称之为宏内核Macrokernel)。按照类似封装的形式,这些术语定义如下: 单内核:也称为宏内核。将内核从整体上作为一个大过程实现,并同时运行在一个单独的地址...
  • 内核和宏内核

    千次阅读 2011-08-31 09:42:28
    内核:多个进程通过(尽量小)内核转发消息. 宏内核:一个进程,直接调用 Linux大部分都是单内核的 操作系统内核可能是微内核,也可能是单内核(后者有时称之为宏内核Macrokernel)。按照类似封装的形式,这些...
  • Linux内核模块时间同步函数汇总

    千次阅读 2012-01-17 10:17:42
    在linux内核模块中能用到的函数比用户空间多,但是它的函数往往被用得很少。当然在内核中叶可以调用用户空间的函数只要直接调用系统调用的封装函数就行,如下: 如调用long gettimeofday(struct timeval tv, struct...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 567,290
精华内容 226,916
关键字:

内核时间