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

测试即可实现:

相关推荐
orion571 天前
Missing Semester Class1:course overview and introduction of shell
linux
用户120487221612 天前
Linux驱动编译与加载
linux·嵌入式
用户805533698032 天前
Input 子系统架构:Core、Handler、Driver 三层是怎么协作的
linux·嵌入式
用户805533698032 天前
RK-Forge外设系列开篇 - 把板子从「能启动」变成「能用」:Ethernet/SPI/MMC 三个纯接线外设
linux·github·嵌入式
七歌杜金房2 天前
我终于又有了自己的 Linux 电脑
linux·debian·mac
tntxia3 天前
linux curl命令详解_curl详解
linux
扛枪的书生4 天前
Linux 网络管理器用法速查
linux
顺风尿一寸4 天前
Java Socket 内核之旅:从 SocketChannel.read() 到 tcp_recvmsg 与 epoll 的完整调用链路
linux
XIAOHEZIcode4 天前
Ubuntu 终端美化全栈指南:Bash 到 Kitty 踩坑实录
linux·ubuntu·命令行
唐青枫4 天前
别再只会用 cron:Linux systemd Timer 定时任务实战详解
linux