Skip to main content
 首页 » 编程设计

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

2024年11月01日33lovecherry

我正在使用 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种方法。
  • 我使用 jiffies 如下:
    sj = jiffies; 
    my_hook(); 
    ej = jiffies; 
    current->total_oh = ej - sj; 
    

    但我看到 sj 和 ej 值之间没有区别。因此 total_oh 不变。
  • 我使用 current_kernel_time() 因为我认为它以纳秒为单位返回时间。但是,再一次,前后时间没有区别。
  • 我使用了 get_cycles。我打印进程退出时的总周期。但是,当我将总周期值转换为毫秒时,它会远大于
    “user_space_avg_recv”值。这没有意义,因为内核内部的测量值总是小于从用户空间测量的时间值。这可能意味着我要么没有使用正确的 API 进行测量,要么在将值从周期转换为毫秒时出错。

  • 我基本上使用以下公式将周期转换为毫秒:
    avg overhead of my hook in milliseconds =  
                 (((cycles / 2.99) / 10^6) / N) 
    

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

    几点:
  • 我的用户空间程序使用 set affinity 绑定(bind)到单个内核。
  • 我正在使用内核 2.6.22.14
  • 为了阻止内核在我的钩子(Hook)中切换上下文,我使用了 preempt_disable() 和 preempt_enable()。因此它不会计算其他内核线程的执行时间。即使这样,由于我的钩子(Hook)使用了一些 I/O,我的线程可能会自愿释放控制,或者可能会发生一些中断,这可能会增加总周期数。

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

    请您参考如下方法:

    您可以使用 function tracer API使用高精度时间戳跟踪所有函数调用和返回。这包括中断事件和上下文切换。然后,您可以分析用户空间中的结果跟踪,以准确了解函数运行所需的时间。
    如果您不能使用函数跟踪 API,您可以调用 do_gettimeofday() 调用以获取微秒分辨率的时间戳,或 getnstimeofday() 用于纳秒级分辨率。这些与用户空间 gettimeofday() 的功能相同call 内部使用。当然,对于非常快速的函数,这可能不够准确;任何比这更快的精度,您可能需要深入研究计时器代码以查看它如何实现周期转换。另请注意,仅仅因为它们具有高分辨率并不意味着它们具有那么高的准确性 - 但它们应该对基准测试有用。
    请注意,任何形式的跟踪都会导致额外的延迟 - do_gettimeofday()需要许多原子比较和交换操作,并且 ftrace 将日志记录代码放在每个函数的前导和后导上。在解释结果时,您应该考虑到这一点。