Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2422927
  • 博文数量: 298
  • 博客积分: 7876
  • 博客等级: 准将
  • 技术积分: 5500
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-23 13:39
文章存档

2013年(2)

2012年(142)

2011年(154)

分类: 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_INTERRUPTIBLETASK_KILLABLE 的所有进程。

wake_up_interruptible(wait_queue_t *q)

从等待队列中唤醒状态为TASK_INTERRUPTIBLE的进程

 

阻塞的实现

在阻塞型驱动程序中,Read实现方法 如下: 如果 进程调用read,但设备没有数据或 数据不足,进程阻塞。当新数据到达后,唤醒被阻塞 进程。

在阻塞型驱动程序中,Write实现方法 如下:如果 进程调用了write,但设备没有足够的空间供其写入数据, 进程阻塞。当设备中的数据被读走后,缓冲区中空出部分空间,则唤醒 进程。

阻塞方法 是文件读写操作的默认方法,但应用程序员可通过使用O_NONBLOCK 标志来人为的设置读写操作为非阻塞方法(该标志定义在中,在打开文件时指定)。如果设置了O_NONBLOCK标志,readwrite的行为是不同的。如果进程在没有数据就绪时调用了read,或者在缓冲区没有空间时调用了write,系统只是简

单地返回-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]#

 

阅读(1218) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~