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

测试即可实现:

相关推荐
coppher6 小时前
Ubuntu 22.04 amd64 离线安装 Docker 完整教程
linux·docker
xyz5997 小时前
如何在 WSL 中删除指定版本的 Ubuntu 以及安装
linux·运维·ubuntu
4caf17 小时前
作业2:6位数码管静态显示
嵌入式硬件·51单片机
亚空间仓鼠7 小时前
OpenEuler系统常用服务(五)
linux·运维·服务器·网络
不做无法实现的梦~7 小时前
STM32解析PPM协议
stm32·单片机·嵌入式硬件
minji...8 小时前
Linux 线程同步与互斥(二) 线程同步,条件变量,pthread_cond_init/wait/signal/broadcast
linux·运维·开发语言·jvm·数据结构·c++
虚伪的空想家8 小时前
k8s集群configmap和secrets备份脚本
linux·容器·kubernetes
czhaii8 小时前
基于Arm Cortex-M7内核GD32H7
单片机·嵌入式硬件
the sun348 小时前
从 QEMU 直接启动到 U-Boot 引导:嵌入式 Linux 启动流程的本质差异
linux·运维·服务器
草莓熊Lotso8 小时前
【Linux 线程进阶】进程 vs 线程资源划分 + 线程控制全详解
java·linux·运维·服务器·数据库·c++·mysql