坑爹的指针列表操作——uboot下面的stdio列表

最近想尝试下在仿真平台的uboot测试,主要还是为了日后仿真ARM的kernel做准备。但是坑爹的是没有串口输出,于是想自己定义输出设备接口,便开始着手研究这个stdio的东西。

这个stdio的列表真是坑爹,看了半天才懂,人老了啊!

初始化列表

坑爹列表的根源

static struct stdio_dev devs;

这个静态全局变量devs中有个列表成员list,

struct list_head list;

struct list_head {
	struct list_head *next, *prev;
};
在stdio_init初始化列表,其实就是自己链接自己。

INIT_LIST_HEAD(&(devs.list));

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

增加列表

初始化完成后,以后每增加一个stdio device,都需要这样操作

int stdio_register (struct stdio_dev * dev)
{
	struct stdio_dev *_dev;

	_dev = stdio_clone(dev);
	if(!_dev)
		return -1;
	list_add_tail(&(_dev->list), &(devs.list));
	return 0;
}

这里的clone,无非就是malloc一个内存,然后拷贝。真正坑爹的是这个add操作。

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next)
{
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

其实实质就是一个双向指针的操作,但是一般双向指针我喜欢这样写,这样就只要两个节点就可以了,我觉得简单啊!

new->next = head;
new->prev = head->prev;
head->prev = next;
head->prev->next = new;

这样就把new插入了最后,但是这个代码不是这样做的,看看实现过程;

在最终的插入的过程有三个节点,new——新增的一个,prev——以前的最后一个,next——就是head,或者说最前面一个无用的东东

对于next,由于它是最后一个节点(也就是最开始那个静态变量devs中的list),因此不需要对next->next赋值,只需要赋值next->prev为新增加的就行;

对于prev,它本来是最后一个,也就是devs中list->prev的那个,现在它变成了倒数第二个,因此需要赋值prev->next=new

对于new,它两端都需要赋值。

在这里需要说明的是,这时一个循环双向列表指针,head的next指向第二个,head的prev指向最后一个。


使用列表

在使用列表时先扫描列表

list_for_each(pos, list) 
#define list_for_each(pos, head) \
	for (pos = (head)->next; prefetch(pos->next), pos != (head); \
		pos = pos->next)

list指devs.list,也就是第一个位置。prefetch先可以认为是空操作。这里的目的就是遍历所有节点

然后针对每一个节点,需要做的操作时从list中找到结构体

dev = list_entry(pos, struct stdio_dev, list);
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)
#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})
typeof是GCC中的一个关键字,目的是获取一个结构体的类型
const typeof( ((type *)0)->member ) *__mptr = (ptr);
这里是定义一个指针,type指struct stdio_dev,member指list,因此这里相当于下面这句话

const struct list_head * __mptr = ptr;

然后再根据list成员在struct中的位置,反推出struct结构体的地址,将其结果转出,得到整个stdio_dev的地址。



你可能感兴趣的:(嵌入式研发,uboot,stdio)