曲径通幽论坛

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

scull 最简可运行注释版

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2010-9-24 13:39:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
看了 LDD 的 scull 代码,发现里面的代码超出第一章的范围,现将适合第一章学习的代码抽取出来,使之可运行,并加上一些详细的注释和做了一点细微的修改。
主程序 scull.c 代码
#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_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);    /*释放每个设备属性结构体空间*/
     }

     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;

     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);
     }

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

   fail:
     scull_cleanup_module();
     return result;
}


module_init(scull_init_module);
module_exit(scull_cleanup_module);
头文件 scull.h 代码:
#define SCULL_QUANTUM    4000
#define SCULL_QSET    1000
#define SCULL_NR_DEVS    4

struct scull_qset {
     void **data;
     struct scull_qset *next;
};


struct scull_dev {
     struct scull_qset *data;  /* 第一个量子集指针 */
     int quantum;              /* 量子个数 */
     int qset;                 /* 量子指针数组大小 */
     unsigned long size;       /* 存储的数据数 */
     struct semaphore sem;     /* 互斥型信号量   */
     struct cdev cdev;      /* 字符设备结构体*/
};
Makefile 文件
obj-m := scull.o

default:
    make -C /lib/modules/`uname -r`/build M=`pwd` modules
卸载与插入模块脚本文件
#!/bin/sh

device="scull"

rmmod scull.ko

rm -f /dev/${device}[0-3]

mknod /dev/${device}0 c 188 0
mknod /dev/${device}1 c 188 1
mknod /dev/${device}2 c 188 2
mknod /dev/${device}3 c 188 3

insmod ./scull.ko $* || exit 1
测试
# sh mknode.sh
# ll > /dev/scull0
# ll > /dev/scull1
# cat /dev/scull0
总用量 120
drwxr-xr-x 3 beyes beyes  4096 2010-09-24 12:40 ./
drwxr-xr-x 4 beyes beyes  4096 2010-09-23 23:31 ../
-rw-r--r-- 1 beyes beyes    82 2010-09-18 11:55 Makefile
-rw-r--r-- 1 beyes beyes   222 2010-09-23 23:31 mknode.sh
-rw-r--r-- 1 beyes beyes    61 2010-09-23 23:30 modules.order
-rw-r--r-- 1 beyes beyes     0 2010-09-18 15:51 Module.symvers
-rw-r--r-- 1 beyes beyes  8254 2010-09-23 23:03 scull.c
-rw-r--r-- 1 beyes beyes   479 2010-09-18 23:55 scull.h
-rw-r--r-- 1 root  root   7224 2010-09-23 23:30 scull.ko
-rw-r--r-- 1 root  root    331 2010-09-23 23:30 .scull.ko.cmd
-rw-r--r-- 1 beyes beyes  1163 2010-09-18 17:12 scull.mod.c
-rw-r--r-- 1 root  root   2780 2010-09-23 23:30 scull.mod.o
-rw-r--r-- 1 root  root  23122 2010-09-23 23:30 .scull.mod.o.cmd
-rw-r--r-- 1 root  root   5083 2010-09-23 23:30 scull.o
-rw-r--r-- 1 root  root  24895 2010-09-23 23:30 .scull.o.cmd
drwxr-xr-x 2 root  root   4096 2010-09-23 23:30 .tmp_versions/
# cat /dev/scull1
总用量 120
drwxr-xr-x 3 beyes beyes  4096 2010-09-24 12:40 ./
drwxr-xr-x 4 beyes beyes  4096 2010-09-23 23:31 ../
-rw-r--r-- 1 beyes beyes    82 2010-09-18 11:55 Makefile
-rw-r--r-- 1 beyes beyes   222 2010-09-23 23:31 mknode.sh
-rw-r--r-- 1 beyes beyes    61 2010-09-23 23:30 modules.order
-rw-r--r-- 1 beyes beyes     0 2010-09-18 15:51 Module.symvers
-rw-r--r-- 1 beyes beyes  8254 2010-09-23 23:03 scull.c
-rw-r--r-- 1 beyes beyes   479 2010-09-18 23:55 scull.h
-rw-r--r-- 1 root  root   7224 2010-09-23 23:30 scull.ko
-rw-r--r-- 1 root  root    331 2010-09-23 23:30 .scull.ko.cmd
-rw-r--r-- 1 beyes beyes  1163 2010-09-18 17:12 scull.mod.c
-rw-r--r-- 1 root  root   2780 2010-09-23 23:30 scull.mod.o
-rw-r--r-- 1 root  root  23122 2010-09-23 23:30 .scull.mod.o.cmd
-rw-r--r-- 1 root  root   5083 2010-09-23 23:30 scull.o
-rw-r--r-- 1 root  root  24895 2010-09-23 23:30 .scull.o.cmd
drwxr-xr-x 2 root  root   4096 2010-09-23 23:30 .tmp_versions/
# cp scull.c  /dev/scull2
# cat /dev/scull2
#define SCULL_QUANTUM    4000
#define SCULL_QSET    1000
#define SCULL_NR_DEVS    4

struct scull_qset {
    void **data;
    struct scull_qset *next;
};


struct scull_dev {
    struct scull_qset *data;  /* 第一个量子集指针 */
    int quantum;              /* 量子个数 */
    int qset;                 /* 量子指针数组大小 */
    unsigned long size;       /* 存储的数据数 */
    struct semaphore sem;     /* 互斥型信号量   */
    struct cdev cdev;      /* 字符设备结构体*/
};
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2025-5-4 02:18 , Processed in 0.070236 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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