You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核驱动模块内存释放问题:kfree放置及rmmod报错解决

Linux内核模块内存释放问题

我正在编写一个Linux内核模块,需要分配内存存储数据。因为要获取用户应用传入的buffer大小,内存分配(kmalloc)只能在VFS API的write函数里执行,无法在该函数外完成。现在搞不清kfree()该放置在何处:若放在cleanup函数中,执行rmmod卸载模块时会触发内核错误,注释掉kfree则错误消失。

write函数代码

ssize_t hcsr04_write(struct file *filp, const char *buffer, size_t length, loff_t * offset)
{
        if (pulsecount < (5)){
            pulseptr[pulsecount] = kmalloc(sizeof(buffer),GFP_ATOMIC);
            sprintf (pulseptr[pulsecount],"%s",buffer);
            pulsecount++;
        }
        else{
            int j = 0;
            while (j<4){
                sprintf (pulseptr[j], "%s", (pulseptr[j+1]) ); // [5 , 20 , 30 , 70 , 50] ===> [20 , 30 , 70 , 50 , 50]
                j++;
            }
            sprintf (pulseptr[4],"%s",buffer);
        }       
    
}

cleanup函数代码

static void __exit hcsr04_module_cleanup(void)
{
    //if (pulseptr!= {NULL,NULL,NULL,NULL,NULL}){
    kfree(pulseptr);
    printk(KERN_INFO "Dynamic memory freed successfully.");
    //}
    //pulseptr = {NULL,NULL,NULL,NULL,NULL};
    gpio_free( GPIO_OUT );
    gpio_free( GPIO_IN );
    hcsr04_lock = 0;
    cdev_del(&hcsr04_cdev);
    unregister_chrdev_region( hcsr04_dev, 1 );
    kobject_put( hcsr04_kobject );
}

rmmod执行时的错误信息

[   93.294821] 8<--- cut here ---
[   93.297928] Unable to handle kernel paging request at virtual address bcf03574
[   93.305253] pgd = 3cdbb3d3
[   93.307993] [bcf03574] *pgd=00000000
[   93.311621] Internal error: Oops: 5 [#1] SMP ARM
[   93.316301] Modules linked in: hcsr04(O-) nfc bnep bluetooth ecdh_generic ecc ipv6 hello(PO) g_serial libcomposite udc_core brcmfmac brcmutil sha256_generic libsha256 vc4 cfg80211 bcm2835_codec(C) rfkill bcm2835_isp(C) bcm2835_v4l2(C) v4l2_mem2mem cec bcm2835_mmal_vchiq(C) videobuf2_dma_contig snd_soc_core videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 snd_compress snd_pcm_dmaengine videobuf2_common snd_pcm videodev snd_timer mc snd raspberrypi_hwmon vc_sm_cma(C) uio_pdrv_genirq uio fixed
[   93.360563] CPU: 0 PID: 478 Comm: rmmod Tainted: P         C O      5.4.72-v7 #1
[   93.368060] Hardware name: BCM2835
[   93.371516] PC is at kfree+0x48/0x2bc
[   93.375235] LR is at hcsr04_module_cleanup+0x18/0xcac [hcsr04]
[   93.381148] pc : [<802fd004>]    lr : [<7f17a36c>]    psr: 20010013
[   93.387501] sp : b80abf08  ip : b80abf38  fp : b80abf34
[   93.392797] r10: 00000081  r9 : b80aa000  r8 : 801011c4
[   93.398095] r7 : 7f17a36c  r6 : 7e92dc38  r5 : 7f17c000  r4 : bcf03570
[   93.404713] r3 : bab24000  r2 : 00000024  r1 : 00000000  r0 : 7f17c000
[   93.411333] Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
[   93.418568] Control: 10c5383d  Table: 38d5806a  DAC: 00000055
[   93.424394] Process rmmod (pid: 478, stack limit = 0x274b04e4)
[   93.430307] Stack: (0xb80abf08 to 0xb80ac000)
[   93.434728] bf00:                   801c8158 80989110 7f17c0c0 00000000 7e92dc38 00000081
[   93.443026] bf20: 801011c4 b80aa000 b80abf4c b80abf38 7f17a36c 802fcfc8 7f17c0c0 00000000
[   93.451324] bf40: b80abfa4 b80abf50 801ca940 7f17a360 72736368 00003430 00000000 80da6b10
[   93.459621] bf60: 80101068 7e92cbe8 00000000 801011c4 b80aa000 00000006 b80abfac b80abf88
[   93.467918] bf80: 80228294 8022938c 00000000 80e05f88 00000000 7e92de2c 00000000 b80abfa8
[   93.476216] bfa0: 80101000 801ca7e0 00000000 7e92de2c 01677694 00000800 6a0f1b00 7e92dbe4
[   93.484513] bfc0: 00000000 7e92de2c 7e92dc38 00000081 7e92df1f 7e92dc34 01677190 00000001
[   93.492811] bfe0: 004e1f70 7e92dbec 004c866f 76f37218 60010030 01677694 00000000 00000000
[   93.501100] Backtrace: 
[   93.503593] [<802fcfbc>] (kfree) from [<7f17a36c>] (hcsr04_module_cleanup+0x18/0xcac [hcsr04])
[   93.512331]  r9:b80aa000 r8:801011c4 r7:00000081 r6:7e92dc38 r5:00000000 r4:7f17c0c0
[   93.520195] [<7f17a354>] (hcsr04_module_cleanup [hcsr04]) from [<801ca940>] (sys_delete_module+0x16c/0x244)
[   93.530073]  r5:00000000 r4:7f17c0c0
[   93.533704] [<801ca7d4>] (sys_delete_module) from [<80101000>] (ret_fast_syscall+0x0/0x28)
[   93.542084] Exception stack(0xb80abfa8 to 0xb80abff0)
[   93.547209] bfa0:                   00000000 7e92de2c 01677694 00000800 6a0f1b00 7e92dbe4
[   93.555507] bfc0: 00000000 7e92de2c 7e92dc38 00000081 7e92df1f 7e92dc34 01677190 00000001
[   93.563801] bfe0: 004e1f70 7e92dbec 004c866f 76f37218
[   93.568922]  r5:7e92de2c r4:00000000
[   93.572549] Code: e3a02024 e5933000 e1a04624 e0243492 (e5943004) 
[   93.578730] ---[ end trace cff8773499967501 ]---

问题分析与解决方法

核心问题

  1. 错误释放非动态分配内存:pulseptr是指针数组,若为全局/静态数组,它本身并非kmalloc分配的内存,直接kfree(pulseptr)会触发内核页错误。
  2. 内存分配逻辑错误:kmalloc(sizeof(buffer))仅分配了指针长度的内存(ARM平台为4字节),无法容纳用户传入的完整字符串;且直接用sprintf访问用户空间地址,会触发非法内存访问。

修正步骤

  1. 修正write函数的内存分配与数据拷贝:
    使用copy_from_user安全拷贝用户空间数据,分配足够内存存储字符串,并检查内存分配结果:

    static char *pulseptr[5];
    static int pulsecount = 0;
    
    ssize_t hcsr04_write(struct file *filp, const char *buffer, size_t length, loff_t * offset)
    {
        char *tmp_buf = kmalloc(length + 1, GFP_ATOMIC);
        if (!tmp_buf) {
            return -ENOMEM;
        }
        // 安全拷贝用户空间数据到内核
        if (copy_from_user(tmp_buf, buffer, length)) {
            kfree(tmp_buf);
            return -EFAULT;
        }
        tmp_buf[length] = '\0'; // 确保字符串以结束符收尾
    
        if (pulsecount < 5){
            pulseptr[pulsecount] = tmp_buf;
            pulsecount++;
        }
        else{
            int j = 0;
            // 替换前先释放旧内存,避免泄漏
            while (j<4){
                kfree(pulseptr[j]);
                pulseptr[j] = pulseptr[j+1];
                j++;
            }
            kfree(pulseptr[4]);
            pulseptr[4] = tmp_buf;
        }
        return length; // 返回实际写入字节数,符合VFS规范
    }
    
  2. 修正cleanup函数的内存释放逻辑:
    遍历指针数组,逐个释放动态分配的元素,而非数组本身:

    static void __exit hcsr04_module_cleanup(void)
    {
        int i;
        // 释放每个动态分配的内存块
        for (i = 0; i < 5; i++) {
            if (pulseptr[i]) {
                kfree(pulseptr[i]);
                pulseptr[i] = NULL;
            }
        }
        printk(KERN_INFO "Dynamic memory freed successfully.");
    
        gpio_free( GPIO_OUT );
        gpio_free( GPIO_IN );
        hcsr04_lock = 0;
        cdev_del(&hcsr04_cdev);
        unregister_chrdev_region( hcsr04_dev, 1 );
        kobject_put( hcsr04_kobject );
    }
    
  3. 补充初始化逻辑:
    在模块初始化函数中,初始化指针数组元素为NULL:

    static int __init hcsr04_module_init(void)
    {
        int i;
        for (i = 0; i < 5; i++) {
            pulseptr[i] = NULL;
        }
        pulsecount = 0;
        // ...其他初始化代码
        return 0;
    }
    

额外注意事项

  • 内核态操作用户空间数据必须使用copy_from_user/copy_to_user,禁止直接访问用户地址。
  • 替换数组元素时,务必先释放旧内存,避免内存泄漏。
  • VFS的write函数必须返回实际写入的字节数,否则用户态程序会判定写入失败。

内容的提问来源于stack exchange,提问作者user13024886

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 09:25:37