1. 程式人生 > >linux裝置驅動之阻塞與非阻塞I/O

linux裝置驅動之阻塞與非阻塞I/O

先做一下與核心阻塞有關的知識儲備:

1)程序休眠:

    程序休眠,簡單的說就是正在執行的程序讓出CPU。休眠的程序會被核心擱置在在一邊,只有當核心再次把休眠的程序喚醒,程序才會會重新在CPU執行。這是核心中的程序排程。一個CPU在同一時間只能有一個程序在執行,微觀序列巨集觀並行,在巨集觀上,我們覺得是所有程序同時進行的。實際上並不是這樣,核心給每個程序分配了4G的虛擬記憶體,並且讓每個程序傻乎乎的以為自己霸佔著CPU執行。同時,核心暗中的將所有的程序按一定的演算法將CPU輪流的給每個程序使用,而休眠就是程序沒有被執行時的一種形式。在休眠下,程序不佔用CPU,等待被喚醒

2)等待佇列

   等待佇列是一個存放著等待某個特定事件程序連結串列

用於存放等待喚醒的程序,等待佇列結構

  1.先看一下佇列頭的樣子:

 /*linux/wait.h*/

 struct __wait_queue_head {

                 spinlock_t lock; //這個是自旋鎖,在這裡不需要理會。

                 struct list_head task_list; //這就是佇列頭中的核心,連結串列頭。

 };

 typedef struct __wait_queue_head wait_queue_head_t;

 2.定義並初始化一個連結串列,在這個連結串列新增需要等待的程序    

  1)靜態定義並初始化,一個函式執行完兩個操作

  DECLARE_WAIT_QUEUE_HEAD(name)

  使用:定義並初始化一個叫name的等待佇列。

  2)分開兩步執行。

  2.1)定義

  wait_queue_head_t test_queue;

  2.2)初始化

  init_waitqueue_head(&test_queue);

初始化函式的位置,它必須在cdev新增函式”cdev_add”。因為”cdev_add”執行成功就意味著裝置可以被操作,裝置被操作前當然需要把所有的事情都幹完,包括等待佇列的初始化。

3)程序休眠

 

  喚醒休眠程序

  void wake_up_interruptible(wait_queue_head_t *queue); //喚醒等待佇列中所有可中斷睡眠的程序

知識點已經介紹完,總結一下上面驅動函式的操作:

1)首先需要定義並初始化一個等待佇列。

2test_read函式中,如果條件不符合,呼叫該函式的程序就會進入休眠。

3)每當另一個程序呼叫test_write函式喚醒等待佇列,test_read中的函式就會再一次判斷條件是否符合,如果不符合,就會繼續休眠,直到哪次的喚醒時條件符合。

非阻塞實現--只需要加上判定條件

     if(filp->f_flags&O_NONBLOCK)

(下面函式實現了阻塞與非阻塞)

  阻塞

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/fcntl.h>

MODULE_LICENSE("Dual BSD/GPL");

#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
//#define GLOBALMEM_MAJOR 250

static int globalmem_major=0;
static const struct file_operations globalmem_fops;
struct globalmem_dev{
	struct cdev cdev;
	unsigned char mem[GLOBALMEM_SIZE];
	unsigned int cur_size;
	wait_queue_head_t test_queue;
};

static struct globalmem_dev dev;

static ssize_t globalmem_read(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
{
	unsigned long p=*ppos;
	int ret=0;

	if(!wait_event_interruptible(dev.test_queue,dev.cur_size)){
		if(p>=GLOBALMEM_SIZE-p)
			return 0;
			if(count>GLOBALMEM_SIZE-p)
				count=GLOBALMEM_SIZE-p;
			if(copy_to_user(buf,(void*)(dev.mem+p),count))
				return -EFAULT;
			else{
				*ppos+=count;
				ret=count;
				dev.cur_size-=ret;
				printk(KERN_INFO "read %d bytes(s) from %d\n",\
					count,p);
				return count;
				}	
		}else
			return -ERESTARTSYS;

	}
}

static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
{
	unsigned long p=*ppos;
	int ret=0;

	if(p>=GLOBALMEM_SIZE-p)
		return 0;
	if(count>GLOBALMEM_SIZE-p)
		count=GLOBALMEM_SIZE-p;
	if(copy_from_user(dev.mem+p,buf,count))
		ret=-EFAULT;
	else{
		*ppos+=count;
		ret=count;
		printk(KERN_INFO "written %d bytes(s) from %d\n",count,p);
	}
	
	dev.cur_size+=ret;
	wake_up_interruptible(&dev.test_queue);
	return ret;	
}
static void globalmem_setup_cdev()
{
	int err;
	dev_t devno=MKDEV(globalmem_major,0);

	cdev_init(&dev.cdev,&globalmem_fops);
	dev.cdev.owner=THIS_MODULE;
	
	init_waitqueue_head(&dev.test_queue);

	err=cdev_add(&dev.cdev,devno,1);
	if(err){
		printk(KERN_NOTICE "Error %d adding globalmem",err);
	}
}

static const struct file_operations globalmem_fops={
	.owner=THIS_MODULE,
	.write=globalmem_write,
	.read=globalmem_read,
};

int globalmem_init(void)
{
	int result;
	dev_t devno=MKDEV(globalmem_major,0);

	if(globalmem_major){
		result=register_chrdev_region(devno,1,"my_globalmem");
	}else{
		result=alloc_chrdev_region(&devno,0,1,"my_globalmem");
		globalmem_major=MAJOR(devno);
	}
	if(result<0){
		return result;
	}
	
	globalmem_setup_cdev();
	return 0;
}

void globalmem_exit(void)
{
	cdev_del(&dev.cdev);
	unregister_chrdev_region(MKDEV(globalmem_major,0),1);
	printk("leavel kernel\n");
	return;
}

module_init(globalmem_init);
module_exit(globalmem_exit);
MODULE_AUTHOR("nw");

非阻塞

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/fcntl.h>

MODULE_LICENSE("Dual BSD/GPL");

#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
//#define GLOBALMEM_MAJOR 250

static int globalmem_major=0;
static const struct file_operations globalmem_fops;
struct globalmem_dev{
	struct cdev cdev;
	unsigned char mem[GLOBALMEM_SIZE];
	unsigned int cur_size;
	wait_queue_head_t test_queue;
};

static struct globalmem_dev dev;

static ssize_t globalmem_read(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
{
	unsigned long p=*ppos;
	int ret=0;
	if(filp->f_flags&O_NONBLOCK){
		if(dev.cur_size>0){
			if(!wait_event_interruptible(dev.test_queue,dev.cur_size)){
				if(p>=GLOBALMEM_SIZE-p)
					return 0;
				if(count>GLOBALMEM_SIZE-p)
					count=GLOBALMEM_SIZE-p;
				if(copy_to_user(buf,(void*)(dev.mem+p),count))
					return -EFAULT;
				else{
					*ppos+=count;
					ret=count;
					dev.cur_size-=ret;
					printk(KERN_INFO "read %d bytes(s) from %d\n",\
						count,p);
					return count;
					}	
			}else
				return -ERESTARTSYS;
		}else
			return -EAGAIN;
	}
}

static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
{
	unsigned long p=*ppos;
	int ret=0;

	if(p>=GLOBALMEM_SIZE-p)
		return 0;
	if(count>GLOBALMEM_SIZE-p)
		count=GLOBALMEM_SIZE-p;
	if(copy_from_user(dev.mem+p,buf,count))
		ret=-EFAULT;
	else{
		*ppos+=count;
		ret=count;
		printk(KERN_INFO "written %d bytes(s) from %d\n",count,p);
	}
	
	dev.cur_size+=ret;
	wake_up_interruptible(&dev.test_queue);
	return ret;	
}
static void globalmem_setup_cdev()
{
	int err;
	dev_t devno=MKDEV(globalmem_major,0);

	cdev_init(&dev.cdev,&globalmem_fops);
	dev.cdev.owner=THIS_MODULE;
	
	init_waitqueue_head(&dev.test_queue);

	err=cdev_add(&dev.cdev,devno,1);
	if(err){
		printk(KERN_NOTICE "Error %d adding globalmem",err);
	}
}

static const struct file_operations globalmem_fops={
	.owner=THIS_MODULE,
	.write=globalmem_write,
	.read=globalmem_read,
};

int globalmem_init(void)
{
	int result;
	dev_t devno=MKDEV(globalmem_major,0);

	if(globalmem_major){
		result=register_chrdev_region(devno,1,"my_globalmem");
	}else{
		result=alloc_chrdev_region(&devno,0,1,"my_globalmem");
		globalmem_major=MAJOR(devno);
	}
	if(result<0){
		return result;
	}
	
	globalmem_setup_cdev();
	return 0;
}

void globalmem_exit(void)
{
	cdev_del(&dev.cdev);
	unregister_chrdev_region(MKDEV(globalmem_major,0),1);
	printk("leavel kernel\n");
	return;
}

module_init(globalmem_init);
module_exit(globalmem_exit);
MODULE_AUTHOR("nw");

除錯app函式略