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验证系统功能:

测试即可实现:

相关推荐
getapi6 分钟前
注塑件的费用构成
linux·服务器·ubuntu
郝学胜-神的一滴35 分钟前
深入解析C/S模型下的TCP通信流程:从握手到挥手的技术之旅
linux·服务器·c语言·网络·网络协议·tcp/ip
释怀不想释怀1 小时前
Linux网络基础(ip,域名)
linux·网络·tcp/ip
初願致夕霞1 小时前
Linux_进程
linux·c++
开开心心就好1 小时前
AI人声伴奏分离工具,离线提取伴奏K歌用
java·linux·开发语言·网络·人工智能·电脑·blender
MAR-Sky1 小时前
keil5中数据的不同定义和单片机(以stc8为例)里的对应关系(idata,xdata,data,code)
单片机·嵌入式硬件
lucky-billy1 小时前
Ubuntu 下一键部署 ROS2
linux·ubuntu·ros2
Thera7771 小时前
【Linux C++】彻底解决僵尸进程:waitpid(WNOHANG) 与 SA_NOCLDWAIT
linux·服务器·c++
阿梦Anmory1 小时前
Ubuntu配置代理最详细教程
linux·运维·ubuntu
云姜.2 小时前
线程和进程的关系
java·linux·jvm