如果你希望避免浪费CPU循环,当你没有事情做时(例如,没有报文等待处理),你应该调用pfring_poll()进行轮询,当有报文需要处理时要求系统唤醒程序。如果你创建一个主动轮询循环时,你可能希望做一些事情,就像下面:
while(<no packet available>) {usleep(1); }
减少CPU循环花费(理论上)一小会(1ms)时间,最佳的实践是usleep()指定的持续时间(或者你希望使用nanosleep()替代usleep())。不幸的是,这并非如此。这些函数使用系统调用来实现sleep。一个简单的系统调用的开销相当低(例如,你能使用时间系统调用的测试程序测试它),每个调用通常会小于100ns,比我们希望的1us休眠更少。现在让我们测量下usleep()和nanosleep()的精度,使用简单的程序(sleep.c)。
#include <string.h>
#include <sys/time.h>
#include <stdio.h>
double delta_time_usec(struct timeval *now,struct timeval *before) {
time_t delta_seconds;
time_t delta_microseconds;
delta_seconds =now->tv_sec - before->tv_sec;
if(now->tv_usec > before->tv_usec)
delta_microseconds = now->tv_usec - before->tv_usec;
else
delta_microseconds = now->tv_usec - before->tv_usec +1000000; /* 1e6 */
return((double)(delta_seconds * 1000000) + (double)delta_microseconds);
}
int main(int argc, char* argv[]) {
inti, n = argc > 1 ? atoi(argv[1]) : 100000;
static struct timeval start, end;
struct timespec req, rem;
inthow_many = 1;
for(i = 0; i < n; i++)
usleep(how_many);
gettimeofday(&end, NULL);
printf("usleep(1) took %f usecs\n", delta_time_usec(&end,&start) / n);
gettimeofday(&start, NULL);
for(i = 0; i < n; i++) {
req.tv_sec = 0, req.tv_nsec = how_many;
nanosleep(&req, &rem);
}
gettimeofday(&end, NULL);
printf("nanosleep(1) took %f usecs\n",delta_time_usec(&end, &start) / n);
}
结果是不同的机器稍有变化,但是他们大约都在60usec左右。
# ./sleep
usleep(1) took 56.248760 usecs
nanosleep(1) took 65.165280 usecs
这意味着使用usleep和nanosleep休眠1ms时,实际上它们休眠了大约60ms。该结果不是很惊讶,其它人早就发现了这个问题。
在实际中这意味着什么呢?我们知道在10G线速下,67ns要接收一个报文,休眠60usec意味着在这段时间中你可接收大约895个报文,你必须有更多的缓冲处理这种情况,否则会出现丢包的情况。它也表示你不能做任何轮询,除了在紧急情况下使用纯粹的主动轮询(例如:在主动报文轮询中不进行任何的usleep/nanosleep),例如:当你从两个网络适配器上对报文进行时间合并时。
总结:主动轮询不是很优雅,但是在高速下处理报文可能是强制性的,为了达到高速和精确的要求。PF_RING应用支持主动和被动轮询。例如你使用的pfcount使用-a标识强制使用主动报文轮询,因此(有些情况下)增加报文捕获性能,代价是导致你的CPU核利用率100%(例如:当你使用主动轮询时确保你也绑定你的应用到一个核上,在pfcount中你可以使用-g来完成)。
参考文档[1] PF_RING ZC(零拷贝)简介
[2] 不是所有服务器都一样(DNA)三部分
[3] 应用之间如何使用PF_RING平衡流量
[4] 使用n2disk和PF_RING构建一个廉价的2*10Gbit(继续)报文记录器