`
mmdev
  • 浏览: 12916533 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

【Linux 驱动】第七章 时间 延迟及延缓操作 (例子详解一)

 
阅读更多

本文所涉及实验为博文http://blog.csdn.net/tianshuai11/article/details/7465587中示例,请先阅读上述博文,然后消化以下例子

一,模块方法

jit.c

#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>

#include <linux/sched.h>  //schedule() 
/*
 * 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; /* s3c24x0 HZ= 200*/

module_param(delay, int, 0);

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

/*
当驱动程序需要延迟比较长的时间,则要用一个长时钟滴答,而忙等待则是其中最不常用到的一种,此方法就是在等待期间锁定处理器,在没有完成延迟之前,处理器始终处于等待中,此方法一般很少使用,因为会大大的降低系统性能,如果不是抢占式内核中应用时,则更加可怕,因为调度器不会抢占在处理器中的进程,则在此延迟期间,处理器就如死掉一般。如果在时间未来临前,中止了中断,则系统就死了。下面是这个方法的程序片段:
 */
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;
     /*下面代码主要是执行延迟时间,然后通过 jiffies显示出来*/
	switch((long)data) {
		case JIT_BUSY: //忙等待
			while (time_before(jiffies, j1))
				cpu_relax();
			break;
		case JIT_SCHED:  //在计算机空闲时运行空闲任务
			while (time_before(jiffies, j1)) {
				schedule();
			}
			break;
		case JIT_QUEUE:  //超时执行代码
			wait_event_interruptible_timeout(wait, 0, delay);
			break;
		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);
	*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);//此函数用秒或者毫秒来填充一个struct timeval的指针变量
	tv2 = current_kernel_time();//内核辅助函数,用以访问struct timeval的两个成员

	/* 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;  /*if you want currentime to output only onec ,disable this line!*/
	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->prevjiffies = j;
		mod_timer(&data->timer, data->timer.expires+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 {
		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_init(&data->tlet, jit_tasklet_fn, (unsigned long)data);
	data->hi = hi;
	if (hi)
		tasklet_hi_schedule(&data->tlet);
	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);
	create_proc_read_entry("jitbusy", 0, NULL, jit_fn, (void *)JIT_BUSY); //根据最后不同的data 调用 jit_fn 不同代码段
	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);

	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);  //insmod  jit.ko
module_exit(jit_cleanup);  //rmmod  jit.ko

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

Makefile

KERNELDIR ?= /lib/modules/$(shell uname -r)/build    
PWD := $(shell pwd)
obj-m := jit.o 
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean


测试:

root@ubuntu:~/桌面/jit# make

root@ubuntu:~/桌面/jit# insmod jit.ko

root@ubuntu:~/桌面/jit# lsmod //查询安装完毕

其中文件currentime,jitbusy,jitsched,jitqueue,jitschedto,jitimer,jitasklet,jitasklethi都是模块jit创建

root@ubuntu:~/桌面/jit#head -6 /proc/currentime //查询currentime 文件的前六行(currentime 为jit程序创建)

/*返回的值为:当前的 jiffies 和 jiffies_64 值, 以 16 进制数的形式.

如同 do_gettimeofday 返回的相同的当前时间.

由 current_kernel_time 返回的 timespec.*/


0x004e4a3e 0x00000001004e4a3e 1334565617.514572
1334565617.511113437
0x004e4a3e 0x00000001004e4a3e 1334565617.514575
1334565617.511113437
0x004e4a3e 0x00000001004e4a3e 1334565617.514576
1334565617.511113437


root@ubuntu:~/桌面/jit# dd bs=20 count=5 < /proc/jitbusy //dd 命令参见【补充1】
5183602 5183852
5183852 5184102
5184102 5184352
5184352 5184602
5184602 5184852
记录了5+0 的读入
记录了5+0 的写出
100字节(100 B)已复制,4.99935 秒,0.0 kB/秒

root@ubuntu:~/桌面/jit# dd bs=20 count=5 < /proc/jitsched
5198924 5199174
5199174 5199424
5199424 5199674
5199674 5199924
5199924 5200174
记录了5+0 的读入
记录了5+0 的写出
100字节(100 B)已复制,4.99645 秒,0.0 kB/秒

root@ubuntu:~/桌面/jit# dd bs=20 count=5 < /proc/jitqueue
5205914 5206164
5206164 5206414
5206414 5206664
5206664 5206914
5206914 5207164
记录了5+0 的读入
记录了5+0 的写出
100字节(100 B)已复制,4.99969 秒,0.0 kB/秒

root@ubuntu:~/桌面/jit# dd bs=20 count=5 < /proc/jitschedto
5212110 5212360
5212360 5212610
5212610 5212860
5212860 5213110
5213110 5213360
记录了5+0 的读入
记录了5+0 的写出
100字节(100 B)已复制,4.99915 秒,0.0 kB/秒

root@ubuntu:~/桌面/jit# cat /proc/jitimer //inirp有没有被中断
time delta inirq pid cpu command
5226700 0 0 4625 3 cat
5226710 10 1 0 3 kworker/0:1
5226720 10 1 0 3 kworker/0:1
5226730 10 1 0 3 kworker/0:1
5226740 10 1 0 3 kworker/0:1
5226750 10 1 0 3 kworker/0:1

root@ubuntu:~/桌面/jit# cat /proc/jitasklet
time delta inirq pid cpu command
5236006 0 0 4626 2 cat
5236006 0 1 13 2 ksoftirqd/2
5236006 0 1 13 2 ksoftirqd/2
5236006 0 1 13 2 ksoftirqd/2
5236006 0 1 13 2 ksoftirqd/2
5236006 0 1 13 2 ksoftirqd/2

root@ubuntu:~/桌面/jit# cat /proc/jitasklethi
time delta inirq pid cpu command
5241403 0 0 4627 2 cat
5241403 0 1 13 2 ksoftirqd/2
5241403 0 1 13 2 ksoftirqd/2
5241403 0 1 13 2 ksoftirqd/2
5241403 0 1 13 2 ksoftirqd/2
5241403 0 1 13 2 ksoftirqd/2


【补充1】dd 是 Linux/UNIX 下的一个非常有用的命令,作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。 dd 的主要选项:

  指定数字的地方若以下列字符结尾乘以相应的数字:
  b=512, c=1, k=1024, w=2, xm=number m
  if=file 输入文件名,缺省为标准输入。
  of=file 输出文件名,缺省为标准输出。
  ibs=bytes 一次读入 bytes 个字节(即一个块大小为 bytes 个字节)。
  obs=bytes 一次写 bytes 个字节(即一个块大小为 bytes 个字节)。
  bs=bytes 同时设置读写块的大小为 bytes ,可代替 ibs 和 obs 。
  cbs=bytes 一次转换 bytes 个字节,即转换缓冲区大小。
  skip=blocks 从输入文件开头跳过 blocks 个块后再开始复制。
  seek=blocks 从输出文件开头跳过 blocks 个块后再开始复制。(通常只有当输出文件是磁盘或磁带时才有效)
  count=blocks 仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数。
  conv=conversion[,conversion...]
  用指定的参数转换文件。
  转换参数:
  ascii 转换 EBCDIC 为 ASCII。
  ebcdic 转换 ASCII 为 EBCDIC。
  ibm 转换 ASCII 为 alternate EBCDIC.
  block 把每一行转换为长度为 cbs 的记录,不足部分用空格填充。
  unblock
  使每一行的长度都为 cbs ,不足部分用空格填充。
  lcase 把大写字符转换为小写字符。
  ucase 把小写字符转换为大写字符。
  swab 交换输入的每对字节。
  noerror出错时不停止。
  notrunc不截短输出文件。
  sync 把每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。
  由于 dd 命令允许二进制方式读写,所以特别适合在原始物理设备上进行输入/输出。例如可以用下面的命令为软盘建立镜像文件:
  dd if=/dev/fd0 of=disk.img bs=1440k
  有趣的是,这个镜像文件能被 HD-Copy ,Winimage 等工具软件读出。再如把第一个硬盘的前 512 个字节存为一个文件:
  dd if=/dev/hda of=disk.mbr bs=512 count=1



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics