Linux内核在哪里锁定和解锁信号量?

时间:2019-03-23 16:44:52

标签: c linux linux-kernel linux-device-driver

在Linux内核(专门用于设备驱动程序)中,我如何知道要锁定哪些变量以及何时需要锁定?特别是,为什么即使开发人员指向全局变量scull_devices,也仅在设置开发人员之后才锁定以下代码?

struct scull_qset {
    void **data;    /* pointer to an array of pointers which each point to a quantum buffer */
    struct scull_qset *next;
};

struct scull_dev {
    struct scull_qset *data;  /* Pointer to first quantum set */
    int quantum;              /* the current quantum size */
    int qset;                 /* the current array size */
    unsigned long size;       /* amount of data stored here */
    unsigned int access_key;  /* used by sculluid and scullpriv */
    struct semaphore sem;     /* mutual exclusion semaphore */
    struct cdev cdev;         /* Char device structure initialized in scull_init_module */
};

struct scull_dev *scull_devices;  /* allocated dynamically in scull_init_module */

int scull_open(struct inode *inode, struct file *filp)
{
    struct scull_dev *dev; /* device information */

    dev = container_of(inode->i_cdev, struct scull_dev, cdev);
    filp->private_data = dev; /* for other methods */

    /* now trim to 0 the length of the device if open was write-only */
    if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
        scull_trim(dev); /* empty out the scull device */
        up(&dev->sem);
    }
    return 0;          /* success */
}

如果需要scull_init_module的代码以获取更完整的图片,则为:

int scull_major = SCULL_MAJOR;
int scull_minor = 0;
int scull_quantum = SCULL_QUANTUM;
int scull_qset = SCULL_QSET;
int scull_nr_devs = SCULL_NR_DEVS;


int scull_init_module(void)
{
    int result, i;
    dev_t dev = 0;

    /* assigns major and minor numbers (left out for brevity sake) */

    /* 
     * allocate the devices -- we can't have them static, as the number
     * can be specified at load time
     */
    scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
    if (!scull_devices) {
        result = -ENOMEM;
        goto fail; 
    }
    memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));

    /* Initialize each device. */
    for (i = 0; i < scull_nr_devs; i++) {
        scull_devices[i].quantum = scull_quantum;
        scull_devices[i].qset = scull_qset;
        init_MUTEX(&scull_devices[i].sem);
        scull_setup_cdev(&scull_devices[i], i);
    }

    /* some other stuff left out for brevity sake */

    return 0; /* succeed */

  fail:            /* isn't this a little redundant? */
    scull_cleanup_module();
    return result;
}


/*
 * Set up the char_dev structure for this device.
 */
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
    int err, devno = MKDEV(scull_major, scull_minor + index);

    cdev_init(&dev->cdev, &scull_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &scull_fops;
    err = cdev_add (&dev->cdev, devno, 1);
    /* Fail gracefully if need be */
    if (err)
        printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}

2 个答案:

答案 0 :(得分:1)

该示例中的锁定与全局scull_devices变量无关,但是锁定用于保护一个scull_dev的属性。

例如假设存在一个read()操作,该操作从size复制了data个字节,而上述scroll_trim()操作则释放了data

因此,当进程#1调用open()并且进程#2尝试同时从已打开的设备进行read()时,read()操作可以访问释放的{{1} }和哎呀。

这就是为什么您需要保护数据免受竞争。信号量是一种方法。互斥另一个通常更合适的互斥量。自旋锁和原子变量也可能起作用。

答案 1 :(得分:0)

锁定-这是保护关键部分的方法

关键部分-在驱动程序代码中,如果多个实例正在访问同一区域,则为关键部分。

多个实例-它可以是线程,常规ioctl cmd(来自用户空间)以及softirq和irq。这取决于您的驱动程序实现。

基于“上下文”,您也应该使用其他锁。

  

可以休眠的线程上下文->信号量/互斥量   非睡眠上下文->自旋锁   softirq,tasklet-> spin_lock_bh   irq-> spin_lock_irq,spin_lock_irqsave

这完全取决于您的要求。

让我们举个例子。如果您使用的是网络驱动程序,则您的netdev具有统计信息和数据包缓冲区,并且这些数据需要通过锁定进行保护,因为它可以被多个实例(如来自用户空间的net_rx_softirq,net_tx_softirq,ioctl / netlink实例)更新,等等。

在这种情况下,根据资源的上下文,您需要使用其他锁/互斥锁,有时您需要使用多个锁。