Linux设备驱动学习(6) 时间、延迟、延缓操作 jit类设备驱动

原创 2012年03月31日 15:48:49

current是查看当前时间的设备,

jitbusy、jitqueue、jitsche、jitscheto是延迟一段时间的设备

jitimer是使用了定时器类的设备,定时器到期时执行一个过程。

jitasklet、jitasklethi是使用了小任务tasklet类的设备,tasklet是为了尽快执行一个过程而设计。

/*
 * jit.c -- the just-in-time module
 *
 * Copyright (C) 2001,2003 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001,2003 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 *
 * $Id: jit.c,v 1.16 2004/09/26 07:02:43 gregkh Exp $
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/time.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>

#include <asm/hardirq.h>
/*
 * This module is a silly one: it only embeds short code fragments
 * that show how time delays can be handled in the kernel.
 */

int delay = HZ; /* the default delay, expressed in jiffies */

module_param(delay, int, 0);

MODULE_AUTHOR("Alessandro Rubini");
MODULE_LICENSE("Dual BSD/GPL");

/* use these as data pointers, to implement four files in one function */
enum jit_files {
	JIT_BUSY,
	JIT_SCHED,
	JIT_QUEUE,
	JIT_SCHEDTO
};

/*
 * This function prints one line of data, after sleeping one second.
 * It can sleep in different ways, according to the data pointer
 */
int jit_fn(char *buf, char **start, off_t offset,
	      int len, int *eof, void *data)
{
	unsigned long j0, j1; /* jiffies */
	wait_queue_head_t wait;

	init_waitqueue_head (&wait);
	j0 = jiffies;
	j1 = j0 + delay;//等待(delay/HZ)秒

	switch((long)data) {
		case JIT_BUSY:
			while (time_before(jiffies, j1))
				cpu_relax();//cpu_relax函数不做任何事,却不让出CPU,忙等待,此种循环等待方式极其浪费系统
			break;
		case JIT_SCHED:
			while (time_before(jiffies, j1)) {
				schedule();//时间没到则主动释放CPU,但进程仍然在运行队列中
			}
			break;
		case JIT_QUEUE://以上time_before函数是采取了监视内核变量jiffies计数器的方式
			wait_event_interruptible_timeout(wait, 0, delay);//这里采取了让进程在等待队列wait上休眠,时间到期时结束休眠
			break;						 //此函数在内部依赖于schedule_timeout()
		case JIT_SCHEDTO:
			set_current_state(TASK_INTERRUPTIBLE);
			schedule_timeout (delay);//这是相对于采取等待队列延时的改进,不等待特定事件而延迟
			break;
	}
	j1 = jiffies; /* actual value after we delayed */

	len = sprintf(buf, "%9li %9li\n", j0, j1);//填充20个字节到buf
	*start = buf;
	return len;
}

/*
 * This file, on the other hand, returns the current time forever
 *///获取当前的时间
int jit_currentime(char *buf, char **start, off_t offset,
                   int len, int *eof, void *data)
{
	struct timeval tv1;
	struct timespec tv2;
	unsigned long j1;
	u64 j2;

	/* get them four */
	j1 = jiffies;
	j2 = get_jiffies_64();
	do_gettimeofday(&tv1);//有接近微秒级的精度
	tv2 = current_kernel_time();//以纳秒精度显示,但是只有时钟滴答的分辨率HZ=1000时即精度为0.001

	/* print */
	len=0;
	len += sprintf(buf,"0x%08lx 0x%016Lx %10i.%06i\n"
		       "%40i.%09i\n",
		       j1, j2,
		       (int) tv1.tv_sec, (int) tv1.tv_usec,
		       (int) tv2.tv_sec, (int) tv2.tv_nsec);
	*start = buf;
	return len;
}

/*
 * The timer example follows
 */

int tdelay = 10;
module_param(tdelay, int, 0);

/* This data structure used as "data" for the timer and tasklet functions */
struct jit_data {	//使用定时器的"设备"
	struct timer_list timer;
	struct tasklet_struct tlet;
	int hi; /* tasklet or tasklet_hi */
	wait_queue_head_t wait;
	unsigned long prevjiffies;
	unsigned char *buf;//缓冲区指针
	int loops;
};
#define JIT_ASYNC_LOOPS 5

void jit_timer_fn(unsigned long arg)
{
	struct jit_data *data = (struct jit_data *)arg;
	unsigned long j = jiffies;
	data->buf += sprintf(data->buf, "%9li  %3li     %i    %6i   %i   %s\n",
			     j, j - data->prevjiffies, in_interrupt() ? 1 : 0,
			     current->pid, smp_processor_id(), current->comm);

	if (--data->loops) {
		data->timer.expires += tdelay;
		data->prevjiffies = j;
		add_timer(&data->timer);//再次注册一个延期tdelay的定时器,到期后再调用自己。
	} else {//循环结束,唤醒等待队列
		wake_up_interruptible(&data->wait);
	}
}

/* the /proc function: allocate everything to allow concurrency */
int jit_timer(char *buf, char **start, off_t offset,
	      int len, int *eof, void *unused_data)
{
	struct jit_data *data;
	char *buf2 = buf;
	unsigned long j = jiffies;

	data = kmalloc(sizeof(*data), GFP_KERNEL);//对每一个读取"设备"的进程都分配一个单独的设备内存。
	if (!data)
		return -ENOMEM;

	init_timer(&data->timer);//初始化定时器
	init_waitqueue_head (&data->wait);//初始化等待队列

	/* write the first lines in the buffer */
	buf2 += sprintf(buf2, "   time   delta  inirq    pid   cpu command\n");
	buf2 += sprintf(buf2, "%9li  %3li     %i    %6i   %i   %s\n",
			j, 0L, in_interrupt() ? 1 : 0,
			current->pid, smp_processor_id(), current->comm);

	/* fill the data for our timer function */
	data->prevjiffies = j;
	data->buf = buf2;
	data->loops = JIT_ASYNC_LOOPS;
	
	/* register the timer */
	data->timer.data = (unsigned long)data;//将指针强制转换成无符号长整型,来作为定时器到期执行函数的参数传入
	data->timer.function = jit_timer_fn;//设置定时器到期时执行的函数
	data->timer.expires = j + tdelay; /* parameter *///定时器到期的时间
	add_timer(&data->timer);//向内核注册定时器

	/* wait for the buffer to fill */
	wait_event_interruptible(data->wait, !data->loops);//等待定时器中的循环结束
	if (signal_pending(current))
		return -ERESTARTSYS;
	buf2 = data->buf;
	kfree(data);
	*eof = 1;
	return buf2 - buf;
}

void jit_tasklet_fn(unsigned long arg)
{
	struct jit_data *data = (struct jit_data *)arg;
	unsigned long j = jiffies;
	data->buf += sprintf(data->buf, "%9li  %3li     %i    %6i   %i   %s\n",
			     j, j - data->prevjiffies, in_interrupt() ? 1 : 0,
			     current->pid, smp_processor_id(), current->comm);

	if (--data->loops) {
		data->prevjiffies = j;
		if (data->hi)
			tasklet_hi_schedule(&data->tlet);//继续调用自身
		else
			tasklet_schedule(&data->tlet);
	} else {//最后tasklet执行结束
		wake_up_interruptible(&data->wait);
	}
}

/* the /proc function: allocate everything to allow concurrency */
int jit_tasklet(char *buf, char **start, off_t offset,
	      int len, int *eof, void *arg)//使用小任务机制来延迟处理
{
	struct jit_data *data;
	char *buf2 = buf;
	unsigned long j = jiffies;
	long hi = (long)arg;

	data = kmalloc(sizeof(*data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	init_waitqueue_head (&data->wait);

	/* write the first lines in the buffer */
	buf2 += sprintf(buf2, "   time   delta  inirq    pid   cpu command\n");
	buf2 += sprintf(buf2, "%9li  %3li     %i    %6i   %i   %s\n",
			j, 0L, in_interrupt() ? 1 : 0,
			current->pid, smp_processor_id(), current->comm);

	/* fill the data for our tasklet function */
	data->prevjiffies = j;
	data->buf = buf2;
	data->loops = JIT_ASYNC_LOOPS;
	
	/* register the tasklet *///注册tasklet
	tasklet_init(&data->tlet, jit_tasklet_fn, (unsigned long)data);
	data->hi = hi;
	if (hi)
		tasklet_hi_schedule(&data->tlet);//高优先级的tasklet
	else
		tasklet_schedule(&data->tlet);

	/* wait for the buffer to fill */
	wait_event_interruptible(data->wait, !data->loops);//等待循环结束

	if (signal_pending(current))
		return -ERESTARTSYS;
	buf2 = data->buf;
	kfree(data);
	*eof = 1;
	return buf2 - buf;
}



int __init jit_init(void)
{
	create_proc_read_entry("currentime", 0, NULL, jit_currentime, NULL);//创建/proc目录下的文件并将文件和读文件的函数相关联
	create_proc_read_entry("jitbusy", 0, NULL, jit_fn, (void *)JIT_BUSY);//函数最后一个参数是私有数据
	create_proc_read_entry("jitsched",0, NULL, jit_fn, (void *)JIT_SCHED);
	create_proc_read_entry("jitqueue",0, NULL, jit_fn, (void *)JIT_QUEUE);
	create_proc_read_entry("jitschedto", 0, NULL, jit_fn, (void *)JIT_SCHEDTO);

	create_proc_read_entry("jitimer", 0, NULL, jit_timer, NULL);
	create_proc_read_entry("jitasklet", 0, NULL, jit_tasklet, NULL);
	create_proc_read_entry("jitasklethi", 0, NULL, jit_tasklet, (void *)1);//最后的1表示使用高优先级的tasklet

	return 0; /* success */
}

void __exit jit_cleanup(void)
{
	remove_proc_entry("currentime", NULL);
	remove_proc_entry("jitbusy", NULL);
	remove_proc_entry("jitsched", NULL);
	remove_proc_entry("jitqueue", NULL);
	remove_proc_entry("jitschedto", NULL);

	remove_proc_entry("jitimer", NULL);
	remove_proc_entry("jitasklet", NULL);
	remove_proc_entry("jitasklethi", NULL);
}

module_init(jit_init);
module_exit(jit_cleanup);


测试设备
#dd bs=20 count=5 < /proc/jitsched || jitqueue || jitbusy || jitschedto
内核会反复调用读设备函数,来填充用户要求的数据缓冲区。
书上说不能用cat命令读,cat每次读取4KB才返回,会使计算机冻结205s,因为内核每次调用读函数都会延迟1s,每次只读20B,那么4K则会延迟(4K/20B)=204.8s没错。
在我虚拟机上cpu空闲的情况下运行结果是:
jitbusy输出两次数据有1000+的差,jitsched则都是整整1000的差。
jitqueue和jitschedto只是偶尔有1000+i的差(0<i<5)
(驱动默认延迟1s,而1s内有1000次时钟中断,每次中断jiffies自加1)


测试其他设备
#cat /proc/jitimer || jitasklet || jitasklethi


具体细节书上有写,看书就知道了。
关于这两个函数我有点疑问
wake_up_interruptible(&data->wait);
wait_event_interruptible(data->wait, !data->loops);//等待循环结束


是先唤醒等待队列,然后判断条件是否满足,满足为真则退出等待队列,否则继续等待,是吗?

如何学好linux设备驱动

Linux系统目前主要维护2.4和2.6两个内核版本,在http://www.kernel.org/ 网站上已经可以下载到最新的2.6内核linux-2.6.38.6,及最新的2.4内核linux-2...
  • shuilaner_
  • shuilaner_
  • 2017年01月10日 16:32
  • 1409

Linux设备驱动程式学习(10)-时间、延迟及延缓操作

 度量时间差时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据 HZ 值来设定,HZ 是个体系依赖的值,在 中定义或该文档包含的某个子平台相关文档中。作为通用的规则,即便假如知道 HZ 的值...
  • luckdog01
  • luckdog01
  • 2008年07月29日 18:48
  • 397

Linux设备驱动程式学习(10)-时间、延迟及延缓操作

度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据 HZ 值来设定,HZ 是个体系依赖的值,在 中定义或该文档包含的某个子平台相关文档中。作为通用的规则,即便假如知道 ...
  • jeffade
  • jeffade
  • 2012年05月11日 14:52
  • 414

Linux驱动学习--时间、延迟及延缓操作

Tasklets 另一个有关于定时的内核设施是 tasklet。它类似内核定时器:在中断时间运行且运行同一个 CPU 上, 并接收一个 unsigned long 参数。不同的是:无法要求在一个指...
  • zwj0403
  • zwj0403
  • 2011年08月16日 15:52
  • 348

时间、延迟及延缓操作

时间、延迟及延缓操作       度量时间差 概念: 时钟中断:由系统定时硬件以周期性的间隔产生 hz:上述间隔由hz的值设定,hz是一个与体系结构相...
  • lenflow
  • lenflow
  • 2014年07月11日 00:35
  • 434

【驱动】linux设备驱动·入门

linux设备驱动    驱动程序英文全称Device Driver,也称作设备驱动程序。驱动程序是用于计算机和外部设备通信的特殊程序,相当于软件和硬件的接口,通常只有操作系统能使用驱动程序。    ...
  • hackdevil
  • hackdevil
  • 2013年06月27日 17:39
  • 384

Windows延缓写入失败 问题的解决方法

         首先声明,这里的解决方法可能不能适用于所有“windows延缓写入失败”的情况。问题描述:       将文件,特别是大文件(如:视频文件)复制到指定的盘符下(如从C盘将视频文件复制...
  • vividboy
  • vividboy
  • 2006年09月03日 21:42
  • 45888

linux设备驱动之时间,延迟及延缓操作(实践)

一 jit.函数源码 /* * jit.c -- the just-in-time module * * Copyright (C) 2001,2003 Alessandro Rubini a...
  • sunstars2009918
  • sunstars2009918
  • 2012年03月21日 17:23
  • 606

【Linux开发】linux设备驱动归纳总结(一):内核的相关基础概念

linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
  • LG1259156776
  • LG1259156776
  • 2016年05月16日 11:55
  • 566

第七章--时间、延迟及延缓操作

本文作为第七章--时间、延迟及延缓操作,主要讲述: 1、HZ、jiffies。 2、短延迟。 3、定时器。...
  • apple_guet
  • apple_guet
  • 2014年03月13日 10:58
  • 669
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux设备驱动学习(6) 时间、延迟、延缓操作 jit类设备驱动
举报原因:
原因补充:

(最多只允许输入30个字)