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

2013年(2)

2012年(142)

2011年(154)

分类: LINUX

2011-03-24 12:50:23

6poll方法应用及其自动创建设备-模拟FIFO

 

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

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

开发板:arm9-mini2440

虚拟机为:Red Hat Enterprise Linux 5

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

 

Poll方法

什么是Poll方法,功能是什么?

系统调用(用户空间)

Open

Close

Read

Write

Ioctl

lseek

Select

驱动(内核空间)

Open

Release

Read

Write

Ioctl

llseek

Poll

 

Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用  进程。

int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set

*exceptfds, const struct  timeval *timeout)

  Maxfd:

文件描述符的范围,比待检测的最大文件描述符大1

  Readfds:

被读监控的文件描述符集

  Writefds:

被写监控的文件描述符集

  Exceptfds:

被异常监控的文件描述符集;

  Timeout:

定时器

 

Timeout取不同的值,该调用有不同的表现:

  Timeout值为0,不管是否有文件满足要求,都立刻返

回,无文件满足要求返回0,有文件满足要求返回一个正值。

  TimeoutNULLselect将阻塞 进程,直到某个文件

满足要求

  Timeout值为正整数,就是等待的最长时间,即 selecttimeout时间内阻塞 进程。

 

Select调用返回时,返回值有如下情况:

1.正常情况下返回满足要求的文件描述符个 数;

2.经过了timeout等待后仍无文件满足要求,返回值为0

3.如果select被某个信号中断,它将返回-1并设置errnoEINTR

4.如果出错,返回-1并设置相应的errno

 

Select系统调用(使用方法)

1. 将要监控的文件添加到文件描述符集

2. 调用Select开始监控

3. 判断文件是否发生变化

 

系统提供了4个宏对描述符集进行操作:

#include

void FD_SET(int fd, fd_set *fdset)

void FD_CLR(int fd, fd_set *fdset)

void FD_ZERO(fd_set *fdset)

void FD_ISSET(int fd, fd_set *fdset)

FD_SET将文件描述符fd添加到文件描述符集fdset中;

FD_CLR从文件描述符集fdset中清除文件描述符fd

FD_ZERO清空文件描述符集fdset

在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化。

FD_ZERO(&fds); //清空集合

FD_SET(fd1,&fds); //设置描述符

FD_SET(fd2,&fds); //设置描述符

maxfdp=fd1+1  //描述符最大值加1,假设fd1>fd2

switch(select(maxfdp,&fds,NULL,NULL,&timeout))

case -1: exit(-1);break; //select错误,退出程序

case 0:break;

default:

if(FD_ISSET(fd1,&fds)) //测试fd1是否可读

 

Poll方法

应用程序常常使用select系统调用,它可能会阻塞进程。这个调用由驱动的 poll方法实现,原型为:

 

unsigned int (*poll)(struct file *filp,poll_table *wait)

Poll设备方法负责完成:

1. 使用poll_wait将等待队列添加到poll_table中。

2. 返回描述设备是否可读或可写的掩码。

位掩码

POLLIN

设备可读

POLLRDNORM

数据可读

POLLOUT

设备可写

POLLWRNORM

数据可写

设备可读通常返回(POLLIN|POLLRDNORM )

设备可写通常返回(POLLOUT|POLLWRNORM )

 

范例

static unsigned int mem_poll(struct file *filp,poll_table *wait)

{

struct scull_pipe *dev =filp->private_data;

unsigned int mask =0;

/* 把进程添加到等待队列 */

poll_wait(filp,&dev->inq,wait);

/*返回掩码*/

if (有数据可读)

mask = POLLIN |POLLRDNORM;/*设备可读*/

return mask;

}

 

 

 

 

 

 

 

 

 

 

 

 

2.程序清单

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

 

poll_fs.h

 

#ifndef _GONG_H_

    #define _GONG_H_

    #include    

   

#undef PDEBUG             /* undef it, just in case */

#ifdef GONG_DEBUG

#  ifdef __KERNEL__

     /* This one if debugging is on, and kernel space */

#    define PDEBUG(fmt, args...) printk( KERN_DEBUG "gong_pipe: " fmt, ## args)

#  else

     /* This one for user space */

#    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)

#  endif

#else

#  define PDEBUG(fmt, args...) /* not debugging: nothing */

#endif

 

#undef PDEBUGG

#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */

 

 

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

    #endif

   

    #define  BUFSIZE    1024

 

    /*定义幻数*/

       

    /* Use 'k' as magic number */

    #define GONG_IOC_MAGIC  'k'

    /* Please use a different 8-bit number in your code */

   

    #define GONG_P_IOCTSIZE _IO(GONG_IOC_MAGIC,   0)

    #define GONG_P_IOCQSIZE _IO(GONG_IOC_MAGIC,   1)

    /* ... more to come */

   

    #define GONG_IOC_MAXNR 1

 

#endif

 

   

poll_fs.c

 

#include

#include

#include

 

#include    /* printk(), min() */

#include      /* kmalloc() */

#include        /* everything... */

#include

#include     /* error codes */

#include     /* size_t */

#include

#include

#include

#include

#include //自动创建

#include //TASK_INTERRUPTIBLE

 

#include "poll_fs.h"

 

int gong_poll_major = GONG_MAJOR;

int gong_poll_minor = 0;

int gong_poll_nr = GONG_NR_DEVS;/* number of pipe devices */

int gong_poll_buffer = BUFSIZE;/* buffer size */

 

 

/*module_param 字段是一个权限值; 你应当使用 中定义的值.

 这个值控制谁可以存取这些模块参数在 sysfs 中的表示.如果 perm 被设为 0,

 就根本没有 sysfs 项. 否则, 它出现在 /sys/module下面, 带有给定的权限.

 使用 S_IRUGO 作为参数可以被所有人读取, 但是不能改变; S_IRUGO|S_IWUSR

 允许 root 来改变参数. 注意, 如果一个参数被 sysfs 修改, 你的模块看到的参数值也改变了,

 但是你的模块没有任何其他的通知. 你应当不要使模块参数可写,

 除非你准备好检测这个改变并且因而作出反应.*/

module_param(gong_poll_major, int, S_IRUGO);

module_param(gong_poll_minor, int, S_IRUGO);

module_param(gong_poll_nr, int, 0);

module_param(gong_poll_buffer, int, 0);

 

struct gong_pipe {

                /*等待队列可实现进程的阻塞,等待队列可看作保存进程的容器*/

        wait_queue_head_t inq, outq;       /* read and write queues */

        char *buffer, *end;                /* begin of buf, end of buf */

        int buffersize;                    /* used in pointer arithmetic */

        char *rp, *wp;                     /* where to read, where to write */

        int nreaders, nwriters;            /* number of openings for r/w */

        struct fasync_struct *async_queue; /* asynchronous readers */

        struct semaphore sem;              /* mutual exclusion semaphore */

        struct cdev cdev;                  /* Char device structure */

       

};

 

 

  

//struct class *dev_class;

//class结构指针,用来保存即将要创建的设备结构名,用于自动在/dev下创建设备文件,再为每个设备调用device_create创建对应的设备

//struct device *device1;

//两个即将在、/dev  下创建的设备名

//自动创建设备文件

static  struct class * myclass;

static  struct device * mydev;

 

/* parameters */

dev_t gong_p_devno;         /* Our first device number */

 

static struct gong_pipe *gong_p_devices;//pointer!!!!!

 

static int gong_p_fasync(int fd, struct file *filp, int mode);

static int spacefree(struct gong_pipe *dev);

 

 

static int gong_p_open(struct inode *inode, struct file *filp)

{

    struct gong_pipe *dev;

 

    /*第一种获取方法*/

    //dev = container_of(inode->i_cdev, struct gong_pipe, cdev);

    //filp->private_data = dev;

 

    /*第二种获取方法*/

        /*获取次设备号*/

    int num = MINOR(inode->i_rdev);

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

    if(num >= gong_poll_nr)

        return -ENODEV;

    dev = &gong_p_devices[num];//

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

    filp->private_data = dev;//!!

   

 

    if (down_interruptible(&dev->sem))

        return -ERESTARTSYS;

    if (!dev->buffer) {//未初始化的buffer一定为NULL吗???

        /* allocate the buffer */

        dev->buffer = kmalloc(gong_poll_buffer, GFP_KERNEL);

        if (!dev->buffer) {

            up(&dev->sem);

            return -ENOMEM;

        }

    }

   

    dev->buffersize = gong_poll_buffer;

    dev->end = dev->buffer + dev->buffersize;//确定缓冲区结束地址

    dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning !!!!*/

 

    /* use f_mode,not  f_flags: it's cleaner (fs/open.c tells why) */

    if (filp->f_mode & FMODE_READ)

        dev->nreaders++;

    if (filp->f_mode & FMODE_WRITE)

        dev->nwriters++;

    up(&dev->sem);

 

    return nonseekable_open(inode, filp);

}

 

 

static int gong_p_release(struct inode *inode, struct file *filp)//关闭文件描述符的时候

{

    struct gong_pipe *dev = filp->private_data;

 

 

    /* remove this filp from the asynchronously notified filp's */

    /*当文件被关闭时必须调用fasync 方法,来从活动的异步读取进程列表中删除该文件。

    尽管这个调用仅当 filp->f_flags 被设置为 FASYNC 时才需要,但不管什么情况,

    调用这个函数不会有问题,并且是普遍的实现方法*/

    gong_p_fasync(-1, filp, 0);

   

    down(&dev->sem);

    if (filp->f_mode & FMODE_READ)

        dev->nreaders--;

    if (filp->f_mode & FMODE_WRITE)

        dev->nwriters--;

    if (dev->nreaders + dev->nwriters == 0) {

        kfree(dev->buffer);

        dev->buffer = NULL; /* the other fields are not checked on open */

    }

    up(&dev->sem);

    return 0;

}

 

 

static ssize_t gong_p_read (struct file *filp, char __user *buf, size_t count,

                loff_t *f_pos)

{

    struct gong_pipe *dev = filp->private_data;

 

   

    if (down_interruptible(&dev->sem))

        return -ERESTARTSYS;

 

    //考虑到多个Read操作同时等待的情况,这里不能用if代理while???

    while (dev->rp == dev->wp) { /* nothing to read */

        up(&dev->sem); /* release the lock */

        if (filp->f_flags & O_NONBLOCK)

            return -EAGAIN;

           

        /*printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid);

        存于 current->comm

        的命令名称是由当前进程执行的程序文件的基本名称( 截短到 15 个字符, 如果需要 ).

        */

        PDEBUG("\"%s\" reading: going to sleep\n", current->comm);

        if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))

            return -ERESTARTSYS; /* signal: tell the fs layer to handle it */

        /* otherwise loop, but first reacquire the lock */

        if (down_interruptible(&dev->sem))

            return -ERESTARTSYS;

    }

   

   

    /* ok, data is there, return something !!!*/

    if (dev->wp > dev->rp)

        count = min(count, (size_t)(dev->wp - dev->rp));//

    else /* the write pointer has wrapped, return data up to dev->end */

        /*the write pointer has wrapped 指的是数据写满 后wp指针会自动设定为开始的位置*/

        count = min(count, (size_t)(dev->end - dev->rp));

        printk("<5> count is %d\n", count);

       

    ///copy_to_user成功返回0  copy过去不会自动添加'\0'???

    //好像要自动添加注意把测试程序的缓冲区memset 这样原有的数据不会造成影响

    if (copy_to_user(buf, dev->rp, count)) {

        up (&dev->sem);

        return -EFAULT;

    }

    dev->rp += count;

    if (dev->rp == dev->end)/*如果rp读完了 指针设定为开始的位置*/

        dev->rp = dev->buffer; /* wrapped */

    up (&dev->sem);

 

    /* finally, awake any writers and return */

    wake_up_interruptible(&dev->outq);

    PDEBUG("\"%s\" did read %li bytes\n",current->comm, (long)count);/////

    return count;

}

 

 

 

/* How much space is free? */

/*循环队列 计算空余空间*/

static int spacefree(struct gong_pipe *dev)

{

    if (dev->rp == dev->wp)

        return dev->buffersize;

//由于gong_poll_buffer = buffersize 所以不用buffersize-1

    return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize);

}

 

/* Wait for space for writing; caller must hold device semaphore.  On

 * error the semaphore will be released before returning. */

static int gong_getwritespace(struct gong_pipe *dev, struct file *filp)

{

   

    /*手工休眠

    (1)创建和初始化一个等待队列

    (2)添加等待队列入口到队列,并设置进程状态

    (3)在检查确认仍然需要休眠之后调用 schedule

    (4)schedule 返回,就到了清理时间:

        void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);

 

    */

    /*认真地看简单休眠中的 wait_event(queue, condition) 和 wait_event_interruptible(queue, condition)

    底层源码会发现,其实他们只是手工休眠中的函数的组合。所以怕麻烦的话还是用wait_event比较好。*/

    while (spacefree(dev) == 0) { /* full */

        //创建和初始化一个等待队列。常由宏定义完成:

        /*常用的做法是放一个 DEFINE_WAIT 在循环的顶部,来实现休眠。*/

        DEFINE_WAIT(wait);

       

        up(&dev->sem);

        if (filp->f_flags & O_NONBLOCK)

            return -EAGAIN;

        PDEBUG("\"%s\" writing: going to sleep\n",current->comm);

       

       

        /*void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);

        queue 和 wait 分别地是等待队列头和进程入口。state 是进程的新状态:

        TASK_INTERRUPTIBLE(可中断休眠,推荐)或TASK_UNINTERRUPTIBLE(不可中断休眠,不推荐)。*/

 

        prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);

        if (spacefree(dev) == 0)

            schedule();

        finish_wait(&dev->outq, &wait);

       

        /*检测进程p是否有待处理的信号(p->thread_info->flags中TIF_SIGPENDING位是否置位)*/

        if (signal_pending(current))

            return -ERESTARTSYS; /* signal: tell the fs layer to handle it */

        if (down_interruptible(&dev->sem))

            return -ERESTARTSYS;

    }

    return 0;

}  

 

 

 

static ssize_t gong_p_write(struct file *filp, const char __user *buf, size_t count,

                loff_t *f_pos)

{

    struct gong_pipe *dev = filp->private_data;

    int result;

 

   

    if (down_interruptible(&dev->sem))

        return -ERESTARTSYS;

 

    /* Make sure there's space to write */

    result = gong_getwritespace(dev, filp);

    if (result)

        return result; /* gong_getwritespace called up(&dev->sem) */

 

    /* ok, space is there, accept something */

    count = min(count, (size_t)spacefree(dev));

    if (dev->wp >= dev->rp)

        count = min(count, (size_t)(dev->end - dev->wp)); /* to end-of-buf */

    else /* the write pointer has wrapped, fill up to rp-1 */

        count = min(count, (size_t)(dev->rp - dev->wp - 1));

    PDEBUG("Going to accept %li bytes to %p from %p\n", (long)count, dev->wp, buf);

    if (copy_from_user(dev->wp, buf, count)) {

        up (&dev->sem);

        return -EFAULT;

    }

    dev->wp += count;

    if (dev->wp == dev->end)

        dev->wp = dev->buffer; /* wrapped */

    up(&dev->sem);

 

    /* finally, awake any reader */

    wake_up_interruptible(&dev->inq);  /* blocked in read() and select() */

 

 

    /*当数据到达时,kill_fasync 被用来通知相关的进程,它的参数是被传递的信号(常常是 SIGIO)和 band(几乎都是 POLL_IN)。*/

    /* and signal asynchronous readers, explained late in chapter 5 */

    if (dev->async_queue)

        kill_fasync(&dev->async_queue, SIGIO, POLL_IN);

       

       

    PDEBUG("\"%s\" did write %li bytes\n",current->comm, (long)count);

    return count;

}

 

 

static unsigned int gong_p_poll(struct file *filp, poll_table *wait)

{

    struct gong_pipe *dev = filp->private_data;

    unsigned int mask = 0;

 

    /*

     * The buffer is circular; it is considered full

     * if "wp" is right behind "rp" and empty if the

     * two are equal.

     */

    down(&dev->sem);

   

    /*poll_wait函数所做的工作是把当前进程的等待队列添加到wait参数指定的等待列表(poll_table)中

    poll_wait函数并不阻塞,程序中poll_wait(filp, &outq, wait)这句话的意思并不是说一直

    等待outq信号量可获得,真正的阻塞动作是上层的select/poll函数中完成的

    对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table;

*/

   

    poll_wait(filp, &dev->inq,  wait);//

    poll_wait(filp, &dev->outq, wait);

    if (dev->rp != dev->wp)

        mask |= POLLIN | POLLRDNORM;    /* readable */

    if (spacefree(dev))

        mask |= POLLOUT | POLLWRNORM;   /* writable */

       

       

    up(&dev->sem);

    return mask;

}

 

 

 

/* remove this filp from the asynchronously notified filp's

scull_p_fasync(-1, filp, 0);*/

static int gong_p_fasync(int fd, struct file *filp, int mode)

{

    struct gong_pipe *dev = filp->private_data;

 

    /*调用fasync_helper 来从相关的进程列表中添加或去除文件。除了最后一个参数,

     其他所有参数都时被提供给 fasync 方法的相同参数并被直接传递。

     当数据到达时,kill_fasync 被用来通知相关的进程,它的参数是被传递的信号(常常是 SIGIO)

     和 band(几乎都是 POLL_IN)。*/

     /*

    fasync_helper is invoked to add or remove entries from the list of interested processes.

    我的理解是:当A、B、C三个进程同时使用FASYNC标志打开同一个设备时候,该函数会被调用三次,

    并且都被加入到设备的fasync_struct数据结构中。当发送通知消息时候,三个进程会同时收到。

    */

    return fasync_helper(fd, filp, mode, &dev->async_queue);///

}

 

 

 

 

int gong_p_ioctl(struct inode *inode, struct file *filp,

                 unsigned int cmd, unsigned long arg)

{

 

    int err = 0;

    struct gong_pipe *dev = filp->private_data;

    /*

     * extract the type and number bitfields, and don't decode

     * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()

     */

    if (_IOC_TYPE(cmd) != GONG_IOC_MAGIC) return -ENOTTY;

    if (_IOC_NR(cmd) > GONG_IOC_MAXNR) return -ENOTTY;

 

    /*

     * the direction is a bitmask, and VERIFY_WRITE catches R/W

     * transfers. `Type' is user-oriented, while

     * access_ok is kernel-oriented, so the concept of "read" and

     * "write" is reversed

     */

    if (_IOC_DIR(cmd) & _IOC_READ)

        err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

    else if (_IOC_DIR(cmd) & _IOC_WRITE)

        err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));

    if (err) return -EFAULT;

 

    switch(cmd) {

        /*

         * The following two change the buffer size for gongpipe.

         * The gongpipe device uses this same ioctl method, just to

         * write less code. Actually, it's the same driver, isn't it?

         */

      case GONG_P_IOCTSIZE:

       

       

      //?????????????????????????????????????????????  

        /*gong_poll_buffer = arg;//这里这样设置完全无用 在打开的时候就会设定缓冲区大小!!!下面为我修改的代码 先释放空间在重新分配*/

        kfree(dev->buffer);

        gong_poll_buffer = arg;

        dev->buffer = kmalloc(gong_poll_buffer, GFP_KERNEL);

        dev->buffersize = gong_poll_buffer;

        dev->end = dev->buffer + dev->buffersize;//确定缓冲区结束地址

        dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning !!!!*/

       

       

        break;

 

      case GONG_P_IOCQSIZE:

        return gong_poll_buffer;

 

      default:  /* redundant, as cmd was checked against MAXNR */

        return -ENOTTY;

    }

    return 0;

 

}

 

 

 

struct file_operations gong_pipe_fops = {

    .owner =        THIS_MODULE,

    .llseek =       no_llseek,

    .read =     gong_p_read,

    .write =        gong_p_write,

    .poll =     gong_p_poll,

    .ioctl =        gong_p_ioctl,

    .open =     gong_p_open,

    .release =  gong_p_release,

    .fasync =   gong_p_fasync,

};

 

 

/*

 * Set up a cdev entry.

 */

 

static void gong_p_setup_cdev(struct gong_pipe *dev, int index)

{

    //int err, devno = gong_p_devno + index;//

    int err;

    //测试 经过测试每次加index的值就是合成的设备号的值

    dev_t devno = MKDEV(gong_poll_major, index);

    printk("<5> gong_p_devno: %ld\n", (long)gong_p_devno);

    printk("<5> index: %ld\n", (long)index);

    printk("<5> devno: %uld\n", (unsigned long)devno);

    devno = gong_p_devno + index;

    printk("<5> gong_p_devno: %ld\n", (long)gong_p_devno);

    printk("<5> index: %ld\n", (long)index);

    printk("<5> devno: %uld\n", (unsigned long)devno);

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

    dev->cdev.owner = THIS_MODULE;

    err = cdev_add (&dev->cdev, devno, 1);//??为啥是1啊??应该是gong_poll_nr? 这里注册难道不是一起注册的啊

    //根据我的猜测这里每次循环只注册了一个所以为1

   

    if (err)

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

}

 

 

int gong_p_init(void)

{

    int result, i;

    myclass = class_create(THIS_MODULE, "my_device_driver");

/*

 * Get a range of minor numbers to work with, asking for a dynamic

 * major unless directed otherwise at load time.

 */

    if (gong_poll_major) {

        gong_p_devno = MKDEV(gong_poll_major, gong_poll_minor);

        result = register_chrdev_region(gong_p_devno, gong_poll_nr, "gong_pipe");

    } else {

        result = alloc_chrdev_region(&gong_p_devno, 0, gong_poll_nr,

                "gong_pipe");

        gong_poll_major = MAJOR(gong_p_devno);

        //根据我的猜测动态分配的设备号 次设备号从0开始

        printk("<5> %ld\n", (long)gong_p_devno);

        gong_p_devno = MKDEV(gong_poll_major, 0);      

        printk("<5> %ld\n", (long)gong_p_devno);

//测试动态分配的设备号的起始位置经常测试次设备号从0开始

 

 

}

    if (result < 0) {

        printk(KERN_WARNING "pipe: can't get major %d\n", gong_poll_major);

        return result;

    }

   

    /*设备注册:

   

    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_pipe_fops);

    /*注册字符设备*/

    //cdev_add(&cdev, MKDEV(gong_poll_major, 0), gong_poll_nr);

 

 

 

    //注意这里分配了gong_poll_nr个数目的gong_p_devices结构的空间

    //要先分配 这样才可以对其他的分配

    //这里要先分配了才能注册设备

    gong_p_devices = kmalloc(gong_poll_nr * sizeof(struct gong_pipe), GFP_KERNEL);

    if (gong_p_devices == NULL) {

        unregister_chrdev_region(gong_p_devno, gong_poll_nr);

        return 0;

    }

    memset(gong_p_devices, 0, gong_poll_nr * sizeof(struct gong_pipe));

   

    for (i = 0; i < gong_poll_nr; i++) {

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

        init_waitqueue_head(&(gong_p_devices[i].outq));

        init_MUTEX(&gong_p_devices[i].sem);

        gong_p_setup_cdev(gong_p_devices + i, i);

    }

   

   

   

#ifdef GONG_DEBUG

    create_proc_read_entry("gongpipe", 0, NULL, gong_read_p_mem, NULL);//??

#endif

 

    //自动创建设备文件

    //mydev = device_create(myclass, NULL, MKDEV(gong_poll_major, 0), "scullpipe0");

    //2.6.26以上

    mydev = device_create(myclass, NULL, MKDEV(gong_poll_major, 0), NULL,"scullpipe0");

    mydev = device_create(myclass, NULL, MKDEV(gong_poll_major, 1), NULL,"scullpipe1");

   

   

    return 0;

}

 

/*

 * This is called by cleanup_module or on failure.

 * It is required to never fail, even if nothing was initialized first

 */

void gong_p_cleanup(void)

{

    int i;

 

    #ifdef GONG_DEBUG

        remove_proc_entry("gong_pipe", NULL);//??/

    #endif

   

    /*

    //卸载模块时候也要同事把/dev/下的设备名卸载掉

   device_unregister(mydev); //卸载掉dev中对应的设备文件

   class_destroy(myclass);   //卸载设备队对应的class

    */

    //?????????????device_destroy device_unregister的区别

   

    if (!gong_p_devices)

        return; /* nothing else to release */

       

    device_destroy(myclass, gong_p_devno);

    //cdev_del(&cdev);

    for (i = 0; i < gong_poll_nr; i++) {

        cdev_del(&gong_p_devices[i].cdev);

        kfree(gong_p_devices[i].buffer);//

    }

    kfree(gong_p_devices);

    unregister_chrdev_region(gong_p_devno, gong_poll_nr);

    gong_p_devices = NULL; /* pedantic */

    class_destroy(myclass);

   

   

}

 

 

module_init(gong_p_init);

module_exit(gong_p_cleanup);

 

MODULE_LICENSE("Dual BSD/GPL");

 

注意:先前驱动程序有错 但是自动创建的设备能看见,不能打开,显示的是:

“no such device or address” ,驱动程序错误在初始化的时候添加设备的时候错误注意的

 

测试代码:

 

scull.h

#include /* needed for the _IOW etc stuff used later */

/*

 * Ioctl definitions

 */

 

/* Use 'k' as magic number */

#define GONG_IOC_MAGIC  'k'

/* Please use a different 8-bit number in your code */

 

#define GONG_P_IOCTSIZE _IO(GONG_IOC_MAGIC,   0)

#define GONG_P_IOCQSIZE _IO(GONG_IOC_MAGIC,   1)

 

pipe_test.c

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "scull.h"

 

int main()

{

         char buffer1[20]={0};

         int pipetest0, pipetest1;

         int code=21, i=0;

        struct pollfd poll_list[2];

       int retval;

 

 

        

         if ((pipetest0 = open("/dev/scullpipe0",O_RDONLY)) < 0)          {

                    printf("open scullpipe0 error! \n");

                   exit(1);

         }

          printf("open scullpipe0 ! \n");

 

         if ((pipetest1 = open("/dev/scullpipe1",O_RDONLY)) < 0)          {

                    printf("open scullpipe1 error! \n");

                   exit(1);

         }

          printf("open scullpipe1 ! \n");

 

         if ( ioctl(pipetest0 ,  GONG_P_IOCTSIZE , code ) < 0)    {

                    printf("pipetest0 ioctl  GONG_P_IOCTSIZE error! \n");

                   exit(1);

         }

 

         printf(" GONG_P_IOCTSIZE : scull_p_buffer0=%d !\n" ,ioctl( pipetest0 , GONG_P_IOCQSIZE , NULL ) );

 

         if ( ioctl(pipetest1 ,  GONG_P_IOCTSIZE , code ) < 0)    {

                    printf("pipetest1 ioctl  GONG_P_IOCTSIZE error! \n");

                   exit(1);

         }

 

         printf(" GONG_P_IOCTSIZE : scull_p_buffer1=%d !\n" ,ioctl( pipetest1 , GONG_P_IOCQSIZE , NULL ) );

 

         close(pipetest0);

         printf("close pipetest0 ! \n");

         close(pipetest1);

         printf("close pipetest1 ! \n");

         if ((pipetest0 = open("/dev/scullpipe0",O_RDONLY)) < 0)          {

                    printf("reopen scullpipe0 error! \n");

                   exit(1);

         }

          printf("reopen scullpipe0 ! \n");

 

         if ((pipetest1 = open("/dev/scullpipe1",O_RDONLY)) < 0)          {

                    printf("reopen scullpipe1 error! \n");

                   exit(1);

         }

          printf("reopen scullpipe1 ! \n");

 

 

       poll_list[0].fd = pipetest0;

       poll_list[1].fd = pipetest1;

       poll_list[0].events = POLLIN|POLLRDNORM;

       poll_list[1].events = POLLIN|POLLRDNORM;

 

       while(1)

       {

           retval = poll(poll_list,(unsigned long)2,-1);

           /* retval 总是大于0或为-1,因为我们在阻塞中工作 */

           if(retval < 0)

                            {

                                       fprintf(stderr,"poll错误: %s\n",strerror(errno));

                                       return -1;

                            }

   

          if(poll_list[0].revents&(POLLIN|POLLRDNORM))

                            {

                                     for(i=0;i<20;i+=5)

                                                        buffer1[i] = 80;

                                     if ((code=read(pipetest0 , buffer1 , 20)) != 20) printf("read from pipetest0 error! code=%d\n",code);

                                     else   printf("read from pipetest0 ok! code=%d \n",code);

                                                                          

                                     for(i=0;i<20;i+=5)

                                               printf("[%d]=%d [%d]=%d [%d]=%d [%d]=%d [%d]=%d\n",i,buffer1[i],i+1,buffer1[i+1],i+2,buffer1[i+2],i+3,buffer1[i+3],i+4,buffer1[i+4]);

                                                                          

                                     }

 

           if(poll_list[1].revents&(POLLIN|POLLRDNORM))

                                     {

                                               if ((code=read(pipetest1 , buffer1 , 20)) != 20) printf("read from pipetest1 error! code=%d \n",code);

                                               else   printf("read from pipetest1 ok! code=%d \n",code);

                                                                          

                                               for(i=0;i<20;i+=5)

                                                        printf("[%d]=%c [%d]=%c [%d]=%c [%d]=%c [%d]=%c\n",i,buffer1[i],i+1,buffer1[i+1],i+2,buffer1[i+2],i+3,buffer1[i+3],i+4,buffer1[i+4]);

                                    

                                               }

 

       }

 

         close(pipetest0 );

         printf("close pipetest0  ! \n");

         close(pipetest1 );

         printf("close pipetest1 ! \n");

 

         printf("\n");

  exit(0);

 

}

 

Arm平台pipe管道测试:

 

[root@FriendlyARM /udisk]# insmod poll_fs.ko

 265289728

 265289728

 gong_p_devno: 265289728

 index: 0

 devno: 265289728ld//测试设备号怎么递增

 gong_p_devno: 265289728

 index: 0

 devno: 265289728ld

 gong_p_devno: 265289728

 index: 1

 devno: 265289729ld

 gong_p_devno: 265289728

 index: 1

 devno: 265289729ld

 gong_p_devno: 265289728

 index: 2

 devno: 265289730ld

 gong_p_devno: 265289728

 index: 2

 devno: 265289730ld

 

[root@FriendlyARM /udisk]#

[root@FriendlyARM /udisk]# ls -l /dev/scullpipe1 自动创建成功

crw-rw----    1 root     root     253,   1 Mar 22 09:03 /dev/scullpipe1

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

[root@FriendlyARM /udisk]# open scullpipe0 !

open scullpipe1 !

 GONG_P_IOCTSIZE : scull_p_buffer0=21 !

 GONG_P_IOCTSIZE : scull_p_buffer1=21 !

close pipetest0 !

close pipetest1 !

reopen scullpipe0 !

reopen scullpipe1 !

[root@FriendlyARM /udisk]# echo 2232132 > /dev/scullpipe0

 count is 8

read from pipetest0 error! code=8

[0]=50 [1]=50 [2]=51 [3]=50 [4]=49

[5]=51 [6]=50 [7]=10 [8]=0 [9]=0

[10]=80 [11]=0 [12]=0 [13]=0 [14]=0

[15]=80 [16]=0 [17]=0 [18]=0 [19]=0

//经过测试 会自动添加’\0’ 在缓冲区的末尾

[root@FriendlyARM /udisk]# echo 2232132 > /dev/scullpipe1

[root@FriendlyARM /udisk]#  count is 8

read from pipetest1 error! code=8

[0]=2 [1]=2 [2]=3 [3]=2 [4]=1

[5]=3 [6]=2 [7]=

 [8]=  [9]=

[10]=  [11]=  [12]=  [13]=  [14]=

[15]=  [16]=  [17]=  [18]=  [19]=

 

read_write_test.c

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "scull.h"

 

int main()

{

         int fd;

         char string[80];

         char in;

         int data;

        

        

                   if ((fd = open("/dev/scullpipe0",O_RDWR)) < 0)        {

                    printf("open scullpipe0 error! \n");

                   exit(1);

         }

        

         while(1)

         {

                            in = getchar();

                            printf("in is %c \n", in);

                            switch(in)

                            {

                                     case '1':    

                                               printf("input your string:");

                                               in = getchar();

                                               gets(string);

                                               printf("string is %s \n", string);

                                               if(write(fd, string, strlen(string)) < 0)

                                                        {

                                                                 printf("write error \n");

                                                                 exit(1);

                                                        }

                                     break;

                                     case '2':

                                               printf("input your read number:");

                                               scanf("%d", &data);

                                               in = getchar();

                                               printf("data is %d\n", data);

                                              

                                              memset(string, -1, sizeof(string));///!!!!!!测试追加'\0'

                                               if(read(fd, string, data) < 0)

                                               {

                                                        printf("read error \n");

                                                        exit(1);

                                               }

                                               else

                                               {

                                                        //string[data] = '\0';

                                                        printf("string is %s \n", string);

                                              }

                                              

                                     break;

                                     case '3':

                                               printf("input your buffer size:");

                                               scanf("%d", &data);

                                               in = getchar();

                                               if ( ioctl(fd ,  GONG_P_IOCTSIZE , data ) < 0)       {

                                             printf("pipetest0 ioctl  GONG_P_IOCTSIZE error! \n");

                                               exit(1);

                                               }

                  

                                               printf(" GONG_P_IOCTSIZE : scull_p_buffer0=%d !\n" ,ioctl( fd , GONG_P_IOCQSIZE , NULL ) );

                                     break;

                            }

                  

                  

         }

        

        

         return 0;

}

 

Arm平台读写测试:

 

[root@FriendlyARM /udisk]# ./read_write_test

1

in is 1

input your string:gongzhi

string is gongzhi

2

in is 2

input your read number:5

data is 5

 count is 5

string is

 [0] = 103

 [1] = 111

 [2] = 110

 [3] = 103

 [4] = 122

 [5] = 80

 [6] = 80

 [7] = 80

 [8] = 80

 [9] = 80

 [10] = 80

 [11] = 80

 [12] = 80

 [13] = 80

 [14] = 80

 [15] = 80

 [16] = 80

 [17] = 80

 [18] = 80

 [19] = 80

//经过这里测试追加’\0’ 得出不追加 与前面矛盾??

1

in is 1

input your string:gongzhi

string is gongzhi

2

in is 2

input your read number:4

data is 4

 count is 4

string is

 [0] = 104

 [1] = 105

 [2] = 103

 [3] = 111

 [4] = 80

 [5] = 80

 [6] = 80

 [7] = 80

 [8] = 80

 [9] = 80

 [10] = 80

 [11] = 80

 [12] = 80

 [13] = 80

 [14] = 80

 [15] = 80

 [16] = 80

 [17] = 80

 [18] = 80

 [19] = 80

//先输入“gongzhi“读出5个字符后管道里剩“hi”,再输入“gongzhi“,从中读取四个 读出的是”higo“,说明读出之前里面的数据为”higongzhi 符合先进先出的fifo规则

 

//重新设置fifo大小为5个字节 并清空了数据

3

in is 3

input your buffer size:5

 GONG_P_IOCTSIZE : scull_p_buffer0=5 !

2

in is 2

input your read number:5

data is 5

//管道里没有数据 阻塞

 

asynctest.c

 

#include

#include

#include

#include

#include

#include

 

int gotdata=0;

void sighandler(int signo)

{

    if (signo==SIGIO)

        gotdata++;

    return;

}

 

char buffer[21];

 

int main(int argc, char **argv)

{

    int pipetest0;

    int count;

    struct sigaction action;

 

    if ((pipetest0 = open("/dev/scullpipe0",O_RDONLY)) < 0) {

         printf("open scullpipe0 error! \n");

        exit(1);

    }

 

    memset(&action, 0, sizeof(action));

    action.sa_handler = sighandler;

    action.sa_flags = 0;

 

    sigaction(SIGIO, &action, NULL);

 

 

        /*异步通知

 

        通过使用异步通知,应用程序可以在数据可用时收到一个信号,而无需不停地轮询。

        启用步骤:

    (1)它们指定一个进程作为文件的拥有者:使用 fcntl 系统调用发出 F_SETOWN 命令,这个拥有者进程的 ID 被保存在 filp->f_owner。目的:让内核知道信号到达时该通知哪个进程。

    (2)使用 fcntl 系统调用,通过 F_SETFL 命令设置 FASYNC 标志。

        */

    fcntl(pipetest0, F_SETOWN, getpid());//F_SETOWN设置异步IO所所有权

   

   

    /*fcntl(pipetest0, F_GETFL) 获得其文件状态标志 然后与FASYNC或运算,最后设置*/

   fcntl(pipetest0, F_SETFL, fcntl(pipetest0, F_GETFL) | FASYNC);//F_SETFL设置文件状态标志

 

    while(1) {

        /* this only returns if a signal arrives */

        sleep(86400); /* one day */

        if (!gotdata)

            continue;

        count=read(pipetest0, buffer, 21);

        /* buggy: if avail data is more than 4kbytes... */

        write(1,buffer,count);

        gotdata=0;

    break;

    }

 

    close(pipetest0 );

    printf("close pipetest0  ! \n");

 

    printf("exit !\n");

  exit(0);

}

 

Arm平台异步测试:

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

[root@FriendlyARM /udisk]# echo 23231 > /dev/scullpipe0

 count : 6

 result : 0

 count : 4

 count : 4

 count : 4

 count is 4

 count : 2

 result : 0

 count : 2

 count : 1

 count : 1

 count : 1

 result : 0

 count : 1

 count : 1

 count : 1

2323close pipetest0  !

exit !

[1] + Done                       ./asynctest

[root@FriendlyARM /udisk]#

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