时间、延时、延缓操作

<linux/param.h>
可通过定义的CPUHz值判断时间

使用 jiffies 计数器
这个计数器和用来读取它的工具函数包含在,通常只需包含 ,它会自动放入linux/jiffies.h
jiffies和jiffies_64必须被当作只读变量

#include <linux/jiffies.h>
unsigned long j, stamp_1, stamp_half, stamp_n;
j = jiffies; /* 读取当前时间值 */
stamp_1 = j + HZ; /* 未来1s*/
stamp_half = j + HZ/2; /* 半秒 */
stamp_n = j + n * HZ / 1000; /* n个毫秒 */
大概50天这个计数器会溢出

最好使用以下宏进行比较
time_after(a,b) a比b时间靠后返回1
time_before(a,b) a比b靠前返回1
time_after_eq(a,b) 靠后或者相等
time_before_eq(a,b) 靠前或者相等

计算时间差diff = (ulong)t2-(ulong)tl;转换为毫秒 msec = diff * 1000/Hz

内核时间和用户空间时间转换
extern unsigned long timespec_to_jiffies(const struct timespec *value);
extern void jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);
extern unsigned long timeval_to_jiffies(const struct timeval *value);
extern void jiffies_to_timeval(const unsigned long jiffies,struct timeval *value);

不能直接读64位jiffies_64如需读取则应该 u64 get_jiffies_64(void);

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

超时
<linux/wait.h>
#define wait_event_timeout(wq, condition, timeout) (1)当condition为真的时候,会返回 (2)当timeout到达时也会返回,不管此时condition为真为假都会返回
#define wait_event_interruptible(wq, condition)
上述函数会在给定的等待队列上休眠,但是会在超时(jiffies)到期时返回
两个函数实现了一种有界限的休眠,这种休眠不会永远继续
wait_queue_head_t    wait;
wait_event_timeout(ts->wait, ts->stopped,msecs_to_jiffies(5));


获取当前时间
驱动一般无需知道时钟时间(用年月日、小时、分钟、秒来表达的时间),只对用户程序才需要,如 cron 和 syslogd。 内核提供了一个将时钟时间转变为秒数值的函数:
unsigned long
mktime(const unsigned int year0, const unsigned int mon0,
       const unsigned int day, const unsigned int hour,
       const unsigned int min, const unsigned int sec)
{
    unsigned int mon = mon0, year = year0;

    /* 1..12 -> 11,12,1..10 */
    if (0 >= (int) (mon -= 2)) {
        mon += 12;    /* Puts Feb last since it has leap day */
        year -= 1;
    }

    return ((((unsigned long)
         (year/4 - year/100 + year/400 + 367*mon/12 + day) +
         year*365 - 719499
     )*24 + hour /* now have hours */
     )*60 + min /* now have minutes */
    )*60 + sec; /* finally seconds */
}
/* 这个函数将时间转换成从1970年1月1日0小时0分0秒到你输入的时间所经过的秒数,溢出时间为2106-02-07 06:28:16。本人认为这个函数的使用应这样:若你要计算2000-02-07 06:28:16 到2000-02-09 06:28:16 所经过的秒数:unsigned long time1 = mktime(2000,2,7,6,28,16)-mktime(2000,2,9,6,28,16); 若还要转成jiffies,就再加上:unsigned long time2 = time1*HZ. 注意溢出的情况!*/

#include <linux/time.h>
导出了 do_gettimeofday 函数,它填充一个指向struct timeval 的指针变量。
void do_gettimeofday(struct timeval *tv);
struct timespec current_kernel_time(void);

/*得到的数据都表示当前时间距UNIX时间基准1970-01-01 00:00:00的相对时间*/
以上两个函数在ARM平台都是通过 xtime 变量得到数据的。
全局变量xtime:它是一个timeval结构类型的变量,用来表示当前时间距UNIX时间基准1970-01-01 00:00:00的相对秒数值。


短延迟
当一个设备驱动需要处理硬件的延迟(latency潜伏期), 涉及到的延时通常最多几个毫秒,在这个情况下, 不应依靠时钟嘀哒,而是内核函数 ndelay, udelay和 mdelay ,他们分别延后执行指定的纳秒数, 微秒数或者毫秒数,定义在 ,原型如下:
#include <linux/delay.h>
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
重要的是记住这 3 个延时函数是忙等待; 其他任务在时间流失时不能运行。
有另一个方法获得毫秒(和更长)延时而不用涉及到忙等待的方法是使用以下函数(在 中声明)
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds)
若能够容忍比请求的更长的延时,应使用 schedule_timeout, msleep 或 ssleep。

内核定时器
当需要调度一个以后发生的动作,而在到达该时间点时不阻塞当前进程,则可使用内核定时器。内核定时器用来调度一个函数在将来一个特定的时间(基于时钟嘀哒)执行,从而可完成各类任务
内核定时器是一个数据结构,它告诉内核在一个用户定义的时间点使用用户定义的参数执行一个用户定义的函数,函数位于和kernel/timer.c。
被调度运行的函数几乎确定不会在注册它们的进程在运行时运行,而是异步运行。实际上,内核定时器通常被作为一个"软件中断"的结果而实现。当在进程上下文之外(即在中断上下文)中运行程序时,必须遵守下列规则:
(1)不允许访问用户空间;
(2)current 指针在原子态没有意义;
(3)不能进行睡眠或者调度. 例如:调用 kmalloc(..., GFP_KERNEL) 是非法的,信号量也不能使用因为它们可能睡眠。
通过调用函数in_interrupt()能够告知是否它在中断上下文中运行,无需参数并如果处理器当前在中断上下文运行就返回非零。
通过调用函数in_atomic()能够告知调度是否被禁止,若调度被禁止返回非零;调度被禁止包含硬件和软件中断上下文以及任何持有自旋锁的时候。
内核定时器的另一个重要特性是任务可以注册它本身在后面时间重新运行,因为每个 timer_list 结构都会在运行前从激活的定时器链表中去连接,因此能够立即链入其他的链表。一个重新注册它自己的定时器一直运行在同一个 CPU.
即便在一个单处理器系统,定时器是一个潜在的态源,这是异步运行直接结果。因此任何被定时器函数访问的数据结构应当通过原子类型或自旋锁被保护,避免并发访问。
内核提供给驱动许多函数来声明、注册以及删除内核定时器:
#include <linux/timer.h>
struct timer_list {
    struct list_head entry;
    unsigned long expires;/*期望定时器运行的绝对 jiffies 值,不是一个 jiffies_64 值,因为定时器不被期望在将来很久到时*/
    void (*function)(unsigned long); /*期望调用的函数*/
    unsigned long data;/*传递给函数的参数,若需要在参数中传递多个数据项,可以将它们捆绑成单个数据结构并且将它的指针强制转换为 unsiged long 的指针传入。这种做法在所有支持的体系上都是安全的并且在内存管理中相当普遍*/
    struct tvec_t_base_s *base;
#ifdef CONFIG_TIMER_STATS
    void *start_site;
    char start_comm[16];
    int start_pid;
#endif
};/*这个结构必须在使用前初始化,以保证所有的成员被正确建立(包括那些对调用者不透明的初始化):*/
void init_timer(struct timer_list *timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
/*在初始化后和调用 add_timer 前,可以改变 3 个公共成员:expires、function和data*/
void add_timer(struct timer_list * timer);
int del_timer(struct timer_list * timer);/*在到时前禁止一个已注册的定时器*/
int del_timer_sync(struct timer_list *timer); /* 如同 del_timer ,但还保证当它返回时, 定时器函数不在任何 CPU 上运行,以避免在 SMP 系统上竞态, 并且在 单处理器内核中和 del_timer 相同。这个函数应当在大部分情况下优先考虑。 如果它被从非原子上下文调用, 这个函数可能睡眠,但是在其他情况下会忙等待。当持有锁时要小心调用 del_timer_sync ,如果这个定时器函数试图获得同一个锁, 系统会死锁。如果定时器函数重新注册自己, 调用者必须首先确保这个重新注册不会发生; 这通常通过设置一个" 关闭 "标志来实现, 这个标志被定时器函数检查*/
int mod_timer(struct timer_list *timer, unsigned long expires); /*更新一个定时器的超时时间, 常用于超时定时器。也可在正常使用 add_timer时在不活动的定时器上调用mod_timer*/
int timer_pending(const struct timer_list * timer); /*通过调用timer_list结构中一个不可见的成员,返回定时器是否在被调度运行*/

例子

static void timer_func(unsigned long data)
{
    char *str = (char *)data;

    printk(KERN_EMERG "%s\n", str);
}

init_timer(timer[i]);
timer[i]->expires = jiffies + HZ * (i + 3);
timer[i]->data = (unsigned long)test;
timer[i]->function = timer_func;
add_timer(timer[i]);


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值