快捷搜索:

深入浅出Linux设备驱动之并发控制

在驱动法度榜样中,当多个线程同时造访相同的资本时(驱动法度榜样中的全局变量是一种范例的共享资本),可能会激发"竞态",是以我们必须对共享资本进行并发节制。Linux内核中办理并发节制的最常用措施是自旋锁与旌旗灯号量(绝大年夜多半时刻作为互斥锁应用)。

自旋锁与旌旗灯号量"类似而不类",类似说的是它们功能上的相似性,"不类"指代它们在本色和实现机理上完全不一样,不属于一类。

自旋锁不会引起调用者就寝,假如自旋锁已经被其余履行单元维持,调用者就不停轮回查看是否该自旋锁的维持者已经开释了锁,"自旋"便是"在原地打转"。而旌旗灯号量则引起调用者就寝,它把进程从运行行列步队上拖出去,除非得到锁。这便是它们的"不类"。

然则,无论是旌旗灯号量,照样自旋锁,在任何时候,最多只能有一个维持者,即在任何时候最多只能有一个履行单元得到锁。这便是它们的"类似"。

鉴于自旋锁与旌旗灯号量的上述特征,一样平常而言,自旋锁得当于维持光阴异常短的环境,它可以在任何高低文应用;旌旗灯号量得当于维持光阴较长的环境,会只能在进程高低文应用。假如被保护的共享资本只在进程高低文造访,则可以以旌旗灯号量来保护该共享资本,假如对共享资本的造访光阴异常短,自旋锁也是好的选择。然则,假如被保护的共享资本必要在中断高低文造访(包括底半部即中断处置惩罚句柄和顶半部即软中断),就必须应用自旋锁。

与旌旗灯号量相关的API主要有:

定义旌旗灯号量

struct semaphore sem;

初始化旌旗灯号量

void sema_init (struct semaphore *sem, int val);

该函数初始化旌旗灯号量,并设置旌旗灯号量sem的值为val

void init_MUTEX (struct semaphore *sem);

该函数用于初始化一个互斥锁,即它把旌旗灯号量sem的值设置为1,等同于sema_init (struct semaphore *sem, 1);

void init_MUTEX_LOCKED (struct semaphore *sem);

该函数也用于初始化一个互斥锁,但它把旌旗灯号量sem的值设置为0,等同于sema_init (struct semaphore *sem, 0);

得到旌旗灯号量

void down(struct semaphore * sem);

该函数用于得到旌旗灯号量sem,它会导致就寝,是以不能在中断高低文应用;

int down_interruptible(struct semaphore * sem);

该函数功能与down类似,不合之处为,down不能被旌旗灯号打断,但down_interruptible能被旌旗灯号打断;

int down_trylock(struct semaphore * sem);

该函数考试测验得到旌旗灯号量sem,假如能够立即得到,它就得到该旌旗灯号量并返回0,否则,返回非0值。它不会导致调用者就寝,可以在中断高低文应用。

开释旌旗灯号量

void up(struct semaphore * sem);

该函数开释旌旗灯号量sem,唤醒等待者。

与自旋锁相关的API主要有:

定义自旋锁

spinlock_t spin;

初始化自旋锁

spin_lock_init(lock)

该宏用于动态初始化自旋锁lock

得到自旋锁

spin_lock(lock)

该宏用于得到自旋锁lock,假如能够急速得到锁,它就顿时返回,否则,它将自旋在那里,直到该自旋锁的维持者开释;

spin_trylock(lock)

该宏考试测验得到自旋锁lock,假如能急速得到锁,它得到锁并返回真,否则急速返回假,实际上不再"在原地打转";

开释自旋锁

接下来,我们给globalvar的驱动法度榜样增添open()和release()函数,并在此中借助自旋锁来保护对全局变量int globalvar_count(记录打开设备的进程数)的造访来实现设备只能被一个进程打开(必须确保globalvar_count最多只能为1):

#include

#include

#include

#include

#include

MODULE_LICENSE("GPL");

#define MAJOR_NUM 254

static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);

static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

static int globalvar_open(struct inode *inode, struct file *filp);

static int globalvar_release(struct inode *inode, struct file *filp);

struct file_operations globalvar_fops =

{

read: globalvar_read, write: globalvar_write, open: globalvar_open, release:

globalvar_release,

};

static int global_var = 0;

static int globalvar_count = 0;

static struct semaphore sem;

static spinlock_t spin = SPIN_LOCK_UNLOCKED;

static int __init globalvar_init(void)

{

int ret;

ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);

if (ret)

{

printk("globalvar register failure");

}

else

{

printk("globalvar register success");

init_MUTEX(&sem);

}

return ret;

}

static void __exit globalvar_exit(void)

{

int ret;

ret = unregister_chrdev(MAJOR_NUM, "globalvar");

if (ret)

{

printk("globalvar unregister failure");

}

else

{

printk("globalvar unregister success");

}

}

static int globalvar_open(struct inode *inode, struct file *filp)

{

//得到自选锁

spin_lock(&spin);

//临界资本造访

if (globalvar_count)

{

spin_unlock(&spin);

return - EBUSY;

}

globalvar_count++;

//开释自选锁

spin_unlock(&spin);

return 0;

}

static int globalvar_release(struct inode *inode, struct file *filp)

{

globalvar_count--;

return 0;

}

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t

*off)

{

if (down_interruptible(&sem))

{

return - ERESTARTSYS;

}

if (copy_to_user(buf, &global_var, sizeof(int)))

{

up(&sem);

return - EFAULT;

}

up(&sem);

return sizeof(int);

}

static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len,

loff_t *off)

{

if (down_interruptible(&sem))

{

return - ERESTARTSYS;

}

if (copy_from_user(&global_var, buf, sizeof(int)))

{

up(&sem);

return - EFAULT;

}

up(&sem);

return sizeof(int);

}

module_init(globalvar_init);

module_exit(globalvar_exit);

为了上述驱动法度榜样的效果,我们启动两个进程分手打开/dev/globalvar。在两个终端中调用./globalvartest.o测试法度榜样,当一个进程打开/dev/globalvar后,别的一个进程将打开掉败,输出"device open failure",如下图:

输出结果

您可能还会对下面的文章感兴趣: