linux驱动开发学习笔记(二)-----------------scull字符驱动学习笔记

本文介绍scull字符驱动,一种用于内存操作的通用驱动。解释主次编号的作用及动态分配方法,并深入分析scull设备结构体及源码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                    scull字符驱动学习笔记

目录
1 scull驱动简介
2 主次编号
3 scull源码分析


-------------------------
|  1 scull驱动简介      |
-------------------------
  scull是一个字符驱动,我们操作一块内存区好像它是一个设备。scull是硬件设备关联不大, 所以它是通用可移植的。


-----------------------
|   2 主次编号        |
----------------------
2.1
  随便提提主次编号吧!
  字符设备通过文件系统中的名字来存取,也就是常说的设备文件。我们查看/dev目录下的文件,会查看到系统下所有设备文件。
  主编号标识设备相连的驱动。次编号用来决定引用哪个设备,依据你的驱动是如何编写的, 你可以从内核得到一个你的设备的直接指针。内核允许多个驱动共享主编号。
2.2
  一些主设备编号是静态分给最普通的设备的,对于新驱动,大多数情况下使用动态分配来获取你的主设备号。
  动态分配主设备号函数
  alloc_chrdev_region
  动态分配的缺点是你无法提前创建设备节点, 因为分配给你的模块的主编号会变化. 对于驱动的正常使用, 这不是问题, 因为一旦编号分配了, 你可从 /proc/devices 中读取它。为使用动态主编号来加载一个驱动, 因此, 可使用一个简单的脚本来代替调用 insmod, 在调用insmod 后, 读取 /proc/devices 来创建特殊文件.
  在scull源码中获取主编号的代码

if (scull_major) {
    dev = MKDEV(scull_major, scull_minor);
    result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
    result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
    scull_major = MAJOR(dev);
}
if (result < 0) {
    printk(KERN_WARNING "scull: can't get major %d/n", scull_major);
    return result;
}



-------------------
| 3 scull源码分析 |
-------------------

scull 设备结构体
struct scull_dev {
    struct scull_qset *data;  /* Pointer to first quantum set */
    int quantum;              /* the current quantum size */
    int qset;                 /* the current array size */
    unsigned long size;       /* amount of data stored here */
    unsigned int access_key;  /* used by sculluid and scullpriv */
    struct semaphore sem;     /* mutual exclusion semaphore     */
    struct cdev cdev;      /* Char device structure        */
};


/*
 * 内存中数据存储结构体。
 */
struct scull_qset {
    void **data;        //二维指针,data保存的是一个已分配的数据存储区的地址。
    struct scull_qset *next;   
};


一个scull设备的布局。
这里我们引入两个新的概念,量子和量子集。
scull_qset 组成一数据结构链表。
每个scull_qset使用一个1000个指针的数组(量子集),同时每一个指针指向4000字节的内存区域(量子)。




下面的代码展示如何将回收整个内存数据区的内存。

int scull_trim(struct scull_dev *dev)
{
    struct scull_qset *next, *dptr;
    int qset = dev->qset; /* "dev" is not-null */
    int i;
    for (dptr = dev->data; dptr; dptr = next)
    { /* all the list items */
    if (dptr->data) {
    for (i = 0; i < qset; i++)
    kfree(dptr->data[i]);
    kfree(dptr->data);
    dptr->data = NULL;
    }
    next = dptr->next;
    kfree(dptr);
    }
    dev->size = 0;
    dev->quantum = scull_quantum;
    dev->qset = scull_qset;
    dev->data = NULL;
    return 0;
}






初始化驱动模块注册函数。
int scull_init_module(void)
{
    int result, i;
    dev_t dev = 0;

/*
 * Get a range of minor numbers to work with, asking for a dynamic
 * major unless directed otherwise at load time.
 */
    if (scull_major) {
        dev = MKDEV(scull_major, scull_minor);
        result = register_chrdev_region(dev, scull_nr_devs, "scull");
    } else {
        /*动态获取主设备号 */
        result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
                "scull");
        /*MAJOR 宏定义获得设备的主设备号*/
        scull_major = MAJOR(dev);
    }
    if (result < 0) {
        printk(KERN_WARNING "scull: can't get major %d/n", scull_major);
        return result;
    }

        /*
     * allocate the devices -- we can't have them static, as the number
     * can be specified at load time
     */
    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));

        /* Initialize each device. */
    /***************************************************************
    scull_devices[i]对应的结构体
    struct scull_dev {
        struct scull_qset *data;  /* Pointer to first quantum set */
        int quantum;              /* the current quantum size */
        int qset;                 /* the current array size */
        unsigned long size;       /* amount of data stored here */
        unsigned int access_key;  /* used by sculluid and scullpriv */
        struct semaphore sem;     /* mutual exclusion semaphore     */
        struct cdev cdev;      /* Char device structure        */
    };
    ***************************************************************/
    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);    //创建字符设备
    }

        /* At this point call the init function for any friend device */
    dev = MKDEV(scull_major, scull_minor + scull_nr_devs);
    dev += scull_p_init(dev);
    dev += scull_access_init(dev);

#ifdef SCULL_DEBUG /* only when debugging */
    scull_create_proc();
#endif

    return 0; /* succeed */

  fail:
    scull_cleanup_module();
    return result;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值