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

2013年(2)

2012年(142)

2011年(154)

分类: LINUX

2011-03-24 12:45:37

4)并发处理-completion应用

 

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

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

开发板:arm9-mini2440

虚拟机为:Red Hat Enterprise Linux 5

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

 

2.程序清单

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

gong.h

 

#ifndef _GONG_H_

         #define _GONG_H_

 

         #include

        

         #ifndef GONG_MAJOR

                   #define GONG_MAJOR 0 /*预设的mem的主设备号*/

         #endif

        

         #ifndef GONG_NR_DEVS

                   #define GONG_NR_DEVS 3/*设备数*/

         #endif

 

         #ifndef GONG_SIZE

                   #define GONG_SIZE 4096

         #endif

        

         /*mem设备描述的结构体*/

         struct gong_dev

         {

                   char *data;

                   unsigned long size;

                   wait_queue_head_t inq;

 

         };//注意 这里要加分号!!!!!!!!!

        

 

         /*定义幻数*/

         #define GONG_IOC_MAGIC 'g'

        

         /* 定义命令 ,这里的命令都是unsigned int类型*/

         #define GONG_IOCPRINT _IO(GONG_IOC_MAGIC, 1) /*没有参数的命令*/

         #define GONG_IOCGETDATA _IOR(GONG_IOC_MAGIC, 2, int) /*从驱动中读数据*/

         #define GONG_IOCSETDATA _IOW(GONG_IOC_MAGIC, 3, int) /*写数据到驱动中*/

 

         //定义命令的最大序列号

         #define GONG_IOC_MAXNR 3

#endif

 

gong.c

 

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

*阻塞版本

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

#include

#include

#include

 

#include /*completion*/

#include /*printk()*/

#include /*kmalloc*/

#include /*everything....*/

#include /*error codes*/

#include /*size_t*/

#include /*O_ACCMODE*/

#include

#include

#include /*capable()*/

 

#include

#include

#include /*access_ok()*/

 

#include "gong.h"

 

static int gong_major = GONG_MAJOR;

bool have_data = false ;/*表明设备有足够空间可供读*/

module_param(gong_major, int, S_IRUGO);

 

struct gong_dev *gong_devp;/*设备结构体指针*/

 

struct cdev cdev;

 

DECLARE_COMPLETION(comp);/* 创建completion(声明+初始化) */

 

 

/*

 

struct file 常见成员:

loff_t f_pos 文件读写位置

struct file_operation *f_op

 

 

struct inode 常见成员:

dev_t i_rdev 设备号

*/

 

 

/*

为啥不能放在这里???

static const struct file_operations gong_fops =

{

         .owner = THIS_MODULE,

         .llseek = gong_llseek,

         .read = gong_read,

         .write = gong_write,

         .open = gong_open,

         .release = gong_release,

         .ioctl = gong_ioctl,//!!为什么这里有‘,’??

 

};//!!!

*/

 

/*文件打开函数*/

int gong_open(struct inode *inode, struct file *filp)

{

         struct gong_dev *dev;

         /*获取次设备号*/

         int num = MINOR(inode->i_rdev);

        

         /*大于最大的次设备号 就错误了 因为注册的时候要指定次设备的数目*/

         if(num >= GONG_NR_DEVS)

                   return -ENODEV;

         dev = &gong_devp[num];//获取为次设备号为num的设备结构

         /*将设备描述结构指针赋值给文件私有数据指针*/

         filp->private_data = dev;//!!

        

         return 0;

 

}

 

/*文件释放函数*/

int gong_release(struct inode *inode, struct file *filp)

{

         return 0;

}

 

/*读函数*/

static ssize_t gong_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 gong_dev *dev = filp->private_data;

        

        

         //并发处理

         printk(KERN_DEBUG "process %i (%s) going to sleep\n",

                            current->pid, current->comm);//!!

         wait_for_completion(&comp);/* 等待completion */

         printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);

 

        

        

        

         /*判断读位置是否有效 从内核空间读到用户空间*/

         if(p >= GONG_SIZE)

                   return 0;

         if(count > GONG_SIZE-p )

                   count = GONG_SIZE-p ;

                  

         /*没有数据可读 考虑为什么不用if 而用while !!!!??*/

         while(!have_data)

         {

                   if(filp->f_flags & O_NONBLOCK)

                            return -EAGAIN;

                           

                   /*wait_event_interruptible(queue, condition) 条件为真时立即返回否则让

                   进程进入TASK_INTERRUPTIBLE的睡眠

                   并挂在queue参数指定的等待队列*/

                   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("<5> read %d butes from %lu\n", count, p);

         }

         have_data = false;/*表明不再有数据可读 */

         return ret;

}

 

/*写函数*/

static ssize_t gong_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 gong_dev *dev = filp->private_data;

        

 

         if(p >= GONG_SIZE)

                   return 0;

         if(count > GONG_SIZE-p)

                   count =  GONG_SIZE-p;

        

         if(copy_from_user(dev->data+p, buf, count))

         {

                   ret = -EFAULT;

         }

         else

         {

                  *poss += count;

                   ret = count;

                   printk("<5> write %d bytes foem %lu\n", count, p);

         }

        

         //并发处理

         printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",

                            current->pid, current->comm);

         complete(&comp);/*唤醒一个等待completion的线程*/

        

        

         have_data = true;/*有新数据可读*/

         /*唤醒读进程

         wake_upwait_queue_t *q

         从等待队列q中唤醒状态为

         TASK_INTERRUPTIBLE,TASK_UNINTERRUPTIBLE,TASK_KILLABLE

         的所有进程*/

         wake_up(&(dev->inq));

         return ret;

}

 

int gong_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) != GONG_IOC_MAGIC)//命令是否操作这一类设备

                   return -EINVAL;

         if(_IOC_NR(cmd) > GONG_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 GONG_IOCPRINT:

          printk("<--- CMD GONG_IOCPRINT Done--->\n\n");

        break;

     

      /* 获取参数 */

      case GONG_IOCGETDATA:

        ioarg = 1101;

        ret = __put_user(ioarg, (int *)arg);

        break;

     

      /* 设置参数 */

      case GONG_IOCSETDATA:

        ret = __get_user(ioarg, (int *)arg);

        printk("<--- In Kernel GONG_IOCSETDATA ioarg = %d --->\n\n",ioarg);

        break;

 

      default:

        return -EINVAL;

    }

    return ret;

}

 

static loff_t gong_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 = GONG_SIZE - 1 + offset;

                            break;

                   default:

                            return -EINVAL;

         }

         if((newpos<0) || (newpos>GONG_SIZE))

                   return -EINVAL;

 

         filp->f_pos = newpos;

         return newpos;

}

 

 

static const struct file_operations gong_fops =

{

         .owner = THIS_MODULE,

         .llseek = gong_llseek,

         .read = gong_read,

         .write = gong_write,

         .open = gong_open,

         .release = gong_release,

         .ioctl = gong_ioctl,//!!为什么这里有‘,’??

 

};//!!!

 

/*设备驱动模块加载函数*/

static int gong_init(void)

{

         int res;

         int i;

         /*dev_t描述是设备 MKDEV把主设备号和次设备号合成*/

         dev_t devno = MKDEV(gong_major, 0);

         /*静态申请设备号*/

         /*int register_chrdev_region(dev_t from, unsigned count, const char *name)

         功能:注册从from开始的count个这备号,主设备号不变,次设备号增加,如果次设备号溢出,主设备号加1

         参数:

         from:要注册的第一个设备号

         count:要注册的设备号个数

         name:设备名(体现在/proc/devices

         */

         if(gong_major)

                   res = register_chrdev_region(devno, GONG_NR_DEVS, "gong_dev");

         else/*动态申请设备号*/

         /*int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

         功能:动态申请count个设备号,第一个设备号的次设备号为baseminor

         参数:

         dev:分配到得设备号

         baseminor:起始次设备号

         count:要注册的设备号个数

         name:设备名(体现在/proc/devices

         */

         {

                   res = alloc_chrdev_region(&devno, 0, GONG_NR_DEVS, "gong_dev");

                   gong_major = MAJOR(devno);        

         }

 

         if(res < 0)

                   return res;

                  

         /*设备注册:

        

         1.分配cdev

                   动态分配:

                            struct cdev *cdev_alloc(void)

                           

         2.初始化cdev

                            void cdev_init(struct cdev *cdev, const struct file_operation *fops)

        

         3.添加cdev

                            int cdev_add(struct cdev *p, dev_t dev, unsigned count)

                           

         */     

                  

         /*初始化cdev结构*/

         cdev_init(&cdev, &gong_fops);

         //cdev.ower = THIS_MODULE; 没有这个成员

         cdev.ops = &gong_fops;

         /*注册字符设备*/

         cdev_add(&cdev, MKDEV(gong_major, 0), GONG_NR_DEVS);

        

         /*为设备描述结构分配内存 有多少个设备分配多少个 */

         gong_devp = kmalloc(GONG_NR_DEVS * sizeof(struct gong_dev), GFP_KERNEL);

         if(!gong_devp)/*申请失败*/

         {

                   res = -ENOMEM;

                   goto fail_malloc;

         }

 

         memset(gong_devp, 0, GONG_NR_DEVS * sizeof(struct gong_dev));

        

         /*为设备分配内存 有多少个设备分配多少个*/

         for(i = 0; i < GONG_NR_DEVS; i++)

         {

                   gong_devp[i].size = GONG_SIZE;

                   gong_devp[i].data = kmalloc(GONG_SIZE, GFP_KERNEL);

                   /*初始化等待队列*/

                   init_waitqueue_head(&(gong_devp[i].inq));

                  

                  

         }

        

        

         return 0;

        

         fail_malloc:

                   /*void unregister_chrdev_region(dev_t from, unsigned count)

                   功能:释放从from开始的count个设备号*/

                   unregister_chrdev_region(devno, GONG_NR_DEVS);

                   return res;

}

 

static void gong_exit(void)

{

 

         cdev_del(&cdev);

         kfree(gong_devp);

         unregister_chrdev_region(MKDEV(gong_major, 0), GONG_NR_DEVS);

 

}

 

MODULE_LICENSE("GPL");

 

module_init(gong_init);

module_exit(gong_exit);

 

/* 自定义函数

static void scull_kfifo_setup_cdev(struct scull_kfifo *dev)

{

         int err, devno = MKDEV(scull_kfifo_major, scull_kfifo_minor );

   

         cdev_init(&dev->cdev, &scull_kfifo_fops);

         dev->cdev.owner = THIS_MODULE;

         err = cdev_add (&dev->cdev, devno, 1);

         if (err)

                   printk(KERN_NOTICE "Error %d adding scull_kfifo", err);

}

 

*/

 

gong_write.c

 

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

*memdevapp.c 

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

#include  

#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/gong_dev0",O_RDWR);  

    if(fd == -1)  

    {  

        printf("open memdev failed!\n");  

        return -1;            

    }  

      

       /*写入设备*/

    write(fd,buf,sizeof(buf));    

 

    return 0;  

}

 

gong_read.c

 

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

*memdevapp.c 

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

#include  

#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/gong_dev0",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;  

}

 

 

Arm平台实验:

[root@FriendlyARM /udisk]# insmod gong.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 gong_dev

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/gong_dev0 c 253 0

[root@FriendlyARM /udisk]# ./read &

[root@FriendlyARM /udisk]# ./read &

[root@FriendlyARM /udisk]# ps

  PID USER       VSZ STAT COMMAND

    1 root      3068 S    init

    2 root         0 SW   [kthreadd]

    3 root         0 SW   [ksoftirqd/0]

    4 root         0 SW   [events/0]

    5 root         0 SW   [khelper]

   11 root         0 SW   [async/mgr]

  209 root         0 SW   [sync_supers]

  211 root         0 SW   [bdi-default]

  213 root         0 SW   [kblockd/0]

  222 root         0 SW   [khubd]

  228 root         0 SW   [kmmcd]

  244 root         0 SW   [rpciod/0]

  251 root         0 SW   [kswapd0]

  298 root         0 SW   [aio/0]

  302 root         0 SW   [nfsiod]

  306 root         0 SW   [crypto/0]

  413 root         0 SW   [mtdblockd]

  623 root         0 SW   [usbhid_resumer]

  667 root      3068 S    syslogd

  670 root      3328 S    /usr/sbin/inetd

  674 root      1940 S    /usr/sbin/boa

  686 root     13084 S    /opt/Qtopia/bin/qpe

  687 root      3392 S    -/bin/sh

  688 root      3068 S    init

  689 root      3068 S    init

  692 root      3068 S    init

  699 root         0 SW   [flush-31:0]

  704 root         0 SW   [scsi_eh_0]

  705 root         0 SW   [usb-storage]

  730 root      1412 D    ./read

  731 root      1412 D    ./read

  732 root      3392 R    ps

[root@FriendlyARM /udisk]# ./write

 write 0 bytes foem 0

 read 0 butes from 0

read ok! code=0

write ok! code=0

[1] - Done                       ./read

[root@FriendlyARM /udisk]# ps

  PID USER       VSZ STAT COMMAND

    1 root      3068 S    init

    2 root         0 SW   [kthreadd]

    3 root         0 SW   [ksoftirqd/0]

    4 root         0 SW   [events/0]

    5 root         0 SW   [khelper]

   11 root         0 SW   [async/mgr]

  209 root         0 SW   [sync_supers]

  211 root         0 SW   [bdi-default]

  213 root         0 SW   [kblockd/0]

  222 root         0 SW   [khubd]

  228 root         0 SW   [kmmcd]

  244 root         0 SW   [rpciod/0]

  251 root         0 SW   [kswapd0]

  298 root         0 SW   [aio/0]

  302 root         0 SW   [nfsiod]

  306 root         0 SW   [crypto/0]

  413 root         0 SW   [mtdblockd]

  623 root         0 SW   [usbhid_resumer]

  667 root      3068 S    syslogd

  670 root      3328 S    /usr/sbin/inetd

  674 root      1940 S    /usr/sbin/boa

  686 root     13084 S    /opt/Qtopia/bin/qpe

  687 root      3392 S    -/bin/sh

  688 root      3068 S    init

  689 root      3068 S    init

  692 root      3068 S    init

  699 root         0 SW   [flush-31:0]

  704 root         0 SW   [scsi_eh_0]

  705 root         0 SW   [usb-storage]

  731 root      1412 D    ./read

  734 root      3392 R    ps

[root@FriendlyARM /udisk]# ./write

 write 0 bytes foem 0

 read 0 butes from 0

write ok! code=0

[root@FriendlyARM /udisk]# read ok! code=0

ps

  PID USER       VSZ STAT COMMAND

    1 root      3068 S    init

    2 root         0 SW   [kthreadd]

    3 root         0 SW   [ksoftirqd/0]

    4 root         0 SW   [events/0]

    5 root         0 SW   [khelper]

   11 root         0 SW   [async/mgr]

  209 root         0 SW   [sync_supers]

  211 root         0 SW   [bdi-default]

  213 root         0 SW   [kblockd/0]

  222 root         0 SW   [khubd]

  228 root         0 SW   [kmmcd]

  244 root         0 SW   [rpciod/0]

  251 root         0 SW   [kswapd0]

  298 root         0 SW   [aio/0]

  302 root         0 SW   [nfsiod]

  306 root         0 SW   [crypto/0]

  413 root         0 SW   [mtdblockd]

  623 root         0 SW   [usbhid_resumer]

  667 root      3068 S    syslogd

  670 root      3328 S    /usr/sbin/inetd

  674 root      1940 S    /usr/sbin/boa

  686 root     13084 S    /opt/Qtopia/bin/qpe

  687 root      3392 S    -/bin/sh

  688 root      3068 S    init

  689 root      3068 S    init

  692 root      3068 S    init

  699 root         0 SW   [flush-31:0]

  704 root         0 SW   [scsi_eh_0]

  705 root         0 SW   [usb-storage]

  736 root      3392 R    ps

[2] + Done                       ./read

[root@FriendlyARM /udisk]# ./write

 write 0 bytes foem 0

write ok! code=0

[root@FriendlyARM /udisk]#

 

注意:completion机制和等待队列的作用有什么区别??

1completion机制在本程序中是在写入数据后,告诉另一线程数据已写入,然后由另一线程从休眠中唤醒开始读数据,若是读线程没有设置阻塞(在本程序中由于线程等待放于阻塞检测前面,所以不用考虑)则返回错误。

2)等待队列机制在本程序中是在写入数据后,告诉另一线程数据已写入,然后由另一线程从休眠中唤醒开始读数据,若是读线程没有设置阻塞则返回错误。

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