曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 4729|回复: 0
打印 上一主题 下一主题

具有 seq_file 接口的 scull

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2010-9-30 00:37:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
http://www.groad.net/bbs/read.php?tid-2779.html 里的 create_proc_read_entry() 创建 /proc 文件的方法去掉,只添加 seq_file 接口方法。

代码
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h>    /* printk() */
#include <linux/slab.h>        /* kmalloc() */
#include <linux/fs.h>        /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>    /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>

#include <asm/system.h>        /* cli(), *_flags */
#include <asm/uaccess.h>    /* copy_*_user */

#include "scull.h"        /* local definitions */

MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");

struct scull_dev *scull_devices;   

int scull_major = 188;    /*主设备号*/
int scull_minor = 0;
int scull_nr_devs = SCULL_NR_DEVS;    /*scull0--scull3*/
int scull_quantum = SCULL_QUANTUM;    /*一个4000量子有4000字节(数组)*/
int scull_qset =    SCULL_QSET;        /*一个scull_qset中有1000个量子数组的指针*/

int scull_trim(struct scull_dev *dev)
{
      struct scull_qset *next, *dptr;
      int qset = dev->qset;   /* "dev" 非空 */
      int i;

      for (dptr = dev->data; dptr; dptr = next) { /* 遍历所有链表项 */
          if (dptr->data) {
              for (i = 0; i < qset; i++)
                  kfree(dptr->data[i]);    /*data[]中的元素是量子(含有4000个整型量)指针*/
              kfree(dptr->data); /*释放掉量子指针空间*/
              dptr->data = NULL;
          }
          next = dptr->next;    /* 找下一个 scull_qset */
          kfree(dptr);    /*释放掉前一个 scull_qset 空间*/
      }
      dev->size = 0;
      dev->quantum = scull_quantum;
      dev->qset = scull_qset;
      dev->data = NULL;
      return 0;
}

static void *scull_seq_start(struct seq_file *s, loff_t *pos)
{
     if (*pos >= scull_nr_devs)   
         return NULL;   
     return scull_devices + *pos;    /*到下一个设备(scull0--scull3),scull_devices指针在scull_init()里分配*/
}

static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
     (*pos)++;    /*第一轮迭代时*pos(位置)为0,迭代一次*pos+1*/
     if (*pos >= scull_nr_devs)
         return NULL;
     return scull_devices + *pos;
}

static void scull_seq_stop(struct seq_file *s, void *v)
{
     /* 本例不需要清除工作,故stop方法为空 */
}

static int scull_seq_show(struct seq_file *s, void *v)
{
     struct scull_dev *dev = (struct scull_dev *) v;
     struct scull_qset *d;
     int i;

     if (down_interruptible(&dev->sem))
         return -ERESTARTSYS;
     seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n",
             (int) (dev - scull_devices), dev->qset,
             dev->quantum, dev->size);
     for (d = dev->data; d; d = d->next) { /* scan the list */
         seq_printf(s, "  item at %p, qset at %p\n", d, d->data);
         if (d->data && !d->next) /* dump only the last item */
             for (i = 0; i < dev->qset; i++) {
                 if (d->data[i])
                     seq_printf(s, "    % 4i: %8p\n",
                             i, d->data[i]);
             }
     }
     up(&dev->sem);
     return 0;
}
     
/*
* 4个迭代器操作方法
*/
static struct seq_operations scull_seq_ops = {
     .start = scull_seq_start,
     .next  = scull_seq_next,
     .stop  = scull_seq_stop,
     .show  = scull_seq_show
};

/*
* 如当读取/proc/scullseq 时,此函数会被调用,然后4个迭代器操作方法会得到
* 执行.假如在该函数中不使用seq_open(),那么在访问/proc/scullseq时会提示
* "已杀死"(读取进程无法访问)
*/

static int scull_proc_open(struct inode *inode, struct file *file)
{
     return seq_open(file, &scull_seq_ops);
}

/*
* 操作 /proc 文件函数
*/
static struct file_operations scull_proc_ops = {
     .owner   = THIS_MODULE,
     .open    = scull_proc_open,    /*open方法需要自定义*/
     .read    = seq_read,
     .llseek  = seq_lseek,
     .release = seq_release
};

int scull_open(struct inode *inode, struct file *filp)
{
      struct scull_dev *dev;

      dev = container_of(inode->i_cdev, struct scull_dev, cdev); /*获得设备属性结构体的指针*/
      filp->private_data = dev; /* 以后会用到这个指针 */

      /* 如果以只写的方法打开设备,那么将这个内存设备的内存长度截断为0 */
      if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
          if (down_interruptible(&dev->sem))  
              return -ERESTARTSYS;
          scull_trim(dev);
          up(&dev->sem);
      }
      return 0;         
}

int scull_release(struct inode *inode, struct file *filp)
{
      return 0;
}
/*读和写都需要用到*/
struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
      struct scull_qset *qs = dev->data;

      if (! qs) {
          qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
          if (qs == NULL)
              return NULL;  /* Never mind */
          memset(qs, 0, sizeof(struct scull_qset));
      }

      /* 沿链表前行 */
      while (n--) {
          if (!qs->next) {
              qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
              if (qs->next == NULL)
                  return NULL;  /* Never mind */
              memset(qs->next, 0, sizeof(struct scull_qset));
          }
          qs = qs->next;
          continue;
      }
      return qs;
}

ssize_t scull_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
      struct scull_dev *dev = filp->private_data;
      struct scull_qset *dptr;
     
      int quantum = dev->quantum;
      int qset = dev->qset;
      int itemsize = quantum * qset;    /*一个scull_qset可存储的总数据量*/
      int item, s_pos, q_pos, rest;
      ssize_t retval = 0;

      if (down_interruptible (&dev->sem))  /*不允许多进程同时读*/
          return (-ERESTARTSYS);
      if (*f_pos >= dev->size)    /*已经没有什么东西可读,返回0*/
          goto out;
      if (*f_pos + count > dev->size)    /*把剩下的数据返回给用户,尽管要求的count大于这个数*/
          count = dev->size - *f_pos;
     

      item = (long)*f_pos / itemsize;        /* 经过的scull_qset数 */
      rest = (long)*f_pos % itemsize;        
      s_pos = rest / quantum;             /* 在scull_qset中经过的量子数 */
      q_pos = rest % quantum;            /* 在一个量子中的位置 */

      dptr = scull_follow (dev, item);

      /*无数据读*/
      if (dptr == NULL || !dptr->data || !dptr->data[s_pos])
          goto out;
     
      /*读取该量子的数据直到结尾*/
      if (count > quantum - q_pos)
          count = quantum - q_pos;

      /*将数据返回给用户态*/
      if (copy_to_user (buf, dptr->data[s_pos] + q_pos, count)) {
          retval = -EFAULT;
          goto out;
      }
      *f_pos += count;  /* 更新文件读位置 */   
      retval = count;      /* 返回读到的字节数 */

out:
      up (&dev->sem);      /*释放信号量*/
      return (retval);
}   

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                  loff_t *f_pos)
{
      struct scull_dev *dev = filp->private_data;
      struct scull_qset *dptr;
      int quantum = dev->quantum, qset = dev->qset;
      int itemsize = quantum * qset;
      int item, s_pos, q_pos, rest;
      ssize_t retval = -ENOMEM; /* 在"goto out" 中使用 */

      if (down_interruptible(&dev->sem))    /*不允许多进程同时写*/
          return -ERESTARTSYS;

      /* 在量子集中寻找链表项, qset 索引以及偏移量 */
      item = (long)*f_pos / itemsize;      /* 经过的scull_qset数 */
      rest = (long)*f_pos % itemsize;      
      s_pos = rest / quantum;       /* 在scull_qset中经过的量子数 */
      q_pos = rest % quantum;          /* 在一个量子中的位置 */

      /* 沿链表前行,直到正确的位置 */
      dptr = scull_follow(dev, item);
      if (dptr == NULL)
          goto out;
      if (!dptr->data) { /*分配到了一个scull_qset结构(前面的scull_qset刚好都用完所以需要再分配一个)*/
          dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);  /*分配指针数组*/
          if (!dptr->data)
              goto out;    /*分配失败*/
          memset(dptr->data, 0, qset * sizeof(char *));    /*分配成功并初始化*/
      }
      if (!dptr->data[s_pos]) {   
          dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);  /*分配量子集空间*/
          if (!dptr->data[s_pos])
              goto out;
      }
      /* 将数据写入该量子集,直到结尾,尽管用户态传来要写大于该量子剩余空间的参数count */
      if (count > quantum - q_pos)
          count = quantum - q_pos;

      if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) { /*用户态数据写入*/
          retval = -EFAULT;
          goto out;
      }
      *f_pos += count;    /*调整文件位置*/
      retval = count;        /*返回写成功的字节数*/

          /* 更新存储的数据数 */
      if (dev->size < *f_pos)
          dev->size = *f_pos;

    out:
      up(&dev->sem);
      return retval;
}


/*系统调用和驱动程序之间的桥梁*/
struct file_operations scull_fops = {
      .owner =    THIS_MODULE,
      .read =     scull_read,
      .write =    scull_write,
      .open =     scull_open,
      .release =  scull_release,
};

void scull_cleanup_module(void)
{
      int i;
      remove_proc_entry("scullseq", NULL); /*移除/proc文件*/        
      dev_t devno = MKDEV(scull_major, scull_minor);
      if (scull_devices) {
          for (i = 0; i < scull_nr_devs; i++) {
              scull_trim(scull_devices + i);    /*退出时对已分配内存空间回收*/
              cdev_del(&scull_devices[i].cdev);    /*卸载字符设备*/
          }   
          kfree(scull_devices);    /*释放每个设备属性结构体空间*/
      }
      unregister_chrdev_region(devno, scull_nr_devs);    /*释放设备号*/
}

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
      int err, devno = MKDEV(scull_major, scull_minor + index);
     
      cdev_init(&dev->cdev, &scull_fops);    /*字符设备初始化函数*/
      dev->cdev.owner = THIS_MODULE;
      dev->cdev.ops = &scull_fops;
      err = cdev_add (&dev->cdev, devno, 1);    /*加载字符设备*/
      if (err)
          printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}

int scull_init_module(void)
{
      int result, i;
      dev_t dev = 0;
      struct proc_dir_entry *entry;   

      dev = MKDEV(scull_major, scull_minor);    /*根据已知的主设备号和次设备号生成dev_t类型设备号*/
      result = register_chrdev_region(dev, scull_nr_devs, "scull");    /*注册字符设备*/
      if (result < 0) {
          printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
          return result;
      }

          /*  每个设备都有一个描述其属性的结构体 */
      scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
      if (!scull_devices) {
          result = -ENOMEM;
          goto fail;  /* Make this more graceful */
      }
      memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));

          /* 对每个设备进行初始化 */
      for (i = 0; i < scull_nr_devs; i++) {
          scull_devices[i].quantum = scull_quantum;
          scull_devices[i].qset = scull_qset;
          init_MUTEX(&scull_devices[i].sem);    /*在初始化每个设备时要先给它加个互斥型信号量*/
          scull_setup_cdev(&scull_devices[i], i);
      }
     
      entry = create_proc_entry("scullseq", 0, NULL);    /*使用更底层的create_proc_entry()创建/proc文件*/
      if (entry)
     entry->proc_fops = &scull_proc_ops;   
      

      return 0; /* 初始化成功 */

    fail:
      scull_cleanup_module();
      return result;
}
Makefile 文件,scull.h 文件,mknode.sh 脚本同 http://www.groad.net/bbs/read.php?tid-2774.html
测试输出
# cp scull.c  /dev/scull0
# ls ../ > /dev/scull1
# ls / > /dev/scull2
# ls /var > /dev/scull3
# cat /proc/scullseq

Device 0: qset 1000, q 4000, sz 11584
  item at f0317e00, qset at f190d000
       0: f190b000
       1: f1908000
       2: f190f000

Device 1: qset 1000, q 4000, sz 27
  item at f6e8ea00, qset at f29c0000
       0: f29c1000

Device 2: qset 1000, q 4000, sz 146
  item at f0317cd0, qset at f6ccb000
       0: f3551000

Device 3: qset 1000, q 4000, sz 72
  item at f0317df0, qset at f2ff2000
       0: f38cc000
建立 seq_file 接口一般顺序小结:
1. 可在初始化函数里使用 create_proc_entry() 建立相关的 /proc 文件。

2. 填充一个 seq_operations 的结构体,如上面的:
static struct seq_operations scull_seq_ops = {
     .start = scull_seq_start,
     .next  = scull_seq_next,
     .stop  = scull_seq_stop,
     .show  = scull_seq_show
};
3. 再建立一个 file_operations 结构,并在这个结构中自定义一个 open 方法,以能执行其中的 4 个迭代器函数,如:
static int scull_proc_open(struct inode *inode, struct file *file)
{
     return seq_open(file, &scull_seq_ops);
}
/*
* 操作 /proc 文件函数
*/
static struct file_operations scull_proc_ops = {
     .owner   = THIS_MODULE,
     .open    = scull_proc_open,    /*open方法需要自定义*/
     .read    = seq_read,
     .llseek  = seq_lseek,
     .release = seq_release
};
这样,如当用 cat 命令来读取 /proc 文件时,就实现了 open ---> read 的顺序。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-3 12:39 , Processed in 0.077185 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表