关于linux的链表之list_entry和内存对齐
2011-07-27 23:13
330 查看
让我们先看看相关宏
/**
* list.h
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* stddef.h
*/
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
/*
* kernel.h
* 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) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
这里要获得整个结构体的首地址,分为以下几步:
1) 将0强制转换为我们想要的类型,然后获取成员的类型,定义成员指针的类型(由编译器做),typeof( ((type *)0)->member ) *__mptr
2) __mptr指向ptr,在list_entry里面就是 list_head *
3)当有了list_head地址后,就可以减去这个地址的偏移获得结构体的头
下面是获得偏移:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
1)首先将0转换为想要的类型,然后获取它的member成员,这里就是list_head
2)将member(list_head)的地址,在这里为0的偏移,其实就是member相对于TYPE的偏移。其实这里全是由编译器完成
这里举个例子:
struct test{
int member;
struct list_head list;
};
int main(int argc, char *argv[])
{
struct test test_ls, *p_test_head;
p_test_head = container_of(&test_ls.list, struct test, list);
/*
第一天语句:typeof( ((type *)0)->member ) *__mptr = (ptr); 这里__mptr指向&test_ls.list
第二条语句:(type *)( (char *)__mptr - offsetof(type,member) );}) 这里用__mptr - &((struct test*)0)->member) == (__mptr - 4)
*/
return 0;
}
这里很多人认为没有必要这么麻烦的,其实一开始很多人都不习惯,包括我。但是仔细研究后发现,这里确实抽象的很好,尤其是内核需要一个统一结构时,具有很好的通用性,而不至于出错。
内存对齐:
内存对齐是编译器对变量存储的一个特殊处理。为了提高CPU的存储速度,编译器对一些变量的起始地址做了对齐处理。
在默认情况下,编译器规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
分配的字节数为结构体中占用空间最大的类型所占用的字节数的整数倍(r如果结构体中还有结构体,他们之间可以合并的)
#pragma pack (value)来告诉编译器粒度,在VC++中也可以通过工程--》设置 来设置
#pragma pack() 来取消对齐
在linux可以在结构体后面加__attribute__((packed))来设置为最小内存, __attribute__((aligned))属性使被设置的对象占用更多的空间。这个属性与你的编译器有关,我在我的机器上使用__attribute__ ((aligned (8))),就无效
struct S {
unsigned short a[3];
} __attribute__ ((aligned (4)));
我想看看netfilter,今天就到这里吧,限于水平,文章难免有误,如果这篇文章有错,请给我留言
!
/**
* list.h
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* stddef.h
*/
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
/*
* kernel.h
* 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) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
这里要获得整个结构体的首地址,分为以下几步:
1) 将0强制转换为我们想要的类型,然后获取成员的类型,定义成员指针的类型(由编译器做),typeof( ((type *)0)->member ) *__mptr
2) __mptr指向ptr,在list_entry里面就是 list_head *
3)当有了list_head地址后,就可以减去这个地址的偏移获得结构体的头
下面是获得偏移:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
1)首先将0转换为想要的类型,然后获取它的member成员,这里就是list_head
2)将member(list_head)的地址,在这里为0的偏移,其实就是member相对于TYPE的偏移。其实这里全是由编译器完成
这里举个例子:
struct test{
int member;
struct list_head list;
};
int main(int argc, char *argv[])
{
struct test test_ls, *p_test_head;
p_test_head = container_of(&test_ls.list, struct test, list);
/*
第一天语句:typeof( ((type *)0)->member ) *__mptr = (ptr); 这里__mptr指向&test_ls.list
第二条语句:(type *)( (char *)__mptr - offsetof(type,member) );}) 这里用__mptr - &((struct test*)0)->member) == (__mptr - 4)
*/
return 0;
}
这里很多人认为没有必要这么麻烦的,其实一开始很多人都不习惯,包括我。但是仔细研究后发现,这里确实抽象的很好,尤其是内核需要一个统一结构时,具有很好的通用性,而不至于出错。
内存对齐:
内存对齐是编译器对变量存储的一个特殊处理。为了提高CPU的存储速度,编译器对一些变量的起始地址做了对齐处理。
在默认情况下,编译器规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
分配的字节数为结构体中占用空间最大的类型所占用的字节数的整数倍(r如果结构体中还有结构体,他们之间可以合并的)
#pragma pack (value)来告诉编译器粒度,在VC++中也可以通过工程--》设置 来设置
#pragma pack() 来取消对齐
在linux可以在结构体后面加__attribute__((packed))来设置为最小内存, __attribute__((aligned))属性使被设置的对象占用更多的空间。这个属性与你的编译器有关,我在我的机器上使用__attribute__ ((aligned (8))),就无效
struct S {
unsigned short a[3];
} __attribute__ ((aligned (4)));
我想看看netfilter,今天就到这里吧,限于水平,文章难免有误,如果这篇文章有错,请给我留言
!
相关文章推荐
- linux 链表(list_entry) 用法
- 深入分析 Linux 内核链表 list_entry...
- Linux 双链表 list_for_each_entry 实现
- 深入分析 Linux 内核链表 list_entry...
- 关于线性表的两种储存结构及单链表逆序解析(ArrayList和LinkedList)
- 关于list_entry
- linux内核部件--通用双向链表list
- Linux中通用链表(list)的解析(4)
- 关于container_of和list_for_each_entry 及其相关函数的分析
- 内核链表中list_entry()函数
- Linux利用list_head结构实现双向链表
- linux内核链表以及list_entry--linux内核数据结构(一)
- ListEntry 链表图解,及解析 InsertTailList & RemoveHeadList&RemoveEntryList函数
- 移动盒子(Boxes in a Line, UVa 12657) 关于STL中list(双向链表的应用)
- (Linux 内核)双向循环链表list_head
- Linux中通用链表(list)的解析(1)
- linux内核学习笔记之——list_for_each_entry
- Linux 中的链表list 使用示例
- ListEntry 链表图解,及解析 InsertTailList & RemoveHeadList&RemoveEntryList函数
- Linux利用list_head结构实现双向链表