Title: Kernel Timers
1Kernel Timers
- Goals.
- Learn how to study kernel code.
- Learn how kernel implements interval timers.
- Learn how signals work in Linux.
- Create a user-space mechanism to measure the
execution time of a multithreaded program.
2Kernel Timers
- Overview.
- Kernel keeps the current time by reading a clock
device - maintains a kernel variable with the current time
- User-mode programs can access current time via
system calls. - gettimeofday( ) is the usual interface to the
current time. - kernel uses current time to determine when
currently running process should be removed from
CPU. - kernel also uses the current time to keep track
of user mode/supervisor mode time for process.
3clocks
- 4 clocks used in Linux
- Real Time Clock (RTC)
- Time Stamp Counter
- Programmable Interval Timer
- Timer of the local APICs (Advanced Programmable
Interrupt Controller) in SMP systems. Wont
cover here.
4clocks
- Real Time Clock (RTC)
- Regular clock that runs on a battery
- Often the Motorola 146818 CMOS RAM RTC
- Capable of issuing periodic interrupts on IRQ8
- So it can also work as an alarm clock
- But not used for interrupts, instead PIT is used
- Linux uses only to derive time and date
- Processes can program RTC by acting on the
/dev/rtc device file - Kernel accesses the RTC through 0x70 and 0x71 I/O
ports - SysAd can set up clock via clock system call
(acts on these two I/O ports)
5clocks
- Time Stamp Counter (TSC)
- All 80x86 microprocessors include a CLK input pin
which receives the clock signal of an external
oscillator - Starting with the Pentium, 80x86 processors
include a 64-bit Time Stamp Counter (TSC) - Can be read with rdtsc assembly language
instruction. - The register is a counter that is incremented at
each clock signal - If the clock ticks at 400MHz, TSC is incremented
once every 2.5 nanoseconds. - Allows Linux to get more accurate time
measurements than delivered by the Programmable
Interval Timer. - Linux must determine the clock signal frequency
while initializing the system. - Done during boot
6clocks
- Programmable Interval Timer (PIT)
- PIT acts as the alarm clock of the system
- Usually implemented by a 8254 CMOS chip
- Uses I/O ports 0x40-0x43
- Linux uses the PIT to issue timer interrupts on
IRQ0 at about 100-Hz frequency (every 10
milliseconds). - The timer interval is called a tick and is stored
in the variable tick - Tradeoff shorter ticks, faster kernel response
time, smoother multimedia, etc. - But then more CPU time is spent handling
interrupts - Alpha and IA-64 1,024 interrupts/second (1
millisecond)
7Kernel Time
- Beginning of UNIX epoch 12A.M. January 1, 1970
(000000 Greenwich Meridian Time (GMT)). - actually is a skip second. Doesnt exist!
- Kernel uses two long int variables
- number of seconds since beginning of epoch
- number of microseconds since beginning of last
second
8Kernel Time
- To read time from user space
- include ltsys/time.h.
- struct timeval theTime
- gettimeofday(theTime, NULL)
- where
- struct timeval
- long tv_sec
- long tv_usec
-
9Kernel Time
- call gettimeofday(theTime, NULL)
- on return the variable theTime.tv_sec contains
number of seconds since beginning of epoch - the variable theTime.tv_usec contains number of
microseconds that have elapsed since last second
began. - the date command translates kernel time to local
time and to Gregorian calendar time epoch.
10Kernel Time
- gettimeofday(theTime, NULL)
- uses sys_gettimeofday( ) in file /kernel/time.c
- Also see do_gettimeofday( ) in file
/arch/i386/kernel/time.c - Note that the / indicates the path to the source
on your computer. Usually is /usr/src/linux
11Kernel Time
- Computing time.
- All modern computers use same basic approach for
keeping track of time. - Hardware has timer device.
- can interrupt every K time units.
- For Linux K set to 10 milliseconds.
- System tracks time by counting number of
interrupts that have occurred since system was
booted. - If know when machine was last booted, can compute
time to nearest 10 milliseconds. - Know when machine was last booted via a
time-of-day clock in i386 machines. Runs with a
battery.
12Updating Kernel Time
bottom half
arch/i386/kernel/time.c
Clock ISR timer_interrupt( )
kernel variable
kernel/sched.c
jiffies
do-timer()
kernel/sched.c
timer_bh()
TIMER_BH
update_times()
update_process_times()
13Updating Kernel Time
arch/i386/kernel/time.c
Clock ISR timer_interrupt( )
lost_ticks counts number of ticks since the last
time the itimer was checked in the bottom half
kernel variable
kernel/sched.c
jiffies
do-timer()
lost_ticks
lost_ticks_system
TIMER_BH
14Updating Kernel Time
kernel variables
ret_from_sys-call()
TIMER_BH
jiffies
7. updates system wide timers
kernel/sched.c
timer_bh(void)
run_old_timers() run_timer_list()
lost_ticks
8. calls
update_times()
lost_ticks_system
9. update_times gets user ticks and system ticks
since last timer interrupt
15Updating Kernel Time
kernel/sched.c
update_times()
update_wall_time(ticks)
11. also calls update_process-times passes
arguments ticks and system
10. updates system time (using ticks) and stores
in kernel variable struct timeval xtime
ticks represent number of timer interrupts
during user time system represents number of
timer interrupts during system time
update_process_times()
13. calculates user/system ticks and calls
counter
12. updates counter for this process. When
counter expires, time slice is up.
update_one_process()
PCB variable
14. compares user ticks to processes
it_virt_value and raises SIGTALRM if bigger
15. does same with system ticks and
it_prof_value and raises SIGTPROF if bigger
16Per Process Timers
- kernel accumulates time and manages timers for
each process. - Per process time values are saved in processs
descriptor. - When kernel creates a task
- allocates a new task descriptor of type struct
task_struct - allocated from the kernels heap space
- uses kmalloc()call.
17Per Process Timers
- struct task_struct contains more than 75 fields
- struct task_struct
-
- long counter
-
- unsigned long policy, rt_priority
- unsigned long it_real_value, it_prof_value,
it_virt_value - unsigned long it_real_incr, it_prof_incr,
it_virt_incr - struct timer_list real_timer
- // contains tms_utime,tms_stime,tms_cutime,
tms_cstime - unsigned long start_time
- long per_cpu_utimeNR_CPUS,per_cpu_stime,cutime
,cstime -
18Per Process Timers
- Fields are updated in the update_process_times()fu
nction located in kernel/sched.c - This function is invoked as part of the
ret_from_sys_call bottom half processing. - This function calls update_one_process()
- update_one_process()calls other functions to
update the values and decide if a signal should
be raised to indicate that a timer has expired. - The counter field of the task_struct structure is
used to determine whether the process needs
scheduling attention.
19Per Process Timers
- Interval timers (it_XXX_value and it_XXX_incr)
- use the kernel time to keep track of 3 different
intervals of time - ITIMER_REAL Reflects the passage of real time.
- ITIMER_VIRTUAL Reflects the passage of virtual
time (i.e., in user mode) - ITIMER_PROF is the total time the process is
running (i.e., in both user and kernel mode) - The it_XXX_incr field stores the interval in
ticks between two signals - The it_XXX_value field stores the current value
of the timer.
20Per Process Timers
- Interval timers (it_XXX_value and it_XXX_incr)
- use the kernel time to keep track of 3 different
intervals of time - ITIMER_REAL Reflects the passage of real time.
- Implemented using it_real_value and it_real_incr
fields. - these values are updated each time a process is
triggered in the scheduler - when time expires, a SIGALRM is raised.
21Per Process Timers
- Interval timers (it_XXX_value and it_XXX_incr)
- ITIMER_VIRTUAL Reflects the passage of virtual
time. - This is time during which the process is active
but is not in a system call (system mode) - This time is incremented only when the
corresponding process is executing. - updated by the do_timer( ) routine
- when it expires, raises a SIGVTALRM.
- Implemented using it_virt_value and it_virt_incr
fields.
22Per Process Timers
- Interval timers (it_XXX_value and it_XXX_incr)
- ITIMER_PROF is the total time the process is
running. - Reflects the passage of time during which the
process is active (virtual time) plus the time
that the kernel is doing work on behalf of the
process (eg, reading a timer). - when this timer expires it sends a SIGPROF signal.
23Per Process Timer
- Each timer is actually a countdown timer
- periodically initialized to a prescribed value
- then reflects the passage of time by counting
down to 0 - when timer reaches 0, raises a signal to notify
another part of the system (in the OS or
user-space program) - then resets the value and begins counting down
again
24Per Process Timer
- Each timer initialized with setitimer()system
call - include ltsys/time.hgt
- setitimer(int timerType, const struct itimerval
value, - struct itimerval oldValue)
- The value type is wrong in the book.
25Per Process Timer
- Each timer initialized with setitimer()system
call - The struct itimerval includes the following
fields - strut itimerval
- struct timeval it_interval
- struct timeval it_value
-
26Per Process Timer
- Idea of setitimer(int timerType, const struct
itimerval value, - struct itimerval oldValue)
- ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF are
constants defined in the sys/time.h file. - timerType parameter is set to one of these
- value parameter is used to initialize second and
microsecond fields of the given timer. - it_value field of value defines the current value
for the timer, ie, the the initial value of the
timer. - it_interval field of value defines the value that
should be used to reset the timer when it reaches
zero. - Each of these two parameters has fields tv_sec
and tv_usec - oldVal is used to return the previous value of
the timer.
27Per Process Timer
- Read a timer with getitimer()system call
- include ltsys/time.hgt
- getitimer(int timerType, const struct itimerval
value) - Idea of getitimer()
- ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF are
constants defined in the sys/time.h file. - timerType parameter is set to one of these
- value parameter is used to return the value of
the given timer (fields tv_sec and tv_usec)
28Setting and Reading a Timer
- Code fragment to set ITIMER_REAL and then read
it. - include ltsys/time.hgt
- struct itimerval v
- v.it_interval.tv_sec9
- v.it_interval.tv_usec999999
- v.it_val.tv_sec9
- v.it_val.tv_usec999999
- setitimer(ITIMER_REAL,v,NULL)
29Setting and Reading a Timer
- Code fragment to read ITIMER_REAL
- include ltsys/time.hgt
- struct itimerval v
- getitimer(ITIMER_REAL,v)
- printf(d seconds, d microsections
- ,,v.it_value.tv_sec,v.it_value.tv_usec,)
30Project 3
- Part A. Use ITIMER_REAL to implement a function
that works like gettimeofday() - Raise a signal once per second
- Use the signal facility to determine when
ITIMER_REAL has been decremented to 0 and to
count the number of seconds that have elapsed.
31Project 3
- Part B. Design and implement facilities that use
the ITIMER_VIRTUAL and ITIMER_PROF interval
timers to profile a process - profile provides actual time of execution (use
the timer from part A) - also provides CPU time (time that the process is
actually running) - also provides user-space time
- also provides kernel-space time
- Use gettimeofday()to compute the programswall
clock runtime.
32Project 3
- Part B (cont)
- all times must have millisecond accuracy (to the
extent the hardware supports it) - Code for millisecond accuracy, may not get it.
- Use the signal facility to create signal handlers
to keep track of the number of seconds of virtual
and profile time - raise a signal once per second.
33Project 3
- Part C Spawn three children.
- Each child must recursively compute the Fibonacci
sequence for N 20, 30, 36 - Fibonacci code given in the outline for the
solution. - May take several minutes to compute N36
- Use the facilities from part A and B to determine
real time, virtual time, profile time for each of
the three process.
34Linux Source Code
- Read the code to understand how interval timers
work.
/usr/src/linux
Architecture dependent code kept in this directory
net
modules
arch
drivers
mm
block
Most code kept in the directories shown
lib
alpha
char
m68k
kernel
fs
i386
ipc
kernel
mm
.
Heart of kernel code kept here
ext2
fat
proc
init
include
35Linux Source Code
- Read the code to understand how interval timers
work.
/usr/src/linux
Architecture dependent code kept in this directory
net
modules
arch
drivers
mm
block
Most code kept in the directories shown
lib
alpha
char
m68k
kernel
fs
i386
ipc
itimer.c is only here
kernel
mm
.
ext2
fat
proc
init
process.c is also here
process.c is here
include
36Solution outline
include ltsys/time.hgt include ltsignal.hgt include
ltunistd.hgt include ltstdio.hgt long unsigned int
fibonacci(unsigned int n) static long
p_realt_secs0, c1_realt_secs0,
c2_realt_secs0 static long p_virtt_secs0,
c1_virtt_secs0, c2_virtt_secs0 static long
p_proft_secs0, c1_proft_secs0,
c2_proft_secs0 static struct itimerval p_realt,
c1_realt, c2_realt static struct itimerval
p_virtt, c1_virtt, c2_virtt static struct
itimerval p_proft, c1_proft, c2_proft
37Solution outline
main(int argc, char argv) long unsigned fib
0 int pid1, pid2 unsigned int fibarg
int status // Get command line argument,
fibarg // Initialize parent, child1, child 2,
and child 3 timer values // Enable your signal
handlers for the parent signal(SIGALRM, )
signal(SIGVTALRM, ) signal(SIGPROF, ) //
Set the parent's itimers
38Solution outline
pid1 fork() if (pid1 0) // Enable child
1 signal handlers (disable parent handlers) //
Set the child 1 itimers // Start child 1 on the
Fibonacci program fib fibonacci(fibarg) //
Read the child 1 itimer values, and report
them getitimer(ITIMER-PROF, ) getitimer(ITIME
R-REAL, ) getitimer(ITIMER-VIRTUAL, )
39Solution outline
printf("\n") printf("Child 1 fib ld, real
time ld sec, ld msec\n",fib,
c1_realt_secs, elapsed_usecs(c1_realt.it_value.tv
_sec,c1_realt.it_value.tv_usec)/1000) printf("Chi
ld 1 fib ld, cpu time ld sec, ld
msec\n",fib, c1_proft_secs, elapsed_usecs(c1_prof
t.it_value.tv_sec,c1_proft.it_value.tv_usec)/1000)
printf("Child 1 fib ld, user time ld sec,
ld msec\n",fib, c1-proft_secs -
c1_virtt_secs, elapsed_usecs(c1_virtt.it_value.tv
_sec,c1_virtt.it_value.tv_usec)/1000) printf("Chi
ld 1 fib ld, kernel time ld sec, ld
msec\n",fib, c1_proft_secs, (elapsed_usecs(c1_pro
ft.it_value.tv_sec,c1_proft.it_value.tv_usec)/1000
) - (elapsed_usecs(c1_virtt.it_value.tv_sec,c1_vi
rtt.it_value.tv_usec)/1000)) fflush(stdout) exit
(0) // end of child code
40Solution outline
else pid2 fork() if (pid2 0) //
Enable child 2 signal handlers (disable parent
handlers) // Set the child 2 itimers // Start
child 2 on the Fibonacci program fib
fibonacci(fibarg) // Read the child 2 itimer
values, and report them // lots more print
statements // end of if else // do the
third child stuff // end else // end
of else
41Solution outline
else / this is the parent / // Start the
parent on the Fibonacci program fib
fibonacci(fibarg) // Wait for the children
to terminate waitpid(0, status, 0)
waitpid(0, status, 0) waitpid(0, status,
0) // Read the parent itimer values and
report them // lots of print statements
// end of main
42Solution outline
long unsigned int fibonacci(unsigned int n) if
(n 0) return 0 else if (n 1 n
2) return 1 else return (fibonacci(n-1)
fibonacci(n-2))