第7章-时间管理.课件

上传人:大米 文档编号:568649506 上传时间:2024-07-25 格式:PPT 页数:42 大小:796KB
返回 下载 相关 举报
第7章-时间管理.课件_第1页
第1页 / 共42页
第7章-时间管理.课件_第2页
第2页 / 共42页
第7章-时间管理.课件_第3页
第3页 / 共42页
第7章-时间管理.课件_第4页
第4页 / 共42页
第7章-时间管理.课件_第5页
第5页 / 共42页
点击查看更多>>
资源描述

《第7章-时间管理.课件》由会员分享,可在线阅读,更多相关《第7章-时间管理.课件(42页珍藏版)》请在金锄头文库上搜索。

1、11 13 32 2第第7 章章 时间管理时间管理度量时间差度量时间差延迟执行延迟执行 内核定时器内核定时器2度量时间差度量时间差度量时间差度量时间差系统定时硬件周期性地产生中断信号,每秒产生中断的次数称为系统定时硬件周期性地产生中断信号,每秒产生中断的次数称为HZ。 HZ 是一个依赖体系结构的值是一个依赖体系结构的值, 定义在定义在 中中或者该文件包含的某一个子平台文件中。或者该文件包含的某一个子平台文件中。 在发布的内核源码中的在发布的内核源码中的缺省值在真实硬件上为缺省值在真实硬件上为 50 1200 ,大部分平台为,大部分平台为100 或者或者 1000; 流行的流行的 x86 PC

2、缺省是缺省是1000,( 以前版本是以前版本是 100)增大增大HZ值可提高时间分辨率,但会增加系统负担值可提高时间分辨率,但会增加系统负担每次当时钟中断发生时每次当时钟中断发生时, 内核内部计数器的值就增加一。这个计内核内部计数器的值就增加一。这个计数器在系统启动时初始化为数器在系统启动时初始化为 0, 因此它代表从最近一次启动以来的因此它代表从最近一次启动以来的时钟中断时钟中断/滴答的数目。这个计数器是一个滴答的数目。这个计数器是一个 64位变量位变量( 即便在即便在 32位的体系上位的体系上),称为,称为 jiffies_64。 驱动开发者通常使用驱动开发者通常使用 jiffies 变量

3、变量, 一个一个 unsigned long, 或者和或者和 jiffies_64 是相同或者是它的最低是相同或者是它的最低32位。位。 使用使用 jiffies 常常是首选常常是首选, 因为它更快因为它更快, 并且在并且在32位架构上对位架构上对jiffies_64 的访问不是原子的的访问不是原子的.3度量时间差度量时间差使用使用 jiffies 计数器计数器#include unsigned long j, stamp_1, stamp_half, stamp_n; j = jiffies; stamp_1 = j + HZ; stamp_half = j + HZ/2; stamp_n

4、= j + n * HZ / 1000; /* n milliseconds */ 32位位 平台上当平台上当 HZ 是是 1000 时时, 计数器约计数器约 50 天溢出一天溢出一次次, 驱动程序应当注意这个事件。驱动程序应当注意这个事件。 使用下面的宏定义进行时间比较是安全的使用下面的宏定义进行时间比较是安全的4度量时间差度量时间差#include int time_after(unsigned long a, unsigned long b); a比比b靠后返回真靠后返回真int time_before(unsigned long a, unsigned long b); a比比b靠前返

5、回真靠前返回真int time_after_eq(unsigned long a, unsigned long b); a比比b靠后或相等返回真靠后或相等返回真int time_before_eq(unsigned long a, unsigned long b); a比比b靠前或相等返回真靠前或相等返回真5度量时间差度量时间差计算时间差计算时间差diff = (long)t2 - (long)t1;将差转化为毫秒将差转化为毫秒msec = diff * 1000 / HZ; 保存当前时间(墙上时间)的结构体保存当前时间(墙上时间)的结构体struct timespec xtime;struc

6、t timespec _kernel_time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */;xtime.tv_sec存放着自存放着自1970年年1月月1日(日(UTC)以来的时间,)以来的时间,1970年年1月月1日称为纪元日称为纪元6度量时间差度量时间差当前时间与内核jiffies变量的转换#include unsigned long timespec_to_jiffies(struct timespec *value); void jiffies_to_timespec(unsigned long jiffies, str

7、uct timespec *value)unsigned long timeval_to_jiffies(struct timeval *value); void jiffies_to_timeval(unsigned long jiffies, struct timeval *value); void do_gettimeofday(struct timeval *tv);/*返回自Epoch 以来的秒和微秒数*/struct timespec current_kernel_time(void);/*返回当前时间,精度1个jiffies */struct timespec xtime;str

8、uct timespec _kernel_time_t tv_sec; /*秒 */ long tv_nsec; /*纳秒 */;struct timeval xtime;struct timeval _kernel_time_t tv_sec; /*秒 */ _kernel_suseconds_t tv_usec; /*微秒 */;71 13 32 2第第7 章章 时间管理时间管理度量时间差度量时间差延迟执行延迟执行 内核定时器内核定时器8延迟执行延迟执行1、长延时、长延时延迟长于一个时钟滴答延迟长于一个时钟滴答忙等待忙等待while (time_before(jiffies, j1) cp

9、u_relax( );J1为延迟终止时间,为延迟终止时间,cpu_relax( )与架构相关,不执与架构相关,不执行大量处理器代码。存在问题:行大量处理器代码。存在问题:如内核配置为不可抢战,循环在延迟期间将锁住处理如内核配置为不可抢战,循环在延迟期间将锁住处理器器如内核配置为可抢战如内核配置为可抢战,处理器不会被锁住处理器不会被锁住,但忙等待浪但忙等待浪费费若循环前禁止了中断,若循环前禁止了中断,jiffies不会更新,将永远不会更新,将永远while9延迟执行延迟执行让出处理器让出处理器忙等待占着处理器,增加了系统负担,下面的延迟让忙等待占着处理器,增加了系统负担,下面的延迟让出处理器出处

10、理器:while (time_before(jiffies, j1) schedule( );当前进程虽然释放了当前进程虽然释放了CPU而不做任何事情,但它仍然而不做任何事情,但它仍然在运行队列中,如果系统中只有一个可运行的进程,在运行队列中,如果系统中只有一个可运行的进程,则又会立即运行。空闲任务得不到运行,耗电!则又会立即运行。空闲任务得不到运行,耗电!10延迟执行延迟执行超时超时#include long wait_event_timeout(wait_queue_head_t q, condition, long timeout);long wait_event_interruptib

11、le_timeout(wait_queue_head_t q, condition, long timeout);调用上述函数的进程会在给定的等待队列上休眠,但调用上述函数的进程会在给定的等待队列上休眠,但是会在超时到期时返回。是会在超时到期时返回。如果超时到期,函数返回如果超时到期,函数返回0;而如果进程由其他事件唤;而如果进程由其他事件唤醒,则会返回剩余的延迟时间,并用醒,则会返回剩余的延迟时间,并用jiffies表示表示上述函数的使用需要声明等待队列头,使用另一个函上述函数的使用需要声明等待队列头,使用另一个函数可免除此麻烦数可免除此麻烦11延迟执行延迟执行#include signed

12、 long schedule_timeout(signed long timeout);timeout是用是用jiffies表示的延迟时间,正常时返回表示的延迟时间,正常时返回0schedule_timeout要求调用者首先设置当前进程的状要求调用者首先设置当前进程的状态,例如:态,例如:set_current_state(TASK_INTERRUPTIBLE);schedule_timeout (delay);状态也可设置为状态也可设置为TASK_UNINTERRUPTIBLE12延迟执行延迟执行2、短延迟、短延迟有时驱动程序不但需要很短的延迟(比时钟节拍短),如有时驱动程序不但需要很短的延

13、迟(比时钟节拍短),如1ms,而且要求延迟精度高,此时前面基于而且要求延迟精度高,此时前面基于jiffies的长延迟就不能用了。的长延迟就不能用了。如如100Hz时钟,节拍间隔至少为时钟,节拍间隔至少为10ms,1000Hz时钟,其节拍间时钟,其节拍间隔也只能到隔也只能到1ms。#include void udelay(unsigned long usecs);void ndelay(unsigned long nsecs);void mdelay(unsigned long msecs);udelay以以忙等待方式忙等待方式将任务延迟指定的微秒,其他函基于此函将任务延迟指定的微秒,其他函基于

14、此函数实现数实现udelay的实现使用软件循环,根据引导期间计算出处理器速度的实现使用软件循环,根据引导期间计算出处理器速度及及loops_per_jiffy变量确定循环次数变量确定循环次数13延迟执行延迟执行上述延迟基于忙等等函数,延迟过程中无法运行其他上述延迟基于忙等等函数,延迟过程中无法运行其他任务任务下列基于下列基于schedule_timeout()的短延迟不涉及忙等待的短延迟不涉及忙等待void msleep(unsigned int millisecs);unsigned long msleep_interruptible(unsigned int millisecs);void

15、 ssleep(unsigned int seconds)上述函数将调用进程延迟指定的时间上述函数将调用进程延迟指定的时间延迟到期时返回值为延迟到期时返回值为0提前唤醒时返回值为原所求休眠时间的剩余毫秒数提前唤醒时返回值为原所求休眠时间的剩余毫秒数141 13 32 2第第7 章章 时间管理时间管理度量时间差度量时间差延迟执行延迟执行 内核定时器内核定时器15内核定时器内核定时器内核定时器是将任务推迟到将来的某个时间执行的一内核定时器是将任务推迟到将来的某个时间执行的一种机制种机制定时器的分类定时器的分类低分辨率定时器:基于周期性事件,也称经典定时器低分辨率定时器:基于周期性事件,也称经典定时

16、器高分辨率定时器:基于时钟事件,高分辨率定时器:基于时钟事件,2.6开始引进,可选开始引进,可选配置,编译进内核配置,编译进内核前面以及下面的讨论均指低分辨率定时器前面以及下面的讨论均指低分辨率定时器16内核定时器内核定时器内核定时器的实现基于内核定时器的实现基于TIMER_SOFTIRQ软中断软中断softirq17内核定时器内核定时器软中断的实现需要经过三个步骤:软中断的实现需要经过三个步骤:1.分配索引号分配索引号/软中断号软中断号2.注册注册/安装软中断处理程序安装软中断处理程序3.触发软中断触发软中断内核定时器基于软中断内核定时器基于软中断TIMER_SOFTIRQ,其实现,其实现也

17、需要上述步骤也需要上述步骤第第1步,分配索引号,步,分配索引号,已完成已完成第第2步,注册步,注册/安装软中断安装软中断TIMER_SOFTIRQ的处理程的处理程序,序,在何处安装在何处安装?18内核定时器内核定时器Linux系统初始化期间调用系统初始化期间调用init_timers()为软中断为软中断TIMER_SOFTIRQ安装中断处理程序安装中断处理程序run_timer_softirqvoid _init init_timers(void) int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,(voi

18、d *)(long)smp_processor_id(); init_timer_stats(); BUG_ON(err = NOTIFY_BAD); register_cpu_notifier(&timers_nb); open_softirq(TIMER_SOFTIRQ, run_timer_softirq);19内核定时器内核定时器软中断软中断TIMER_SOFTIRQ安装中断处理程序安装中断处理程序run_timer_softirq后需要触发才能运行,在何处触发?在定时器中断处理程序中触发定时器软中断TIMER_SOFTIRQ注册为中断处理程序注册为中断处理程序定时器中断处理程的核心函

19、数是定时器中断处理程的核心函数是do_timer()与与update_process_times(),定时器软中断在定时器软中断在update_process_times()中触发中触发20内核定时器内核定时器do_timer()do_timer()负责全系统范围的、全局性的任务:负责全系统范围的、全局性的任务:更新更新jiffies值值,处理进程统计处理进程统计。在多处理器系统上,会选择一个特定的。在多处理器系统上,会选择一个特定的CPU来来执行这两个任务,而不涉及其他执行这两个任务,而不涉及其他CPUvoid do_timer(unsigned long ticks) jiffies_64

20、 += ticks; update_wall_time(); calc_global_load();更新墙钟时间更新墙钟时间更新系统负载统计更新系统负载统计(确定在前1分钟、5分钟、15分钟内,平均有多少个就绪状态的进程在就绪队上等等)21内核定时器内核定时器update_process_times()函数/* Called from the timer interrupt handler to charge one tick to the current * process. user_tick is 1 if the tick is user time, 0 for system. */v

21、oid update_process_times(int user_tick)struct task_struct *p = current;int cpu = smp_processor_id();/* */account_process_tick(p, user_tick);run_local_timers();rcu_check_callbacks(cpu, user_tick);printk_tick();perf_event_do_pending();scheduler_tick();run_posix_cpu_timers(p);22内核定时器内核定时器account_proces

22、s_tick 使用 account_user_time 或 account_sys_time 来更新进程在用户态或核心态消耗的CPU时间,即 task structure 中的utime 或 stime成员。如果进程超出了Rlimit指定的CPU份额限制,那么还会每隔1秒发送 SIGXCPU 信号23内核定时器内核定时器run_local_timers触发定时器软中断TIMER_SOFTIRQ,或对其进行到期操作。void run_local_timers(void) hrtimer_run_queues(); /* raise the timer softirq */ raise_softi

23、rq(TIMER_SOFTIRQ); softlockup_tick();scheduler_tick是一个用于CPU调度器的辅助函数run_posix_cpu_timers使当前注册的POSIX定时器开始运行24内核定时器内核定时器软中断TIMER_SOFTIRQ被触发后,其处理程序run_timer_softirq稍后即可执行,处理程序做什么事?下面先介绍定时器数据结构与定时器管理再介绍处理程序25内核定时器内核定时器定时器数据结构定时器数据结构struct timer_list struct list_head entry; unsigned long expires; void (*f

24、unction)(unsigned long); unsigned long data; struct tvec_t_base_s *base;entry:定时器链表的入口expires:定时器到期时间,单位是jiffiesfunction:指向定时器处理函数,超时时调用base;指向一个基元素,其中的定时器按到期时间排序26内核定时器内核定时器kernel/timer.cstruct tvec_t_base_s .unsigned long timer_jiffies;tvec_root_t tv1;tvec_t tv2;tvec_t tv3;tvec_t tv4;tvec_t tv5; _

25、cacheline_aligned_in_smp;typedef struct tvec_s struct list_head vecTVN_SIZE; tvec_t;typedef struct tvec_root_s struct list_head vecTVR_SIZE; tvec_root_t;6425627内核定时器内核定时器第第1组组256项项,到期时间到期时间0255第第2组组64项项,第第1项到期时间项到期时间256256*2-1,第第2项到期时间项到期时间512256*3-1,第,第3项到期时间以此类推项到期时间以此类推第第3、4、5组与第组与第2组类似组类似base存储到

26、期时间为0的定时器的struct time_list到期时间1到期时间255到期时间256511到期时间512-76728内核定时器内核定时器1.第第1组中的索引项指向的数组元素,保存了稍后即将执行的组中的索引项指向的数组元素,保存了稍后即将执行的各定时器的各定时器的time_list实例。每当遇到一个时钟中断时,内实例。每当遇到一个时钟中断时,内核都扫描该链表,执行所有定时器函数,并将索引位置加核都扫描该链表,执行所有定时器函数,并将索引位置加1。刚执行过的定时器则从数据结构中移除。下一次发生时钟。刚执行过的定时器则从数据结构中移除。下一次发生时钟中断时,将执行新的数组位置上的的定时器,并将

27、其从数据中断时,将执行新的数组位置上的的定时器,并将其从数据结构中移除,同样将索引加结构中移除,同样将索引加1,依次类推。在所有项都处理,依次类推。在所有项都处理后,索引值为后,索引值为255,将索引值回,将索引值回0。2.将第将第2组的第组的第1项移至第项移至第1组的组的256项中,其余项依次前移。项中,其余项依次前移。第第3、4、5组亦如此。组亦如此。3.重复重复1的操作的操作29内核定时器内核定时器定时器处理程序定时器处理程序run_timer_softirq()static void run_timer_softirq(struct softirq_action *h) struct

28、tvec_base *base = _get_cpu_var(tvec_bases); hrtimer_run_pending(); if (time_after_eq(jiffies, base-timer_jiffies)_run_timers(base);30内核定时器内核定时器static inline void _run_timers(struct tvec_base *base)struct timer_list *timer;spin_lock_irq(&base-lock);while (time_after_eq(jiffies, base-timer_jiffies) wh

29、ile (!list_empty(head) void (*fn)(unsigned long); unsigned long data;timer = list_first_entry(head, struct timer_list,entry);fn = timer-function;data = timer-data;detach_timer(timer, 1);spin_unlock_irq(&base-lock);call_timer_fn(timer,fn,data);Spin_unlock_irq(&base-lock)_run_timers()31内核定时器内核定时器2、使用定

30、时器、使用定时器定时器的使用很简单,只需创建一个定时器结构体定时器的使用很简单,只需创建一个定时器结构体struct time_list,编写超时时执行的处理函数,然后激活即可,共,编写超时时执行的处理函数,然后激活即可,共3步。步。1)创建定时器结构体)创建定时器结构体DEFINE_TIMER(_name, _function, _expires, _data)静态创建一个timer_list结构实例name: timer_list结构名称function:处理函数expires:到期时间data:传给处理函数的参数struct timer_list struct list_head ent

31、ry;unsigned long expires;void (*function)(unsigned long); unsigned long data;struct tvec_t_base_s *base;32内核定时器内核定时器也可动态创建一个timer_list结构实例struct timer_list my_timer;init_timer(&my_timer);填充数据结构my_timer.expires = jiffies + delay; my_timer.data = 0; my_timer.function = my_function; struct timer_list s

32、truct list_head entry;unsigned long expires;void (*function)(unsigned long); unsigned long data;struct tvec_t_base_s *base;33内核定时器内核定时器2)编写定时器处理函数)编写定时器处理函数void my_timer_function(unsigned long data);3)激活定时器add_timer(&my_timer);/*将定时器插入管理数组中*/l定时器其他操作更改已激活定时器的超时时间mod_timer(&my_timer, jiffies + new_de

33、lay); /* new expiration */超时前删除定时器del_timer(&my_timer);del_timer_sync(&my_timer);34内核定时器内核定时器例例1 jitimer1)jitimer声明及初始化声明及初始化struct jit_data struct timer_list timer; struct tasklet_struct tlet; int hi; wait_queue_head_t wait; unsigned long prevjiffies; unsigned char *buf; int loops;35内核定时器内核定时器int j

34、it_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 i

35、n the buffer */buf2 += sprintf(buf2, time delta inirq pid cpu commandn);buf2 += sprintf(buf2, %9li %3li %i %6i %i %sn,j, 0L, in_interrupt() ? 1 : 0,current-pid, smp_processor_id(), current-comm);/* fill the data for our timer function */36内核定时器内核定时器data-prevjiffies = j;data-buf = buf2;data-loops = J

36、IT_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_event_interruptible(data-wait, !data-loops);if (signal_pending(current)return -ERESTARTSYS;buf2 = dat

37、a-buf;kfree(data);*eof = 1;return buf2 - buf;37内核定时器内核定时器2)编写)编写jitimer处理函数处理函数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 %sn, j, j - data-prevjiffies, in_interrupt() ? 1 : 0, current-

38、pid, smp_processor_id(), current-comm);if (-data-loops) data-timer.expires += tdelay;data-prevjiffies = j;add_timer(&data-timer); else wake_up_interruptible(&data-wait); 38内核定时器内核定时器int _init jit_init(void)create_proc_read_entry(currentime, 0, NULL, jit_currentime, NULL);create_proc_read_entry(jitbu

39、sy, 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,

40、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 */module_init(jit_init);39内核定时器内核定时器例例 2 shortprint1)声明及初始化定时器声明及初始化定时器 static struct timer_list shortp_timer; #define TIMEOUT 5*HZ /* Wait a

41、long time */在在shortp_init()中初始化中初始化 init_timer(&shortp_timer); shortp_timer.function = shortp_timeout; shortp_timer.data = 0; 40内核定时器内核定时器2)编写定时器处理函数)编写定时器处理函数static void shortp_timeout(unsigned long unused) unsigned long flags; unsigned char status; if (! shortp_output_active) return; spin_lock_irq

42、save(&shortp_out_lock, flags); status = inb(shortp_base + SP_STATUS);if (status & SP_SR_BUSY) = 0 | (status & SP_SR_ACK) shortp_timer.expires = jiffies + TIMEOUT;add_timer(&shortp_timer);spin_unlock_irqrestore(&shortp_out_lock, flags);return;spin_unlock_irqrestore(&shortp_out_lock, flags);shortp_int

43、errupt(shortp_irq, NULL, NULL);41内核定时器内核定时器3)激活定时器)激活定时器static void shortp_start_output(void)if (shortp_output_active) return; shortp_output_active = 1;shortp_timer.expires = jiffies + TIMEOUT;add_timer(&shortp_timer);queue_work(shortp_workqueue, &shortp_work);在在shortp_write()中调用上述函数中调用上述函数42内核定时器内核

44、定时器修改定时器修改定时器static void shortp_do_write(void) unsigned char cr = inb(shortp_base + SP_CONTROL); mod_timer(&shortp_timer, jiffies + TIMEOUT); outb_p(*shortp_out_tail, shortp_base+SP_DATA); shortp_incr_out_bp(&shortp_out_tail, 1); if (shortp_delay) udelay(shortp_delay); outb_p(cr | SP_CR_STROBE, shortp_base+SP_CONTROL); if (shortp_delay) udelay(shortp_delay); outb_p(cr & SP_CR_STROBE, shortp_base+SP_CONTROL);

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号