container_of宏

玩转内核链表list_head


container_of 是 Linux 内核中一个常用的宏,用于从结构体的成员指针获取包含该成员的整个结构体的指针。

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({              \
    void *__mptr = (void *)(ptr);                   \
    BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&   \
             !__same_type(*(ptr), void),            \
             "pointer type mismatch in container_of()");    \
    ((type *)(__mptr - offsetof(type, member))); })

ptr:指向结构体中某个成员的指针。
type:结构体的类型。
member:结构体中的成员名。
offsetof: 计算结构体成员member在type这个结构体中的偏移

#define offsetof(type, member) ((size_t)(&((type *)0)->member))

例子:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>

#define MAX_NAME_LEN 32
#define MAX_ID_LEN 10

struct list_head device_list;

#define I2C_TYPE 1
#define SPI_TYPE 2

char *dev_name[]={
  "none",
  "I2C",
  "SPI"
};

struct device
{
  int type;
  char name[MAX_NAME_LEN];
  struct list_head list;
};

struct i2c_node
{
  int data;
  unsigned int reg;
  struct device dev;
};

struct spi_node
{  
  unsigned int reg;
  struct device dev;
};

void display_i2c_device(struct device *device)
{
  struct i2c_node *i2c_device = container_of(device, struct i2c_node, dev);

  pr_info("\t  i2c_device->data: %d\n", i2c_device->data);
  pr_info("\t  i2c_device->reg: %#x\n", i2c_device->reg);
}

void display_spi_device(struct device *device)
{
  struct spi_node *spi_device = container_of(device, struct spi_node, dev);
  pr_info("\t  spi_device->reg: %#x\n", spi_device->reg);
}

void display_device(struct device *device)
{
    pr_info("\t  dev.type: %d\n", device->type);
    pr_info("\t  dev.type: %s\n", dev_name[device->type]);
    pr_info("\t  dev.name: %s\n", device->name);
}

void display_list(struct list_head *list_head)
{
  int i=0;
  struct list_head *p;
  struct device *entry;

  pr_info("-------list---------\n");
  list_for_each(p, list_head)
  {
    pr_info("node[%d]\n",i++);
    entry=list_entry(p, struct device, list);

    switch(entry->type)
    {
      case I2C_TYPE:
        display_i2c_device(entry);
        break;
      case SPI_TYPE:
        display_spi_device(entry);
        break;
      default:
        pr_info("unknown device type!\n");
        break;
    }
    display_device(entry);
  }
  pr_info("-------end----------\n");
}

void i2c_register_device(struct device *device)
{
  struct i2c_node *i2c_device = container_of(device, struct i2c_node, dev);

  i2c_device->dev.type = I2C_TYPE;
  strcpy(i2c_device->dev.name,"yikoulinux");

  list_add(&device->list, &device_list);  
}

void spi_register_device(struct device *device)
{
  struct spi_node *spi_device = container_of(device, struct spi_node, dev);

  spi_device->dev.type = SPI_TYPE;
  strcpy(spi_device->dev.name,"yikoupeng");

  list_add(&device->list, &device_list);  
}

void i2c_unregister_device(struct device *device)
{
  struct i2c_node *i2c_device = container_of(device, struct i2c_node, dev);

  list_del(&device->list);
}

void spi_unregister_device(struct device *device)
{
  struct spi_node *spi_device = container_of(device, struct spi_node, dev);

  list_del(&device->list);
}


static int hello_init(void)                                                                                                                                                                
{

  struct i2c_node dev1;
  struct spi_node dev2;

  INIT_LIST_HEAD(&device_list);
  dev1.data = 1;
  dev1.reg = 0x40009000;
  i2c_register_device(&dev1.dev);
  dev2.reg  = 0x40008000;
  spi_register_device(&dev2.dev);  
  display_list(&device_list);
  i2c_unregister_device(&dev1.dev);
  display_list(&device_list);  
  return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT "Goodbye, kernel\n");
}           
            
module_init(hello_init);
module_exit(hello_exit);
            
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");

posted @ 2024-02-18 16:29  梦过无声  阅读(36)  评论(0编辑  收藏  举报