Linux操作系统中的“迭代器“

7 篇文章 0 订阅

常规教科书中使用的双向链表

typedef struct double_linke_list_s {
	char * username;
	unsigned int age;
    struct double_linke_list_s *pre; //指向前一个指针节点
    struct double_linke_list_s *next; //指向下一个指针节点
} double_linked_list;

我们可以看出双向链表链接的类型和双向链表的类型是保持一致的,也就是说节点占用的内存是sizeof(double_linke_list_s),如果在os分配的内存可能要看一下内存对齐的问题。

同时再科普一下结构体和函数下面有一个attribute是干什么的?这是函数和结构体的属性。
以函数为例 :

attribute ((constructor))会使函数在main()函数之前被执行

attribute ((destructor))会使函数在main()退出后执行

以结构体为例:

struct blc_cmd {
    int type; /* types of commands whose value can be referenced by blc_cmd in blc_cmd.h. */
    int cnt; /* the size of info */
    void *info;
} __attribute__((__packed__));

结构体里面的属性可以设置是否按照os设计的内存对其方式等。

linux系统提供的双向链表api

/* Doubly linked list head or element. */
struct ovs_list {
    struct ovs_list *prev;  /* 指向前一个指针节点 */
    struct ovs_list *next;  /* 指向下一个指针节点 */
};

这样的结构体一般是嵌入到结构体的内部,作为迭代器来使用,来实现顺序索引。可以通过迭代器的指针++ --来顺序访问。

struct vtep_vrf_entry {
    struct ovs_list node;
    ovs_be32 vtep_ip;
    struct vrf_entry entry;
};

我们想看看他是怎么把东西链接起来的? 当把struct ovs_list node 放在结构体的任何位置会不会有影响?
首先在操作系统对于一个结构体分配的内存在结构体中是连续的,这是这种思想的前提。它可能跨页,但是绝对不会跨段。
我们对于的node的地址可以通过 &node来计算出(1)
我们对于该结构体的大小是可以通过sizeof(结构体)求出(2)
结构体中node的相对位置找到对于结构体分配的首地址到node的偏移量(3)
-------对于怎么求偏移量其实是汇编语言offset的问题。
以上3条消息缺一不可

公式为 结构体的首地址 = node的地址 - 结构体中node到首地址的偏移量 (os其实要分高地址位和低地址可能会出入但是思想不变哒,后面户提供os实现的宏)
我们第一眼看到觉得这个很奇怪数据要放到哪里,我们建立链表的时候不就是为了存放数据。这样的好处在哪呢?请听我一步步分析!

二者对比分析

1.首先从占用的内存的角度来看一下

考虑内存对齐的影响
linux提供的list中只有两个指针,所以占用的大小是两个指针的大小 对应一个指针的大小我解释过和对应os的int大小相等。
而教科书版本是要数据占用的大小可能因为操作系统的分配会有一些很小的内存碎片利用不到。

2.使用方便来说

首先无需质疑的就是教科书版本的很容易上手就是普通链表的操作。–仅仅适合学习练习方便
因为根据node的位置可以找到整个结构体的首地址已经被封装成库,除了不是很容易看懂外很好用。像C++一样的迭代器用起来还是很爽的。

对应上述公式是怎么用宏运算出来的?

对应的宏 有兴趣可以研究一下 g++ -E 将宏展开一下会好很多

#ifdef __GNUC__
#define OBJECT_OFFSETOF(OBJECT, MEMBER) offsetof(typeof(*(OBJECT)), MEMBER)
#else
#define OBJECT_OFFSETOF(OBJECT, MEMBER) \
((char *) &(OBJECT)->MEMBER - (char *) (OBJECT))
#endif

/* Yields the size of MEMBER within STRUCT. */
#define MEMBER_SIZEOF(STRUCT, MEMBER) (sizeof(((STRUCT *) NULL)->MEMBER))

/* Yields the offset of the end of MEMBER within STRUCT. */
#define OFFSETOFEND(STRUCT, MEMBER) \
(offsetof(STRUCT, MEMBER) + MEMBER_SIZEOF(STRUCT, MEMBER))

/* Given POINTER, the address of the given MEMBER in a STRUCT object, returns
the STRUCT object. */
#define CONTAINER_OF(POINTER, STRUCT, MEMBER) \
((STRUCT *) (void *) ((char *) (POINTER) - offsetof (STRUCT, MEMBER)))

/* Given POINTER, the address of the given MEMBER within an object of the type
* that that OBJECT points to, returns OBJECT as an assignment-compatible
* pointer type (either the correct pointer type or "void *"). OBJECT must be
* an lvalue.
*
* This is the same as CONTAINER_OF except that it infers the structure type
* from the type of '*OBJECT'. */
#define OBJECT_CONTAINING(POINTER, OBJECT, MEMBER) \
((OVS_TYPEOF(OBJECT)) (void *) \
((char *) (POINTER) - OBJECT_OFFSETOF(OBJECT, MEMBER)))

/* Given POINTER, the address of the given MEMBER within an object of the type
* that that OBJECT points to, assigns the address of the outer object to
* OBJECT, which must be an lvalue.
*
* Evaluates to (void) 0 as the result is not to be used. */
#define ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER) \
((OBJECT) = OBJECT_CONTAINING(POINTER, OBJECT, MEMBER), (void) 0)

/* As explained in the comment above OBJECT_OFFSETOF(), non-GNUC compilers
* like MSVC will complain about un-initialized variables if OBJECT
* hasn't already been initialized. To prevent such warnings, INIT_CONTAINER()
* can be used as a wrapper around ASSIGN_CONTAINER. */
#define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \
((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER))

文章参考

有问题请在下方留言

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值