阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时,并不挂起,它要么放弃,要么不
停地查询,直至可以进行操作为止。
一、linux设备驱动中的阻塞之等待队列
在 Linux 驱动程序中,可以使用等待队列( Wait Queue )来实现阻塞进程的唤醒。
在本例中使用如下:
当一个读驱动进程访问驱动buff为空时阻塞并等待写进程的唤醒。
当进程写驱动buff为满阻塞并等待读进程(读进程会取走一部分驱动buff空间)唤醒。
wait_queue_head_t my_queue;/*定义等待队列头部*/
init_waitqueue_head(&my_queue);/*初始化等待队列头部*/
DECLARE_WAIT_QUEUE_HEAD (name);/*定义并初始化等待队列头部*/
DECLARE_WAITQUEUE(name, tsk);/*定义等待队列元素*/
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);/*添加等待队列*/
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);/*移除等待队列*/
/* 等待事件,等待第 1 个参数queue作为等待队列头部的队列被唤醒,而且第2个参数condition必须满足*/
wait_event(queue, condition);/**/
wait_event_interruptible(queue, condition);/*可被信号打断*/
wait_event_timeout(queue, condition, timeout);/*超时退出*/
wait_event_interruptible_timeout(queue, condition, timeout);/*可超时和信号打断*/
/*唤醒队列*/
void wake_up(wait_queue_head_t *queue);/*与wait_event和wait_event_timeout配套使用*/
void wake_up_interruptible(wait_queue_head_t *queue);/*与上同理*/
/*使队列睡眠*/
sleep_on(wait_queue_head_t *q );/*于wake_up配对使用*/
interruptible_sleep_on(wait_queue_head_t *q );/*于wake_up_interruptible配对使用*/
二、linux设备驱动中并发于竞态控制
并发和竞态广泛存在,中断屏蔽、原子操作、自旋锁和互斥体都是解决并发问题的机制。中断屏蔽很少单独被
使用,原子操作只能针对整数进行,因此自旋锁和互斥体应用最为广泛。
自旋锁会导致死循环,锁定期间不允许阻塞,因此要求锁定的临界区小。互斥体允许临界区阻塞,可以适用于
临界区大的情况
1.中断屏蔽:只能禁止和使能本 CPU 内的中断,因此,并不能解决 SMP 多 CPU 引发的竞态。
local_irq_disable()/* 屏蔽中断*/
/* 临界区 */
local_irq_enable()/* 开中断 */
2.原子操作:原子操作可以保证对一个整型数据的修改是排他性的(只能针对整形).
3.自旋锁:
1)CPU 在等待自旋锁时不做任何有用的工作,仅仅是等待。因此,只有在占用锁的时间极短的情况下,使用自旋锁才是合理的。
2)自旋锁可能导致系统死锁。引发这个问题最常见的情况是递归使用一个自旋锁,即如果一个已经拥有某个自旋锁的 CPU 想 第二次获得这个自旋锁,则该 CPU 将死锁。
3)在自旋锁锁定期间不能调用可能引起进程调度的函数。如果进程获得自旋锁之后再阻塞,如调用 copy_from_user ()、
copy_to_user ()、 kmalloc ()和 msleep ()等函数,则可能导致内核的崩溃。
spinlock_t lock; /*定义*/
spin_lock_init(&lock);/*初始化*/
spin_lock (&lock) ; /*上锁*/
spin_unlock (&lock) ; /*解锁*/
4.信号量:信号量与操作系统中的经典概念 PV 操作对应。
struct semaphore sem;/*定义*/
void sema_init(struct semaphore *sem, int val);/*初始化*/
/*获取信号量*/
void down(struct semaphore * sem);/*会导致睡眠,因此不能在中断上下文中使用*/
int down_interruptible(struct semaphore * sem);/*能被信号打断*/
int down_trylock(struct semaphore * sem);/*非柱塞获取,可以在中断上下文中使用*/
void up(struct semaphore * sem);/*释放信号量*/
5.互斥锁:(linux中互斥尽量用mutex,不用semaphore->https://www.cnblogs.com/svitter/p/4005996.html)
定义:include/linux/mutex.h
实现:kernel/mutex.c
struct mutex my_mutex; /* 定义
mutex_init(&my_mutex); /* 初始化
mutex_lock(&my_mutex); /* 获取
mutex_unlock(&my_mutex); /* 释放
三、代码
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#define GLOBALFIFO_SIZE (uint32_t)20
#define GLOBALFIFO_MAJOR (uint8_t)'g'
static int globalfifo_major = GLOBALFIFO_MAJOR;
struct FIFO_DEV{
struct cdev cdev;
uint32_t current_len;
uint8_t mem[GLOBALFIFO_SIZE];
struct mutex mutex;
wait_queue_head_t r_wait;
wait_queue_head_t w_wait;
};
struct FIFO_DEV *fifo_dev;
static int globalfifo_open(struct inode *inode, struct file *filp)
{
filp->private_data = fifo_dev;
return 0;
}
static ssize_t globalfifo_read(struct file *filp, char __user * buf, size_t size,loff_t * ppos)
{
unsigned int count = size;
int ret = 0;
struct FIFO_DEV *dev = filp->private_data;
DECLARE_WAITQUEUE(wait, current);//申明初始化等待队列
mutex_lock(&dev->mutex);//获取互斥锁(获取失败睡眠)
add_wait_queue(&dev->r_wait, &wait);//添加等待队列
while (dev->current_len == 0) {
if (filp->f_flags & O_NONBLOCK) {//如果是非阻塞退出
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE);//标记状态进入浅睡眠
mutex_unlock(&dev->mutex); //释放锁
schedule(); //进程调度,本进程睡眠,等待写进程调用wake_up_interruptible(&dev->r_wait);唤醒 #include
if (signal_pending(current)) {//被唤醒如果是被信号唤醒 #include
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex);
}
if (count > dev->current_len)
count = dev->current_len;
if (copy_to_user(buf, dev->mem, count)) {
ret = -EFAULT;
goto out;
} else {
memcpy(dev->mem, dev->mem + count, dev->current_len - count);
dev->current_len -= count;
printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count,dev->current_len);
wake_up_interruptible(&dev->w_wait);//唤醒可能的写阻塞
ret = count;
}
out:
mutex_unlock(&dev->mutex);;
out2:
remove_wait_queue(&dev->r_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
static ssize_t globalfifo_write(struct file *filp, const char __user * buf,size_t count, loff_t *ppos)
{
struct FIFO_DEV *dev = filp->private_data;
int ret;
DECLARE_WAITQUEUE(wait, current);
mutex_lock(&dev->mutex);
add_wait_queue(&dev->w_wait, &wait);
while (dev->current_len == GLOBALFIFO_SIZE) {
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex);
schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex);
}
if (count > GLOBALFIFO_SIZE - dev->current_len)
count = GLOBALFIFO_SIZE - dev->current_len;
if (copy_from_user(dev->mem + dev->current_len, buf, count)) {
ret = -EFAULT;
goto out;
} else {
dev->current_len += count;
printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count,dev->current_len);
wake_up_interruptible(&dev->r_wait);
ret = count;
}
out:
mutex_unlock(&dev->mutex);;
out2:
remove_wait_queue(&dev->w_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
static const struct file_operations globalfifo_fops = {
.owner = THIS_MODULE,
//.llseek = globalfifo_llseek,
.read = globalfifo_read,
.write = globalfifo_write,
//.unlocked_ioctl = globalfifo_ioctl,
.open = globalfifo_open,
//.release = globalfifomem_release,
};
struct class *globalfifo_class;
static int __init drive_init(void)
{
int ret;
dev_t devno = MKDEV(globalfifo_major,0);
if(globalfifo_major)
ret = register_chrdev_region(devno, 1, "drive_fifo");
else{
ret = alloc_chrdev_region(&devno, 0, 1, "drive_fifo");
globalfifo_major = MAJOR(devno);
}
printk("\n alloc char device num .\n");
if (ret < 0)
return ret;
fifo_dev = kzalloc(sizeof(struct FIFO_DEV), GFP_KERNEL);
if (!fifo_dev) {
ret = -ENOMEM;
goto fail_malloc;
}
cdev_init(&fifo_dev->cdev, &globalfifo_fops);
//ifo_dev->cdev.owner = THIS_MODULE;
ret = cdev_add(&fifo_dev->cdev, devno, 1);
if (ret)
printk(KERN_NOTICE "Error %d adding drive_fifo", ret);
/*创建一个设备节点*/
//device_create(class_create(THIS_MODULE, "drive_fifo"), NULL, devno, NULL, "drive_fifo");
globalfifo_class = class_create(THIS_MODULE, "drive_fifo_class");
if(globalfifo_class == NULL)
{
printk("create class failed! \n");
return -1;
}
device_create(globalfifo_class,NULL, devno, NULL, "drive_fifo_node");
mutex_init(&fifo_dev->mutex);
init_waitqueue_head(&fifo_dev->r_wait);
init_waitqueue_head(&fifo_dev->w_wait);
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return ret;
}
module_init(drive_init);
static void __exit drive_exit(void)
{
cdev_del(&fifo_dev->cdev);
kfree(fifo_dev);
unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1);
device_destroy(globalfifo_class, MKDEV(globalfifo_major, 0)); //delete device node under /dev
class_destroy(globalfifo_class); //delete class created under sys/class
}
module_exit(drive_exit);
MODULE_AUTHOR("Barry Song ");
MODULE_LICENSE("GPL v2");
四、测试
1.启动两个进程,一个进程 cat/dev/drive_fifo_node & 在后台执行,一个进程 “echo 字符串 /dev/drive_fifo_node” 在前台执行。
# cat /dev/globalfifo &
[1] 20910 //终端输出进程号 可用kill -9 num结束进程
# echo 'I want to be' > /dev/globalfifo
I want to be
# echo 'a great Chinese Linux Kernel Developer' > /dev/globalfifo
a great Chinese Linux kernel Developer
注;往 /dev/drive_fifo_node 里面 echo 需要 root 权限,直接运行 “sudo echo” 是不行的,可先sudo su 进入超级权限模式