linux内核之阻塞 IO(2)

来源:互联网 发布:元数据与大数据 编辑:程序博客网 时间:2024/05/29 08:00

前面我们讲了简单休眠方式实现阻塞IO,下面就是高级休眠(手工休眠)

一 前面有个休眠规则没讲,这里补上,来ldd3上总结的不错

(1)永远不要在原子的上下文中进入休眠,即我们的驱动程序不能在拥有自旋锁,seqlock或者RCU锁时休眠。如果我们禁止了中断也不能休眠。在拥有信号量时休眠时合法的,但是必须仔细检查拥有信号量时休眠的代码,确保不会发生死锁。

(2)必须检查休眠等待的条件真正为真。

二 手工休眠步骤:

(1)分配初始化wait_queue_结构,然后加入对应的等待队列。

(2)设置进程的状态,让出处理器

  set_current_state(TASK_INTERRUPTIBLE);

  schedule();//让出处理器

  (3)休眠唤醒后得完成清理工作,将队列从等待队列中移走

执行这三步的函数有好几个:
prepare_wait(wait_queue_head_t *queue,wait_queue_t *wait,int state);//加入队列,设置状态

finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);//唤醒清理工作


还有add_wait_queue() 的组合函数,具体自己了解下


三 独占等待

前面讲简单休眠时提到过独占等待,这里讲下,ldd3(page 160),独占等待的结果是进程每次只会唤醒其中一个(以某种有序的方式),从而不会产生”疯狂野兽“问题。void prapare_to_wait_exclusive(wait_queue_head_t *queue,wait_queue_t *wait,int state);独占等待函数


eg:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/device.h>






#define FIFO_SIZE 1024


struct wq_fifo_node
{
struct cdev mycdev;
unsigned int current_len;
unsigned char *fifo;
struct semaphore sem_t;
unsigned int rt; 
unsigned int wt;
wait_queue_head_t w_wait;
wait_queue_head_t r_wait;
};


static dev_t dev_num;
static struct class *wq_class;
static struct device *wq_device;
static struct wq_fifo_node *wq_fifo;
struct fasync_struct *fapp;


static int wq_open(struct inode *inode, struct file *file)
{
//struct wq_fifo_node *mywq = container_of(inode->i_cdev,struct wq_fifo_node,mycdev);
//file->private_data = wq_fifo;
/*down(&wq_fifo->sem_t);
wq_fifo->fifo = kmalloc(FIFO_SIZE*sizeof(unsigned char),GFP_KERNEL);
wq_fifo->rt = 0;
wq_fifo->wt = 0;
up(&wq_fifo->sem_t);*/
printk("file is open\n");
return 0;
}


static ssize_t wq_write(struct file *file,const char __user *user_buf,size_t count, loff_t *ppos)
{
int ret;
int i,index;
//struct wq_fifo_node *wq_fifo = file->private_data;
unsigned char *tmp_buf = kmalloc(count,GFP_KERNEL);
printk("__FUNCTION__ %s __LINE__ %d\n",__FUNCTION__,__LINE__);
down(&(wq_fifo->sem_t));
DEFINE_WAIT(wait);
if(((wq_fifo->wt+1) % FIFO_SIZE )== wq_fifo->rt)
{
if(file->f_flags & O_NONBLOCK)
{
ret = -EAGAIN;
goto out1;//非阻塞IO直接返回
}


up(&(wq_fifo->sem_t));//在进入休眠前释放锁
//阻塞IO
prepare_to_wait(&(wq_fifo->w_wait),&wait,TASK_INTERRUPTIBLE);
schedule();
finish_wait(&(wq_fifo->w_wait),&wait);
if(signal_pending(current))
return -ERESTARTSYS;
//wait_event_interruptible((wq_fifo->w_wait),((wq_fifo->wt+1)%FIFO_SIZE != wq_fifo->rt));
down(&(wq_fifo->sem_t));
}

if(((wq_fifo->rt-wq_fifo->wt+FIFO_SIZE)-1)%FIFO_SIZE>= count)
{

copy_from_user(tmp_buf,user_buf,count);
for(i =0;i<count;i++)
{
wq_fifo->fifo[wq_fifo->wt] = tmp_buf[i];
wq_fifo->wt = (wq_fifo->wt+1)%FIFO_SIZE;
}
ret = count;
kfree(tmp_buf);
}
else
{
ret = ((wq_fifo->wt-wq_fifo->rt+FIFO_SIZE)-1)%FIFO_SIZE;
copy_from_user(tmp_buf,user_buf,ret);
for(i =0;i<ret;i++)
{
wq_fifo->fifo[wq_fifo->wt] = tmp_buf[i];
wq_fifo->wt = (wq_fifo->wt+1)%FIFO_SIZE;
}
kfree(tmp_buf);
}

wake_up_interruptible(&(wq_fifo->r_wait));
kill_fasync(&fapp, SIGIO, POLL_IN);
printk("__LINE__ %d wq_fifo->rt = %d wq_fifo->wt = %d\n",__LINE__,wq_fifo->rt,wq_fifo->wt);
out1:
up(&(wq_fifo->sem_t));
   return ret;
}


static ssize_t wq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret;
int i;
printk("__FUNCTION__ %s __LINE__ %d\n",__FUNCTION__,__LINE__);
printk("read size %d\n",size);
    unsigned char *tmp_buf = kmalloc(size,GFP_KERNEL);
//struct wq_fifo_node *wq_fifo = file->private_data;
printk("__LINE__ %d wq_fifo->rt = %d wq_fifo->wt = %d\n",__LINE__,wq_fifo->rt,wq_fifo->wt);
down(&(wq_fifo->sem_t));
DEFINE_WAIT(wait);
if(wq_fifo->wt == wq_fifo->rt)
{
if(file->f_flags&O_NONBLOCK)
{
ret = -EAGAIN;
goto out1;
}
up(&(wq_fifo->sem_t));
prepare_to_wait(&(wq_fifo->r_wait),&wait,TASK_INTERRUPTIBLE);
schedule();
finish_wait(&(wq_fifo->r_wait),&wait);
if(signal_pending(current))
return -ERESTARTSYS;
//wait_event_interruptible((wq_fifo->r_wait),wq_fifo->wt != wq_fifo->rt);
   down(&(wq_fifo->sem_t));
printk("pid is %d\n",current->pid);
printk("__FUNCTION__ %s __LINE__ %d\n",__FUNCTION__,__LINE__);
printk("__LINE__ %d wq_fifo->rt = %d wq_fifo->wt = %d\n",__LINE__,wq_fifo->rt,wq_fifo->wt);
}
if((wq_fifo->wt-wq_fifo->rt+FIFO_SIZE)%FIFO_SIZE>= size)
{
for(i = 0;i < size;i++)
{
tmp_buf[i] = wq_fifo->fifo[wq_fifo->rt];
wq_fifo->rt = (wq_fifo->rt+1)%FIFO_SIZE;
}
ret = size;
copy_to_user(buf,tmp_buf,ret);
}
else
{
ret = (wq_fifo->wt-wq_fifo->rt+FIFO_SIZE)%FIFO_SIZE;
printk("__FUNCTION__ %s __LINE__ %d\n",__FUNCTION__,__LINE__);
printk("__LINE__ %d wq_fifo->rt = %d wq_fifo->wt = %d\n",__LINE__,wq_fifo->rt,wq_fifo->wt);
for(i = 0;i < ret;i++)
{
tmp_buf[i] = wq_fifo->fifo[wq_fifo->rt];
wq_fifo->rt = (wq_fifo->rt+1)%FIFO_SIZE;
}
printk("__FUNCTION__ %s __LINE__ %d\n",__FUNCTION__,__LINE__);
copy_to_user(buf,tmp_buf,ret);
kfree(tmp_buf);
}

wake_up_interruptible(&(wq_fifo->w_wait));
out1:
up(&(wq_fifo->sem_t));
return ret;
}


static int wq_fasync(int fd, struct file *filp, int mode)
{
 
return fasync_helper(fd,filp,mode,&fapp);
}


static int wq_release(struct inode *inode, struct file *file)
{

//struct wq_fifo_node *wq_fifo = file->private_data;
//kfree(wq_fifo->fifo);
//wq_fifo->fifo = NULL;
printk("file is close!\n");
return 0;
}




static unsigned int wq_poll(struct file *filp, poll_table *wait)
{
unsigned int mask =0;
down(&(wq_fifo->sem_t));
poll_wait(filp, &wq_fifo->w_wait,  wait);
poll_wait(filp, &wq_fifo->r_wait, wait);
if (wq_fifo->wt != wq_fifo->rt)
mask |= POLLIN | POLLRDNORM;/* readable */
if ((wq_fifo->wt +1)%FIFO_SIZE != wq_fifo->rt)
mask |= POLLOUT | POLLWRNORM;/* writable */
up(&(wq_fifo->sem_t));
return mask;
}




static struct file_operations f_ops = {
.owner = THIS_MODULE,
.open  = wq_open,
.read  = wq_read,
.write = wq_write,
.fasync = wq_fasync,
.poll = wq_poll,
.release = wq_release,
};




static void setup_cdev(struct wq_fifo_node *wq)
{
int err;
   //cdev =  cdev_alloc();
    cdev_init(&(wq->mycdev),&f_ops);
    wq->mycdev.owner = THIS_MODULE;
    wq->mycdev.ops = &f_ops;
    err = cdev_add(&(wq->mycdev),dev_num,1);
if(err)
{
printk("cdev add_error!!\n");
}
 }


static int __init waitqueue_init()
{
printk(KERN_ALERT "%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
wq_fifo = kmalloc(sizeof(struct wq_fifo_node),GFP_KERNEL);


wq_fifo->fifo = kmalloc(FIFO_SIZE*sizeof(unsigned char),GFP_KERNEL);
wq_fifo->rt = 0;
wq_fifo->wt = 0;


    init_waitqueue_head(&(wq_fifo->r_wait));
    init_waitqueue_head(&(wq_fifo->w_wait));
    init_MUTEX(&(wq_fifo->sem_t));
    alloc_chrdev_region(&dev_num,0,1,"work_queue");
    setup_cdev(wq_fifo);
    wq_class = class_create(THIS_MODULE,"wq_class");
    wq_device = device_create(wq_class,NULL,dev_num,NULL,"wq_device");
    printk(KERN_ALERT "%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
    return 0;
}


static void __exit waitqueue_exit()
{
//if(wq_fifo->fifo == NULL)
    kfree(wq_fifo->fifo);
cdev_del(&(wq_fifo->mycdev));
kfree(wq_fifo->fifo);
device_del(wq_device);
class_unregister(wq_class);

}


module_init(waitqueue_init);
module_exit(waitqueue_exit);
MODULE_AUTHOR("liwenzou");
MODULE_LICENSE("GPL");

测试:可以开启两个窗口执行 cat /dev/wq_device

           在开启一个窗口执行 echo "dghfdgh">/dev/wq_device

观察 cat输出,dmesg查看printk(红色字体)输出的唤醒进程号,再换用独占等待对比下效果


0 0
原创粉丝点击