C Linux设备驱动程序访问控制

C Linux设备驱动程序访问控制,c,linux,linux-kernel,linux-device-driver,C,Linux,Linux Kernel,Linux Device Driver,我正在为一些新的硬件实现一个设备驱动程序,希望一次只允许一个进程访问该设备。并发读/写操作会使硬件混乱到很可能需要硬复位的程度。我还有以下问题: 在示例代码中,open()调用使用锁,但close()不使用锁。这里是否仍然存在竞争条件,或者scull\u s\u count的减量是否保证是原子的?基本上,在这个例子中,我想知道当另一个进程结束并关闭设备时,如果一个进程正试图打开设备,会发生什么情况 我假设我不需要在我的read()和write()调用中检查打开标志的状态(我正在做一些类似于示例的

我正在为一些新的硬件实现一个设备驱动程序,希望一次只允许一个进程访问该设备。并发读/写操作会使硬件混乱到很可能需要硬复位的程度。我还有以下问题:

  • 在示例代码中,
    open()
    调用使用锁,但
    close()
    不使用锁。这里是否仍然存在竞争条件,或者
    scull\u s\u count
    的减量是否保证是原子的?基本上,在这个例子中,我想知道当另一个进程结束并关闭设备时,如果一个进程正试图打开设备,会发生什么情况

  • 我假设我不需要在我的
    read()
    write()
    调用中检查打开标志的状态(我正在做一些类似于示例的
    scull\u count
    ),因为进入这些调用的唯一方法是,如果用户空间应用程序已经通过成功调用
    open()
    接收到
    fd
    。这个假设正确吗

  • 多亏了tadman的评论,我对内核的
    原子机制进行了最粗略的搜索。下面是我现在拥有的一些伪代码:

    int open(struct inode *inode, struct file *filp) {
      spin_lock(&lock);
      if (atomic_read(&open_flag)) {
        spin_unlock(&lock);
        return -EBUSY;
      }
      atomic_set(&open_flag, 1);
      /* do other open() related stuff */
      spin_unlock(&lock);
      return 0;
    }
    
    int close(struct inode *inode, struct file *filp) {
      int rc;
      /* do close() stuff */
      atomic_set(&open_flag, 0);
      return rc;
    }
    
    open_标志
    是一个
    atomic_t
    ,它是分配给
    kzalloc()
    的较大结构的一部分。结果,它被初始化为零

    因此,这里的代码表明锁的目的是防止多个进程/线程同时打开设备,而
    open\u标志
    是一个
    atomic\u t
    的事实防止了我在上面问题1中所关心的竞争条件。这个实现是否足够?此外,我仍在寻找问题2的答案


    示例代码使用自旋锁,但互斥锁是否更合适?代码段相对较小,几乎没有争用,因此睡觉和醒来可能比旋转性能差。锁/互斥锁始终从用户上下文访问,因此您应该可以安全地睡眠。

    您所指的示例确实存在缺陷。减量绝对不能保证是原子的,而且几乎肯定不会

    但实际上,我不认为有编译器/CPU组合会产生可能失败的代码。最糟糕的情况是,一个CPU内核可能完成关闭,然后另一个内核可能调用open并返回忙碌状态,因为它有一个过时的缓存标志值

    Linux为此提供了
    原子*
    函数,还提供了
    *\u位
    原子位标志操作。请参阅内核文档中的core_api/atomic_ops.rst

    正确且简单的his模式示例如下所示:

    unsigned long mydriver_flags;
    #define IN_USE_BIT 0
    
    static int mydriver_open(struct inode *inode, struct file *filp)
    {
            if (test_and_set_bit(IN_USE_BIT, &mydriver_flags))
                    return -EBUSY;
            /* continue with open */
            return 0;
    }
    
    static int mydriver_close(struct inode *inode, struct file *filp)
    {
            /* do close stuff first */
            smp_mb__before_atomic();
            clear_bit(IN_USE_BIT, &mydriver_flags);
            return 0;
    }
    
    对于每个设备,一个真正的驱动程序应该有一个设备状态结构,其中包含
    mydriver\u标志
    。而不是对整个驱动程序使用一个全局变量,如示例所示

    也就是说,你试图做的可能不是一个好主意。即使一次只有一个进程可以打开设备,进程的打开文件描述符也会在进程中的所有线程之间共享。多个线程可以同时对同一文件描述符进行
    read()
    write()
    调用

    如果进程打开了一个文件描述符并调用了
    fork()
    ,则该描述符将被继承到新进程中。这是一种多进程同时打开设备的方式,尽管存在上述“单一打开”限制

    因此,在驱动程序的文件操作中,您仍然必须是线程安全的,因为用户仍然可以让多个线程/进程同时打开设备并进行调用。如果你已经做到了安全,为什么要阻止用户这么做呢?也许他们知道自己在做什么,并且会确保他们的多个司机的开场白会“轮流”打电话,而不会产生冲突


    也考虑在开放调用中使用OAXEXL标志以使单选为可选的可能性。

    < P>您认为问题是错的。如果硬件不能处理并发读/写,那么就由驱动程序来执行。驱动程序是可以访问硬件的单个进程。驱动程序允许以线程安全的方式访问自身。来自用户空间的读/写不应该直接进入硬件,它们应该由驱动程序处理,驱动程序处理硬件,但硬件需要。例如,write()处理程序可以将数据转储到队列中并设置一个标志,这样您的write_硬件无限循环就可以在可以的情况下将数据写入硬件。

    我对linux内核编程有点生疏,但同时使用原子和自旋锁对我来说似乎有点开销

    希望一次只允许一个进程访问设备吗

    如果这是您所需要的,那么scull实现工作正常,scull驱动程序的openimplementation中的自旋锁确保一次只有一个进程获得有效的文件句柄

    我想我很确定(没有检查)skull示例在发布(close)中没有使用锁,因为只有打开它的进程才能关闭它,如果文件句柄无效,其他进程将无法进入发布代码

    示例代码使用自旋锁,但互斥锁是否更合适

    自旋锁实现速度更快,足以完成此任务

    int scull_s_release(struct inode *inode, struct file *filp)
    {
      scull_s_count--; /* release the device */
    
      /* from there until the function return is the only place where a race can occur
      *  so I wouldn't define the scull implementation "flawed" */
    
      MOD_DEC_USE_COUNT;
      return 0;
    }
    

    您和其他提供答案的人都正确地认为示例存在缺陷,而TrentP正确地认为,如果您使用原子位操作,如
    test\u和\u set\u bit()
    (或者您可以使用
    atomic\u add\u,除非()
    ,等等),则根本不需要锁

    然而,他的回答也不是完全正确的,因为它没有考虑到问题
    static unsigned long mydriver_flags;
    #define IN_USE_BIT 0
    
    static int mydriver_open(struct inode *inode, struct file *filp)
    {
            if (test_and_set_bit(IN_USE_BIT, &mydriver_flags))
                    return -EBUSY;
            /* continue with open */
            return 0;
    }
    
    static int mydriver_close(struct inode *inode, struct file *filp)
    {
            /* do close stuff first */
            smp_mb_before_atomic();
            clear_bit(IN_USE_BIT, &mydriver_flags);
            return 0;
    }