Linux 下的推迟执行
发布日期:2021-06-30 18:42:21 浏览次数:3 分类:技术文章

本文共 3647 字,大约阅读时间需要 12 分钟。

准备中秋节

说个活动,评论文章点赞排名,用心评论哦,前5名获得每人 19 心意红包。

感谢大家的支持

我最近在用freertos,想让一个任务在某个时间后再执行,找了一圈,竟然没有这样才处理机制,因为也是新手入门freertos,可能需要自己实现,当然了,自己实现的话,机制就很多了,但是有个问题是,自己实现的话,就感觉不够规范,因为这样的原因,我还特意从Linux上移植了time_before和time_after过去,用了下,感觉还是很爽的。

Linux 有延迟执行的机制,有几种办法

1、忙等待

听到这个就知道了,如果是忙等待的话,肯定是占用cpu的,所以忙等待其实也是使用了time_before这个宏来实现。

#define time_after(a,b)        \	    (typecheck(unsigned long, a) && \	     typecheck(unsigned long, b) && \	     ((long)((b) - (long)(a)) < 0))	time_before(a,b)    time_after(b,a)

这个是实现的原型,time_before也就是time_after反过来而已,我们之前有一篇文章讨论了time_after宏的实现和用法。不清楚的同学可以去看看,其中把无符号强制转成有符号是关键。

那我们怎么使用这个忙等待呢

很简单

unsigned long timeout = jiffies +10;	while(time_before(jiffies,timeout));

while 循环会一直执行,因为time_before会一直返回true,知道jiffies的时间超过timeout的时间,这时候就会返回false。

也可以是这样使用

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

这个是等待2秒,一秒钟的节拍数是HZ,所以2秒就是2*HZ,这个好像太简单了些。

2、短延迟

这个也类似于忙等待,但是这个忙等待使用的函数不同,我们使用jiffies使用的是系统软件滴答数来做延迟,精度和时间上都有一定的局限性,但是使用delay函数的话,会相对好一些,时间的精准度会比较好。

void udelay(unsigned long usecs)	void ndelay(unsigned long usecs)	void mdelay(unsigned long usecs)

学习单片机的同学都知道,CPU执行的时间可以通过指令周期来确定时间,指令周期就是执行一条简单的指令所花费的时间,80C51下我知道是多少,ARM我还不懂,但是这些我们也不用太关心,每个体系结构下的delay实现,他们都自己计算实现好了,这也是使用系统和单片机的好处,封装什么的都搞好,就是要会使用才是关键。

用延迟实现的弊端就是会一直占用CPU时间,系统调用需要非常良好的性能,所以我们使用上面delay函数的时候,如果大于1ms的话,就可以换一种实现方式了。

640?wx_fmt=png

1、时钟周期 = 振荡周期,名称不同而已,都是等于单片机晶振频率的倒数,如常见的外接12M晶振,那它的时钟周期=1/12M。

2、机器周期,8051系列单片机的机器周期=12*时钟周期,之所以这样分是因为单个时钟周期根本干不了一件完整的事情(如取指令、写寄存器、读寄存器等),而12个时钟周期就能基本完成一项基本操作了。

3、指令周期。一个机器周期能完成一项基本操作,但一条指令常常是需要多项基本操作结合才能完成,完成一条指令所需的时间就是指令周期,当然不同的指令,其指令周期就不一样的了。

3、schedule_timeout

在上面两种方法的局限下,这个应该是最好的实现方式了,它的好是因为他可以睡眠,睡眠有一个好处就是不需要占用CPU资源,等时间到了再起床去干活就好了。

set_current_state(TASK_UNINTERRUPTIBLE);	    schedule_timeout(S*HZ);

上面的代码是让当前任务进入不可中断状态,任何睡眠S秒后再起床,使用schedule_timeout的时候,一定要记得设置状态,不然不能睡觉就麻烦了,也要注意你自己写的代码能不能睡眠,要不然引起问题就更尴尬了。

fastcall signed long __sched schedule_timeout(signed long timeout)	{	    struct timer_list timer;	    unsigned long expire;		    switch (timeout)	    {	    case MAX_SCHEDULE_TIMEOUT:	        /*	         * These two special cases are useful to be comfortable	         * in the caller. Nothing more. We could take	         * MAX_SCHEDULE_TIMEOUT from one of the negative value	         * but I' d like to return a valid offset (>=0) to allow	         * the caller to do everything it want with the retval.	         */	        schedule();	        goto out;	    default:	        /*	         * Another bit of PARANOID. Note that the retval will be	         * 0 since no piece of kernel is supposed to do a check	         * for a negative retval of schedule_timeout() (since it	         * should never happens anyway). You just have the printk()	         * that will tell you if something is gone wrong and where.	         */	        if (timeout < 0)	        {	            printk(KERN_ERR "schedule_timeout: wrong timeout "	                   "value %lx from %p\n", timeout,	                   __builtin_return_address(0));	            current->state = TASK_RUNNING;	            goto out;	        }	    }		    expire = timeout + jiffies;		    init_timer(&timer);	    timer.expires = expire;	    timer.data = (unsigned long) current;	    timer.function = process_timeout;		    add_timer(&timer);	    schedule();	    del_singleshot_timer_sync(&timer);		    timeout = expire - jiffies;		 out:	    return timeout < 0 ? 0 : timeout;	}

网上有挺多文章说明了这个函数的实现,首先是初始化一个timer,然后往timer里去传初始化参数,其中有一个参数是current,这个是一个宏,这个宏的作用是获取当前的task,然后再设置超时时间,超时时间到了之后,通过调用process_timeout去唤醒之前休眠的task。

我们可以这样使用,当做一个delay来嵌入自己的代码中

inline static void snd_xx_delay_long(void)	{	  set_current_state(TASK_UNINTERRUPTIBLE);	  schedule_timeout(1);	}

640?wx_fmt=jpeg

扫码或长按关注

转载地址:https://linus.blog.csdn.net/article/details/100788379 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:中秋节快乐
下一篇:老师好

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月06日 15时35分52秒