| 
 | 
 
将 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 的顺序。 |   
 
 
 
 |