最近想尝试下在仿真平台的uboot测试,主要还是为了日后仿真ARM的kernel做准备。但是坑爹的是没有串口输出,于是想自己定义输出设备接口,便开始着手研究这个stdio的东西。
这个stdio的列表真是坑爹,看了半天才懂,人老了啊!
坑爹列表的根源
static struct stdio_dev devs;
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;
}
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;
}
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中找到结构体
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;