阻塞操作:执行设备操作时,若不能获得资源,挂起进程(进入睡眠状态)直到满足可操作的条件后再进行操作。
非阻塞操作:执行设备操作时,若不能获得资源,要么放弃、要么不停查询。
struct globalfifo_dev{
struct cdev cdev;
unsigned int current_len;//FIFO中数据长度
unsigned char mem[GLOBALFIFO_SIZE];
struct mutex mutex;
/*****************************************
*增加两个等待队列头部,分别对应读和写
*******************************************/
wait_queue_head_t r_wait;
wait_queue_head_t w_wait;
};
struct globalfifo_dev *globalfifo_devp;
注:等待队列一般来实现阻塞进程的唤醒。
5. 加载函数XXX_init
static int __init globalfifo_init(void){
//allocate dev_t
...
//init mutex
...
/***************************************
*初始化“等待队列头部”
***************************************/
init_waitqueue_head(&globalfifo_devp->r_wait);
init_waitqueue_head(&globalfifo_devp->w_wait);
//register cdev
...
}
module_init(globalfifo_init);
注:
“等待队列头部”与“等待队列元素”:“等待队列元素”是要添加到“等待队列头部”实现之后的唤醒
6. 读函数read: 读函数中需要唤醒写进程
static ssize_t globalfifo_read(struct file *filp, char __user *buff, size_t size, loff_t *ppos)
{
unsigned int count = size;
int ret = 0;
struct globalfifo_dev *dev = filp->private_data;
/*****************************************************
*定义并初始化“等待队列元素”wait
******************************************************/
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();
/***********************************
*浅睡眠有可能是被信号唤醒,而我们只需要写进程唤醒我们,所以需要判断是不是信号唤醒
***********************************/
if (signal_pending(current)){
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex);//不是信号唤醒,访问资源前需要加锁
}
if (count > dev->current_len)
count = dev->current_len;
if (copy_to_user(buff, dev->mem, count)){
ret = -EFAULT;
goto out;
}else{
memcpy(dev->mem, dev->mem+count, dev->current_len - count);//读完之后需要将未读数据往前挪
dev->current_len -= count;//已读的count字节的数据空出来了
printk(KERN_INFO"read %d bytes, 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->w_wait, &wait);//将“等待队列元素”从“头部”删除
set_current_state(TASK_RUNNING);//设置成就绪状态
return ret;
}
7.写函数write
与读函数差不多
static ssize_t globalfifo_write(struct file *filp, const char __user *buff, size_t size, loff_t *ppos)
{
unsigned int count = size;
int ret;
struct globalfifo_dev *dev = filp->private_data;
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)//整个FIFO大小 减去 有效数据大小
count = GLOBALFIFO_SIZE - dev->current_len;
if (copy_from_user(dev->mem+dev->current_len, buff, count)){
ret = -EFAULT;
goto out;
}else
{
dev->current_len += count;
printk(KERN_INFO"written %d bytes, 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 ssize_t XXX_write(struct file *filp, const char __user *buff, size_t size, loff_t *ppos)
{
...
DECLARE_WAITQUEUE(wait, current);//定义等待队列元素
add_wait_queue(&dev->w_wait, &wait);//添加等待元素到队列
/*等待设备缓冲区可写*/
do{
avail = device_writable(...);
if (avail < 0)
{
if (filp->f_flags & O_NONBLOCK){//阻塞
ret = -EAGAIN;
goto out;
}
}
__set_current_state(TASK_INTERRUPTIBLE);//改变进程状态
schedule();//调度其他进程
if (signal_pending(current)){//判断是否信号唤醒
ret = -ERESTARTSYS;
goto out2;
}
}while(avail<0);
device_write()//写数据
out:
remove_wait_queue(&XXX_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}