/*
* This solution 1 based on those provided by
* Don Lindsay, Fall, 1995, and Sam Siewert, Spring, 1996
* comment by MANIO
*/
#include
<
sys
/
time.h
>
#include
<
signal.h
>
#include
<
unistd.h
>
#include
<
stdio.h
>
#define
MAXSEC 1
#define
MAXUSEC 0
long
unsigned
int
fibonacci(unsigned
int
n);
long
elapsed_usecs(
long
sec,
long
usec)
//
求出已经过去的时间 (纳秒)
{
if
(usec
&&
(sec
<
MAXSEC))
//
时间大于0,小于MAXSEC秒,则返回剩下的纳秒数
return
1000000
-
usec;
else
if
(usec
&&
(sec
>=
MAXSEC)) {
//
纳秒数大于0,秒数大于等于MAXSEC
printf(
"
possibly inaccurate interval time:
"
);
//
时间可能不准确
fflush(stdout);
//
将缓冲区内容写到文件stdout,即屏幕
return
0
;
}
else
return
0
;
//
纳秒数等于0或者秒数小于MAXSEC ,返回0
};
static
long
p_realt_secs
=
0
, c1_realt_secs
=
0
, c2_realt_secs
=
0
;
static
long
p_virtt_secs
=
0
, c1_virtt_secs
=
0
, c2_virtt_secs
=
0
;
static
long
p_proft_secs
=
0
, c1_proft_secs
=
0
, c2_proft_secs
=
0
;
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;
/*
struct itimerval {
struct timeval it_interval; // timer interval
struct timeval it_value; // current value
};
struct timeval {
time_t tv_sec; // seconds ,type is long
suseconds_t tv_usec; // microseconds ,type is long
};
*/
void
p_realt_handler()
{
p_realt_secs
++
;
//
记录这个函数被执行的次数,也是realtime走过的时间数
/*
printf("p_realt_handler ");
fflush(stdout);
*/
signal(SIGALRM, p_realt_handler);
//
当SIGALRM(ITIMER_REAL超时的时候产生)信号到来时,启动p_realt_handler函数
}
void
c1_realt_handler()
{
c1_realt_secs
++
;
/*
printf("c1_realt_handler ");
fflush(stdout);
*/
signal(SIGALRM, c1_realt_handler);
//
当SIGALRM(ITIMER_REAL超时的时候产生)信号到来时
}
void
c2_realt_handler()
{
c2_realt_secs
++
;
/*
printf("c2_realt_handler ");
fflush(stdout);
*/
signal(SIGALRM, c2_realt_handler);
//
当SIGALRM(ITIMER_REAL超时的时候产生)信号到来时
}
void
p_virtt_handler()
{
p_virtt_secs
++
;
/*
printf("p_virtt_handler ");
fflush(stdout);
*/
signal(SIGVTALRM, p_virtt_handler);
//
当SIGVTALRM(ITIMER_VIRTUAL超时的时候产生)信号到来时
}
void
c1_virtt_handler()
{
c1_virtt_secs
++
;
/*
printf("c1_virtt_handler ");
fflush(stdout);
*/
signal(SIGVTALRM, c1_virtt_handler);
}
void
c2_virtt_handler()
{
c2_virtt_secs
++
;
/*
printf("c2_virtt_handler ");
fflush(stdout);
*/
signal(SIGVTALRM, c2_virtt_handler);
}
void
p_proft_handler()
{
p_proft_secs
++
;
/*
printf("p_proft_handler ");
fflush(stdout);
*/
signal(SIGPROF, p_proft_handler);
}
void
c1_proft_handler()
{
c1_proft_secs
++
;
/*
printf("c1_proft_handler ");
fflush(stdout);
*/
signal(SIGPROF, c1_proft_handler);
}
void
c2_proft_handler()
{
c2_proft_secs
++
;
/*
printf("c2_proft_handler ");
fflush(stdout);
*/
signal(SIGPROF, c2_proft_handler);
}
//
把总时间设为1秒,则每一秒都超时,每一秒都产生信号
main(
int
argc,
char
**
argv)
{
long
unsigned fib
=
0
;
int
pid1, pid2;
unsigned
int
fibarg;
int
status;
if
(argc
==
2
)
sscanf(argv[
1
],
"
%ld
"
,
&
fibarg);
else
fibarg
=
30
;
printf(
"
fibarg = %ld
"
, fibarg);
p_realt.it_interval.tv_sec
=
MAXSEC;
//
REALTIME超时时间设为1秒
p_realt.it_interval.tv_usec
=
MAXUSEC;
p_realt.it_value.tv_sec
=
MAXSEC;
//
REALTIME当前时间设为1秒
p_realt.it_value.tv_usec
=
MAXUSEC;
p_virtt.it_interval.tv_sec
=
MAXSEC;
//
虚拟时间设为1秒
p_virtt.it_interval.tv_usec
=
MAXUSEC;
p_virtt.it_value.tv_sec
=
MAXSEC;
//
虚拟当前时间设为1秒
p_virtt.it_value.tv_usec
=
MAXUSEC;
p_proft.it_interval.tv_sec
=
MAXSEC;
//
PROF时间设为1秒
p_proft.it_interval.tv_usec
=
MAXUSEC;
p_proft.it_value.tv_sec
=
MAXSEC;
//
PROF当前时间设为1秒
p_proft.it_value.tv_usec
=
MAXUSEC;
c1_realt.it_interval.tv_sec
=
MAXSEC;
c1_realt.it_interval.tv_usec
=
MAXUSEC;
c1_realt.it_value.tv_sec
=
MAXSEC;
c1_realt.it_value.tv_usec
=
MAXUSEC;
c1_virtt.it_interval.tv_sec
=
MAXSEC;
c1_virtt.it_interval.tv_usec
=
MAXUSEC;
c1_virtt.it_value.tv_sec
=
MAXSEC;
c1_virtt.it_value.tv_usec
=
MAXUSEC;
c1_proft.it_interval.tv_sec
=
MAXSEC;
c1_proft.it_interval.tv_usec
=
MAXUSEC;
c1_proft.it_value.tv_sec
=
MAXSEC;
c1_proft.it_value.tv_usec
=
MAXUSEC;
c2_realt.it_interval.tv_sec
=
MAXSEC;
c2_realt.it_interval.tv_usec
=
MAXUSEC;
c2_realt.it_value.tv_sec
=
MAXSEC;
c2_realt.it_value.tv_usec
=
MAXUSEC;
c2_virtt.it_interval.tv_sec
=
MAXSEC;
c2_virtt.it_interval.tv_usec
=
MAXUSEC;
c2_virtt.it_value.tv_sec
=
MAXSEC;
c2_virtt.it_value.tv_usec
=
MAXUSEC;
c2_proft.it_interval.tv_sec
=
MAXSEC;
c2_proft.it_interval.tv_usec
=
MAXUSEC;
c2_proft.it_value.tv_sec
=
MAXSEC;
c2_proft.it_value.tv_usec
=
MAXUSEC;
//
事先设定,当此三种信号到来时,启用p字开头的三个函数
signal(SIGALRM, p_realt_handler);
signal(SIGVTALRM, p_virtt_handler);
signal(SIGPROF, p_proft_handler);
if
(setitimer(ITIMER_VIRTUAL,
&
p_virtt, (
struct
itimerval
*
)
0
)
==
-
1
)
//
设置ITIMER_VIRTUAL的值为struct p_virtt?????????????
perror(
"
parent virtual timer
"
);
if
(setitimer(ITIMER_REAL,
&
p_realt, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
parent real timer
"
);
if
(setitimer(ITIMER_PROF,
&
p_proft, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
parent profile timer
"
);
pid1
=
fork();
if
(pid1
==
0
) {
//
child process
signal(SIGALRM, c1_realt_handler);
//
设定c1开头的这三个函数为信号处理函数
signal(SIGVTALRM, c1_virtt_handler);
signal(SIGPROF, c1_proft_handler);
if
(setitimer(ITIMER_VIRTUAL,
&
c1_virtt, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
child 1 virtual timer
"
);
if
(setitimer(ITIMER_REAL,
&
c1_realt, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
child 1 real timer
"
);
if
(setitimer(ITIMER_PROF,
&
c1_proft, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
child 1 profile timer
"
);
fib
=
fibonacci(fibarg);
getitimer(ITIMER_PROF,
&
c1_proft);
getitimer(ITIMER_REAL,
&
c1_realt);
getitimer(ITIMER_VIRTUAL,
&
c1_virtt);
printf(
"
"
);
printf(
"
Child 1 fib = %ld, real time = %ld sec, %ld millisec
"
,
fib, c1_realt_secs,
elapsed_usecs(c1_realt.it_value.tv_sec,
c1_realt.it_value.tv_usec)
/
1000
);
printf(
"
Child 1 fib = %ld, cpu time = %ld sec, %ld millisec
"
,
fib, c1_proft_secs,
elapsed_usecs(c1_proft.it_value.tv_sec,
c1_proft.it_value.tv_usec)
/
1000
);
printf(
"
Child 1 fib = %ld, user time = %ld sec, %ld millisec
"
,
fib, c1_virtt_secs,
elapsed_usecs(c1_virtt.it_value.tv_sec,
c1_virtt.it_value.tv_usec)
/
1000
);
printf(
"
Child 1 fib = %ld, kernel time = %ld sec, %ld millisec
"
,
fib, c1_proft_secs
-
c1_virtt_secs,
(elapsed_usecs(c1_proft.it_value.tv_sec,
c1_proft.it_value.tv_usec)
/
1000
)
-
(elapsed_usecs(c1_virtt.it_value.tv_sec,
c1_virtt.it_value.tv_usec)
/
1000
));
fflush(stdout);
exit(
0
);
}
else
{
pid2
=
fork();
if
(pid2
==
0
) {
signal(SIGALRM, c2_realt_handler);
signal(SIGVTALRM, c2_virtt_handler);
signal(SIGPROF, c2_proft_handler);
if
(setitimer(ITIMER_VIRTUAL,
&
c2_virtt, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
child 1 virtual timer
"
);
if
(setitimer(ITIMER_REAL,
&
c2_realt, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
child 1 real timer
"
);
if
(setitimer(ITIMER_PROF,
&
c2_proft, (
struct
itimerval
*
)
0
)
==
-
1
)
perror(
"
child 1 profile timer
"
);
fib
=
fibonacci(fibarg);
getitimer(ITIMER_PROF,
&
c2_proft);
getitimer(ITIMER_REAL,
&
c2_realt);
getitimer(ITIMER_VIRTUAL,
&
c2_virtt);
printf(
"
"
);
printf(
"
Child 2 fib = %ld, real time = %ld sec, %ld millisec
"
,
fib, c2_realt_secs,
elapsed_usecs(c2_realt.it_value.tv_sec,
c2_realt.it_value.tv_usec)
/
1000
);
printf(
"
Child 2 fib = %ld, cpu time = %ld sec, %ld millisec
"
,
fib, c2_proft_secs,
elapsed_usecs(c2_proft.it_value.tv_sec,
c2_proft.it_value.tv_usec)
/
1000
);
printf(
"
Child 2 fib = %ld, user time = %ld sec, %ld millisec
"
,
fib, c2_virtt_secs,
elapsed_usecs(c2_virtt.it_value.tv_sec,
c2_virtt.it_value.tv_usec)
/
1000
);
printf(
"
Child 2 fib = %ld, kernel time = %ld sec, %ld millisec
"
,
fib, c2_proft_secs
-
c2_virtt_secs,
(elapsed_usecs(c2_proft.it_value.tv_sec,
c2_proft.it_value.tv_usec)
/
1000
)
-
(elapsed_usecs(c2_virtt.it_value.tv_sec,
c2_virtt.it_value.tv_usec)
/
1000
));
fflush(stdout);
exit(
0
);
}
else
{
/*
this is the parent
*/
fib
=
fibonacci(fibarg);
waitpid(
0
,
&
status,
0
);
waitpid(
0
,
&
status,
0
);
getitimer(ITIMER_PROF,
&
p_proft);
getitimer(ITIMER_REAL,
&
p_realt);
getitimer(ITIMER_VIRTUAL,
&
p_virtt);
printf(
"
"
);
printf(
"
Parent fib = %ld, real time = %ld sec, %ld millisec
"
,
fib, p_realt_secs,
elapsed_usecs(p_realt.it_value.tv_sec,
p_realt.it_value.tv_usec)
/
1000
);
printf(
"
Parent fib = %ld, cpu time = %ld sec, %ld millisec
"
,
fib, p_proft_secs,
elapsed_usecs(p_proft.it_value.tv_sec,
p_proft.it_value.tv_usec)
/
1000
);
printf(
"
Parent fib = %ld, user time = %ld sec, %ld millisec
"
,
fib, p_virtt_secs,
elapsed_usecs(p_virtt.it_value.tv_sec,
p_virtt.it_value.tv_usec)
/
1000
);
printf(
"
Parent fib = %ld, kernel time = %ld sec, %ld millisec
"
,
fib, p_proft_secs
-
p_virtt_secs,
(elapsed_usecs(p_proft.it_value.tv_sec,
p_proft.it_value.tv_usec)
/
1000
)
-
(elapsed_usecs(p_virtt.it_value.tv_sec,
p_virtt.it_value.tv_usec)
/
1000
));
fflush(stdout);
exit(
0
);
}
}
printf(
"
this line should never be printed
"
);
}
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
));
}
/*
Linux provide three timers for each process (the timers are part of the
process descriptor). The manpages for getitimer and setitimer describe
the 3 timers and associated signals. One timer measures real "wall clock"
time, and thus "ticks" whether the associated process is running or not.
Another timer measures the time a process is running in "user mode", and
thus ticks while the process is running, but not during execution of
system calls by the kernel on behalf of a process. A third timer, the
profile timer, measures the time a process is running in user mode and
the time that system calls take which are being executed on behalf of
the process (kernel mode). Given these three timers, it is possible
therefore to compute real time, cpu time (time in user and kernel mode),
user time, and kernel time. The kernel time is computer by subtracting
the "virtual" time from the "profile" time for a process.
All 3 timers work by counting down from a vaule set with the setitimer
system call. When zero is reached, all three will generate a specific
signal (and may reset if a reset time has been specified). It should
also be noted that a child process does not inherit its parent's timers,
but that timers do persist across an exec() call.
*/
/*
On Linux systems, the code is found in /usr/src/linux/kernel. It should
first be noted that getitimer and setitimer are system calls, and therefore
like a Linux system calls have kernel source functions sys_* (sys_setitimer
and sys_getitimer), which are called from the system trap whereby an
interrupt is generated and a handler calls the appropriate kernel function
when an application calls the system call library interface (in libc).
The two kernel routines simply convert time to/from jiffies, the internal
relative time in 10 ms ticks since boot-up, and either setting or getting
the timer value from the current process' descriptor value.
In include/linux/sched.h, current is defined as a pointer to a task_struct.
This pointer simply provides access to the process descriptor for the
currently running process which includes associated timer values.
To understand how the time is kept, if you look in the main.c program
for linux, you see that as part of the kernel initialization sched_init()
is called. This function does a request_irq(TIMER_IRQ, do_timer, 0, "timer")
among other things. This means that the hardware cpu timer will cause
do_timer() to be executed whenever it expires and causes an IRQ which it
does every 10 ms on Linux and most Unix systems. The do_timer() advances
jiffies every 10 ms, and furthermore contains code to distinguish the mode
of the current process...
if(user_mode(regs) {
...
// Update ITIMER_VIRT for current task if not a system call
if (current->it_virt_value && !(--current->it_virt_value)) {
current->it_virt_value = current->it_virt_incr;
send_sig(SIGVTALRM, current, 1);
}
Note that if decrementing the it_virt_value causes it to be zero, then
the appropriate signal is raised to the current process.
The following code is executed whether current is in user_mode or not, and
thus implements the definition of profile time:
// Update ITIMER_PROF for the current task
if(current->it_prof_value && !(--currrent->it_prof_value)) {
current->it_prof_value = current->it_prof_incr;
send_sig(SIGPROF, current, 1);
}
So, we see the implementation of the time keeping for 2 of the 3 timers.
SIGALRM for the third, real time, is generated from the routine schedule().
This routine contains the code:
if(ticks && p->it_real_value) {
if(p->it_real_value <= ticks) {
send_sig(SIGALRM, p, 1);
...
do {
p->it_real_value += p->it_real_incr;
} while (p->it_real_value <= ticks);
The code tests to see if the real-time timer has expired, and if so it
signals the process p and resets its timer if so configured. Note that
all processes currently under the dispatch management of the scheduler
are handled with this code so that all real-time timers are decremented
whether the associated processes are currently running or not (unlike the
other two timers which only need to be decremented when the associated
process is the current process).
The file fork.c contains code to zero it_virt_value, etc. Since these
variables are not mentioned elsewhere in the kernel, they are presumably
unaffected by other system calls such as exec().
*/