在编写驱动程序时通常自定义一个数据结构来存储全局变量。当涉及到工作队列work_struct时,可以把work_struct嵌入到上述说的自定义数据结构中,利用container_of传递特征,即可实现不使用全局变量而可以访问全局变量的效果。
//定义一个含有work_struct工作队列结构体的结构体
struct mydata{
int x;
int y;
struct work_struct work;
int z;
};
//定义一个结构体drvdata
static struct mydata drvdata;
在模块加载初始化函数中调度工作队列。
static int __init xxx_init(void)
{
………
schedule_work();
………
}
此时重点关注到调用的工作函数中。我们需要通过传进的work_struct来定位到全局变量结构struct mydata drvdata的首地址。
static void mywork_func(struct work_struct *pwork)
{
struct mydata *pdata;
……
pdata = (struct mydata *)container_of(pwork,struct pdata,work);
……
}
来看container_of的函数原型:container_of(ptr, type, member)。
第一个参数是已知的成员变量内存地址,第二个参数传入已知成员所在结构体的变量类型,第三个参数传入已知成员所在的结构体变量类型中的成员名。可以根据已知成员的内存地址,求出含有其的所在结构体变量内存首地址。
原理分析:
结构体变量首地址 = (char *)ptr – ptr在结构中的偏移。假设偏移的地址大小是offset,关键就是求出offset的值。
所以假设结构体变量drvdata在0地址,因此offset就是ptr的所在地址。则p = ((struct mydata*)0),(u32)(&p->work)就是offset的大小
1、(type *)0,把0地址强制转换为type类型变量,用一个指针指向它。
2、((type *)0->member)通过上一步转换得到的指针访问type类型中的member
3、typeof((type *)0->member),typeof是c语言32个关键字之一,作用是根据变量名求出变量类型。
4、const typeof((type *)0->member) *mptr,定义一个已知成员类型的临时指针变量,保存已知成员的地址。
5、(char *)_mptr把已知成员地址转换为char *类型的指针,方便计算出偏移的大小
6、offsetof(type,member)。Offsetof是一个宏定义作用是求出type类型中已知成员member在type结构中偏移的字节。
7、(char *)__mptr – offsetof(type,member),使用已知成员的真实地址减去成员所在结构的实际偏移量,得到结构体变量首地址。
8、((type *)(char *)__mptr – offsetof(type,member))最后把已求出的(char *)地址转换为type*地址。
内核的函数只要知道其如何使用即可,无需太过于深入了解。