可行的阻塞型IO和休眠的初次应用

注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方

1.本文所介绍的程序平台

虚拟机为:Ubuntu 12.10

开发板上系统内核版本:linux-3.5.7.2

等待队列

Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可

看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,

从等待等列中取出进程。

Linux 3.5.7.2内核提供了如下关于等待队列的操作:

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标志来人为的设置读写操作为非阻塞方法(该标志定义在<linux/fcntl.h>中,在打开文件时指定)。如果设置了O_NONBLOCK标志,readwrite的行为是不同的。如果进程在没有数据就绪时调用了read,或者在缓冲区没有空间时调用了write,系统只是简

单地返回-EAGAIN,而不会阻塞进程。

2.程序清单

本次实验程序为国嵌培训代码,本人作了改动和较为详细的注释,如有错误请指出。

memdev.h

//头文件

#ifndef _MEMDEV_H_

#define _MEMDEV_H_

#include <linux/ioctl.h>

#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 <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 <linux/slab.h>

#include <asm/io.h>

#include <asm/uaccess.h>

#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,

   .unlocked_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<MEMDEV_NR_DEVS;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("fulinux");

MODULE_LICENSE("GPL");

module_init(memdev_init);

module_exit(memdev_exit);

memdevapp_read.c

/********************************************* 

*memdevapp.c 

*********************************************/

#include <stdio.h> 

#include <stdlib.h> 

#include <unistd.h> 

#include <fcntl.h>  

 

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 <stdio.h> 

#include <stdlib.h> 

#include <unistd.h> 

#include <fcntl.h>  

 

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;  

}

 /**************************************************************************

Makefile

**************************************************************************/

  1 ifneq ($(KERNELRELEASE),)
  2
  3 obj-m:=memdev.o
  4
  5 else
  6
  7 KERNELDIR:=/lib/modules/3.5.7.2/build
  8
  9 PWD:=$(shell pwd)
 10
 11 default:
 12        
 13         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
 14
 15 install:
 16         insmod ./memdev.ko
 17
 18 uninstall:
 19         rmmod memdev
 20
 21 clean:
 22         rm -rf *.o *.mod.* *.ko
 23
 24 endif

fulinux@ubuntu:~$ clear
fulinux@ubuntu:~$ cd memdev2/
fulinux@ubuntu:~/memdev2$ ls -l
总用量 24
-rw-rw-r-- 1 fulinux fulinux  257  3月  6 14:41 Makefile
-rw-rw-r-- 1 fulinux fulinux  759  3月  6 14:37 memdevapp_read.c
-rw-rw-r-- 1 fulinux fulinux  515  3月  6 14:36 memdevapp_write.c
-rw-rw-r-- 1 fulinux fulinux 6920  3月  6 14:49 memdev.c
-rw-rw-r-- 1 fulinux fulinux  772  3月  6 13:32 memdev.h
fulinux@ubuntu:~/memdev2$ sudo make
[sudo] password for fulinux:
make -C /lib/modules/3.5.7.2/build M=/home/fulinux/memdev2 modules
make[1]: 正在进入目录 `/usr/src/linux-source-3.5.0/linux-source-3.5.0'
  CC [M]  /home/fulinux/memdev2/memdev.o
/home/fulinux/memdev2/memdev.c:397:5: 警告: 从不兼容的指针类型初始化 [默认启用]
/home/fulinux/memdev2/memdev.c:397:5: 警告: (在‘mem_fops.unlocked_ioctl’的初始化附近) [默认启用]
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/fulinux/memdev2/memdev.mod.o
  LD [M]  /home/fulinux/memdev2/memdev.ko
make[1]:正在离开目录 `/usr/src/linux-source-3.5.0/linux-source-3.5.0'
fulinux@ubuntu:~/memdev2$ ls
Makefile           memdev.c   memdev.mod.c  modules.order
memdevapp_read.c   memdev.h   memdev.mod.o  Module.symvers
memdevapp_write.c  memdev.ko  memdev.o
fulinux@ubuntu:~/memdev2$ cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  5 ttyprintk
  6 lp
  7 vcs
 10 misc
 13 input
 14 sound
 21 sg
 29 fb
 99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
252 bsg
253 watchdog
254 rtc

Block devices:
  1 ramdisk
259 blkext
  7 loop
  8 sd
  9 md
 11 sr
 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
252 device-mapper
253 virtblk
254 mdp
fulinux@ubuntu:~/memdev2$ sudo make install
insmod ./memdev.ko
fulinux@ubuntu:~/memdev2$ cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  5 ttyprintk
  6 lp
  7 vcs
 10 misc
 13 input
 14 sound
 21 sg
 29 fb
 99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
251 memdev
252 bsg
253 watchdog
254 rtc

Block devices:
  1 ramdisk
259 blkext
  7 loop
  8 sd
  9 md
 11 sr
 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
252 device-mapper
253 virtblk
254 mdp
fulinux@ubuntu:~/memdev2$ ls -al memdev3
ls: 无法访问memdev3: 没有那个文件或目录
fulinux@ubuntu:~/memdev2$ mknod /dev/memdev3 c 251 0
mknod: "/dev/memdev3": 权限不够
fulinux@ubuntu:~/memdev2$ sudo mknod /dev/memdev3 c 251 0
fulinux@ubuntu:~/memdev2$ ls -al memdev3                
ls: 无法访问memdev3: 没有那个文件或目录
fulinux@ubuntu:~/memdev2$ ls -al /dev/memdev3
crw-r--r-- 1 root root 251, 0  3月  6 15:31 /dev/memdev3
fulinux@ubuntu:~/memdev2$ gcc -o memdevapp_read memdevapp_read.c
memdevapp_read.c: 在函数‘main’中:
memdevapp_read.c:24:5: 警告: 隐式声明与内建函数‘strcpy’不兼容 [默认启用]
fulinux@ubuntu:~/memdev2$ gcc -o memdevapp_write memdevapp_write.c
memdevapp_write.c: 在函数‘main’中:
memdevapp_write.c:20:5: 警告: 隐式声明与内建函数‘strcpy’不兼容 [默认启用]
fulinux@ubuntu:~/memdev2$ ./memdevapp_write
buf:This is write
open memdev failed!
fulinux@ubuntu:~/memdev2$ sudo ./memdevapp_write
buf:This is write
fulinux@ubuntu:~/memdev2$ sudo ./memdevapp_read
buf:This is read
buf:This is write
fulinux@ubuntu:~/memdev2$

 

你可能感兴趣的:(ioctl)