簡體   English   中英

Linux 內核在哪里鎖定和解鎖信號量?

[英]Linux Kernel Where to lock and unlock semaphores?

在 Linux 內核中(特別是對於設備驅動程序),我如何知道要鎖定哪些變量以及何時需要鎖定? 特別是,為什么下面代碼中的鎖定只發生在 dev 被設置之后,即使 dev 指向一個全局變量 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);
}

示例中的鎖定與全局scull_devices變量無關,但鎖定用於保護一個scull_dev屬性。

例如,假設存在read()操作從data復制size字節,而提到的scroll_trim()操作釋放data

所以,當進程#1通話open()和處理#2試圖read()在同一時間從已經打開的裝置,所述read()運算可存取釋放data和oopses。

這就是您需要保護數據免受競爭的原因。 信號量是一種方式; 互斥另一個通常更合適的。 自旋鎖和原子變量也可能起作用。

鎖定 - 這是保護臨界區的方法

臨界區 - 在您的驅動程序代碼中,如果多個實例正在訪問同一區域,那就是臨界區。

多個實例 - 它可以是線程、常規 ioctl cmd(來自用戶空間)以及 softirq 和 irq。 這取決於您的驅動程序實現。

基於“上下文”,您也應該使用不同的鎖。

可以休眠的線程上下文 -> 信號量/互斥量非休眠上下文 -> 自旋鎖軟中斷,tasklet -> spin_lock_bh irq -> spin_lock_irq,spin_lock_irqsave

它完全基於您的要求。

讓我們舉個例子。 如果您正在開發網絡驅動程序,您的 netdev 具有統計信息和數據包緩沖區,並且這些需要由鎖保護,因為它可以由多個實例更新,例如來自用戶空間的 net_rx_softirq、net_tx_softirq、ioctl/netlink 請求等等。

在這種情況下,根據資源的上下文,您需要使用不同的鎖/互斥鎖,有時您需要 1 個以上的鎖。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM