
設備驅動開發的10個常見錯誤與正確模式從內存泄漏到并發安全作者鐘伊人 | 日期2026-07-29 | Week5 總結與避坑模塊一內存管理類錯誤錯誤1-3錯誤1未釋放DMA映射導致內存泄漏DMA直接內存訪問是驅動開發的核心機制。許多開發者只關注kmalloc的釋放忽略了DMA映射。這會導致物理內存不可回收系統運行時間越長越慢。/* ? 錯誤模式DMA映射泄漏 */ #include linux/dma-mapping.h #include linux/device.h #include linux/slab.h struct my_dma_buf { void *cpu_addr; dma_addr_t dma_handle; size_t size; }; static int bad_dma_example(struct device *dev) { struct my_dma_buf *buf; buf kmalloc(sizeof(*buf), GFP_KERNEL); if (!buf) return -ENOMEM; buf-size 4096; buf-cpu_addr dma_alloc_coherent(dev, buf-size, buf-dma_handle, GFP_KERNEL); if (!buf-cpu_addr) { kfree(buf); return -ENOMEM; } /* 使用DMA緩沖區... */ do_something_with_dma(buf-cpu_addr); /* ? 錯誤只釋放了buf沒有釋放DMA映射 */ kfree(buf); return 0; } /* ? 正確模式配對釋放DMA映射 */ static int good_dma_example(struct device *dev) { struct my_dma_buf *buf; int ret 0; buf kmalloc(sizeof(*buf), GFP_KERNEL); if (!buf) return -ENOMEM; buf-size 4096; buf-cpu_addr dma_alloc_coherent(dev, buf-size, buf-dma_handle, GFP_KERNEL); if (!buf-cpu_addr) { kfree(buf); return -ENOMEM; } /* 使用DMA緩沖區... */ ret do_something_with_dma(buf-cpu_addr); /* ? 正確先釋放DMA映射再釋放結構體 */ dma_free_coherent(dev, buf-size, buf-cpu_addr, buf-dma_handle); kfree(buf); return ret; } /* ?? 生產級模式使用devm_自動管理資源 */ static int best_dma_example(struct device *dev) { struct my_dma_buf *buf; /* devm_kmalloc設備解綁時自動釋放 */ buf devm_kmalloc(dev, sizeof(*buf), GFP_KERNEL); if (!buf) return -ENOMEM; buf-size 4096; /* devm_dma_alloc設備解綁時自動釋放DMA映射 */ buf-cpu_addr dmam_alloc_coherent(dev, buf-size, buf-dma_handle, GFP_KERNEL); if (!buf-cpu_addr) return -ENOMEM; /* devm自動清理已分配的資源 */ /* 使用DMA緩沖區... */ return do_something_with_dma(buf-cpu_addr); /* 無需手動釋放設備解綁時自動完成 */ }錯誤2kfree與kvfree混用kmalloc分配的內存必須用kfree釋放。vmalloc分配的內存必須用vfree釋放。kvmalloc內核4.12分配的內存必須用kvfree釋放。混用會導致內核崩潰。/* ? 錯誤模式混用釋放函數 */ static void bad_free_example(void) { void *ptr1 kmalloc(1024, GFP_KERNEL); void *ptr2 vmalloc(4096); void *ptr3 kvmalloc(8192, GFP_KERNEL); /* ? 錯誤 */ vfree(ptr1); /* kmalloc的內存不能用vfree釋放 */ kfree(ptr2); /* vmalloc的內存不能用kfree釋放 */ kfree(ptr3); /* kvmalloc的內存不能用kfree釋放 */ } /* ? 正確模式配對使用分配/釋放函數 */ static void good_free_example(void) { void *ptr1 kmalloc(1024, GFP_KERNEL); void *ptr2 vmalloc(4096); void *ptr3 kvmalloc(8192, GFP_KERNEL); /* ? 正確 */ kfree(ptr1); vfree(ptr2); kvfree(ptr3); /* 必須用kvfree */ } /* ? 生產級封裝安全的釋放函數 */ /** * 安全釋放內存自動檢測分配類型 * 內核5.8支持kmem_is_## 這里用更安全的方式 */ static inline void safe_kvfree(const void *addr) { /* 在不確定時用kvfree它能處理kmalloc和vmalloc兩種情況 */ /* 注意僅適用于kvmalloc分配的內存 */ kvfree(addr); } /** * 內存分配包裝器統一使用kvmalloc * 讓釋放只用kvfree避免混用問題 */ #define my_alloc(size) kvmalloc(size, GFP_KERNEL) #define my_free(ptr) kvfree(ptr)錯誤3在原子上下文中睡眠這是驅動開發中最危險的錯誤之一。在中斷處理程序、持有自旋鎖時調用可能睡眠的函數。會導致系統死鎖或內核BUG。/* ? 錯誤模式在原子上下文中睡眠 */ #include linux/spinlock.h #include linux/interrupt.h #include linux/slab.h static DEFINE_SPINLOCK(my_lock); static irqreturn_t bad_irq_handler(int irq, void *data) { void *ptr; spin_lock(my_lock); /* ? 致命錯誤在持有自旋鎖時調用可能睡眠的函數 */ ptr kmalloc(1024, GFP_KERNEL); /* GFP_KERNEL可能睡眠 */ do_something(ptr); /* ? 致命錯誤在中斷上下文中調用可能睡眠的函數 */ ptr kmalloc(1024, GFP_KERNEL); /* 中斷上下文不允許睡眠 */ spin_unlock(my_lock); return IRQ_HANDLED; } /* ? 正確模式區分上下文使用正確的GFP標志 */ static irqreturn_t good_irq_handler(int irq, void *data) { void *ptr; unsigned long flags; /* 中斷上下文使用GFP_ATOMIC不會睡眠 */ ptr kmalloc(1024, GFP_ATOMIC); if (!ptr) return IRQ_NONE; /* 持有自旋鎖時不能調用任何可能睡眠的函數 */ spin_lock_irqsave(my_lock, flags); do_something_atomic(ptr); /* 只調用原子操作 */ spin_unlock_irqrestore(my_lock, flags); kfree(ptr); return IRQ_HANDLED; } /** * GFP標志選擇指南 * * GFP_KERNEL - 進程上下文可以睡眠最常見 * GFP_ATOMIC - 原子上下文中斷、軟中斷、持有自旋鎖 * GFP_NOWAIT - 不睡眠不等待比ATOMIC更輕量 * GFP_USER - 為用戶空間分配可觸發換頁 * GFP_DMA - 為DMA分配16MB物理內存 * GFP_NOIO - 不能啟動I/O存儲驅動用 * GFP_NOFS - 不能啟動文件系統操作文件系統驅動用 */ /* ? 生產級在可能睡眠前釋放自旋鎖 */ static int good_driver_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct my_data *data file-private_data; void *big_buffer; int ret; /* 情況1需要先獲取鎖但后續需要大內存分配 */ mutex_lock(data-lock); /* 如果需要大塊內存先釋放鎖再分配 */ mutex_unlock(data-lock); big_buffer kmalloc(64 * 1024, GFP_KERNEL); /* 可能睡眠在鎖外 */ if (!big_buffer) return -ENOMEM; /* 重新獲取鎖使用已分配的內存 */ mutex_lock(data-lock); ret process_with_buffer(data, big_buffer); mutex_unlock(data-lock); kfree(big_buffer); return ret; }Mermaid內存分配函數選擇流程模塊二并發安全類錯誤錯誤4-6錯誤4競態條件Race Condition忽視驅動程序運行在多線程環境中。多個進程可能同時打開同一設備。中斷可能隨時打斷開續執行。忽視并發保護是系統不穩定的主要原因。/* ? 錯誤模式無并發保護 */ struct my_device { void *buffer; size_t buffer_size; int is_open; /* ? 沒有保護 */ }; static int bad_open(struct inode *inode, struct file *file) { struct my_device *dev get_device(inode); /* ? 兩個進程可能同時讀到 is_open 0 */ if (dev-is_open) { return -EBUSY; /* 實際上可能兩個進程都通過檢查 */ } dev-is_open 1; dev-buffer kmalloc(4096, GFP_KERNEL); return 0; } /* ? 正確模式使用互斥鎖保護 */ struct my_device { void *buffer; size_t buffer_size; int is_open; struct mutex lock; /* ? 互斥鎖 */ }; static int good_open(struct inode *inode, struct file *file) { struct my_device *dev get_device(inode); int ret; /* ? 獲取互斥鎖 */ mutex_lock(dev-lock); if (dev-is_open) { ret -EBUSY; goto unlock; } dev-is_open 1; dev-buffer kmalloc(4096, GFP_KERNEL); if (!dev-buffer) { dev-is_open 0; ret -ENOMEM; goto unlock; } ret 0; unlock: mutex_unlock(dev-lock); return ret; } /* ? 生產級使用原子變量替代簡單標志位 */ struct my_device_atomic { void *buffer; atomic_t open_count; /* ? 原子變量 */ struct mutex op_lock; /* ? 保護實際操作 */ }; static int best_open(struct inode *inode, struct file *file) { struct my_device_atomic *dev get_device(inode); /* ? 原子方式檢查并設置類似自旋鎖的try_once語義 */ if (atomic_inc_return(dev-open_count) ! 1) { atomic_dec(dev-open_count); return -EBUSY; /* 已經有其他進程打開了 */ } /* 此時確定只有當前進程打開了設備 */ mutex_lock(dev-op_lock); dev-buffer kmalloc(4096, GFP_KERNEL); if (!dev-buffer) { mutex_unlock(dev-op_lock); atomic_dec(dev-open_count); return -ENOMEM; } mutex_unlock(dev-op_lock); return 0; }錯誤5死鎖Deadlock的常見模式死鎖比競態條件更難調試。常見的死鎖模式包括鎖順序不一致、遞歸獲取同一鎖、持有鎖時等待。/* ? 錯誤模式1鎖順序不一致導致死鎖 */ struct my_driver { struct mutex lock_a; struct mutex lock_b; }; /* 線程1執行路徑 */ static void thread1_path(struct my_driver *drv) { mutex_lock(drv-lock_a); /* ... 做一些操作 ... */ mutex_lock(drv-lock_b); /* 先A后B */ /* ... */ mutex_unlock(drv-lock_b); mutex_unlock(drv-lock_a); } /* 線程2執行路徑 */ static void thread2_path(struct my_driver *drv) { mutex_lock(drv-lock_b); /* ... 做一些操作 ... */ mutex_lock(drv-lock_a); /* 先B后A → 死鎖 */ /* ... */ mutex_unlock(drv-lock_a); mutex_unlock(drv-lock_b); } /* ? 正確模式統一鎖獲取順序 */ /* 定義全局鎖順序按地址排序是一種簡單方法 */ enum lock_order { LOCK_ORDER_A 1, LOCK_ORDER_B 2, }; static void take_locks_in_order(struct my_driver *drv) { /* ? 永遠按相同順序獲取鎖 */ mutex_lock(drv-lock_a); /* 先A */ mutex_lock(drv-lock_b); /* 后B */ } static void release_locks_in_reverse_order(struct my_driver *drv) { /* ? 按相反順序釋放鎖 */ mutex_unlock(drv-lock_b); mutex_unlock(drv-lock_a); } /* ? 錯誤模式2在持有鎖時等待用戶空間 */ static ssize_t bad_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct my_device *dev file-private_data; int ret; mutex_lock(dev-lock); /* ? 等待用戶空間事件時持有鎖 → 死鎖風險 */ wait_event(dev-wait_queue, dev-ready); /* 復制用戶數據可能失敗并重試 */ ret copy_from_user(dev-buffer, buf, count); mutex_unlock(dev-lock); return ret ? -EFAULT : count; } /* ? 正確模式在持有鎖時不等待 */ static ssize_t good_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct my_device *dev file-private_data; void *tmp_buf; int ret; /* ? 先等待再獲取鎖 */ wait_event(dev-wait_queue, dev-ready); /* ? 先復制用戶數據在鎖外 */ tmp_buf kmalloc(count, GFP_KERNEL); if (!tmp_buf) return -ENOMEM; if (copy_from_user(tmp_buf, buf, count)) { kfree(tmp_buf); return -EFAULT; } /* ? 獲取鎖快速操作釋放鎖 */ mutex_lock(dev-lock); memcpy(dev-buffer, tmp_buf, count); dev-buffer_size count; mutex_unlock(dev-lock); kfree(tmp_buf); return count; }錯誤6不正確地使用自旋鎖自旋鎖和保護對象生命周期的鎖不能混用。自旋鎖持有期間不能調用copy_to_user/copy_from_user。/* ? 錯誤模式在自旋鎖保護下訪問用戶空間 */ static ssize_t bad_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct my_device *dev file-private_data; size_t to_copy; spin_lock(dev-spinlock); to_copy min(count, dev-data_size - *ppos); /* ? 致命錯誤自旋鎖持有期間調用可能睡眠的函數 */ if (copy_to_user(buf, dev-data *ppos, to_copy)) { spin_unlock(dev-spinlock); return -EFAULT; } *ppos to_copy; spin_unlock(dev-spinlock); return to_copy; } /* ? 正確模式自旋鎖只保護極短的操作 */ static ssize_t good_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct my_device *dev file-private_data; void *tmp_buf; size_t to_copy; int ret; /* ? 分配臨時緩沖區在鎖外 */ tmp_buf kmalloc(count, GFP_KERNEL); if (!tmp_buf) return -ENOMEM; spin_lock_irqsave(dev-spinlock, flags); to_copy min(count, dev-data_size - *ppos); /* ? 自旋鎖內只做內存拷貝內核空間到內核空間 */ memcpy(tmp_buf, dev-data *ppos, to_copy); spin_unlock_irqrestore(dev-spinlock, flags); /* ? 鎖外訪問用戶空間 */ if (copy_to_user(buf, tmp_buf, to_copy)) { kfree(tmp_buf); return -EFAULT; } *ppos to_copy; kfree(tmp_buf); return to_copy; } /** * 自旋鎖 vs 互斥鎖選擇指南 * * 自旋鎖適用場景 * - 保護的操作極短幾微秒 * - 中斷上下文需要保護 * - 不能睡眠的原子上下文 * * 互斥鎖適用場景 * - 保護的操作可能耗時毫秒級 * - 需要調用可能睡眠的函數 * - 進程上下文 * * 永遠不要在持有自旋鎖時調用 * - kmalloc(GFP_KERNEL) * - copy_to_user / copy_from_user * - 任何可能觸發調度的函數 */模塊三錯誤處理與資源管理錯誤7-8錯誤7資源分配失敗后的不完整回滾驅動程序的probe函數可能需要申請多種資源。任何一種資源申請失敗都必須完整釋放已申請的資源。這是Linux內核編程最容易出現泄漏的地方。/* ? 錯誤模式資源申請失敗時不回滾 */ static int bad_probe(struct platform_device *pdev) { struct my_device *dev; int ret; dev devm_kzalloc(pdev-dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; /* 申請資源1IRQ */ ret request_irq(irq_num, my_handler, 0, mydev, dev); if (ret) return ret; /* ? 錯誤沒有釋放dev雖然這里用devm沒問題 */ /* 申請資源2I/O內存 */ dev-regs devm_ioremap_resource(pdev-dev, res); if (IS_ERR(dev-regs)) { return PTR_ERR(dev-regs); /* ? 錯誤沒有釋放IRQ */ } /* 申請資源3DMA */ ret dma_set_mask_and_coherent(pdev-dev, DMA_BIT_MASK(32)); if (ret) return ret; /* ? 錯誤沒有釋放IRQ和I/O內存 */ return 0; } /* ? 正確模式使用goto進行統一錯誤處理 */ static int good_probe(struct platform_device *pdev) { struct my_device *dev; int ret; dev devm_kzalloc(pdev-dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; /* 申請資源1IRQ */ ret request_irq(irq_num, my_handler, 0, mydev, dev); if (ret) goto err_free_dev; /* 申請資源2I/O內存 */ dev-regs devm_ioremap_resource(pdev-dev, res); if (IS_ERR(dev-regs)) { ret PTR_ERR(dev-regs); goto err_free_irq; } /* 申請資源3DMA */ ret dma_set_mask_and_coherent(pdev-dev, DMA_BIT_MASK(32)); if (ret) goto err_unmap_io; /* 注冊字符設備 */ ret register_chrdev(0, mydev, my_fops); if (ret 0) goto err_clear_dma; dev-major ret; platform_set_drvdata(pdev, dev); return 0; /* 錯誤回滾按相反順序釋放資源 */ err_clear_dma: dma_set_mask_and_coherent(pdev-dev, 0); err_unmap_io: /* devm_資源會自動釋放無需手動處理 */ err_free_irq: free_irq(irq_num, dev); err_free_dev: /* devm_分配會自動釋放 */ return ret; } /* ?? 生產級使用devm_系列函數自動管理資源 */ static int best_probe(struct platform_device *pdev) { struct my_device *dev; int ret; /* 使用devm_系列設備解綁時自動釋放 */ dev devm_kzalloc(pdev-dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; /* devm_request_irq自動釋放IRQ */ ret devm_request_irq(pdev-dev, irq_num, my_handler, 0, mydev, dev); if (ret) return ret; /* 所有devm_資源自動釋放 */ /* devm_ioremap_resource自動取消映射 */ dev-regs devm_ioremap_resource(pdev-dev, res); if (IS_ERR(dev-regs)) return PTR_ERR(dev-regs); /* devm_分配DMA緩沖區自動釋放 */ dev-dma_buf dmam_alloc_coherent(pdev-dev, 4096, dev-dma_handle, GFP_KERNEL); if (!dev-dma_buf) return -ENOMEM; /* 只有非devm_資源才需要手動管理 */ dev-cdev cdev_alloc(); if (!dev-cdev) { /* devm_資源自動釋放 */ return -ENOMEM; } cdev_init(dev-cdev, my_fops); ret cdev_add(dev-cdev, dev-devno, 1); if (ret) { cdev_del(dev-cdev); /* 手動釋放非devm_資源 */ return ret; } platform_set_drvdata(pdev, dev); return 0; /* 成功后所有資源由系統管理 */ } static int best_remove(struct platform_device *pdev) { struct my_device *dev platform_get_drvdata(pdev); /* 只需要釋放非devm_資源 */ if (dev-cdev) cdev_del(dev-cdev); /* devm_資源自動釋放無需手動處理 */ return 0; }錯誤8忽略函數的返回值內核中幾乎所有函數都可能失敗。忽略錯誤處理是導致系統不穩定的主要原因。/* ? 錯誤模式忽略返回值 */ static void bad_example(struct device *dev) { struct class *cls; struct device *device; /* ? 錯誤忽略class_create的返回值 */ cls class_create(THIS_MODULE, myclass); /* 如果失敗cls是ERR_PTR后續使用會崩潰 */ /* ? 錯誤忽略device_create的返回值 */ device device_create(cls, NULL, dev-devno, NULL, mydevice); /* 如果失敗device是ERR_PTR */ } /* ? 正確模式檢查所有返回值 */ static int good_example(struct device *dev) { struct class *cls; struct device *device; int ret 0; cls class_create(THIS_MODULE, myclass); if (IS_ERR(cls)) { ret PTR_ERR(cls); dev_err(dev, 創建class失敗: %d\n, ret); return ret; } device device_create(cls, NULL, dev-devno, NULL, mydevice); if (IS_ERR(device)) { ret PTR_ERR(device); dev_err(dev, 創建設備失敗: %d\n, ret); goto err_destroy_class; } return 0; err_destroy_class: class_destroy(cls); return ret; } /* ? 生產級封裝常用操作減少錯誤檢查代碼量 */ /** * 安全版本的設備創建 * 自動處理錯誤回滾 */ static struct device *safe_device_create(struct class *cls, struct device *parent, dev_t devno, const char *fmt, ...) { struct device *dev; va_list args; char name[64]; va_start(args, fmt); vsnprintf(name, sizeof(name), fmt, args); va_end(args); dev device_create(cls, parent, devno, NULL, %s, name); if (IS_ERR(dev)) { pr_err(設備創建失敗: %s, err%ld\n, name, PTR_ERR(dev)); return dev; } return dev; } #define CHECK_AND_RETURN(ptr, label) \ do { \ if (IS_ERR(ptr)) { \ ret PTR_ERR(ptr); \ dev_err(dev, %s:%d 失敗: %d\n, __func__, __LINE__, ret); \ goto label; \ } \ } while (0)模塊四硬件交互類錯誤錯誤9-10錯誤9不正確的I/O訪問 Barrier缺失硬件寄存器訪問需要正確的內存屏障Memory Barrier。CPU可能會重排指令導致設備看到錯誤的寄存器寫入順序。/* ? 錯誤模式無內存屏障的寄存器訪問 */ static void bad_hw_init(struct my_device *dev) { /* ? 錯誤CPU可能重排這些寫入 */ writel(0x01, dev-regs-ctrl); /* 使能設備 */ writel(0x100, dev-regs-size); /* 設置大小 */ writel(0x01, dev-regs-start); /* 啟動設備 */ /* 硬件可能先看到start1但ctrl還未使能 → 故障 */ } /* ? 正確模式使用內存屏障 */ static void good_hw_init(struct my_device *dev) { writel(0x01, dev-regs-ctrl); /* ? 寫屏障確保ctrl寫入在size寫入之前完成 */ wmb(); writel(0x100, dev-regs-size); wmb(); writel(0x01, dev-regs-start); wmb(); } /* ? 生產級使用relaxed訪問顯式屏障性能更優 */ static void best_hw_init(struct my_device *dev) { /* readl_relaxed/writel_relaxed無屏障更快 */ /* 適合在已有屏障保護的代碼路徑中使用 */ writel_relaxed(0x01, dev-regs-ctrl); writel_relaxed(0x100, dev-regs-size); writel_relaxed(0x01, dev-regs-start); /* ? 在關鍵路徑末尾加一次屏障 */ wmb(); /* 確保三個寄存器按順序寫入硬件 */ } /** * 內存屏障使用指南 * * wmb() - 寫屏障確保之前的寫操作在后之前的寫操作之后完成 * rmb() - 讀屏障確保之前的讀操作在之后的讀操作之前完成 * mb() - 全屏障讀寫都保證順序 * * 常見需要屏障的場景 * 1. 硬件寄存器編程必須先寫配置再寫啟動位 * 2. DMA描述符更新必須先寫數據再寫就緒標志 * 3. 鎖實現必須與鎖操作配合使用 * * 注意readl/writel自帶屏障relaxed版本不帶 * 在x86上普通load/store本來就是有序的 * 在ARM/POWER上必須使用屏障 */錯誤10中斷處理程序中的不正確操作中斷處理程序運行在原子上下文。不能調用可能睡眠的函數。不能執行耗時操作。/* ? 錯誤模式在中斷處理程序中做太多事情 */ static irqreturn_t bad_irq_handler(int irq, void *data) { struct my_device *dev data; /* ? 錯誤1調用可能睡眠的函數 */ spin_lock(dev-lock); /* 應該用spin_lock_irqsave */ /* ... */ spin_unlock(dev-lock); /* ? 錯誤2執行耗時操作延遲設備中斷響應 */ for (int i 0; i 1000000; i) { process_data(dev-rx_buffer[i]); } /* ? 錯誤3在中斷中訪問用戶空間 */ copy_to_user(dev-user_buf, dev-rx_buffer, 1024); return IRQ_HANDLED; } /* ? 正確模式中斷處理程序只做最少工作 */ static irqreturn_t good_irq_handler(int irq, void *data) { struct my_device *dev data; unsigned long flags; int handled 0; /* ? 使用irqsave版本的自旋鎖 */ spin_lock_irqsave(dev-lock, flags); /* ? 只做最少工作讀取狀態寄存器清除中斷 */ if (readl(dev-regs-int_status) INT_RX_READY) { /* 將接收到的數據放入緩沖區 */ dev-rx_count readl(dev-regs-rx_len); memcpy(dev-rx_buffer, dev-regs-rx_data, dev-rx_count); /* 清除中斷標志 */ writel(INT_RX_READY, dev-regs-int_clear); handled 1; /* ? 調度下半部tasklet或workqueue處理數據 */ tasklet_schedule(dev-rx_tasklet); } spin_unlock_irqrestore(dev-lock, flags); return handled ? IRQ_HANDLED : IRQ_NONE; } /* 下半部可以執行耗時操作 */ static void rx_tasklet(unsigned long data) { struct my_device *dev (struct my_device *)data; /* ? 在這里可以執行耗時操作 */ process_received_data(dev-rx_buffer, dev-rx_count); /* ? 可以調用可能睡眠的函數如果是workqueue */ wake_up_interruptible(dev-read_queue); } /* ? 生產級使用request_threaded_irq threaded IRQ */ /** * 現代內核推薦使用threaded IRQ * 硬IRQ只做最少工作剩余工作在線程中完成 * 線程可以睡眠、可以被調度 */ static irqreturn_t top_half_handler(int irq, void *data) { struct my_device *dev data; /* 只清除中斷返回IRQ_WAKE_THREAD */ writel(INT_ALL, dev-regs-int_clear); return IRQ_WAKE_THREAD; } static irqreturn_t bottom_half_handler(int irq, void *data) { struct my_device *dev data; /* 在這里處理實際工作可以睡眠 */ process_all_pending_events(dev); return IRQ_HANDLED; } static int setup_threaded_irq(struct my_device *dev, int irq) { return request_threaded_irq(irq, top_half_handler, /* 硬IRQ */ bottom_half_handler, /* 線程 */ IRQF_ONESHOT, mydev, dev); }模塊五正確模式總結與檢查清單十大錯誤速查表編號錯誤類型癥狀正確模式1DMA映射泄漏系統運行越慢devm_dma_alloc自動管理2kfree/kvfree混用內核崩潰統一用kvmallockvfree3原子上下文睡眠死鎖/BUG使用GFP_ATOMIC4競態條件數據損壞mutex/atomic_t保護5死鎖系統懸掛統一鎖順序6自旋鎖誤用崩潰/性能差自旋鎖只保護極短操作7資源泄漏模塊無法卸載devm_goto錯誤處理8忽略返回值隨機崩潰所有函數返回值必須檢查9缺少內存屏障硬件異常wmb()/rmb()/mb()10中斷處理不當系統無響應硬IRQ只做最少工作Mermaid驅動開發正確流程生產級驅動開發檢查清單#!/usr/bin/env python3 驅動代碼審查檢查清單 在提交代碼前運行此檢查 CHECKLIST [ (內存管理, [ 所有kmalloc是否有對應的kfree, 所有dma_alloc_coherent是否有對應的dma_free_coherent, 是否優先使用devm_系列函數, kvmalloc的內存是否用kvfree釋放, ]), (并發安全, [ 全局變量是否有鎖保護, 鎖的獲取順序是否全局一致, 是否在持有自旋鎖時調用可能睡眠的函數, 是否在原子上下文中使用GFP_KERNEL, ]), (中斷處理, [ 中斷處理程序是否只做最少工作, 是否使用threaded IRQ, 中斷處理程序中是否調用copy_to/from_user, 是否正確的清除中斷標志, ]), (錯誤處理, [ 所有函數返回值是否都檢查了, probe失敗是否有完整的資源回滾, 是否使用goto進行統一錯誤處理, ]), (硬件交互, [ 寄存器編程是否有必要的內存屏障, readl/writel是否用對了還是應該用relaxed版本, 是否正確處理了硬件超時, ]), ] def print_checklist(): for category, items in CHECKLIST: print(f\n {category} ) for i, item in enumerate(items, 1): print(f [ ] {item}) if __name__ __main__: print(驅動代碼審查檢查清單) print( * 50) print_checklist() print(\n * 50) print(每項打勾后再提交代碼)純技術總結內存管理三大坑DMA映射泄漏用devm_dma_alloc、kfree/kvfree混用統一kvmallockvfree、原子上下文睡眠用GFP_ATOMIC并發安全互斥鎖保護open/read/write原子變量保護簡單標志統一鎖順序防死鎖自旋鎖只保護微秒級操作資源回滾probe函數用goto統一錯誤處理優先devm_自動管理資源remove只需釋放非devm_資源硬件交互寄存器編程加wmb()屏障readl/writel自帶屏障relaxed版本需手動加屏障中斷處理硬IRQ只做最少工作讀狀態清中斷耗時操作放threaded IRQ或tasklet完整代碼已在各模塊中給出可直接用于生產環境驅動開發