scull字符设备源代码完全解析

基础篇

首先是模块装载到内核时调用的初始化部分:int scull_init_module(void);

首先对于字符设备的访问时通过/dev下的设备名称进行的,在Linux操作系统的/dev下利用ls -l命令可以查看文件的详细信息,开头字母为c的即为字符设备文件,在日期前的两个数字就是相应设备的主设备号和次设备号。在本函数中如果scull_major不为0,则利用MKDEV(scull_major, scull_minor)函数获得设备号的dev_t类型,利用register_chrdev_region(dev, scull_nr_devs, "scull")函数分配设备编号,参数scull_nr_devs为申请设备编号的个数。如果scull_major为0,则利用alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
    "scull")函数动态分配设备编号,利用MAJOR宏获得主设备编号。接下来632-635行是动态分配设备号失败情况的处理。

在讲解下面的函数之前要了解一些驱动程序中重要的数据结构:

主要是file_operations结构,里面主要存放一些设备方法的函数指针。

file结构,系统为每个打开的文件在内核中对应一个file结构,其中一个重要的成员就是一个file_operation结构的指针。

inode结构,需要注意的是对于单个文件也许有多个file结构,但只会有一个inode结构。

在scull中,利用scull_dev结构表示每个设备,其定义如下:

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  */
};

好了,接着回到代码

程序的641行scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL)就是为了设备申请空间,大小为设备编号数目和表示每个设备的scull_dev结构大小。642行到644行是申请失败时的处理。下面memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev))函数来清空申请的内存。

接下来的初始化可以帮助我们理解scull内存使用的结构

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

可以通过下面这张图来理解scull中的内存使用结构:

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

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

我们一共申请了4个设备编号,要对四个设备的内存进行初始化,所以最外层的for循环很好理解

quantum表示量子的大小

qset表示数组的大小,即图中指向每个量子的指针数组的大小。

下面是信号互斥量的定义,以后再说

之后也是结构体的一些简单填充,在这里只是简单的赋值,只要熟悉即可,在下篇中的read函数中会详细讲到此结构体。

下面的部分函数可以暂时不看,今天只是刚刚接触到Linux驱动程序的开始,就先写这么多,还不太习惯,以后会常常更新,争取和大家共同进步。

 

 


基础篇

首先是模块装载到内核时调用的初始化部分:int scull_init_module(void);

首先对于字符设备的访问时通过/dev下的设备名称进行的,在Linux操作系统的/dev下利用ls -l命令可以查看文件的详细信息,开头字母为c的即为字符设备文件,在日期前的两个数字就是相应设备的主设备号和次设备号。在本函数中如果scull_major不为0,则利用MKDEV(scull_major, scull_minor)函数获得设备号的dev_t类型,利用register_chrdev_region(dev, scull_nr_devs, "scull")函数分配设备编号,参数scull_nr_devs为申请设备编号的个数。如果scull_major为0,则利用alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
    "scull")函数动态分配设备编号,利用MAJOR宏获得主设备编号。接下来632-635行是动态分配设备号失败情况的处理。

在讲解下面的函数之前要了解一些驱动程序中重要的数据结构:

主要是file_operations结构,里面主要存放一些设备方法的函数指针。

file结构,系统为每个打开的文件在内核中对应一个file结构,其中一个重要的成员就是一个file_operation结构的指针。

inode结构,需要注意的是对于单个文件也许有多个file结构,但只会有一个inode结构。

在scull中,利用scull_dev结构表示每个设备,其定义如下:

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  */
};

好了,接着回到代码

程序的641行scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL)就是为了设备申请空间,大小为设备编号数目和表示每个设备的scull_dev结构大小。642行到644行是申请失败时的处理。下面memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev))函数来清空申请的内存。

接下来的初始化可以帮助我们理解scull内存使用的结构

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

可以通过下面这张图来理解scull中的内存使用结构:

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

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

我们一共申请了4个设备编号,要对四个设备的内存进行初始化,所以最外层的for循环很好理解

quantum表示量子的大小

qset表示数组的大小,即图中指向每个量子的指针数组的大小。

下面是信号互斥量的定义,以后再说

之后也是结构体的一些简单填充,在这里只是简单的赋值,只要熟悉即可,在下篇中的read函数中会详细讲到此结构体。

下面的部分函数可以暂时不看,今天只是刚刚接触到Linux驱动程序的开始,就先写这么多,还不太习惯,以后会常常更新,争取和大家共同进步。

 

 


猜你喜欢

转载自blog.csdn.net/qq_38330846/article/details/78533835
今日推荐