Linux编程:使用 perf lock 分析锁竞争

0. 引言

本文将介绍如何使用 perf lock 诊断锁竞争,并通过示例代码和分析。

1. perf lock recordperf lock report 的工作原理

perf lock recordperf lock report 是用来监控和分析锁竞争的工具。

  • perf lock record:该命令用于监控运行中的程序并记录锁操作数据,包括锁的争用和等待时间。

  • perf lock report:该命令生成一个报告,展示通过 perf lock record 捕获到的锁数据统计信息,包括锁的获取次数、争用次数、平均等待时间等。

2. 自定义编译 perf 工具

一般需要自定义编译perf才能启动 perf lock 功能

2.1 内核配置

为了使 perf lock 能够追踪锁的名称和相关数据,,需要启用与 lockstatperf events 相关的选项:

  • CONFIG_PERF_EVENTS:启用 perf 事件子系统。
  • CONFIG_LOCK_STAT:启用锁统计功能,允许 perf lock 追踪锁的状态。

使用 perf 命令确认是否启用锁统计

可以通过以下命令确认 perf lock 是否被启用:

perf lock record --help

3. 示例代码:多线程锁竞争

以下是一个多线程示例代码 lock_example.c :

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#define NUM_THREADS 100
#define NUM_ITERATIONS 100000

// 定义共享资源
long shared_resource = 0;

// 定义互斥锁和自旋锁
pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_spinlock_t spin_lock;

// 每个线程将执行的任务
void* mutex_worker(void* arg) {
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        pthread_mutex_lock(&mutex_lock);  // 获取互斥锁
        shared_resource++;
        pthread_mutex_unlock(&mutex_lock);  // 释放互斥锁
    }
    return NULL;
}

void* spinlock_worker(void* arg) {
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        pthread_spin_lock(&spin_lock);  // 获取自旋锁
        shared_resource--;
        pthread_spin_unlock(&spin_lock);  // 释放自旋锁
    }
    return NULL;
}

void* mixed_worker(void* arg) {
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        pthread_mutex_lock(&mutex_lock);  // 获取互斥锁
        shared_resource += 10;
        pthread_mutex_unlock(&mutex_lock);  // 释放互斥锁

        pthread_spin_lock(&spin_lock);  // 获取自旋锁
        shared_resource -= 10;
        pthread_spin_unlock(&spin_lock);  // 释放自旋锁
    }
    return NULL;
}

int main() {
    srand(time(NULL));

    // 初始化自旋锁
    pthread_spin_init(&spin_lock, PTHREAD_PROCESS_PRIVATE);

    // 创建线程
    pthread_t threads[NUM_THREADS];
    
    // 随机选择任务类型
    for (int i = 0; i < NUM_THREADS; i++) {
        int task_type = rand() % 3;  // 随机选择任务类型(0: mutex, 1: spinlock, 2: mixed)
        
        if (task_type == 0) {
            pthread_create(&threads[i], NULL, mutex_worker, NULL);
        } else if (task_type == 1) {
            pthread_create(&threads[i], NULL, spinlock_worker, NULL);
        } else {
            pthread_create(&threads[i], NULL, mixed_worker, NULL);
        }
    }

    // 等待所有线程完成
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }

    // 输出最终的共享资源值
    printf("Final value of shared resource: %ld\n", shared_resource);

    // 销毁自旋锁
    pthread_spin_destroy(&spin_lock);

    return 0;
}

使用gcc -o lock_example lock_example.c -lpthread -O2 -g编译以上程序

4. 使用 perf lock 命令进行分析

  • 运行 perf lock record
sudo perf lock record ./lock_example

此命令会跟踪并记录在运行 lock_example 时发生的所有锁竞争事件。

  • 生成 perf lock report
sudo perf lock report

示例输出解析

以下是一个 perf lock report 的示例输出:

Name              acquired  contended     avg wait   total wait    max wait   min wait
--------------------------------------------------------------------------
                      64693      64693       785 ns     50.79 ms      9.09 us     463 ns
    jiffies_lock          3          3       858 ns      2.57 us      1.09 us     685 ns
                      2          2      1.79 us      3.57 us      1.80 us     1.78 us
                      2          2      1.46 us      2.93 us      1.72 us     1.20 us
    rcu_state           2          2      1.68 us      3.37 us      2.59 us     778 ns
                      1          1      1.55 us      1.55 us      1.55 us     1.55 us

字段解释

  • Name:锁的名称或标识符。
  • acquired:锁被获取的次数。
  • contended:锁竞争的次数。
  • avg wait:平均等待时间。
  • total wait:总等待时间。
  • max wait:最大等待时间。
  • min wait:最小等待时间。

5. 锁竞争调用栈分析

使用 perf report 可以查看锁竞争的调用栈。以下是一个 lock:contention_begin 事件的调用栈分析:

$ sudo ./perf report
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 64K of event 'lock:contention_begin'
# Event count (approx.): 64703
#
# Children      Self  Trace output                   
# ........  ........  ...............................
#
    99.98%    99.98%  0xffff000100b6f4c4 (flags=SPIN)
            |          
            |--55.16%--invoke_syscall
            |          __arm64_sys_futex
            |          do_futex
            |          futex_wake
            |          _raw_spin_lock
            |          queued_spin_lock_slowpath
            |          __traceiter_contention_begin
            |          __traceiter_contention_begin
            |          
             --44.82%--futex_wait
                       __futex_wait
                       futex_wait_setup
                       futex_q_lock
                       _raw_spin_lock
                       queued_spin_lock_slowpath
                       __traceiter_contention_begin
                       __traceiter_contention_begin

     0.00%     0.00%  0xffffa00085e96a40 (flags=SPIN)
     0.00%     0.00%  0xffff0001feefb240 (flags=SPIN)
     0.00%     0.00%  0xffff0001fef63240 (flags=SPIN)
     0.00%     0.00%  0xffffa00085f4e980 (flags=SPIN)
     0.00%     0.00%  0xffff0001fef2f240 (flags=SPIN)


# Samples: 64K of event 'lock:contention_end'
# Event count (approx.): 64703
#
# Children      Self  Trace output              
# ........  ........  ..........................
#
    99.98%    99.98%  0xffff000100b6f4c4 (ret=0)
            |          
            |--55.16%--invoke_syscall
            |          __arm64_sys_futex
            |          do_futex
            |          futex_wake
            |          _raw_spin_lock
            |          queued_spin_lock_slowpath
            |          __traceiter_contention_end
            |          __traceiter_contention_end
            |          
             --44.82%--futex_wait
                       __futex_wait
                       futex_wait_setup
                       futex_q_lock
                       _raw_spin_lock
                       queued_spin_lock_slowpath
                       __traceiter_contention_end
                       __traceiter_contention_end

     0.00%     0.00%  0xffffa00085e96a40 (ret=0)
     0.00%     0.00%  0xffff0001feefb240 (ret=0)
     0.00%     0.00%  0xffff0001fef63240 (ret=0)
     0.00%     0.00%  0xffffa00085f4e980 (ret=0)
     0.00%     0.00%  0xffff0001fef2f240 (ret=0)


#
# (Tip: Show current config key-value pairs: perf config --list)
#

调用栈解析

  • lock:contention_begin 事件调用栈:
  • 主要锁0xffff000100b6f4c4 (flags=SPIN) 表示发生竞争的自旋锁。
  • 关键函数调用路径
    • invoke_syscall__arm64_sys_futexdo_futexfutex_wake_raw_spin_lockqueued_spin_lock_slowpath__traceiter_contention_begin。这个路径表明线程正在与 futex 机制交互以处理锁竞争。
    • 竞争比例:99.98%的竞争发生在 0xffff000100b6f4c4 锁上。
  • lock:contention_end 事件调用栈:
  • 主要锁0xffff000100b6f4c4 (ret=0) 表示竞争结束,锁成功被获取。
  • 关键函数调用路径
    • contention_begin 类似,包括 futex 操作和 _raw_spin_lock,但最终 ret=0 表示锁被成功获取,竞争结束。
  • 解析:
  • 锁竞争的高频率表明该自旋锁有频繁的竞争。
  • futex 系统调用和自旋锁机制在竞争过程中起着关键作用。
  • 锁的竞争结束时,路径返回 ret=0 表示锁已成功获取。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值