驱动虽然是运行在内核空间的,但是驱动是运行在进程上下文中的,
当系统中有多个进程并发的请求驱动的服务时,就会导致竞态,所以驱动必须解决的问题就是对共享资源的访问。
并发访问会导致竞态。
当内核的多条执行路径同时访问同一个共享资源时,就会造成竞态。
例如中断ISR的执行,或者多核CPU的不同CPU核的并行执行。
常见的共享资源有,全局变量,共享内存,等等。
解决竞态的方法是,保证对共享资源的互斥访问。
访问共享资源的代码段,成为临界代码段。(critical section)
linux中提供了多种实现互斥的方法,包括
中断屏蔽,原子操作,自旋锁,信号量,互斥体。
在驱动开发中,推荐使用的是spinlock和mutex。
mutex的实现依赖于spinlock,即,spinlock是更底层的互斥手段。
使用mutex和spinlock有如下基本原则:
1)当锁不可得时,mutex的开销是process context的切换时间,而spinlock的开销是spin,所以,如果临界代码段很小,推荐使用spinlock,节省开销。
2) mutex保护的临界代码段,可以包含引起阻塞的代码,但是spinlock绝对禁止保护可能阻塞的代码。阻塞意味着进程切换,如果进程被切换,而另一个进程又试图获取spinlock,死锁就会发生。
3)mutex存在与process context,所以,如果需要在ISR或者SISR中使用,那么只能使用spinlock,或者使用mutex_trylock()进行访问,避免阻塞。
来看看具体的应用。
作为内核通信的一种机制,互斥也是一种event driven communication。
所以,无论是spinlock还是mutex,都对应于一个对象实体。
利用kernel提供的服务函数,对spinlock或者mutex的成员进行修改,并对进程进行相应的处理。
mutex会引起进程调度,
当mutex_lock内核服务被请求时,内核会检查mutex的数据,并对进程进行调度和阻塞,然后修改mutex的数据。
当mutex_unlock内核服务被请求时,内核会检查mutex的数据,并对关联的阻塞进程进行唤醒,然后修改mutex的数据。
整个过程中,进程都只是和kernel进行通信,kernel负责调度,进程间通过kernel间接实现互斥通信。
spinlock不会引起进程调度,
当spin_lock内核服务被请求时,内核会检查spinlock的数据,如果锁不可得,进程自旋等待下一次测试。如果锁可得,内核会修改spinlock的数据,并返回,执行后续的代码。
当spin_unlock内核服务被请求时,内核会修改spinlock的数据,并返回,执行后续的代码。
来看一个具体的例子。
struct globalmem_dev{
struct cdev cdev;
unsigned char mem[SIZE];
struct mutex mutex;
};
DEV中内嵌一个CDEV,为了实现互斥,DEV中内嵌了一个mutex。
当进程调用DEV的DRIVER时,首先去尝试获得这个mutex,这样就可以防止多个进程并发调用DRIVER,对mem访问发生竞态。
static struct globalmem_dev globalmem;
static int __init globalmem_dev_init()
{
int ret;
...
mutex_init(&globalmem.mutex);
...
return ret;
}
module_init(globalmem_dev_init);
在驱动加载函数中,初始化了Mutex.
static ssize_t globalmem_read(struct file* filp, char __user *buf, size_t size, loff_t* ppos)
{
int ret;
struct globalmem_dev * devp = filp->private_data;
...
mutex_lock(&devp->mutex);
copy_to_user(buf, dev->mem, count);
mutex_unlock(&devp->mutex);
...
return ret;
}
static ssize_t globalmem_write(struct file* filp, char __user *buf, size_t size, loff_t* ppos)
{
int ret;
struct globalmem_dev * devp = filp->private_data;
...
mutex_lock(&devp->mutex);
copy_from_user(dev->mem,buf, count);
mutex_unlock(&devp->mutex);
...
return ret;
}
在FOPS中,对访问公共资源的代码段,进行互斥保护。