当前位置: 首页 > news >正文

linux 内存踩踏导致的空指针问题分析纪要

1,查看日志信息打印

我们看到日志发现发包的skb模块有NULL pointer情况,我们看代码分析skb指针不可能出现是空指针,这个时候我们怀疑可能是出现了踩内存导致的空指针情况,所以我们首先需要找到系统PANIC的条件,也就是触发空指针(踩内存的条件)。

2,分析问题出现场景

最开始测试说这个问题是必现或者高概率复现的,我们查看日志看着空指针又没看出什么,后面我们自己想复现问题找到问题的触发条件,后面我们发现wifi测速时这个问题出现的概率比较高,但是后面我们加了一些打开符号表和其他的调试信息后发现问题问题出现的概率也不是很高,驱动专家们怀疑这个时候改了逻辑打开了符号表,可能踩到了其他的内存,这个时候我们只能通过测试场景去看问题了。

3,猜测内存踩踏点

最开始出现的场景是wifi测速时出现的,虽然PANIC收集的信息不是很完整,但是我们可以看到是wifi模块的tx_buf发包函数出现了空指针,所以我们先和协议的同事确认了一些测速时一些发包情况和发包函数,我们首先看到了协议栈的发包函数使用的是ndo_start_xmit。

netdev_tx_t ndo_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    // 停止队列以避免更多数据包进入
    netif_stop_queue(dev);

    // 调用硬件发送接口发送数据包
    if (0 == hardware_send_frame(skb->data, skb->len)) {
        // 更新设备统计信息
        dev->stats.tx_packets++;
        dev->stats.tx_bytes += skb->len;
        dev_kfree_skb(skb); // 释放数据包
        return NETDEV_TX_OK;
    } else {
        // 发送失败,重启队列
        netif_wake_queue(dev);
        return NETDEV_TX_BUSY;
    }
}

 和协议栈的专家确认以及查看协议栈的发包函数的实现以及发包的流程,发现该线程是可重入的,但是没有加锁,所以可能会有问题,所以我们试着把ndo_start_xmit换成有RCU锁保护的dev_queue_xmit 函数,我们来看看函数dev_queue_xmit 的具体实现。

int dev_queue_xmit(struct sk_buff *skb)
{
    struct net_device *dev = skb->dev;
    struct Qdisc *q;
    int rc = -ENOMEM;

    // 使用RCU读取设备的队列规则
    rcu_read_lock();                  // 开启RCU读端临界区
    q = rcu_dereference(dev->qdisc);  // 安全地读取qdisc
    rcu_read_unlock();                // 关闭RCU读端临界区

    // 入队操作和发送流程
    if (q->enqueue) {
        rc = q->enqueue(skb, q);
        if (likely(rc == NET_XMIT_SUCCESS)) {
            __netif_schedule(q);
            rc = NET_XMIT_SUCCESS;
        }
    }

    // 无队列设备直接发送
    if (dev->flags & IFF_LOOPBACK) {
        rc = dev_hard_start_xmit(skb, dev, NULL, NULL);
    }

    return rc;
}

struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
                                    struct netdev_queue *txq, int *ret) 
{
    struct sk_buff *skb = first;
    int rc = NETDEV_TX_OK;

    while (skb) {
        struct sk_buff *next = skb->next;

        skb->next = NULL;
        rc = xmit_one(skb, dev, txq, next != NULL); // 调用 xmit_one 发送数据包

        if (unlikely(!dev_xmit_complete(rc))) {
            skb->next = next;
            goto out;
        }

        skb = next;
        if (netif_xmit_stopped(txq) && skb) {
            rc = NETDEV_TX_BUSY;
            break;
        }
    }

out:
    *ret = rc;
    return skb;
}

换成上面的函数后测速接近300次没有出现空指针相关的问题了。 

总结:我们使用和封装线程函数时我们需要考虑不同的场景,考虑函数的调用情况,看函数是否可以重入,如果是就需要考虑加入一些锁机制来实现互斥,还有一些共享资源的使用情况也是如此。

相关文章:

  • 湖北建筑安全员C1证考试难度怎么样
  • Linux 第三讲 --- 基础指令(三)
  • 智能医疗辅助诊断:深度解析与实战教程
  • 免费干净!付费软件的平替款!
  • 基于 Spring Boot + Vue 的 [业务场景] 管理系统设计与实现
  • 微软VSCode 能否击败 Cursor 和 Windsurf?
  • 【仪器仪表专题】案例:怎么理解电池模拟器模拟电池内阻的功能
  • 用户登录不上linux服务器
  • AMBA-CHI协议详解(二十六)
  • docker容器安装的可道云挂接宿主机的硬盘目录:解决群晖 威联通 飞牛云等nas的硬盘挂接问题
  • 淘宝 API 接口开发最佳实践:商品详情数据抓取与错误处理方案
  • Android之JNI详解
  • IntelliJ IDEA历史版本下载安装链接
  • MCP基础学习五:MCP的优化与高级功能
  • RAG创建向量数据库:docsearch = FAISS.from_texts(documents, embeddings)
  • GGML源码逐行调试(下)
  • Linux基础3
  • 如何从项目目标到成功标准:构建可量化、可落地的项目评估体系
  • 代码随想录第16天:(二叉树)
  • Jieba分词的原理及应用(三)
  • 微信如何做微商城网站建设/阿里巴巴官网首页
  • 茶叶公司商城网站建设/八八网
  • 专业建站推广服务/百度打开
  • 国内互动网站建设/南宁网站制作
  • 关于公司网络优化方案/高中同步测控优化设计答案
  • 网站建设评价标准/交换友情链接的网站标准是什么