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

相關(guān)新聞

PhotoRec終極數(shù)據(jù)恢復(fù)指南:免費(fèi)開(kāi)源工具如何拯救你的數(shù)字記憶

PhotoRec終極數(shù)據(jù)恢復(fù)指南:免費(fèi)開(kāi)源工具如何拯救你的數(shù)字記憶

PhotoRec終極數(shù)據(jù)恢復(fù)指南:免費(fèi)開(kāi)源工具如何拯救你的數(shù)字記憶 【免費(fèi)下載鏈接】testdisk TestDisk & PhotoRec 項(xiàng)目地址: https://gitcode.com/gh_mirrors/te/testdisk 你是否曾經(jīng)因?yàn)檎`刪重要文件而徹夜難眠?是否在硬盤(pán)崩潰后感到絕望無(wú)助&a…

2026/7/29 20:18:44 閱讀更多
告別傳統(tǒng)Agent開(kāi)發(fā)!Mellea的Intrinsics技術(shù)如何提升AI系統(tǒng)穩(wěn)定性

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

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

2026/7/29 20:18:44 閱讀更多
Obsidian + Claude Code 搭自動(dòng)化發(fā)布客戶端:Playwright × 掘金/知乎/CSDN 的全鏈路踩坑實(shí)錄

Obsidian + Claude Code 搭自動(dòng)化發(fā)布客戶端:Playwright × 掘金/知乎/CSDN 的全鏈路踩坑實(shí)錄

Obsidian Claude Code 搭自動(dòng)化發(fā)布客戶端:Playwright 掘金/知乎/CSDN 的全鏈路踩坑實(shí)錄 AI工具人PM 的實(shí)戰(zhàn)筆記:前幾篇講了掘金、知乎各自的自動(dòng)化踩坑。這三篇合起來(lái)——我用 Obsidian Playwright Claude Code Electron,從源碼文件到三…

2026/7/29 21:18:46 閱讀更多
從紙質(zhì)家政合同到電子簽,家政公司遠(yuǎn)程簽完服務(wù)協(xié)議

從紙質(zhì)家政合同到電子簽,家政公司遠(yuǎn)程簽完服務(wù)協(xié)議

家政合同為什么總簽得散 家政公司的簽約對(duì)象有兩個(gè):一邊是上門(mén)服務(wù)的阿姨,一邊是雇用服務(wù)的客戶,兩邊都分散在城市各處,簽約時(shí)間還常常湊不到一塊。紙質(zhì)服務(wù)協(xié)議要打印、要見(jiàn)面簽、要回收歸檔,遇上阿姨剛培訓(xùn)完就要上戶…

2026/7/29 21:18:46 閱讀更多
STM32 采集效率翻倍!ADC+DMA 乒乓緩沖 + FreeRTOS 并行架構(gòu)全拆解

STM32 采集效率翻倍!ADC+DMA 乒乓緩沖 + FreeRTOS 并行架構(gòu)全拆解

記得最早做 ADC 采集項(xiàng)目的時(shí)候,踩過(guò)一個(gè)很經(jīng)典的坑:用輪詢方式讀多通道 ADC,CPU 全程死等轉(zhuǎn)換完成,啥別的事都干不了;后來(lái)?yè)Q成單緩沖 DMA,采集倒是不用 CPU 管了,但處理數(shù)據(jù)的時(shí)候要么停掉 DMA…

2026/7/29 21:18:46 閱讀更多
Carla仿真系列:1_Carla + ROS 仿真環(huán)境從零搭建,跑通完整數(shù)據(jù)流

Carla仿真系列:1_Carla + ROS 仿真環(huán)境從零搭建,跑通完整數(shù)據(jù)流

做算法驗(yàn)證,最頭疼的問(wèn)題就是沒(méi)有實(shí)車和傳感器。 之前我都是用 Carla 仿真來(lái)替代,但每次搭環(huán)境都會(huì)踩一遍坑。這次把完整的搭建過(guò)程記錄下來(lái),并集成到一個(gè)鏡像中方便管理,從容器啟動(dòng)到 ROS 數(shù)據(jù)可視化,一條線走通。 1…

2026/7/29 21:08:46 閱讀更多
面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

前兩個(gè)月,我在重構(gòu) AlgoMooc 網(wǎng)站過(guò)程中,發(fā)現(xiàn)一個(gè)問(wèn)題:在 Claude Code 里把一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,結(jié)果可能比 1 個(gè) agent 從頭干到尾還慢? 大多數(shù)人的第一反應(yīng)是反過(guò)來(lái)的:活是并行干的&#…

2026/7/29 0:15:24 閱讀更多
# 鴻蒙 HarmonyOS 應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫(huà)渲染精講

# 鴻蒙 HarmonyOS 應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫(huà)渲染精講

一、應(yīng)用概述 骰子(Dice Roller) 是一款經(jīng)典的休閑娛樂(lè)應(yīng)用,模擬了真實(shí)擲骰子的過(guò)程。應(yīng)用投擲兩個(gè)骰子(六面標(biāo)準(zhǔn)骰),使用 Unicode 骰面符號(hào)直觀展示每個(gè)骰子的點(diǎn)數(shù),并伴有快速滾動(dòng)的動(dòng)畫(huà)效果?!?/p>

2026/7/29 0:15:24 閱讀更多