Linux内核:seek机制

在我们编写CC1100驱动程序和倒车雷达驱动程序的时候,我们都用到了当前读取数据标志位,即我们在驱动程序中会定义一个容器用来存放数据,每一次我们读完一个数据时,我们都会将我们的读数据变量加1,这样下一次再来读取数据的时候就不会读到以前的数据了,都会是全新的数据。当然这个方法是我们自己在编写驱动程序时候自己定义的。是否在Linux内核中本来就有这样的机制呢?答案是肯定的。这个机制就是seek机制,那么seek机制又是如何实现记录读取数据位置的机智的呢?同poll机制一样,在file->fop里面也定义有seek的函数指针,那就是llseek,这个函数指针当然是指向我们自己编写的驱动程序的函数了。其实seek实现的机制比较简单,主要就是围绕当前文件指针file->f_ops这个指针来的,这个指针存放的是当前驱动程序文件的读写数据位置,我们在哪里用到了f_ops这个指针呢?首先我们来看一下我们比较熟悉的read函数和open函数,我们知道在这两个函数中我们所要用到的参数都是一样的CC1100_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)和static ssize_t CC1100_write(struct file *filp, const char *buff, size_t count, loff_t *offp)其里面的参数都是一样的,第一个参数是file(该设备文件)、第二个参数是用户层传过来的buff指针,第三个是用户层传过来的写入或者读取长度大小(size_t),那么第四个参数是什么呢,好像我们之前都没有用过,这第四个参数保存的数据就是我们上面讲的file->f_ops,用来记录我们驱动程序文件的读取数据的位置。通常如果我们不用seek机制的话,这个参数也一般都用不到,其值永远都是0。所以从上面我们也可以推出,要想使用read或者是write的第四个参数的话,我们必须使用 seek函数来改变f_ops指针的指向seek的机制就是用来改变文件指针的值的。通常我们使用seek机制都会遵循一定的模板,首先我们要在驱动程序中编写我们自己的seek函数,大致模板为:

在驱动程序中首先要定义自己的seek函数,该函数的目的是定义f_ops的起始偏移的位置,当然如果你不定义这个函数,直接使用read和write的f_ops也是没有问题的,只不过入股使用到seek机制都会定义这个函数,因为这样驱动程序就增加了一项功能,即可以选择不同的起始偏移位置。通常该函数的模式都是一样的:

loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
        struct scull_dev *dev = filp->private_data;
        loff_t newpos;

        switch(whence)
        {
        case 0: /* SEEK_SET */  //从起始位置进行偏移
                newpos = off;
                break;

        case 1: /* SEEK_CUR */  //从当前位置进行偏移。
                newpos = filp->f_pos + off;
                break;

        case 2: /* SEEK_END */  //以数据容器尾部作为偏移
                newpos = dev->size + off;
                break;

        default: /* can't happen */
                return -EINVAL;
        }
        if (newpos < 0)
                return -EINVAL;
        filp->f_pos = newpos;  //这句代码就是关键,改变驱动文件的f_pos的指向。
        return newpos;   //返回当前指针
} 


同时也必须在file_operations里面指定file_operations->llseek指针的指向scull_llseek函数,方式类似open等函数。
static struct file_operations _fops = 
{
       .............
       .llseek = scull_llseek
       ..............
}


编写能够与llseek配套的write函数,因为seek机制必须要和read、write等函数配套使用才能发挥其效力。

static ssize_t globalmem_write(struct file *filp, const char __user *buf,
   size_t size, loff_t *ppos)
 {
   unsigned long p = *ppos;
   unsigned int count = size;
   int ret = 0;
 
   struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
 
   /*分析和获取有效的写长度*/
   if (p >= GLOBALMEM_SIZE)
     return count ?  - ENXIO: 0;
   if (count > GLOBALMEM_SIZE - p) //当count+p> GLOBALMEM_SIZE则表示如果按照当前的读取数据大小	//和文件指针值读取数据的话,将会超过数据的最大值
     count = GLOBALMEM_SIZE - p;
 
   if (down_interruptible(&dev->sem))//获得信号量
   {
     return  - ERESTARTSYS;
   }
   /*用户空间->内核空间*/
 
   //数据复制的位置将从dev->mem + p开始   
   if (copy_from_user(dev->mem + p, buf, count))
     ret =  - EFAULT;
   else
   {
      *ppos +=count;
     ret = count;
 
     printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);
   }
   up(&dev->sem); //释放信号量
   return ret;
 }

编写能够与llseek配套的read函数

 static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,
   loff_t *ppos)
  {
    unsigned long p =  *ppos;
    unsigned int count = size;
    int ret = 0;
    struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
  
    /*分析和获取有效的写长度*/
    if (p >= GLOBALMEM_SIZE)
      return count ?  - ENXIO: 0;
    if (count > GLOBALMEM_SIZE - p)
      count = GLOBALMEM_SIZE - p;
  
    if (down_interruptible(&dev->sem))
    {
      return  - ERESTARTSYS;
    }
  
    /*内核空间->用户空间*/
    if (copy_to_user(buf, (void*)(dev->mem + p), count))
    {
      ret =  - EFAULT;
    }
    else
    {
      *ppos += count;
      ret = count;
  
      printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
    }
   up(&dev->sem); //释放信号量
 
   return ret;
 }




你可能感兴趣的:(linux驱动,linux内核)