Linux内核默认允许多个进程打开同一字符设备

问题反馈: 应用开发同事反馈同一个句柄,也就是同一个字符设备为何允许多个进程访问,能否修复?

分析思路:

Step1: 通过lsof查看具体有哪些进程在访问指定的字符设备节点

lsof 命令用于识别不同进程正在访问的文件,是故障排除和系统维护不可或缺的工具,与 netstatfuser 命令类似

Step2: 确实存在当OPTSCDevice进程访问节点/dev/opt_idf2_dio后,

/opt test/idf2 dio demo 也都是同时可以open该节点/dev/opt_idf2_dio

那么如何实现呢?这种情况在Linux设备驱动开发中很常见

1. 为什么同一个句柄允许两个进程open控制?

设备驱动设计问题

复制代码
// 典型的驱动open函数可能这样写:
static int mydev_open(struct inode *inode, struct file *filp)
{
    // 没有检查设备是否已被其他进程打开
    return 0;  // 总是成功
}

根本原因:

  1. 缺少互斥检查:驱动没有跟踪设备是否已被打开

  2. 默认允许:Linux内核默认允许多个进程打开同一字符设备

  3. 设计需求:某些设备确实需要允许多进程访问(如串口、摄像头)

2. 解决方案

方案1:驱动层加锁(推荐)

在设备驱动中添加打开计数和互斥机制:

复制代码
#include <linux/mutex.h>

static DEFINE_MUTEX(dev_lock);
static int dev_open_count = 0;

static int mydev_open(struct inode *inode, struct file *filp)
{
    mutex_lock(&dev_lock);
    
    if (dev_open_count > 0) {
        mutex_unlock(&dev_lock);
        return -EBUSY;  // 设备忙,返回错误
    }
    
    dev_open_count++;
    mutex_unlock(&dev_lock);
    
    // 其他初始化代码
    return 0;
}

static int mydev_release(struct inode *inode, struct file *filp)
{
    mutex_lock(&dev_lock);
    dev_open_count--;
    mutex_unlock(&dev_lock);
    return 0;
}

方案2:使用文件锁(应用层解决)

在应用层使用**flock()fcntl()**:

复制代码
#include <sys/file.h>

int fd = open("/dev/opt_idf2_dio", O_RDWR);
if (fd < 0) {
    // 处理错误
}

// 尝试获取独占锁
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
    if (errno == EWOULDBLOCK) {
        printf("Device is already in use by another process\n");
        close(fd);
        exit(1);
    }
}

// 使用设备...
flock(fd, LOCK_UN);  // 释放锁
close(fd);

实际代码通过driver层来修复:

复制代码
diff --git a/drivers/opt_idf2/opt-idf2-dio.c b/drivers/opt_idf2/opt-idf2-dio.c
index 2bd8fe60c48b..267e406afd61 100755
--- a/drivers/opt_idf2/opt-idf2-dio.c
+++ b/drivers/opt_idf2/opt-idf2-dio.c
@@ -139,6 +139,8 @@
 #define LOG_INFO    (1 << 3)
 #define LOG_DEBUG   (1 << 4)

+static int dev_open_count = 0;^M
+^M
 //static unsigned int g_log_flg = LOG_ERROR | LOG_WARN | LOG_NOTICE | LOG_INFO | LOG_DEBUG;
 static unsigned int g_log_flg = LOG_ERROR | LOG_WARN | LOG_NOTICE;

@@ -303,14 +305,15 @@ void opt_shutdown_fn(void)
 {
     // 同步文件系统(在工作队列中允许阻塞)
     pr_info("[%s] Syncing filesystems and shutdown OS ...\n", __func__);
+    pr_info("[%s] Cansel shutdown OS just for battery test ...\n", __func__);^M
     //opt_printk("[%s] Syncing filesystems and shutdown OS ...\n", __func__);
     //kernel_sys_sync();
     //kernel_sync();  // 正确的方法
-    ksys_sync();
+    // ksys_sync();^M

     /* 立即强制关机 - 适用于"shutdown -r now"场景 */
     //kernel_power_off();
-       rk8xx_battery_shutdown();
+       // rk8xx_battery_shutdown();^M

     /* 如果希望更优雅的关机,可以使用:
      * orderly_poweroff(true);
@@ -513,7 +516,13 @@ static int opt_idf2_dio_open(struct inode *inode, struct file *filp)
     struct opt_idf2_dio_dev *dio_dev = container_of(inode->i_cdev, struct opt_idf2_dio_dev, cdev);
     LOGD("+ %s\r\n",__FUNCTION__);
     mutex_lock(&dio_dev->mutex_lock);         //上锁
+    if (dev_open_count > 0) {^M
+           printk("[%s] failed, pls check it. \n", __func__);^M
+           mutex_unlock(&dio_dev->mutex_lock);^M
+           return -EBUSY;  // 设备忙,返回错误^M
+    }^M
     filp->private_data = dio_dev;
+    dev_open_count++;^M
     mutex_unlock(&dio_dev->mutex_lock);       //解锁
     LOGD("- %s\r\n",__FUNCTION__);
     return 0;
@@ -1085,8 +1094,12 @@ static long opt_idf2_dio_ioctl(struct file *filp, unsigned int cmd, unsigned lon

 static int opt_idf2_dio_release(struct inode *inode, struct file *filp)
 {
-    // struct opt_idf2_dio_dev *dio_dev = container_of(inode->i_cdev, struct opt_idf2_dio_dev, cdev);
+    struct opt_idf2_dio_dev *dio_dev = container_of(inode->i_cdev, struct opt_idf2_dio_dev, cdev);^M
     // LOGD("+ %s\r\n",__FUNCTION__);
+    printk("[%s] --------  \n", __func__);^M
+    mutex_lock(&dio_dev->mutex_lock);         //上锁^M
+    dev_open_count--;^M
+    mutex_unlock(&dio_dev->mutex_lock);       //解锁^M
     return 0;
 }

@@ -1229,7 +1242,7 @@ static int opt_idf2_dio_probe(struct platform_device *pdev)
     int ret;
     struct device *dev = &pdev->dev;
     struct opt_idf2_dio_dev *dio_dev;
-    printk(" idf2 opt_idf2_dio_probe!\n");
+    printk("hzs opt_idf2_dio_probe! version: v2.0.1  \n");^M
     opt_printk("+ %s opt_printk idf2 dio probed+LIU driver ver:0.01\r\n",__FUNCTION__);
     dio_dev = kzalloc(sizeof(*dio_dev), GFP_KERNEL);
        if (NULL == dio_dev){
(END)

烧录kernel验证系统功能:

测试即可实现:

相关推荐
乡野码圣2 小时前
【RK3588 Android12】同步机制选型指南
嵌入式硬件
sweetone2 小时前
飞利浦HX333S冲牙器原理及维修
单片机·嵌入式硬件
云山工作室2 小时前
基于单片机的飞机客舱窗帘控制系统(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计
Source.Liu2 小时前
【Ubuntu】关机重启命令
linux·运维·ubuntu
iCode5042 小时前
CentOS Stream 9修改静态IP
linux·tcp/ip·centos
llilian_162 小时前
延迟信号发生器 延迟脉冲信号发生器在激光触发领域的应用 高速脉冲信号发生器
功能测试·单片机·嵌入式硬件·测试工具·自动化
代码游侠3 小时前
ARM嵌入式开发代码实践——LED灯闪烁(汇编版)
arm开发·笔记·嵌入式硬件·学习·架构
不怕犯错,就怕不做3 小时前
RK3562+RK817在关机状态下提升充电电流至2A解决方案
linux·驱动开发·嵌入式硬件