| 
 | 
 
主代码仍然按照 http://www.groad.net/bbs/read.php?tid-2774.html 
 
代码里主要添加了老式创建 /proc 文件的方法,即使用 create_proc_read_entry 函数。 
 
代码:#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; 
 } 
  
 int scull_read_procmem(char *buf, char **start, off_t offset, 
                    int count, int *eof, void *data) 
 { 
     int i, j, len = 0; 
     int limit = count - 80; /* 不打印超出(PAGE_SIZE-80) 个字节信息 */ 
  
     for (i = 0; i < scull_nr_devs && len <= limit; i++) { 
         struct scull_dev *d = &scull_devices[i]; 
         struct scull_qset *qs = d->data; 
         if (down_interruptible(&d->sem)) 
             return -ERESTARTSYS; 
         len += sprintf(buf+len,"\nDevice %i: qset %i, q %i, sz %li\n", 
                 i, d->qset, d->quantum, d->size); 
         for (; qs && len <= limit; qs = qs->next) { /* 扫描链表 */ 
             len += sprintf(buf + len, "  item at %p, qset at %p\n",      /*sprintf()返回值是双引号里的字节数(一个数字一个字节)*/ 
                     qs, qs->data); 
             if (qs->data && !qs->next) /* 只打印最后一个scull_qset的量子指针信息 */ 
                 for (j = 0; j < d->qset; j++) { 
                     if (qs->data[j]) 
                         len += sprintf(buf + len, 
                                 "    % 4i: %8p\n", 
                                 j, qs->data[j]); 
                 } 
         } 
         up(&scull_devices[i].sem); 
     } 
     *eof = 1; 
     return len; 
 } 
  
 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; 
      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);    /*释放每个设备属性结构体空间*/ 
      } 
      remove_proc_entry ("scullmem", NULL);     
      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);    /*加载字符设备*/ 
      /* Fail gracefully if need be */ 
      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 *parent; 
  
      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); 
      } 
      parent = proc_mkdir ("myproc", NULL); 
      create_proc_read_entry ("scullmem", 0744, parent, scull_read_procmem, NULL); /*只为演示,事实上不需要加可执行权限*/     
  
      return 0; /* 初始化成功 */ 
  
    fail: 
      scull_cleanup_module(); 
      return result; 
 } 
  
  
 module_init(scull_init_module); 
 module_exit(scull_cleanup_module);  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/myproc/scullmem  
 
Device 0: qset 1000, q 4000, sz 10457 
  item at f07e8710, qset at f0622000 
       0: f0627000 
       1: f0623000 
       2: f0620000 
 
Device 1: qset 1000, q 4000, sz 17 
  item at f04f39b0, qset at f3c8b000 
       0: f3c8e000 
 
Device 2: qset 1000, q 4000, sz 146 
  item at f07e80d8, qset at f0625000 
       0: f0621000 
 
Device 3: qset 1000, q 4000, sz 72 
  item at f04f3ed0, qset at f3c8c000 
       0: f3c8f000  |   
 
 
 
 |