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);//等待循环结束
是先唤醒等待队列,然后判断条件是否满足,满足为真则退出等待队列,否则继续等待,是吗?