A CPU can still only be running one process at a time, but multitasking gets around that by scheduling which tasks will be running at any given time. The act of switching from one task to another is called Context Switching.
--CPU 一次只能运行一个进程,在multitasking 下, 当CPU 处理的进行需要从硬盘读取一些数据时,虽然这个时间也是按miliseconds来计算,但是这个时间还是要比CPU 运算的时间长,所以在CPU 的这个等待的时间里,可以将当前处理的task 切换出去,让CPU 去处理其他的task。 这个过程就叫作Context switch。
测试Context Switch time(进程上下文切换时间)
创建两个进程(实时进程)并在它们之间传送一个令牌,如此往返传送一定的次数。其中一个进程在读取令牌时就会引起阻塞。另一个进程发送令牌后等待其返回时也处于阻塞状态。发送令牌带来的开销与上下文切换带来的开销相比,可以忽略不计。 (利用管道传递令牌)
测试程序(1) 使用gettimeofday()获取当前时间
--------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <sched.h>
#include <sys/types.h>
#include <unistd.h> //pipe()
int main()
{
int x, i, fd[2], p[2] ;
char send = 's';
char receive;
pipe(fd);
pipe(p);
struct timeval tv;
struct sched_param param;
param.sched_priority = 0;
while ((x = fork()) == -1);
if (x==0) {
sched_setscheduler(getpid(), SCHED_FIFO, ¶m);
gettimeofday(&tv, NULL);//
printf("Before Context Switch Time %u us\n", tv.tv_usec);
for (i = 0; i < 10000; i++) {
read(fd[0], &receive, 1);
write(p[1], &send, 1);
}
exit(0);
}
else {
sched_setscheduler(getpid(), SCHED_FIFO, ¶m);
for (i = 0; i < 10000; i++) {
write(fd[1], &send, 1);
read(p[0], &receive, 1);
}
gettimeofday(&tv, NULL);
printf("After Context SWitch Time %u us\n", tv.tv_usec);
}
return 0;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
注释:
使用C语言编写程序需要获得当前精确时间(UNIX到现在的时间),或者为执行计时,可以使用gettimeofday()函数。
#include <sys/time.h>
int gettimeofday(struct
timeval*tv, struct timezone *tz);
其参数tv是保存获取时间结果的结构体,参数tz用于保存时区结果(若不使用则传入NULL即可)。
结构体
timeval的定义为:
struct
timeval{
long int tv_sec; // 秒数
long int tv_usec; // 微秒数
}
它获得的时间精确到微秒(1e-6 s)量级。在一段代码前后分别使用gettimeofday可以计算代码执行时间
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
测试结果(进程切换时间不超过5us)
--------------------------------------------------
Before Context Switch Time 617087 us
After Context SWitch Time 702420 us
702420us - 617087us = 85333 us
85333us / 20000 = 4.26665 us
进程切换时间为 4.26665 us
注: cpu MHz : 2801.042
测试程序(2) 使用rdtsc()获取当前时间
--------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <sys/types.h>
#include <unistd.h>
long long rdtsc()
{
__asm("rdtsc");
}
int main()
{
int x, i, fd[2], p[2];
char send = 's';
char receive;
pipe(fd);
pipe(p);
struct sched_param param;
param.sched_priority = 0;
while ((x = fork()) == -1);
if (x==0) {
sched_setscheduler(getpid(), SCHED_FIFO, ¶m);
printf("Before Context Switch Time %lld\n", rdtsc());
for (i = 0; i < 10000; i++) {
read(fd[0], &receive, 1);
write(p[1], &send, 1);
}
exit(0);
}
else {
sched_setscheduler(getpid(), SCHED_FIFO, ¶m);
for (i = 0; i < 10000; i++) {
write(fd[1], &send, 1);
read(p[0], &receive, 1);
}
printf("After Context Switch Time %lld\n", rdtsc());
}
return 0;
}
测试结果(进程切换时间不超过5us)
--------------------------------------------------
Before Context Switch Time 16208184381648
After Context Switch Time 16208424333213
16208424333213 - 16208184381648 = 239951565(clock cycle)
239951565 * 0.357009998 ns = 85665107.74074687 ns
85665107.74074687 ns / 20000 = 4283.255387037 ns = 4.28325 5387037 us
注: cpu MHz : 2801.042
---------------------------------------------
2 801 042 000Hz
clock cycle = 1 000 000 000 ns / 2 801 042 000 = 0.357009998ns
查看CPU性能参数
cat /proc/cpuinfo
测试程序(3) 可直接获得进程上下文切换时间
--------------------------------------------------
#include <stdio.h>
#include <stdlib.h> //drand48()
#include <sched.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h> //gettimeofday()
#include <time.h>
typedef unsigned long long u64;
double clockCycleTimeS,clockRateHZ;
/* 获取当前时间,返回秒 */
double second() {
struct timeval tv;
gettimeofday(&tv,0);
return tv.tv_sec + 1e-6 * tv.tv_usec;
}
/* 获取当前时间,返回clock cycle */
u64 rdtsc() {
u64 tsc;
__asm__ __volatile__("rdtsc" : "=A" (tsc));
return tsc;
}
/* 睡眠us微秒 */
void selectsleep(unsigned us) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = us;
select(0, 0, 0, 0, &tv);
}
/* 计算当前CPU的工作频率 */
void calibrate() {
double sumx = 0;
double sumy = 0;
double sumxx = 0;
double sumxy = 0;
double slope;
const unsigned n = 30;
unsigned i;
for (i=0; i<n; i++) {
double breal,real,ticks;
u64 bticks;
breal = second();
bticks = rdtsc();
selectsleep((unsigned)(10000 + drand48() * 200000));
ticks = rdtsc() - bticks;
real = second() - breal;
sumx += real;
sumxx += real * real;
sumxy += real * ticks;
sumy += ticks;
}
slope = ( (sumxy - (sumx*sumy) / n) /
(sumxx - (sumx*sumx) / n) );
clockRateHZ = slope;
clockCycleTimeS = 1.0 / slope;
printf("%3.3f MHz\n", clockRateHZ*1e-6);
}
int main()
{
calibrate();
int x, i, p1[2], p2[2], time[2];
char send = 's';
char receive;
u64 old_time;
pipe(p1);
pipe(p2);
pipe(time);
struct sched_param param;
param.sched_priority = 0;
while ((x = fork()) == -1);
if (x==0)
{
sched_setscheduler(getpid(), SCHED_FIFO, ¶m);
old_time = rdtsc();
write(time[1], &old_time, sizeof(old_time));
for (i = 0; i < 10000; i++) {
read(p1[0], &receive, 1);
write(p2[1], &send, 1);
}
exit(0);
}
else
{
u64 new_time;
sched_setscheduler(getpid(), SCHED_FIFO, ¶m);
for (i = 0; i < 10000; i++) {
write(p1[1], &send, 1);
read(p2[0], &receive, 1);
}
new_time = rdtsc();
read(time[0], &old_time, sizeof(old_time));
printf("Latency time = %3.3f us\n",
1e6 * (new_time - old_time) * clockCycleTimeS / 20000 );
}
return 0;
}
测试结果(Linux-2.6.21 + RealTime Patch)
--------------------------------------------------
2801.226 MHz
Latency time = 8.129 us