我正在使用 Linux 安全模块 Hook 向 recv() 系统调用添加一些自定义功能。与原始的 recv() 相比,我想测量此功能的开销。我编写了一个简单的 tcp 服务器,可以在有和没有模块的情况下运行。此 tcp 服务器调用 recv() 函数“N”次。它测量每个 recv 所花费的时间,例如:
clock_gettime(before);
recv()
clock_gettime(after);
global_time += after - before.
最后,我用“global_time/N”打印单个recv() 的平均时间。让我们将此时间称为“user_space_avg_recv”时间。
在我的模块中,我想放置时间测量函数来计算我的钩子(Hook)的确切执行时间。我尝试了3种方法。
sj = jiffies;
my_hook();
ej = jiffies;
current->total_oh = ej - sj;
但我看到 sj 和 ej 值之间没有区别。因此 total_oh 不变。
“user_space_avg_recv”值。这没有意义,因为内核内部的测量值总是小于从用户空间测量的时间值。这可能意味着我要么没有使用正确的 API 进行测量,要么在将值从周期转换为毫秒时出错。
我基本上使用以下公式将周期转换为毫秒:
avg overhead of my hook in milliseconds =
(((cycles / 2.99) / 10^6) / N)
2.99 因为我的时钟频率是 2.99Ghz
几点:
问题:
如何在内核中准确测量函数执行时间?
请您参考如下方法:
您可以使用 function tracer API使用高精度时间戳跟踪所有函数调用和返回。这包括中断事件和上下文切换。然后,您可以分析用户空间中的结果跟踪,以准确了解函数运行所需的时间。
如果您不能使用函数跟踪 API,您可以调用 do_gettimeofday()
调用以获取微秒分辨率的时间戳,或 getnstimeofday()
用于纳秒级分辨率。这些与用户空间 gettimeofday()
的功能相同call 内部使用。当然,对于非常快速的函数,这可能不够准确;任何比这更快的精度,您可能需要深入研究计时器代码以查看它如何实现周期转换。另请注意,仅仅因为它们具有高分辨率并不意味着它们具有那么高的准确性 - 但它们应该对基准测试有用。
请注意,任何形式的跟踪都会导致额外的延迟 - do_gettimeofday()
需要许多原子比较和交换操作,并且 ftrace 将日志记录代码放在每个函数的前导和后导上。在解释结果时,您应该考虑到这一点。