全部博文(298)
分类: LINUX
2011-03-24 12:43:27
(3)阻塞型IO和休眠的初次应用
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1.本文所介绍的程序平台
开发板:arm9-mini2440
虚拟机为:Red Hat Enterprise Linux 5
开发板上系统内核版本:linux-2.6.32.2
等待队列
在Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可
看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,
从等待等列中取出进程。
Linux 2.6内核提供了如下关于等待队列的操作:
1、定义等待队列
wait_queue_head_t my_queue
2、初始化等待队列
init_waitqueue_head(&my_queue)
3、定义并初始化等待队列
DECLARE_WAIT_QUEUE_HEAD(my_queue)
4、有条件睡眠
wait_event(queue,condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在 queue参数所指定的等待队列上。
wait_event_interruptible(queue,condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数 所指定的等待队列上。
int wait_event_killable(wait_queue_t queue, condition)
当condition(一个布尔表达式)为真时,立即返 回;否则让进程进入TASK_KILLABLE的睡眠, 并挂在queue参数所指定的等待队列上。
5、无条件睡眠(老版本,建议不再使用)
sleep_on(wait_queue_head_t *q)
让进程进入不可中断的睡眠,并把它放入等待队列。
interruptible_sleep_on(wait_queue_head_t *q)
让进程进入可中断的睡眠,并把它放入等待队列。
6、从等待队列中唤醒 进程
wake_up(wait_queue_t *q)
从等待队列中唤醒状态为TASK_UNINTERRUPTIBLE,
TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有进程。
wake_up_interruptible(wait_queue_t *q)
从等待队列中唤醒状态为TASK_INTERRUPTIBLE的进程
阻塞的实现
在阻塞型驱动程序中,Read实现方法 如下: 如果 进程调用read,但设备没有数据或 数据不足,进程阻塞。当新数据到达后,唤醒被阻塞 进程。
在阻塞型驱动程序中,Write实现方法 如下:如果 进程调用了write,但设备没有足够的空间供其写入数据, 进程阻塞。当设备中的数据被读走后,缓冲区中空出部分空间,则唤醒 进程。
阻塞方法 是文件读写操作的默认方法,但应用程序员可通过使用O_NONBLOCK 标志来人为的设置读写操作为非阻塞方法(该标志定义在
单地返回-EAGAIN,而不会阻塞 进程。
2.程序清单
本次实验程序为国嵌培训代码,本人作了改动和较为详细的注释,如有错误请指出。
memdev.h
//头文件
#ifndef _MEMDEV_H_
#define _MEMDEV_H_
#include
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0 /*预设的mem的主设备号*/
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2 /*设备数*/
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
/*mem设备描述的结构体*/
struct mem_dev
{
char *data;
unsigned long size;
wait_queue_head_t inq;
};
/*定义幻数*/
#define MEMDEV_IOC_MAGIC 'k'
/* 定义命令 ,这里的命令都是unsigned int类型*/
#define MEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC, 1)
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)
#define MEMDEV_IOC_MAXNR 3 //定义命令的最大序列号
#endif
memdev.c
/*********************************************
*memdev.c 阻塞版本
*********************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "memdev.h"
static int mem_major = MEMDEV_MAJOR;
bool have_data = false; /*表明设备有足够空间可供读*/
module_param(mem_major,int,S_IRUGO);
struct mem_dev *mem_devp;/*设备结构体指针*/
struct cdev cdev;
/*文件打开函数*/
int mem_open(struct inode *inode,struct file *filp)
{
struct mem_dev *dev;
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
/*大于最大的次设备号就错误了 因为注册的时候要指定次设备的数目*/
if(num >=MEMDEV_NR_DEVS)
return -ENODEV;
dev = &mem_devp[num];
/*将设备描述结构指针赋值给文件私有数据指针*/
filp->private_data = dev;
return 0;
}
/*文件释放函数*/
int mem_release(struct inode *inode,struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t mem_read(struct file *filp,char __user *buf,size_t size,loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret = 0;
/*获得设备结构体指针由于read没有struct inode *inode结构 而且函数的
参数不能改 只能改函数名所以只能在open函数里面设置*/
struct mem_dev *dev = filp->private_data;
/*判断读位置是否有效*/
if(p >= MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
/*没有数据可读 考虑为什么不用if 而用while !!!!??*/
while(!have_data)
{
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
wait_event_interruptible(dev->inq, have_data);
}
/*读数据到用户空间*/
if(copy_to_user(buf,(void*)(dev->data+p),count))
{
ret = -EFAULT;
}
else
{
*poss +=count;
ret = count;
printk(KERN_INFO "read %d bytes from %lu\n",count,p);
}
have_data = false; /*表明不再有数据可读 */
return ret;
}
/*写函数*/
static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret=0;
struct mem_dev *dev = filp->private_data;
if(p>=MEMDEV_SIZE)
return 0;
if(count>MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
if(copy_from_user(dev->data+p,buf,count))
{
ret = -EFAULT;
}
else
{
*poss += count;
ret = count;
printk(KERN_INFO "write %d bytes from %lu\n",count,p);
}
have_data = true;/*有新数据可读*/
/*唤醒读进程*/
wake_up(&(dev->inq));
return ret;
}
int memdev_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err = 0;
int ret = 0;
int ioarg = 0;
/* 检测命令的有效性 */
if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC)//命令是否操作这一类设备
return -EINVAL;
if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR) // 命令序列号是否超出定义
return -EINVAL;
/* 根据命令类型,检测参数空间是否可以访问 */
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
/* 根据命令,执行相应的操作 */
switch(cmd) {
/* 打印当前设备信息 */
case MEMDEV_IOCPRINT:
printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");
break;
/* 获取参数 */
case MEMDEV_IOCGETDATA:
ioarg = 1101;
ret = __put_user(ioarg, (int *)arg);
break;
/* 设置参数 */
case MEMDEV_IOCSETDATA:
ret = __get_user(ioarg, (int *)arg);
printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);
break;
default:
return -EINVAL;
}
return ret;
}
static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence)
{
case 0:
newpos = offset;
break;
case 1:
newpos = filp->f_pos + offset;
break;
case 2:
newpos = MEMDEV_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}
if((newpos<0) || (newpos>MEMDEV_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
static const struct file_operations mem_fops =
{
.owner = THIS_MODULE,
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
.ioctl = memdev_ioctl,/*为什么这里有‘,’??*/
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
int result;
int i;
dev_t devno = MKDEV(mem_major,0);
/*静态申请设备号*/
if(mem_major)
result = register_chrdev_region(devno,2,"memdev");
else/*动态申请设备号*/
{
result = alloc_chrdev_region(&devno,0,2,"memdev");
mem_major = MAJOR(devno);
}
if(result < 0)
return result;
/*初始化cdev结构*/
cdev_init(&cdev,&mem_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
/*注册字符设备*/
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
/*为设备描述结构分配内存*/
mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL);
if(!mem_devp)/*申请失败*/
{
result = -ENOMEM;
goto fail_malloc;
}
memset(mem_devp,0,MEMDEV_NR_DEVS * sizeof(struct mem_dev));
/*为设备分配内存*/
for(i=0;i
{
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
memset(mem_devp[i].data,0,MEMDEV_SIZE);
/*初始化等待队列*/
init_waitqueue_head(&(mem_devp[i].inq));
}
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
static void memdev_exit(void)
{
cdev_del(&cdev);
kfree(mem_devp);
unregister_chrdev_region(MKDEV(mem_major,0),2);
}
MODULE_AUTHOR("Zechin Liao");
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
memdevapp_read.c
/*********************************************
*memdevapp.c
*********************************************/
#include
#include
#include
#include
int main()
{
int fd;
char buf[4096];
//FILE *fp0 = NULL;
/*初始化buf*/
strcpy(buf,"This is read");
printf("buf:%s\n",buf);
/*打开设备文件*/
fd=open("/dev/memdev3",O_RDWR);
if(fd == -1)
{
printf("open memdev failed!\n");
return -1;
}
lseek(fd,0,SEEK_SET);
/*清除buf*/
strcpy(buf,"nothing");
/*读出设备*/
read(fd,buf,sizeof(buf));
/*检测结果*/
printf("buf:%s\n",buf);
return 0;
}
memdevapp_write.c
/*********************************************
*memdevapp.c
*********************************************/
#include
#include
#include
#include
int main()
{
int fd;
char buf[4096];
//FILE *fp0 = NULL;
/*初始化buf*/
strcpy(buf,"This is write");
printf("buf:%s\n",buf);
/*打开设备文件*/
fd=open("/dev/memdev3",O_RDWR);
if(fd == -1)
{
printf("open memdev failed!\n");
return -1;
}
/*写入设备*/
write(fd,buf,sizeof(buf));
return 0;
}
Arm平台实验:
[root@FriendlyARM /udisk]# insmod memdev.ko
[root@FriendlyARM /udisk]# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 s3c2410_serial
253 memdev
254 rtc
Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[root@FriendlyARM /udisk]# mknod /dev/memdev3 c 253 0
[root@FriendlyARM /udisk]# ./memdevapp_
./memdevapp_read ./memdevapp_write
[root@FriendlyARM /udisk]# ./memdevapp_read&
[root@FriendlyARM /udisk]# buf:This is read
[root@FriendlyARM /udisk]# ./memdevapp_write
buf:This is write
write 4096 bytes from 0
read 4096 bytes from 0
buf:This is write
[1] + Done ./memdevapp_read
[root@FriendlyARM /udisk]#