Linux驱动--函数,宏,结构体理解记录

本文深入探讨Linux内核中字符设备驱动的实现原理,包括字符驱动模型、设备号分配及文件系统中对字符设备文件的访问。详细解释了如何通过cdev结构表示每个字符驱动,以及如何利用list_head数据结构实现设备的管理。

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

1,contain_of 宏---内核

       该宏定义在include/linux/kernel.h中,首先来贴出它的代码:

  1. 439/**
  2. 440 * container_of - cast a member of a structure out to the containing structure
  3. 441 * @ptr: the pointer to the member.
  4. 442 * @type: the type of the container struct this is embedded in.
  5. 443 * @member: the name of the member within the struct.
  6. 444 *
  7. 445 */
  8. 446#define container_of(ptr, type, member) ({ \
  9. 447 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
  10. 448 (type *)( (char *)__mptr - offsetof(type,member) );})

     根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针

2,linux 字符cdev和inode

相关数据结构:

  struct cdev {

   struct kobject kobj;

   struct module *owner;

   const struct file_operations *ops;

   struct list_head list;

   dev_t dev;

   unsigned int count;

  };

  struct kobj_map {

   struct probe {

   struct probe *next;

   dev_t dev;

   unsigned long range;

   struct module *owner;

   kobj_probe_t *get;

   int (*lock)(dev_t, void *);

   void *data;

   } *probes[255];

   struct mutex *lock;

  };

  static struct char_device_struct {

   struct char_device_struct *next;

   unsigned int major;

   unsigned int baseminor;

   int minorct;

   char name[64];

   struct file_operations *fops;

   struct cdev *cdev;               

  } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

  #define CHRDEV_MAJOR_HASH_SIZE        255

  下面本文通过一下三个方面以及他们的关联来描述字符设备驱动:

  1. 字符驱动模型

  2. 字符设备的设备号

  3. 文件系统中对字符设备文件的访问

  1. 字符驱动模型

  每个字符驱动由一个 cdev 结构来表示.

  在设备驱动模型(device driver model)中, 使用 (kobject mapping domain) 来记录字符设备驱动.

  这是由 struct kobj_map 结构来表示的. 它内嵌了255个struct probe指针数组

  kobj_map由全局变量 cdev_map 引用: static struct kobj_map *cdev_map;

点击查看原始尺寸

  相关函数说明:

  cdev_alloc() 用来创建一个cdev的对象

  cdev_add() 用来将cdev对象添加到驱动模型中,其主要是通过kobj_map()来实现的.

  kobj_map() 会创建一个probe对象,然后将其插入cdev_map中的某一项中,并关联probe->data 指向 cdev

  struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)

  根据设备号,在cdev_map中查找其cdev对象内嵌的kobject. (probe->data->kobj),返回的是cdev的kobject


2. 字符设备的设备号

  字符设备的主,次设备号的分配:

  全局数组 chrdevs 包含了255(CHRDEV_MAJOR_HASH_SIZE 的值)个 struct char_device_struct的元素.

  每一个对应一个相应的主设备号.

  如果分配了一个设备号,就会创建一个 struct char_device_struct 的对象,并将其添加到 chrdevs 中.

  这样,通过chrdevs数组,我们就可以知道分配了哪些设备号.

点击查看原始尺寸

  相关函数:

  register_chrdev_region( ) 分配指定的设备号范围

  alloc_chrdev_region( ) 动态分配设备范围

  他们都主要是通过调用函数__register_chrdev_region() 来实现的

  要注意,这两个函数仅仅是注册设备号! 如果要和cdev关联起来,还要调用cdev_add()

  register_chrdev( ) 申请指定的设备号,并且将其注册到字符设备驱动模型中.

  它所做的事情为:

  1. 注册设备号, 通过调用 __register_chrdev_region() 来实现

  2. 分配一个cdev, 通过调用 cdev_alloc() 来实现

  3. 将cdev添加到驱动模型中, 这一步将设备号和驱动关联了起来. 通过调用 cdev_add() 来实现

  4. 将第一步中创建的 struct char_device_struct 对象的 cdev 指向第二步中分配的cdev. 由于register_chrdev()是老的接口,这一步在新的接口中并不需要.

  3. 文件系统中对字符设备文件的访问

  对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev, 如果i_cdev为 NULL ,则说明该设备文件没有被打开.

  由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个链表

点击查看原始尺寸

  首先,系统调用open打开一个字符设备的时候, 通过一系列调用,最终会执行到 chrdev_open.

  (最终是通过调用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 这一系列的调用过程,本文暂不讨论)

  int chrdev_open(struct inode * inode, struct file * filp)

  chrdev_open()所做的事情可以概括如下:

  1. 根据设备号(inode->i_rdev), 在字符设备驱动模型中查找对应的驱动程序, 这通过kobj_lookup() 来实现, kobj_lookup()会返回对应驱动程序cdev的kobject.

  2. 设置inode->i_cdev , 指向找到的cdev.

  3. 将inode添加到cdev->list的链表中.

  4. 使用cdev的ops 设置file对象的f_op

  5. 如果ops中定义了open方法,则调用该open方法

  6. 返回.

  执行完 chrdev_open()之后,file对象的f_op指向cdev的ops,因而之后对设备进行的read, write等操作,就会执行cdev的相应操作.

3,Linux内核list_head学习网页

http://www.cnblogs.com/zhuyp1015/archive/2012/06/02/2532240.html



### 回答1: `gd` 结构体是 `U-Boot` 中的全局数据结构。它包含了 `U-Boot` 运行期间所需要的一些全局变量和指针,例如内存大小、环境变量指针等。 下面是一个 `U-Boot` 中的 `gd` 结构体的定义: ```c struct global_data { ulong flags; /* state flags for this record */ ulong baudrate; /* console baudrate */ ulong cpu_clk; /* CPU clock in Hz */ ulong bus_clk; /* Bus clock in Hz */ ulong mem_clk; /* Mem clock in Hz */ ulong arch_number; /* unique id for this board */ ulong ram_size; /* RAM size */ ulong flash_size; /* Flash size */ ulong flash_offset; /* start of FLASH */ int env_valid; /* true if env area is valid */ char env_buf[ENV_SIZE]; /* buffer for getenv() */ ulong env_addr; /* Address of Environment struct */ ulong env_sect_size; /* size of one complete sector */ ulong env_sectors; /* number of sectors available */ uchar env_flash[CFG_ENV_ADDR_REDUND] __aligned(ARCH_DMA_MINALIGN); /* Image environment in FLASH */ uchar env_redund[CFG_ENV_ADDR_REDUND] __aligned(ARCH_DMA_MINALIGN); /* Image redundant environment in FLASH*/ bd_t *bd; /* board description pointer */ }; ``` `gd` 结构体中的变量和指针的具体含义可以参考注释。 ### 回答2: Linux中的gd结构体是指全局数据结构(global data structure),用于存储截至到当前时间点的系统全局信息。gd结构体位于内存中的一个固定地址,它是Linux内核启动时创建的,包含了许多与系统运行相关的信息。gd结构体中包含了物理内存的起始地址和大小、设备树的地址、文件系统根目录、驱动程序的初始化函数列表等。 而U-boot中的gd结构体是指全局数据结构(global data structure),用于存储与启动过程相关的全局信息。U-boot是一种开源的嵌入式系统引导加载器,用于加载运行操作系统或其他应用程序。gd结构体存储了关于启动和引导过程的重要信息,如内存布局、启动设备、命令行参数、环境变量等。gd结构体是在U-boot启动时动态创建的,通过它可以在运行过程中获取和设置各种全局变量,以支持系统的引导和启动流程。 无论是在Linux还是U-boot中,gd结构体都扮演着重要的角色,它提供了一个全局的数据存储空间,方便在系统运行过程中存储和访问各种全局变量。通过gd结构体,可以在系统的整个生命周期内记录和获取运行时的全局信息,为系统的正常运行和用户的操作提供了必要的数据支持。 ### 回答3: gd结构体Linux内核中的一个重要数据结构,它在内核初始化期间被创建,用于管理全局数据。它定义在头文件"include/linux/gd.h"中。gd结构体包含了许多字段,下面是一些重要的字段及其功能: 1. flags:包含了一些标志位,用于表示一些系统状态信息,比如是启动时的标志。 2. env_addr:存储着环境变量的起始地址。 3. env_valid:标志着环境变量的有效性,为1时表示有效。 4. ram_base、ram_size:存储着内存的起始地址和大小。 5. arch:用于存储体系结构相关的信息。 6. baudrate:串口通信的波特率。 7. bootfile:引导该系统的启动文件名。 8. boot_device:标志着启动设备的类型和编号。 9. lcd_color_index:存储了LCD显示颜色索引。 除了上述字段外,gd结构体还包含了一些用于存储引导时加载的设备树、已加载内核的前一级地址以及其他一些系统配置信息的字段。 而在U-boot中,也有类似的gd结构体用于管理全局数据。U-boot的gd结构体提供了与Linux内核中的gd结构体类似的功能,并且额外包含一些U-boot特有的字段。这些字段用于记录U-boot启动的相关信息,比如启动设备类型和编号、启动设备的起始地址、U-boot的启动参数等。 总的来说,gd结构体Linux和U-boot中是非常重要的数据结构,它们提供了全局数据的管理和存储,方便操作系统和引导程序进行相关的启动和配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值