220 likes | 231 Views
Timers and Time Management. Ok-Kyun Ha 2007.05.18. Contents. Kernel Notion of Time The Tick Rate: HZ Jiffies Hardware Clocks and Timers The Timer Interrupt Handler Timers Delaying Execution. Introduction. The passing of time is very important to the kernel
E N D
Timers and Time Management Ok-Kyun Ha 2007.05.18
Contents • Kernel Notion of Time • The Tick Rate: HZ • Jiffies • Hardware Clocks and Timers • The Timer Interrupt Handler • Timers • Delaying Execution
Introduction • The passing of time is very important to the kernel • almost kernel functions are time driven (not event driven) • Focus (in this chapter) - system timer and timer interrupt - the system timer is a programmable piece of hardware that issues an interrupt at a fixed frequency - dynamic timers - used to schedule events that run once after specified time complete 3
Kernel notion of Time • Kernel can comprehend and manage time through tick rate of system timer in hardware • When the system timer goes off, it issues an interrupt that the kernel handles via a special interrupt handler • The kernel keeps track of it simply because the kernel controls the timer interrupt • The timer interrupt is very important to the management of the operating system 4
The Tick Rate: HZ • The tick rate is programmed on system boot based on a static preprocessor define, HZ • The value of HZ differs for each supported architectures • The kernel defines the value in <asm/param.h> #define HZ 1000 /* internal kernel time frequency */ • When writing kernel code, never assume that HZ has any given value 5
The Tick Rate - The Ideal HZ Value • Changing its frequency has a reasonable impact on the system • Increasing the tick rate means the timer interrupt runs more frequently • Benefits - the timer interrupt has a higher resolution - all timed events have a higher resolution - the accuracy of timed events improves • Downside - implies more frequent timer interrupts - implies higher overhead - processor must spend more time executing the timer interrupt handler 6
Jiffies • The global variable jiffies holds the number of ticks that have occurred since the system booted extern unsigned long volatile jiffies; /* <linux/jiffies.h> */ - on boot: jiffies = 0; - each timer interrupt: jiffies++; • convert forms of jiffies - second to jiffies: (second*HZ) - jiffies to second: (jiffies/HZ) ex) unsigned long next_tick = jiffies + _1; /* one tick from now */ unsigned long later = jiffies + 5 * HZ; /* five second from now */ 7
jiffies_64 (jiffies on 64-bit machines) bit 63 31 0 jiffies on 32-bit machine Jiffies – Internal Representation of Jiffies • The jiffies variable has 32 bit in size on 32-bit architectures and 64-bit on 64-bit architectures - overflow: 49.7days (1000HZ / 32-bit) • jiffies_64 extern u64 jiffies_64; /* <jiffies.h> */ - overlays the jiffies variable over the start of the jiffies_64 variable - jiffies is the lower 32 bit of the full 64-bit jiffies_64 variable - time management code uses the entire 64 bits - prevents overflow of the full 64-bit value 8
Jiffies – Jiffies Wraparound • equal to maximum and it is incremented, it wraps around to zero • macros for comparing tick counts #define time_after(unknown, known) ((long)(known) – (long)(unknown) < 0) #define time_before(unknown, known) ((long)(unknown) – (long)(known) < 0) #define time_after_eq(unknown, known) ((long)(unkown) – (long)(known) >= 0) #define time_before_eq(unknown, known) ((long)(known) – (long)(unkown) >= 0) unsigned long timeout = jiffies + HZ/2; /* timeout in 0.5s */ /* … */ if (timeout > jiffies) { /* we did not time out, good … */ } else { /* we time out, error … */ } unsigned long timeout = jiffies + HZ/2; /* timeout in 0.5s */ /* … */ if (time_after(jiffies, timeout)) { /* we did not time out, good … */ } else { /* we time out, error … */ } 9
Hardware Clock and Timers • Real-Time Clock (RTC) - a nonvolatile device for storing the system time - the kernel reads the RTC and use it to initialize the wall time - stored in the ‘xtime’variable • System Timer - serves a much more important (and frequent) role in the kernel’s timekeeping - provide a mechanism for driving an interrupt at a periodic rate - primary system timer is the PIT (on x86) - the kernel programs the PIT on boot to drive the system timer interrupt at HZ frequency 10
The Timer Interrupt Handler (1/3) • The architecture-dependent routine - registered as the interrupt handler for the system timer • Obtain the xtime_lock lock, which protects access to jiffies_64 and the wall time value, xtime • Acknowledge or reset the system timer as required • Periodically save the updated wall time to the real time clock • Call the architecture-independent timer routine, do_timer() • The architecture-independent routine: do_timer() • Increment the jiffies_64 count by one • Update resource usages for the currently running process (such as system and user time) • Run any dynamic timers that have expired • Execute scheduler_tick() • Update the wall time, which is stored in xtime • Calculate the load average 11
The Timer Interrupt Handler (2/3) • do_timer( ) void do_timer (struct pt_regs *regs) { jiffies_64 ++; update_process_times(user_mode(regs)); update_times( ); } void update_process_times(int user_tick) { struct task_struct *p = current; int cpu = smp_processor_id(); int system = user_tick ^ 1; update_one_process(p, user_tick, system, cpu); run_local_timers(); scheduler_tick(user_tick, system); } 12
The Timer Interrupt Handler (3/3) • update_times() • all this occurs every 1/HZ of a second void update_times(void) { unsigned long ticks; ticks = jiffies – wall_jiffies; if (ticks) { wall_jiffies += ticks; update_wall_time (ticks); } last_time_offset = 0; calc_load(ticks); } 13
Timers • Bottom-half - used great for deferring work until later - not so much to delay work, but simply to not do the work now • Kernel Timer - can delaying a work during a specified amount of time - easy to use • perform some initial setup • specify an expiration time • specify a function to execute upon said expiration • activate the timer 14
Timers – Using Timer (1/2) • represented by struct timer_list struct timer_list { struct list_head entry; /* entry in linked list of timers */ unsigned long expires; /* expiration value, in jiffies */ spinlock_t lock; /* lock protecting this timer */ void (*function)(unsigned long); /* the timer handler function */ unsigned long data; /* lone argument to the handler */ struct tvec_t_base_s *base; /* internal timer field */ }; 15
Timers – Using Timer (2/2) • Using 1. define using timer struct timer_list my_timer; 2. initialize the timer init_timer(&my_timer); 3. setting the timer funtions my_timer.expires = jiffies + delay; my_timer.data = 0; my_timer.function = my_function; 4. activate the timer add_timer(&my_timer); 16
Timers – Timer Race condition • potential race conditions exist (run asynchronously) - a timer could be executed another processor in multi-processing system - a potential race condition that must be guarded against exists when deleting timers • delete timer - del_timer(&my_timer); - del_timer_sync(&my_timer); • modify timer: changes the expiration of a given timer - del_timer(my_timer); my_timer->expires = jiffies + new_delay; add_timer(my_timer); - mod_timer(&my_timer, jiffies+new_delay); 17
Timers – The Timer Implementation • The Timer Implementation - the kernel executes timers in bottom-half context after the timer interrupt completes - the timer interrupt handler runs update_process_times() which calls run_local_timers() - the kernel partitions timers into five groups based on their expiration value - timers move down through the groups as their expiration time draws closer void run_local_timers(void) { raise_softirq(TIMER_SOFTIRQ); } 18
Delaying Execution • kernel code (especially drivers) needs a way to delay execution for some time without using timers or bottom-half mechanism - usually to allow hardware time to complete a given task - network card driver should wait at least the two microseconds before continuing • the solutions (depending on the semantics of the delay) - Busy Looping - Small Delays - schedule_timeout() 19
Delaying Execution – Busy looping • Busy looping: spin in a loop until the desired number of clock ticks pass • reschedule for current process - conditionally invokes the scheduler only if there is some more important task to run unsigned long delay = jiffies + 5 * HZ; while (time_before(jiffies, delay)) ; unsigned long delay = jiffies + 5 * HZ; while (time_before(jiffies, delay)) cond_resched(); 20
Delaying Execution – Small Delays • requires very short and rather precise delays - kernel provide two functions for microsecond and millisecond delays - delays execution by busy looping for the specified number of microseconds or milliseconds • using the functions - the udelay() function should be called only for small delays - larger delays on fast machines might result in overflow - as delays over 1 millisecond for longer durations mdelay() function should be called void udelay(unsigned long usecs); /* udelay(150) */ void mdelay(unsigned long msecs); /* mdelay(150) */ 21
Delaying Execution – schedule_timeout() • optimal method - task to sleep until at least the specified time has elapsed - the kernel wakes the task up and places it back on the runqueue • using the functions • sleeping on a waiting queue, with a timeout - first occur want to make desirable to wait for a specific event or wait for a specified time to elapse - call schedule_timeout() instead of schedule() after placing itself on a wait queue /* set task’state to interruptible sleep */ set_current_state(TASK_INTERRUPTIBLE); /*TASK_UNINTERRUPTIBLE */ /* take a nap and wake up in “s” seconds */ schedule_timeout(s * HZ); 22