当前位置: 首页 > news >正文

container_of宏

contained_of

工作队列传参优化

利用遍历查找

⛺️定义了btn_work_content结构体,包含两个关键成员:

  • 一个work_struct用于工作队
  • 一个指向btn_resource的指针

像给每个工作队列贴上了一个标签,告诉它"你是负责处理哪个按键的"

⛺️当内核的工作队列线程执行这个函数时,它只会收到一个work_struct指针,不知道这个work属于哪个按键。循环判断,把传入的work指针和数组中的每个btn_work进行比较。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>struct btn_resource{char* name;int gpio;int irq;
};
static struct btn_resource btn_info[]={{ .name = "KEY_UP" , .gpio =PAD_GPIO_A +28 },{ .name = "KEY_DOWN" , .gpio =PAD_GPIO_B +30 },{ .name = "VOL_UP" , .gpio =PAD_GPIO_B +31 },{ .name = "VOL_DOWN" , .gpio =PAD_GPIO_B +9 }		
};
#define BTN_COUNT ARRAY_SIZE(btn_info)/*需要建立工作队列和按键的联系*/
struct btn_work_content{struct work_struct btn_work;struct btn_resource* key;
};
static struct btn_work_content btn_content[BTN_COUNT];static void btn_handle(struct btn_work_content* cet){struct btn_resource* p=cet->key;int kstate=gpio_get_value(p->gpio);pr_info("The %s is %s!\n",p->name,kstate?"released":"pressed");}
static void btn_work_fun(struct work_struct* work){int i;for(i=0;i<BTN_COUNT;i++){if(work == &btn_content[i].btn_work){btn_handle(&btn_content[i]);return;}}
}//中断处理函数
static irqreturn_t button_isr(int irq,void* dev){//注册工作队列struct btn_work_content* pdata=(struct btn_work_content*)dev;schedule_work(&pdata->btn_work);return IRQ_HANDLED;	
}
static int __init btn_init(void){int i;int ret;for(i=0;i<BTN_COUNT;i++){struct btn_resource* p=&btn_info[i];gpio_request(p->gpio,p->name);gpio_direction_input(p->gpio);p->irq=gpio_to_irq(p->gpio);//建立按键和工作队列的联系btn_content[i].key=p;//初始化工作队列INIT_WORK(&btn_content[i].btn_work,btn_work_fun);ret=request_irq(p->irq,button_isr,IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING,p->name,&btn_content[i]);if(ret){pr_err("request_irq fails\n");continue;}}pr_info("the workqueue driver is loaded!\n");return 0;
}
static void __exit btn_exit(void){int i;for(i=0;i<BTN_COUNT;i++){struct btn_resource* p=&btn_info[i];//取消工作队列free_irq(p->irq,&btn_content[i]);//禁止新的中断	cancel_work_sync(&btn_content[i].btn_work);gpio_free(p->gpio);}pr_info("the workqueue driver is removed!\n");
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");

如何建立按键和工作队列的联系

在这里插入图片描述


中断处理函数参数传的结构体指针
在这里插入图片描述

注意参数

在这里插入图片描述

需要额外的数据结构来维护映射关系,需要遍历查找,代码更复杂,效率更低

利用container_of

这种结构体关联思想在按键非常多时候就不适用了,而且循环判断会牺牲一部分性能

先介绍offsetof
#include <stdio.h>
#include <stddef.h> //offsetof//结构体
struct MyInfo1{char a;int b;double c;
};
struct MyInfo2{int a;double b;char c;
};
//%zu:z是size_t,u是unsigned_int
int main(){printf("MyInfo1 : offsetof a is %zu!\n",offsetof(struct MyInfo1,a ));printf("MyInfo1 : offsetof b is %zu!\n",offsetof(struct MyInfo1,b));printf("MyInfo1 : offsetof c is %zu!\n",offsetof(struct MyInfo1,c ));printf("MyInfo1 total size is %zu!\n",sizeof(struct MyInfo1));printf("MyInfo2: offsetof a is %zu!\n",offsetof(struct MyInfo2,a ));printf("MyInfo2 : offsetof b is %zu!\n",offsetof(struct MyInfo2,b ));printf("MyInfo2 : offsetof c is %zu!\n",offsetof(struct MyInfo2,c ));printf("MyInfo2 total size is %zu!\n",sizeof(struct MyInfo2));return 0;
}

%zu-----z:size_t , u:unsigned

offsetof宏的作用是计算结构体中某个成员相对于结构体起始位置的字节偏移量

输出结果:

在这里插入图片描述

这里就是内存对齐


MyInfo1的内存布局:

先是a占1字节,然后是3字节的填充,接着是b占4字节,最后是c占8字节

  • int类型通常需要在4字节边界上对齐才能高效访问,下一个可用的4字节对齐位置就是地址4。a实际占一字节编译器在a后面悄悄插入了3个填充字节来实现这个对齐要求。
  • double类型通常需要8字节对齐,double c从偏移量8开始,这正好是8字节边界对齐。

MyInfo2的内存布局:

先是a占4字节,然后是4字节的填充,接着是b8字节,最后是c占1字节,填充7字节成为8的整数倍

  • 成员b需要8字节对齐,而位置8是下一个可用的对齐地址,所以在a之后又插入了4个字节的填充,使得b能从偏移量8开始。
  • c只占1字节,那么结构体总共需要16加1等于17字节,但17不是8的倍数,所以编译器会在结构体末尾再填充7字节,使总大小变成24字节。

⛺️​offsetof(结构体类型, 成员名)

  • 第一个参数是结构体的类型(注意要写完整的struct 结构体名)
  • 第二个参数是你想查询的成员名称。
  • 这个宏会返回一个size_t类型的值,表示该成员距离结构体开头的字节数。

在这里插入图片描述

类型在前,成员在后

什么是container_of

container_of宏想要解决的问题:

通过一个指向某个结构体的某个成员的指针,反推出整个结构体的起始地址


实际例子:

struct Student {int id;           // 学号,假设占4字节char name[20];    // 姓名,占20字节double score;     // 成绩,占8字节
};

目前知道这个学生的名字,想得到学生的成绩。也就是参数是指向成绩的指针,把指推导出学生信息的指针。

无类型安全的简化版宏

在这里插入图片描述

char类型的大小恰好是一个字节,把ptr转换成char *类型后,指针运算就变成了以字节为单位


减法运算(char *)(ptr) - offsetof(type, member)

这一步就是用成员的地址减去它的偏移量,得到结构体的起始地址。

注意这里得到的结果是char *类型,

//内核里常用的 container_of 宏的完整版写法,多了类型安全
#define container_of(ptr, type, member) ({                      \const typeof(((type *)0)->member) *__mptr = (ptr);          \(type *)((char *)__mptr - offsetof(type, member));          \
})

这个宏的正确性完全依赖于你传入的三个参数是准确对应的。如果传入的ptr实际上不是指向type类型结构体中的member成员,那么计算出来的地址就是错误的,后续访问会导致未定义行为,可能引发段错误或者更糟糕的内存损坏。

#include <stdio.h>
#include <stddef.h> //offsetof//结构体
struct Student {int id;           // 假设从偏移量0开始,占4字节char name[20];    // 假设从偏移量4开始,占20字节double score;     // 假设从偏移量24开始,占8字节
};//contained_of宏需要自己定义,内核有包含它的头文件
#define container_of(ptr, type, member) \((type *)((char *)(ptr) - offsetof(type, member)))int main(){//printf("Student : offsetof a is %zu!\n",offsetof(struct Student,id ));//printf("Student : offsetof b is %zu!\n",offsetof(struct Student,name));//printf("Student : offsetof c is %zu!\n",offsetof(struct Student,score));//printf("Student total size is %zu!\n",sizeof(struct Student));struct Student stu={1345,"liuyifei",90};//已知指向分数的指针double *score_ptr=&stu.score;//利用contained_of反推机构体指针struct Student* getValue=container_of(score_ptr,struct Student, score);//打印其他信息printf("name is %s,id is %d!\n",getValue->name,getValue->id);return 0;
}

结构体成员的指针->结构体指针->访问其他成员

利用container_of宏简化程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>struct btn_resource{char* name;int gpio;int irq;struct work_struct btn_work;//成员,非指针
};
static struct btn_resource btn_info[]={{ .name = "KEY_UP" , .gpio =PAD_GPIO_A +28 },{ .name = "KEY_DOWN" , .gpio =PAD_GPIO_B +30 },{ .name = "VOL_UP" , .gpio =PAD_GPIO_B +31 },{ .name = "VOL_DOWN" , .gpio =PAD_GPIO_B +9 }		
};
#define BTN_COUNT ARRAY_SIZE(btn_info)static void btn_work_fun(struct work_struct* work){int kstate;struct btn_resource* pdata=container_of(work,struct btn_resource,btn_work);kstate=gpio_get_value(pdata->gpio);pr_info("The %s is %s\n",pdata->name,kstate?"released":"pressed");
}//中断处理函数
static irqreturn_t button_isr(int irq,void* dev){//注册工作队列struct btn_resource* pdata=(struct btn_resource*)dev;schedule_work(&pdata->btn_work);return IRQ_HANDLED;	
}
static int __init btn_init(void){int i;int ret;for(i=0;i<BTN_COUNT;i++){struct btn_resource* p=&btn_info[i];gpio_request(p->gpio,p->name);gpio_direction_input(p->gpio);p->irq=gpio_to_irq(p->gpio);//初始化工作队列INIT_WORK(&btn_info[i].btn_work,btn_work_fun);ret=request_irq(p->irq,button_isr,IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING,p->name,p);if(ret){pr_err("request_irq fails\n");continue;}}pr_info("the container_of driver is loaded!\n");return 0;
}
static void __exit btn_exit(void){int i;for(i=0;i<BTN_COUNT;i++){struct btn_resource* p=&btn_info[i];free_irq(p->irq,p);cancel_work_sync(&btn_info[i].btn_work);gpio_free(p->gpio);}pr_info("the container_of driver is removed!\n");
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");

注意函数类型传参

在这里插入图片描述


这里使用了container_of宏,这是个非常经典的C语言技巧。通过work_struct成员的地址,反向计算出包含它的btn_resource结构体的起始地址,这样就能访问到按键的名称和GPIO信息了。
在这里插入图片描述

首先把work_struct放入按键中,代表一个按键一个驱动程序

中断处理函数传参是btn_resource*类型,然后可以利用该类型存在work_struct成员给函数传惨

点睛之笔就是:btn_work_fun函数通过work反推出btn_resource类型

http://www.dtcms.com/a/582959.html

相关文章:

  • 创建网站要钱吗windows系统没有wordpress
  • 网站开发流程三大部分测评网站怎么做
  • iPhone 17 Pro Max 的评测和用户反馈
  • Python 中的异步编程:从基础到实战
  • 怎么制作自己的个人网站网址导航发布页
  • 网站建设售后服务方案百度招聘
  • 做网站编辑好还是美工好番禺区住房和建设局物业网站
  • Unity YooAsset HybridCLR 热更基础教程
  • 【Nginx】怎样清除 Nginx 的缓存?
  • 网站制作公海南省海口市龙华区
  • 【电子元器件·17】三极管的发射极e、基极b、集电极c 和 类型、主回路电流方向 的 快速判断方法
  • 浙江省城乡和住房建设厅网站首页dede做购物网站
  • ps做素材下载网站装潢设计和室内设计的区别
  • 程序员求职突围
  • 江苏营销型网站公司如何做网络推广推广
  • 网站建设与维护的论述题网站建设销售的技巧话语
  • 进程替换(主要接口讲解)
  • 网站开发常用语言总结怎么让别人访问我建的网站
  • 接平面设计私活的网站怎样做旅游城市住宿网站
  • nodejs同时做网站和后台管理怎么申请一个域名
  • 天津营销类网站设计网站建设管理要求
  • 地矿局网站建设方案深圳互联网设计公司
  • ubuntu更改使用期望的内核版本
  • 芋道后端部署后总自己挂?从 Nginx 报错到 OOM Kill 的完整排查与修复(2核2G 服务器实战)
  • 哪个网站可以做照片分享申远空间设计公司
  • 系统开发必须遵守的原则有哪些网站可以做多少优化关键词
  • PyTorch深度学习进阶(一)(经典卷积神经网络 LeNet)
  • 北京搭建网站做棋牌网站
  • UiPath2025笔记第十节:利用java反射编写智能体
  • 如何查网站的空间wordpress 移动端模板下载