测量linux内核中函数的执行时间

Met*_*hos 26 linux-kernel

我正在使用Linux安全模块挂钩为recv()系统调用添加一些自定义功能.与pristine recv()相比,我想测量此功能的开销.我编写了一个简单的tcp服务器,我运行和没有我的模块.此tcp服务器调用recv()函数'N'次.它通过以下方式测量每个recv所花费的时间:

clock_gettime(before);
recv()
clock_gettime(after);
global_time += after - before.
Run Code Online (Sandbox Code Playgroud)

最后,我使用"global_time/N"打印单个recv()的平均时间.让我们把这次称为"user_space_avg_recv"时间.

在我的模块中,我想放置时间测量函数来计算我的钩子的确切执行时间.我尝试了3种方法.

  1. 我用jiffies如下:

    sj = jiffies;
    my_hook();
    ej = jiffies;
    current->total_oh = ej - sj;
    
    Run Code Online (Sandbox Code Playgroud)

    但我发现sj和ej值之间没有区别.因此total_oh没有变化.

  2. 我使用current_kernel_time(),因为我认为它以纳秒为单位返回时间.但是,再一次,之前和之后没有差别.

  3. 我用了get_cycles.我打印进程退出时的总循环数.但是,当我将总周期值转换为毫秒时,它会比"user_space_avg_recv"值大得多.这没有意义,因为内核中的测量值总是小于从用户空间测量的时间值.这可能意味着我要么使用正确的API进行测量,要么在将值从周期转换为毫秒时出错.

我基本上使用以下公式将周期转换为毫秒:

avg overhead of my hook in milliseconds = 
             (((cycles / 2.99) / 10^6) / N)
Run Code Online (Sandbox Code Playgroud)

2.99因为我的时钟频率是2.99Ghz

一些要点:

  • 我的用户空间程序使用set affinity绑定到单个核心.

  • 我正在使用内核2.6.22.14

  • 为了阻止内核在我的钩子内切换上下文,我使用preempt_disable()和preempt_enable().因此,它不会计算其他内核线程的执行时间.即便如此,由于我的钩子使用了一些I/O,我的线程可能会自动释放控制,或者可能会发生一些可能会增加总周期数的中断.

问题:如何在内核中准确测量函数执行时间?

bdo*_*lan 21

您可以使用函数跟踪器API来获取所有函数调用和返回的跟踪,并使用高精度时间戳.这包括中断事件和上下文切换.然后,您可以分析用户空间中的结果跟踪,以准确了解函数运行的时间.

如果您不能使用函数跟踪器API,则可以调用该do_gettimeofday()调用以获得微秒分辨率时间戳或getnstimeofday()纳秒分辨率.这些是用户空间gettimeofday()调用在内部使用的相同功能.当然,对于非常快速的功能,这可能不够准确; 比任何更快的准确度,你可能需要深入研究定时器代码,看看它如何实现循环转换.还要注意,仅仅因为它们具有高分辨率并不意味着它们具有那么高的准确性 - 但它们应该用于基准测试目的.

请注意,任何形式的跟踪都会导致额外的延迟 - do_gettimeofday()需要进行大量的原子比较和交换操作,并且ftrace将日志记录代码放在每个单独的函数之前和之后.在解释结果时应该考虑到这一点.