时间、延迟以及延缓操作

HZ和jiffies

内核通过定时器中断来跟踪时间流。时钟中断由系统定时器一周期性的间隔产生,这个间隔有内核根据HZ的值决定。HZ是一个与体系结构有关的常数,定义在<linux/param.h>或者该文件包含的某个子平台的相关文件中。HZ是用来定义每一秒中有多少次时钟中断。例如HZ为1000,代码每秒产生1000次时钟中断。
全局变量jiffies用于记录系统启动以来产生的节拍的总数,一个节拍代表多长时间,与HZ大小有关,例如HZ = 200,则一个jiffies对应5ms(1s = 1000ms,1000ms / 200 = 5ms)时间。比较两个节点的宏如下,包含头文件<linux/jiffies.h>
int time_after(unsigned long a, unsigned long b); b > a 时返回真
int time_before(unsigned long a, unsigned long b); b < a 时返回真
int time_after_eq(unsigned long a, unsigned long b); b >= a 时返回真
int time_before_eq(unsigned long a, unsigned long b); b <= a 时返回真
计算两个jiffies实例之间的差:diff = (long)t2 - (long)t1;
将差值转换为毫秒值:msec = diff * 1000 / HZ;
 

获取当前时间

获取时间的函数原型如下:

#include <linux/time.h>

struct timeval {
	time_t tv_sec; /* seconds */
	suseconds_t tv_usec; /* microseconds */
};
void do_gettimeofday(struct timeval *tv);

struct timespec {
	time_t tv_sec; // seconds 
	long tv_nsec; // and nanoseconds 
};
struct timespec current_kernel_time(void);

 

延迟执行

长延迟
#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秒 */
第一行调用set_current_state已设置当前进程状态,调度器只有在超时到期且其状态为TASK_RUNNING时才会运行这个进程。如果不希望被用户空间打断,可以将进程状态设置为TASK_UNINTERRUPTIBLE。

短延迟
如果需要处理涉及到几十个毫秒级别的延迟,可以使用下面的函数进行处理。
#include <Linux/delay.h>
void ndelay(unsigned long nsecs); 延迟纳秒
void udelay(unsigned long usecs); 延迟微秒
void mdelay(unsigned long msecs); 延迟毫秒
注意:这个三个延迟函数都是忙等待函数,因而在延迟的过程中无法运行其他任务。实现毫秒级(或者更长)延迟还有一个不涉及忙等待的方法,包含头文件<Linux/delay.h>,具体如下:
void msleep(unsigned int millisecs); 休眠毫秒
void ssleep(unsigned int seconds); 休眠秒
unsigned long msleep_interruptible(unsigned int millisecs); 休眠毫秒,中断可以唤醒
 

工作队列

工作队列有struct workqueue_struct的类型,该结构定义在<linux/workqueue.h>中。在使用之前,我们必须显示的创建一个工作队列,可使用下面两个函数:
struct workqueue_struct *create_wrokqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
每个工作队列有一个或多个专用的进程(“内核线程”),这些进程运行提交到该队列的函数。如果我们使用create_workqueue,则内核会在系统中的每个处理器上为该工作队列创建专用的线程。在许多情况下,众多的线程可能对性能具有某种程度的杀伤力;因此,如果单个工作线程足够使用,那么应该使用create_singlethread_workqueue创建工作队列。

要向一个工作队列提交一个任务,需要填充一个work_struct结构,可以通过下面的宏在编译时完成:
DECLARE_WORK(name, void (*function)(void *), void *data);
其中name是要声明的结构名称,function是要从工作队列中调用的函数,data是传递给该函数的值。
如果需要运行时构造work_struct结构,使用下面的宏:
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
INIT_WORK完成更加彻底的结构初始化工作;在首次构造该结构时,应该使用这个宏。如果work_struct结构已经提交到工作队列,只是需要修改该结构,则应该使用PREPARE_WORK。

将工作提交到工作队列,可以使用下面的两个函数之一:
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);
他们都是将work添加到指定的queue。queue_delayed_work会在经过指定的jiffies(由delay指定)之后才会被执行。如果工作被成功添加到队列,上述函数返回1,。返回值为非0值表示给定的work_struct结构已经在等待队列中,不能再次加入。
结束使用工作队列后,释放资源使用:void destroy_workqueue(struct workqueue_struct *queue);
 

共享队列

在许多情况下,设备驱动程序不需要有自己的工作队列。如果我们只是偶尔需要向队列中提交任务,有一种更简单、更有效的方法就是使用内核提供的共享的默认工作队列。但是使用的时候要注意,因为是与其他人共享该工作队列,所以我们不应该长期占用该队列,即不能长时间休眠。并且我们的任务可能需要更长的时间才能获得处理器时间
添加工作的方法是:
int schedule_work(struct work_struct *work);
带延迟的版本:
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
我们在创建一个简单的字符设备代码的基础上添加一个共享队列用作测试。通过ioctl来触发添加工作schedule_work。示例代码如下:

static struct work_struct work;

static void notification_work(struct work_struct *work)
{
	pr_info("%s\n", __func__);
}

static long csdn_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	/* ohters code */
	switch (cmd)
	{
    case CSDN_IOC_GET:
        pr_info("CSDN_IOC_GET \n");
        schedule_work(&work);
        break;
    /* ohters code */
    }
	/* ohters code */
}

static __init int csdn_init(void)
{
	/* ohters code */
	INIT_WORK(&work, notification_work);
	/* ohters code */
}

打印结果如下:
[ 6164.910398] csdn_ioctl
[ 6164.910399] CSDN_IOC_GET
[ 6164.920519] notification_work

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值