总结一下这两天做的一些事情,前几天Boss交给我一个小任务,大概意思就是为公司的一个新的项目选定操作系统,这个项目对系统的实时性要求特别高,然后看看用什么办法能测量出操作系统的实时性能
先介绍下背景知识,操作系统分为实时操作系统和分时操作系统,这是由他们的应用范围来决定的,我们通常所用的Windows,Linux都是基于分时的通用操作系统,实时操作系统比较有名的是wind river公司的vxworks,加拿大的QNX还有基于linux的xenomai, RTLinux等等,实时操作系统和分时操作系统最大的区别就是实时操作系统的任务都能保证在固定的时间内完成,必须在规定的时间内完成,哪怕一次任务有9999次在规定的时间内完成,只有一次没有在规定的时间内完成,那么这个系统也不能称之为实时的操作系统。
网上查了些资料,对这些操作系统都做了比较,然而意见不一,有人说vxworks好,有人说QNX好…….,想了一下还不如自己想个办法来对这些系统的实时性进行测试,到时候一目了然,***白猫抓住老鼠才是猫。
硬件平台x86_64, 软件平台Linux+xenomai,既然要测实时性能,就要说说有哪些因素影响Linux实时性能,大概有以下几点
1. 内核进入临界区会屏蔽所有中断,即Linux内核有大量的不可抢占去,这里说的是2.6以后的内核,因为2.4之前的内核都是不可抢占的,实时性更差
2. 虚拟内存存取时间的不确定性
3. 内核粗糙的时钟粒度
最初我的想法是测出中断延迟时间,也就是硬件产生一个中断,到cpu开始执行该中断服务程序的时间。为什么会存在这段时间,就是上面所说的第一点,当硬件产生一个中断时,内核可能在某个临界区内,如果响应了该中断,可能会对临界区内的资源造成不同步,所以屏蔽了中断,也就延迟了中断,明白了这段时间的存在,我们就有了目标。
中断延迟时间一般在us级别,因为Linux的系统时钟一般为10ms到1ms之间,所以我们不可能用Linux的内核定时器来产生中断然后记录产生中断的时间
X86下有三种定时器
RTC定时器 频率在2-8192HZ之间,精度太低,不可能达到us级
PIC可编程中断定时器 一般将其变成为1000HZ或者100HZ,精度也不能满足要求
TSC时间戳计数器 这个是cpu主频相对应的,即cpu时钟信号来一次,该计数器加一次,不过这个是个计数器,不能像定时器一样产生中断,到现在也没有想到用什么硬件定时器的方法来产生中断,如果哪位读者或者网友知道望不吝赐教,感激不尽。
我想到用软中断的方法来模拟硬中断,即向进程发送一个信号,然后该进程调用信号处理程序,发送信号之后,马上计时,这个要通过读取TSC寄存器,获得cpu经历了多少个时钟,然后在信号处理程序的入口处再一次读取TSC寄存区,得到此时cpu经历的时钟数,两次相减就得到从发送信号到进入信号处理程序cpu所经历的时钟数,然后除以cpu的主频,就得到了具体的时间。
下面是测试程序的部分源码
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#if defined(__i386__)
static __inline__ unsigned long long rdtsc(void) //32位系统下读取TSC计数器的值
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x; //
}
#elif defined(__x86_64__)
static __inline__ unsigned long long rdtsc(void) //64位系统下读取TSC计数器的值
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#endif
volatile int end;
int sum;
void signal_handler(int signal)
{
end = rdtsc();
/*int i=0;
for(; i< 0 ; ++i)
{
sum+=1;
}
*/
}
int main(void)
{
signal(SIGUSR1, signal_handler);
volatile int start = 0;
register int end_new = 0;
int loop;
char result[50] = {''};
int fd = open("result_load.txt", O_CREAT|O_RDWR);
const int MAX_COUNT = 10000000;
const float CPU_MHZ = 3093.059; //use cat /proc/cpuinfo get the value
const float CPU_tick_count_per_second = CPU_MHZ*1000*1000;
for(loop =0; loop<1000;loop++) //测试10000次
{
printf("start to send signal.n");
start = rdtsc(); //开始计数
kill(getpid(),SIGUSR1); //立即发送信号
printf("runtickcount:%d,runtime:%f/sn",end-start,(end -start)/CPU_tick_count_per_second);
sleep(1);
sprintf(result, "run time:%fn", ((end-start)/CPU_tick_count_per_second));
write(fd, result, 50);
}
return 0;
}
运行此程序就可以得到系统对软终端的响应时间,上面的情况是在系统没有负载的情况下,现实应用中系统往往是有负载的,甚至有很重的负载,所以我们为了模拟真实情况,对系统加了一些负载,写了如下几个负载,充分利用到系统各方面的资源
1) 创建4个进程反复进行内存的分配和释放操作; (利用内存)
2) 创建4个进程反复进行大文件的写操作 (利用磁盘IO)
3) 创建四个进程反复的进行开平方操作 (利用CPU)
加了这样的负载以后,系统的负载已经很重了,差一点的机器会出现交互响应很慢,甚至不能交互,下面是负载程序的源代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <fcntl.h>
#define MALLOC_COUNT 2000000000
#define BUFFER_SIZE 1000
char std_buf[3] = "xffxffxff";
void xad_fill_buf(char pattern[3], unsigned long bufsize, char *buf)
{
int loop;
for (loop = 0; loop < (bufsize / 3); loop++)
{
*buf++ = pattern[0];
*buf++ = pattern[1];
*buf++ = pattern[2];
}
}
int main()
{
double a = 2.0, b = 3.0, c = 4.0, d = 5.0;
int i,j;
int count = 0;
int total_bytes = 0;
pid_t pid1, pid2, pid3, pid4, pid5;
FILE* f;
double array[4]={a, b, c, d};
for(i=0;i<4;i++) //四个开平方的进程
{
if(fork() == 0)
{
for(j=0;j<1000000;j++)
printf("The sqrt(a) is:%fn",sqrt(array[i]));
exit(0);
}
}
for(i=0;i<4;i++) //四个分配释放内存的进程
{
if(fork() == 0)
{
for(i=0;i<400;i++)
{
char *p = (char*)malloc(MALLOC_COUNT);
printf("200M memeory allocated.n");
sleep(1);
//free(p);
}
return 0;
}
}
for(i=0;i<4;i++) //四个写大文件的进程
{
if(fork() == 0)
{
char filename[10];
char buf[BUFFER_SIZE];
sprintf(filename, "file_%d", i);
int fd = open(filename, O_RDWR|O_CREAT);
if(fd<0)
{
printf("Create file failed.n");
exit(-1);
}
if((f = fdopen(fd, "r+")) == NULL)
{
strerror("errorn");
exit(-1);
}
while( (fwrite(&buf, 1, BUFFER_SIZE,f)) == BUFFER_SIZE)
{
count++;
xad_fill_buf(std_buf, BUFFER_SIZE, buf);
if(count == 100000)
{
total_bytes+=100; //每次写100M
printf("%dM has written to the disk.n", total_bytes);
count = 0;
}
}
exit(0);
}
}
return 0;
}
最后给出测试结果,我的平台上没有负载测试的时间如下图
下面是有负载时测试的输出,左边为负载程序运行,右边为延迟时间