設(shè)備驅(qū)動開發(fā)的10個常見錯誤與正確模式:從內(nèi)存泄漏到并發(fā)安全
設(shè)備驅(qū)動開發(fā)的10個常見錯誤與正確模式從內(nèi)存泄漏到并發(fā)安全作者鐘伊人 | 日期2026-07-29 | Week5 總結(jié)與避坑模塊一內(nèi)存管理類錯誤錯誤1-3錯誤1未釋放DMA映射導(dǎo)致內(nèi)存泄漏DMA直接內(nèi)存訪問是驅(qū)動開發(fā)的核心機(jī)制。許多開發(fā)者只關(guān)注kmalloc的釋放忽略了DMA映射。這會導(dǎo)致物理內(nèi)存不可回收系統(tǒng)運(yùn)行時間越長越慢。/* ? 錯誤模式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緩沖區(qū)... */ 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緩沖區(qū)... */ ret do_something_with_dma(buf-cpu_addr); /* ? 正確先釋放DMA映射再釋放結(jié)構(gòu)體 */ dma_free_coherent(dev, buf-size, buf-cpu_addr, buf-dma_handle); kfree(buf); return ret; } /* ?? 生產(chǎn)級模式使用devm_自動管理資源 */ static int best_dma_example(struct device *dev) { struct my_dma_buf *buf; /* devm_kmalloc設(shè)備解綁時自動釋放 */ buf devm_kmalloc(dev, sizeof(*buf), GFP_KERNEL); if (!buf) return -ENOMEM; buf-size 4096; /* devm_dma_alloc設(shè)備解綁時自動釋放DMA映射 */ buf-cpu_addr dmam_alloc_coherent(dev, buf-size, buf-dma_handle, GFP_KERNEL); if (!buf-cpu_addr) return -ENOMEM; /* devm自動清理已分配的資源 */ /* 使用DMA緩沖區(qū)... */ return do_something_with_dma(buf-cpu_addr); /* 無需手動釋放設(shè)備解綁時自動完成 */ }錯誤2kfree與kvfree混用kmalloc分配的內(nèi)存必須用kfree釋放。vmalloc分配的內(nèi)存必須用vfree釋放。kvmalloc內(nèi)核4.12分配的內(nèi)存必須用kvfree釋放?;煊脮?dǎo)致內(nèi)核崩潰。/* ? 錯誤模式混用釋放函數(shù) */ 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的內(nèi)存不能用vfree釋放 */ kfree(ptr2); /* vmalloc的內(nèi)存不能用kfree釋放 */ kfree(ptr3); /* kvmalloc的內(nèi)存不能用kfree釋放 */ } /* ? 正確模式配對使用分配/釋放函數(shù) */ 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 */ } /* ? 生產(chǎn)級封裝安全的釋放函數(shù) */ /** * 安全釋放內(nèi)存自動檢測分配類型 * 內(nèi)核5.8支持kmem_is_## 這里用更安全的方式 */ static inline void safe_kvfree(const void *addr) { /* 在不確定時用kvfree它能處理kmalloc和vmalloc兩種情況 */ /* 注意僅適用于kvmalloc分配的內(nèi)存 */ kvfree(addr); } /** * 內(nèi)存分配包裝器統(tǒng)一使用kvmalloc * 讓釋放只用kvfree避免混用問題 */ #define my_alloc(size) kvmalloc(size, GFP_KERNEL) #define my_free(ptr) kvfree(ptr)錯誤3在原子上下文中睡眠這是驅(qū)動開發(fā)中最危險的錯誤之一。在中斷處理程序、持有自旋鎖時調(diào)用可能睡眠的函數(shù)。會導(dǎo)致系統(tǒng)死鎖或內(nèi)核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); /* ? 致命錯誤在持有自旋鎖時調(diào)用可能睡眠的函數(shù) */ ptr kmalloc(1024, GFP_KERNEL); /* GFP_KERNEL可能睡眠 */ do_something(ptr); /* ? 致命錯誤在中斷上下文中調(diào)用可能睡眠的函數(shù) */ ptr kmalloc(1024, GFP_KERNEL); /* 中斷上下文不允許睡眠 */ spin_unlock(my_lock); return IRQ_HANDLED; } /* ? 正確模式區(qū)分上下文使用正確的GFP標(biāo)志 */ 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; /* 持有自旋鎖時不能調(diào)用任何可能睡眠的函數(shù) */ spin_lock_irqsave(my_lock, flags); do_something_atomic(ptr); /* 只調(diào)用原子操作 */ spin_unlock_irqrestore(my_lock, flags); kfree(ptr); return IRQ_HANDLED; } /** * GFP標(biāo)志選擇指南 * * GFP_KERNEL - 進(jìn)程上下文可以睡眠最常見 * GFP_ATOMIC - 原子上下文中斷、軟中斷、持有自旋鎖 * GFP_NOWAIT - 不睡眠不等待比ATOMIC更輕量 * GFP_USER - 為用戶空間分配可觸發(fā)換頁 * GFP_DMA - 為DMA分配16MB物理內(nèi)存 * GFP_NOIO - 不能啟動I/O存儲驅(qū)動用 * GFP_NOFS - 不能啟動文件系統(tǒng)操作文件系統(tǒng)驅(qū)動用 */ /* ? 生產(chǎn)級在可能睡眠前釋放自旋鎖 */ 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需要先獲取鎖但后續(xù)需要大內(nèi)存分配 */ mutex_lock(data-lock); /* 如果需要大塊內(nèi)存先釋放鎖再分配 */ mutex_unlock(data-lock); big_buffer kmalloc(64 * 1024, GFP_KERNEL); /* 可能睡眠在鎖外 */ if (!big_buffer) return -ENOMEM; /* 重新獲取鎖使用已分配的內(nèi)存 */ mutex_lock(data-lock); ret process_with_buffer(data, big_buffer); mutex_unlock(data-lock); kfree(big_buffer); return ret; }Mermaid內(nèi)存分配函數(shù)選擇流程模塊二并發(fā)安全類錯誤錯誤4-6錯誤4競態(tài)條件Race Condition忽視驅(qū)動程序運(yùn)行在多線程環(huán)境中。多個進(jìn)程可能同時打開同一設(shè)備。中斷可能隨時打斷開續(xù)執(zhí)行。忽視并發(fā)保護(hù)是系統(tǒng)不穩(wěn)定的主要原因。/* ? 錯誤模式無并發(fā)保護(hù) */ struct my_device { void *buffer; size_t buffer_size; int is_open; /* ? 沒有保護(hù) */ }; static int bad_open(struct inode *inode, struct file *file) { struct my_device *dev get_device(inode); /* ? 兩個進(jìn)程可能同時讀到 is_open 0 */ if (dev-is_open) { return -EBUSY; /* 實(shí)際上可能兩個進(jìn)程都通過檢查 */ } dev-is_open 1; dev-buffer kmalloc(4096, GFP_KERNEL); return 0; } /* ? 正確模式使用互斥鎖保護(hù) */ 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; } /* ? 生產(chǎn)級使用原子變量替代簡單標(biāo)志位 */ struct my_device_atomic { void *buffer; atomic_t open_count; /* ? 原子變量 */ struct mutex op_lock; /* ? 保護(hù)實(shí)際操作 */ }; static int best_open(struct inode *inode, struct file *file) { struct my_device_atomic *dev get_device(inode); /* ? 原子方式檢查并設(shè)置類似自旋鎖的try_once語義 */ if (atomic_inc_return(dev-open_count) ! 1) { atomic_dec(dev-open_count); return -EBUSY; /* 已經(jīng)有其他進(jìn)程打開了 */ } /* 此時確定只有當(dāng)前進(jìn)程打開了設(shè)備 */ 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的常見模式死鎖比競態(tài)條件更難調(diào)試。常見的死鎖模式包括鎖順序不一致、遞歸獲取同一鎖、持有鎖時等待。/* ? 錯誤模式1鎖順序不一致導(dǎo)致死鎖 */ struct my_driver { struct mutex lock_a; struct mutex lock_b; }; /* 線程1執(zhí)行路徑 */ 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執(zhí)行路徑 */ 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); } /* ? 正確模式統(tǒng)一鎖獲取順序 */ /* 定義全局鎖順序按地址排序是一種簡單方法 */ enum lock_order { LOCK_ORDER_A 1, LOCK_ORDER_B 2, }; static void take_locks_in_order(struct my_driver *drv) { /* ? 永遠(yuǎn)按相同順序獲取鎖 */ 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); /* ? 等待用戶空間事件時持有鎖 → 死鎖風(fēng)險 */ wait_event(dev-wait_queue, dev-ready); /* 復(fù)制用戶數(shù)據(jù)可能失敗并重試 */ 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); /* ? 先復(fù)制用戶數(shù)據(jù)在鎖外 */ 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不正確地使用自旋鎖自旋鎖和保護(hù)對象生命周期的鎖不能混用。自旋鎖持有期間不能調(diào)用copy_to_user/copy_from_user。/* ? 錯誤模式在自旋鎖保護(hù)下訪問用戶空間 */ 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); /* ? 致命錯誤自旋鎖持有期間調(diào)用可能睡眠的函數(shù) */ 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; } /* ? 正確模式自旋鎖只保護(hù)極短的操作 */ 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; /* ? 分配臨時緩沖區(qū)在鎖外 */ 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); /* ? 自旋鎖內(nèi)只做內(nèi)存拷貝內(nèi)核空間到內(nèi)核空間 */ 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 互斥鎖選擇指南 * * 自旋鎖適用場景 * - 保護(hù)的操作極短幾微秒 * - 中斷上下文需要保護(hù) * - 不能睡眠的原子上下文 * * 互斥鎖適用場景 * - 保護(hù)的操作可能耗時毫秒級 * - 需要調(diào)用可能睡眠的函數(shù) * - 進(jìn)程上下文 * * 永遠(yuǎn)不要在持有自旋鎖時調(diào)用 * - kmalloc(GFP_KERNEL) * - copy_to_user / copy_from_user * - 任何可能觸發(fā)調(diào)度的函數(shù) */模塊三錯誤處理與資源管理錯誤7-8錯誤7資源分配失敗后的不完整回滾驅(qū)動程序的probe函數(shù)可能需要申請多種資源。任何一種資源申請失敗都必須完整釋放已申請的資源。這是Linux內(nèi)核編程最容易出現(xiàn)泄漏的地方。/* ? 錯誤模式資源申請失敗時不回滾 */ 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內(nèi)存 */ 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內(nèi)存 */ return 0; } /* ? 正確模式使用goto進(jìn)行統(tǒng)一錯誤處理 */ 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內(nèi)存 */ 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; /* 注冊字符設(shè)備 */ 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; } /* ?? 生產(chǎn)級使用devm_系列函數(shù)自動管理資源 */ static int best_probe(struct platform_device *pdev) { struct my_device *dev; int ret; /* 使用devm_系列設(shè)備解綁時自動釋放 */ 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緩沖區(qū)自動釋放 */ 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; /* 成功后所有資源由系統(tǒng)管理 */ } 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忽略函數(shù)的返回值內(nèi)核中幾乎所有函數(shù)都可能失敗。忽略錯誤處理是導(dǎo)致系統(tǒng)不穩(wěn)定的主要原因。/* ? 錯誤模式忽略返回值 */ static void bad_example(struct device *dev) { struct class *cls; struct device *device; /* ? 錯誤忽略class_create的返回值 */ cls class_create(THIS_MODULE, myclass); /* 如果失敗cls是ERR_PTR后續(xù)使用會崩潰 */ /* ? 錯誤忽略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, 創(chuàng)建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, 創(chuàng)建設(shè)備失敗: %d\n, ret); goto err_destroy_class; } return 0; err_destroy_class: class_destroy(cls); return ret; } /* ? 生產(chǎn)級封裝常用操作減少錯誤檢查代碼量 */ /** * 安全版本的設(shè)備創(chuàng)建 * 自動處理錯誤回滾 */ 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(設(shè)備創(chuàng)建失敗: %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缺失硬件寄存器訪問需要正確的內(nèi)存屏障Memory Barrier。CPU可能會重排指令導(dǎo)致設(shè)備看到錯誤的寄存器寫入順序。/* ? 錯誤模式無內(nèi)存屏障的寄存器訪問 */ static void bad_hw_init(struct my_device *dev) { /* ? 錯誤CPU可能重排這些寫入 */ writel(0x01, dev-regs-ctrl); /* 使能設(shè)備 */ writel(0x100, dev-regs-size); /* 設(shè)置大小 */ writel(0x01, dev-regs-start); /* 啟動設(shè)備 */ /* 硬件可能先看到start1但ctrl還未使能 → 故障 */ } /* ? 正確模式使用內(nèi)存屏障 */ 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(); } /* ? 生產(chǎn)級使用relaxed訪問顯式屏障性能更優(yōu) */ static void best_hw_init(struct my_device *dev) { /* readl_relaxed/writel_relaxed無屏障更快 */ /* 適合在已有屏障保護(hù)的代碼路徑中使用 */ writel_relaxed(0x01, dev-regs-ctrl); writel_relaxed(0x100, dev-regs-size); writel_relaxed(0x01, dev-regs-start); /* ? 在關(guān)鍵路徑末尾加一次屏障 */ wmb(); /* 確保三個寄存器按順序?qū)懭胗布?*/ } /** * 內(nèi)存屏障使用指南 * * wmb() - 寫屏障確保之前的寫操作在后之前的寫操作之后完成 * rmb() - 讀屏障確保之前的讀操作在之后的讀操作之前完成 * mb() - 全屏障讀寫都保證順序 * * 常見需要屏障的場景 * 1. 硬件寄存器編程必須先寫配置再寫啟動位 * 2. DMA描述符更新必須先寫數(shù)據(jù)再寫就緒標(biāo)志 * 3. 鎖實(shí)現(xiàn)必須與鎖操作配合使用 * * 注意readl/writel自帶屏障relaxed版本不帶 * 在x86上普通load/store本來就是有序的 * 在ARM/POWER上必須使用屏障 */錯誤10中斷處理程序中的不正確操作中斷處理程序運(yùn)行在原子上下文。不能調(diào)用可能睡眠的函數(shù)。不能執(zhí)行耗時操作。/* ? 錯誤模式在中斷處理程序中做太多事情 */ static irqreturn_t bad_irq_handler(int irq, void *data) { struct my_device *dev data; /* ? 錯誤1調(diào)用可能睡眠的函數(shù) */ spin_lock(dev-lock); /* 應(yīng)該用spin_lock_irqsave */ /* ... */ spin_unlock(dev-lock); /* ? 錯誤2執(zhí)行耗時操作延遲設(shè)備中斷響應(yīng) */ 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); /* ? 只做最少工作讀取狀態(tài)寄存器清除中斷 */ if (readl(dev-regs-int_status) INT_RX_READY) { /* 將接收到的數(shù)據(jù)放入緩沖區(qū) */ dev-rx_count readl(dev-regs-rx_len); memcpy(dev-rx_buffer, dev-regs-rx_data, dev-rx_count); /* 清除中斷標(biāo)志 */ writel(INT_RX_READY, dev-regs-int_clear); handled 1; /* ? 調(diào)度下半部tasklet或workqueue處理數(shù)據(jù) */ tasklet_schedule(dev-rx_tasklet); } spin_unlock_irqrestore(dev-lock, flags); return handled ? IRQ_HANDLED : IRQ_NONE; } /* 下半部可以執(zhí)行耗時操作 */ static void rx_tasklet(unsigned long data) { struct my_device *dev (struct my_device *)data; /* ? 在這里可以執(zhí)行耗時操作 */ process_received_data(dev-rx_buffer, dev-rx_count); /* ? 可以調(diào)用可能睡眠的函數(shù)如果是workqueue */ wake_up_interruptible(dev-read_queue); } /* ? 生產(chǎn)級使用request_threaded_irq threaded IRQ */ /** * 現(xiàn)代內(nèi)核推薦使用threaded IRQ * 硬IRQ只做最少工作剩余工作在線程中完成 * 線程可以睡眠、可以被調(diào)度 */ 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; /* 在這里處理實(shí)際工作可以睡眠 */ 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); }模塊五正確模式總結(jié)與檢查清單十大錯誤速查表編號錯誤類型癥狀正確模式1DMA映射泄漏系統(tǒng)運(yùn)行越慢devm_dma_alloc自動管理2kfree/kvfree混用內(nèi)核崩潰統(tǒng)一用kvmallockvfree3原子上下文睡眠死鎖/BUG使用GFP_ATOMIC4競態(tài)條件數(shù)據(jù)損壞mutex/atomic_t保護(hù)5死鎖系統(tǒng)懸掛統(tǒng)一鎖順序6自旋鎖誤用崩潰/性能差自旋鎖只保護(hù)極短操作7資源泄漏模塊無法卸載devm_goto錯誤處理8忽略返回值隨機(jī)崩潰所有函數(shù)返回值必須檢查9缺少內(nèi)存屏障硬件異常wmb()/rmb()/mb()10中斷處理不當(dāng)系統(tǒng)無響應(yīng)硬IRQ只做最少工作Mermaid驅(qū)動開發(fā)正確流程生產(chǎn)級驅(qū)動開發(fā)檢查清單#!/usr/bin/env python3 驅(qū)動代碼審查檢查清單 在提交代碼前運(yùn)行此檢查 CHECKLIST [ (內(nèi)存管理, [ 所有kmalloc是否有對應(yīng)的kfree, 所有dma_alloc_coherent是否有對應(yīng)的dma_free_coherent, 是否優(yōu)先使用devm_系列函數(shù), kvmalloc的內(nèi)存是否用kvfree釋放, ]), (并發(fā)安全, [ 全局變量是否有鎖保護(hù), 鎖的獲取順序是否全局一致, 是否在持有自旋鎖時調(diào)用可能睡眠的函數(shù), 是否在原子上下文中使用GFP_KERNEL, ]), (中斷處理, [ 中斷處理程序是否只做最少工作, 是否使用threaded IRQ, 中斷處理程序中是否調(diào)用copy_to/from_user, 是否正確的清除中斷標(biāo)志, ]), (錯誤處理, [ 所有函數(shù)返回值是否都檢查了, probe失敗是否有完整的資源回滾, 是否使用goto進(jìn)行統(tǒng)一錯誤處理, ]), (硬件交互, [ 寄存器編程是否有必要的內(nèi)存屏障, readl/writel是否用對了還是應(yīng)該用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(驅(qū)動代碼審查檢查清單) print( * 50) print_checklist() print(\n * 50) print(每項打勾后再提交代碼)純技術(shù)總結(jié)內(nèi)存管理三大坑DMA映射泄漏用devm_dma_alloc、kfree/kvfree混用統(tǒng)一kvmallockvfree、原子上下文睡眠用GFP_ATOMIC并發(fā)安全互斥鎖保護(hù)open/read/write原子變量保護(hù)簡單標(biāo)志統(tǒng)一鎖順序防死鎖自旋鎖只保護(hù)微秒級操作資源回滾probe函數(shù)用goto統(tǒng)一錯誤處理優(yōu)先devm_自動管理資源remove只需釋放非devm_資源硬件交互寄存器編程加wmb()屏障readl/writel自帶屏障relaxed版本需手動加屏障中斷處理硬IRQ只做最少工作讀狀態(tài)清中斷耗時操作放threaded IRQ或tasklet完整代碼已在各模塊中給出可直接用于生產(chǎn)環(huán)境驅(qū)動開發(fā)

相關(guān)新聞

告別傳統(tǒng)Agent開發(fā)!Mellea的Intrinsics技術(shù)如何提升AI系統(tǒng)穩(wěn)定性

告別傳統(tǒng)Agent開發(fā)!Mellea的Intrinsics技術(shù)如何提升AI系統(tǒng)穩(wěn)定性

告別傳統(tǒng)Agent開發(fā)!Mellea的Intrinsics技術(shù)如何提升AI系統(tǒng)穩(wěn)定性 【免費(fèi)下載鏈接】mellea Mellea is a library for writing generative programs. 項目地址: https://gitcode.com/gh_mirrors/me/mellea 在AI應(yīng)用開發(fā)領(lǐng)域,傳統(tǒng)Agent系統(tǒng)常常面臨…

2026/7/29 20:18:44 閱讀更多
MIPI CSI-2協(xié)議引擎寄存器配置實(shí)戰(zhàn):從虛擬通道到FIFO深度優(yōu)化

MIPI CSI-2協(xié)議引擎寄存器配置實(shí)戰(zhàn):從虛擬通道到FIFO深度優(yōu)化

1. 項目概述與核心價值在嵌入式圖像處理系統(tǒng)的開發(fā)中,尤其是涉及攝像頭傳感器與主處理器(如應(yīng)用處理器、FPGA或ASIC)通信的場景,MIPI CSI-2協(xié)議是當(dāng)之無愧的“血管”。它定義了攝像頭與主機(jī)之間高速、串行、差分的數(shù)據(jù)傳輸標(biāo)準(zhǔn)。然…

2026/7/30 1:11:12 閱讀更多
支持向量機(jī)(SVM)實(shí)戰(zhàn):Python實(shí)現(xiàn)與參數(shù)調(diào)優(yōu)指南

支持向量機(jī)(SVM)實(shí)戰(zhàn):Python實(shí)現(xiàn)與參數(shù)調(diào)優(yōu)指南

1. 支持向量機(jī)實(shí)戰(zhàn):從理論到Python代碼的完整指南支持向量機(jī)(SVM)作為機(jī)器學(xué)習(xí)中的經(jīng)典算法,在分類和回歸問題上表現(xiàn)出色。我第一次接觸SVM是在處理一個圖像分類項目時,當(dāng)時被它在小樣本數(shù)據(jù)集上的優(yōu)異表現(xiàn)所震撼。不同于神經(jīng)網(wǎng)絡(luò)需要大量數(shù)據(jù)…

2026/7/30 1:11:12 閱讀更多
國內(nèi)穩(wěn)定使用GPT、Gemini、Claude三大AI模型的直連實(shí)戰(zhàn)指南

國內(nèi)穩(wěn)定使用GPT、Gemini、Claude三大AI模型的直連實(shí)戰(zhàn)指南

如果你最近在尋找能夠在國內(nèi)穩(wěn)定使用的AI助手,可能已經(jīng)發(fā)現(xiàn)了這樣一個尷尬的現(xiàn)實(shí):官方渠道訪問困難,而各種"免費(fèi)教程"往往藏著各種套路——要么是過時的信息,要么需要復(fù)雜的配置,甚至有些直接就是騙局。本文…

2026/7/30 1:11:12 閱讀更多
訪問控制技術(shù)深度解析:從DAC到ABAC,構(gòu)建企業(yè)級安全防線

訪問控制技術(shù)深度解析:從DAC到ABAC,構(gòu)建企業(yè)級安全防線

1. 項目概述:從“門禁”到“數(shù)字邊界”的守護(hù)邏輯聊到信息安全,很多人第一反應(yīng)是防火墻、殺毒軟件或者加密技術(shù)。但在我十多年的從業(yè)經(jīng)歷里,我發(fā)現(xiàn)一個被嚴(yán)重低估卻又無處不在的核心基石:訪問控制。你可以把它理解為數(shù)字世界的“門…

2026/7/30 1:01:11 閱讀更多
[GESP202606 四級] 掃雷

[GESP202606 四級] 掃雷

B4557 [GESP202606 四級] 掃雷 https://www.luogu.com.cn/problem/B4557 中國計算機(jī)學(xué)會(CCF)2026年6月C四級講解——掃雷 https://www.bilibili.com/video/BV1MCMg6AEXR/ B4557 [GESP202606 四級] 掃雷 https://www.bilibili.com/video/BV1ZKTj6ZEVh/ 2…

2026/7/30 0:01:06 閱讀更多