曲径通幽论坛

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

具有老式 /proc 文件创建方法的 scull

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2010-9-27 23:24:08 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
主代码仍然按照 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
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2025-5-3 13:28 , Processed in 0.111914 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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