Linux内核源码分析-链表代码分析

Linux epoll内核源码剖析 IO多路复用接口Linux内核源码剖析,源码之前,了无密码 poll Linux内核源码剖析 select Linux内核源码剖析 epoll Linux内核源码剖析 epoll Linux内核源码剖析 前面介绍了select/poll,此文章将讲解epoll,epoll是select/poll的增强版,epoll更加高效,当然也更加复杂 epoll高效的一个重要的原因是在获取事件的时候,它无须遍... 阅读详情

 Linux内核源码分析-链表代码分析
分析人:余旭
分析时间:2005年11月17日星期四 11:40:10 AM
雨 温度:10-11度
编号:1-4 类别:准备工作
Email:yuxu9710108@163.com
时代背景:开始在www.linuxforum.net Linux内核技术论坛上面发贴,在网友的帮忙下,
解决了一些问题。
版权声明:版权保留。本文用作其他用途当经作者本人同意,转载请注明作者姓名
All Rights Reserved. If for other use,must Agreed By the writer.Citing this
text,please claim the writer's name.
Copyright (C) 2005 YuXu
**************************************************
-------------双向循环链表---------------------------
来源于:list.h
设计思想:尽可能的代码重用,化大堆的链表设计为单个链表。
链表的构造:如果需要构造某类对象的特定列表,则在其结构中定义一个类型为list_
head指针的成员,通过这个成员将这类对象连接起来,形成所需列表,并通过通用链表函
数对其进行操作。其优点是只需编写通用链表函数,即可构造和操作不同对象的列表,而
无需为每类对象的每种列表编写专用函数,实现了代码的重用。

如果想对某种类型创建链表,就把一个list_head类型的变量嵌入到该类型中,用list_
head中的成员和相对应的处理函数来对链表进行遍历。如果想得到相应的结构的指针,使
用list_entry可以算出来。
-------------防止重复包含同一个头文件---------------
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
...
#endif
用于防止重复包含同一个list.h头文件
-----------struct list_head{}及初始化宏---------
struct list_head
{
struct list_head *next, *prev;
};
list_head从字面上理解,好像是头结点的意思。但从这里的代码来看却是普通结点的结构
体。在后面的代码中将list_head当成普通的结点来处理。

--LIST_HEAD_INIT()--LIST_HEAD()--INIT_LIST_HEAD()------
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) /
struct list_head name = LIST_HEAD_INIT(name)
分析:name当为结构体struct list_head{}的一个结构体变量,&(name)为该结构体变量的
地址。用name结构体变量的始地址将该结构体变量进行初始化。

#define INIT_LIST_HEAD(ptr) do { /
(ptr)->next = (ptr); (ptr)->prev = (ptr); /
} while (0)
1.ptr为一个结构体的指针,而name为一个结构体变量;
2.ptr使用时候,当用括号,(ptr);

------------__list_add()---list_add()-------------
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;
}
1.普通的在两个非空结点中插入一个结点,注意new,prev,next都不能是空值。
2.即:适用于中间结点插入。首结点和尾结点则由于指针为空,不能用此函数。
3.在prev指针和next指针所指向的结点之间插入new指针所指向的结点。

static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
在head和head->next两指针所指向的结点之间插入new所指向的结点。
即:在head指针后面插入new所指向的结点。此函数用于在头结点后面插入结点。
注意:对只有一个单个结点的链表,则head->next为空,list_add()不能用。

-------------list_add_tail()-------------------
static inline void list_add_tail(struct list_head *new, struct list_head *head
)
{
__list_add(new, head->prev, head);
}
在头结点指针head所指向结点的前面插入new所指向的结点。也相当于在尾结点后面增加一
个new所指向的结点。(条件是:head->prev当指向尾结点)
注意:
1.head->prev不能为空,即若head为头结点,其head->prev当指向一个数值,一般为指向
尾结点,构成循环链表。
2.对只有单个结点的头结点调用此函数则会出错。

-----------__list_del()---list_del()--------------
static inline void __list_del(struct list_head * prev, struct list_head * next
)
{
next->prev = prev;
prev->next = next;
}
在prev和next指针所指向的结点之间,两者互相所指。在后面会看到:prev为待删除的结
点的前面一个结点,next为待删除的结点的后面一个结点。

static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
删除entry所指的结点,同时将entry所指向的结点指针域封死。
对LIST_POISON1,LIST_POISON2的解释说明:
Linux 内核中解释:These are non-NULL pointers that will result in page faults
under normal circumstances, used to verify that nobody uses non-initialized
list entries.
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)
常规思想是:entry->next = NULL; entry->prev = NULL;
注意:Linux内核中的‘=’都与前后隔了一个空格,这样比紧靠前后要清晰。

---------------list_del_init()--------------------
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
删除entry所指向的结点,同时将entry所指向的结点的next,prev指针域指向自身。

-----------list_move()--list_move_tail()----------
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}
将list结点前后两个结点互相指向彼此,删除list指针所指向的结点,再将此结点插入
head,和head->next两个指针所指向的结点之间。
即:将list所指向的结点移动到head所指向的结点的后面。

static inline void list_move_tail(struct list_head *list, struct list_head *
head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
删除了list所指向的结点,将其插入到head所指向的结点的前面,如果head->prev指向链
表的尾结点的话,就是将list所指向的结点插入到链表的结尾。

---------------------list_empty()-------------
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
注意:
1.如果是只有一个结点,head,head->next,head->prev都指向同一个结点,则这里会返
回1,但链表却不为空,仍有一个头结点
2.return 后面不带括号,且为一个表达式。
3.测试链表是否为空,但这个空不是没有任何结点,而是只有一个头结点。

--------------------list_empty_careful()---------
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
分析:
1.只有一个头结点head,这时head指向这个头结点,head->next,head->prev指向head,即
:head==head->next==head->prev,这时候list_empty_careful()函数返回1。
2.有两个结点,head指向头结点,head->next,head->prev均指向后面那个结点,即:
head->next==head->prev,而head!=head->next,head!=head->prev.所以函数将返回0
3.有三个及三个以上的结点,这是一般的情况,自己容易分析了。
注意:这里empty list是指只有一个空的头结点,而不是毫无任何结点。并且该头结点必
须其head->next==head->prev==head

---------------__list_splice()------------------
static inline void __list_splice(struct list_head *list, struct list_head *
head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;

first->prev = head;
head->next = first;

last->next = at;
at->prev = last;
}

--------------------list_splice()----------------

static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}
分析:
情况1:
普遍的情况,每个链表都至少有3个以上的结点:

====>此处作者画了图,可显示不出来,郁闷!!!

========》待作者上传一个word文档,图在里面。
------------------------------------------------------------------------------
-------------


这种情况会丢弃list所指向的结点,这是特意设计的,因为两个链表有两个头结点,要去
掉一个头结点。只要一个头结点。


------------------------------------------------------------------------------
---------------------------------

特殊情况1:
初始情况:


------------------------------------------------------------------------

特殊情况2:
初始情况:

--------------------list_splice_init()-----------------------------------

static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
{
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}

--------------------/asm-i386/posix_types.h-------
typedef unsigned int __kernel_size_t;

------/linux/types.h---------size_t---------------
#ifndef _SIZE_T
#define _SIZE_T
typedef __kernel_size_t size_t;
#endif
-------------/linux/compiler-gcc4.h--------------
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
分析准备:__compiler_offsetof(),为gcc编译器中的编译方面的参数,查阅gcc方面的文
档:
--->gcc.pdf.Download from www.gnu.org。其中解释如下:
#define offsetof(type, member) __builtin_offsetof (type, member)

自己分析:即:__builtin_offsetof(a,b)就是#define offsetof(TYPE, MEMBER) ((size
_t) &((TYPE *)0)->MEMBER)。__builtin_offsetof(a,b)和offsetof(TYPE,MEMBER)本质一
样的,只是offsetof()宏是由程序员自己来设计(详见后面讲解)。而__builtin_offsetof
()宏就是在编译器中已经设计好了的函数,直接调用即可。明白了这个区别后,下面的代
码很好理解。
-------/linux/stddef.h-----offsetof()-----------
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
-------------------------------
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
1.对__compiler_offsetof()宏的分析:
__compiler_offsetof来确认编译器中是否内建了功能同offsetof()宏一样的宏。若已经内
建了这样的宏,则offsetof()就是使用这个内建宏__compiler_offsetof()即:__builtin
_offsetof()宏。如果没有定义__compiler_offsetof()宏,则offsetof()宏就由程序员来
设计之。

2.对offsetof()宏的分析:(以下引用论坛)---曾经的腾讯QQ的笔试题。
宿舍舍友参加qq笔试,回来讨论一道选择题,求结构中成员偏移。
想起Linux内核链表,数据节点携带链表节点,通过链表访问数据的方法,用到offsetof宏
,今天把它翻了出来:
#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )

一共4步
1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;
2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;
3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址;
4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型.巧妙之处在于将0转换成(TYPE*),结
构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;
举例说明:
#include<stdio.h>
typedef struct _test
{
char i;
int j;
char k;
}Test;
int main()
{
Test *p = 0;
printf("%p/n", &(p->k));
}
自己分析:这里使用的是一个利用编译器技术的小技巧,即先求得结构成员变量在结构体
中的相对于结构体的首地址的偏移地址,然后根据结构体的首地址为0,从而得出该偏移地
址就是该结构体变量在该结构体中的偏移,即:该结构体成员变量距离结构体首的距离。
在offsetof()中,这个member成员的地址实际上就是type数据结构中member成员相对于结
构变量的偏移量。对于给定一个结构,offsetof(type,member)是一个常量,list_entry(
)正是利用这个不变的偏移量来求得链表数据项的变量地址。

---------------------typeof()--------------------
--->我开始不懂,源代码中也查不到,网上发贴请教。由liubo1977在www.linuxforum.
net上的Linux内核技术论坛上解答,QQ:84915771
答复:
unsigned int i;
typeof(i) x;
x=100;
printf("x:%d/n",x);
typeof() 是 gcc 的扩展,和 sizeof() 类似。

------------------------
container_of()和offsetof()并不仅用于链表操作,这里最有趣的地方是 ((type *)0)->
member,它将0地址强制 "转换" 为 type 结构的指针,再访问到 type 结构中的 member
成员。在 container_of 宏中,它用来给 typeof() 提供参数,以获得 member 成员的数
据类型;

---------------container_of()--------------------
container_of() 来自/linux/kernel.h
内核中的注释:container_of - cast a member of a tructure 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 truct.

#define container_of(ptr, type, member) ({ /
const typeof( ((type *)0)->member ) *__mptr = (ptr); /
(type *)( (char *)__mptr - offsetof(type,member) );})
自己分析:
1.(type *)0->member为设计一个type类型的结构体,起始地址为0,编译器将结构体的起
始的地址加上此结构体成员变量的偏移得到此结构体成员变量的偏移地址,由于结构体起
始地址为0,所以此结构体成员变量的偏移地址就等于其成员变量在结构体内的距离结构体
开始部分的偏移量。即:&(type *)0->member就是取出其成员变量的偏移地址。而其等于
其在结构体内的偏移量:即为:(size_t)(& ((type *)0)->member)经过size_t的强制类型
转换后,其数值为结构体内的偏移量。该偏移量这里由offsetof()求出。

2.typeof( ( (type *)0)->member )为取出member成员的变量类型。用其定义__mptr指针
.ptr为指向该成员变量的指针。__mptr为member数据类型的常量指针,其指向ptr所指向的
变量处。

3.(char *)__mptr转换为字节型指针。(char *)__mptr - offsetof(type,member) )用来
求出结构体起始地址(为char *型指针),然后(type *)( (char *)__mptr - offsetof(
type,member) )在(type *)作用下进行将字节型的结构体起始指针转换为type *型的结构
体起始指针。
这就是从结构体某成员变量指针来求出该结构体的首指针。指针类型从结构体某成员变量
类型转换为该结构体类型。

-----------茶余饭后一点小资料----------------------
学辛苦了,看点收集的小东东:
以下文字摘自微软中国研究院前任院长,现微软高级副总裁李开复先生《一封写给中国学
生的信》:
“我的老板 Rick室Rashid博士是目前微软公司主管研究的高级副总裁,他已经功成名就,
却始终保持一颗学习和进取的心。现在,他每年仍然编写大约50,000行程序。他认为:用
最新的技术编程可以使他保持对计算机最前沿技术的敏感,使自己能够不断进步。今天,
有些博士生带低年级的本科生和硕士生做项目,就自满地认为自己已经没有必要再编程了
。其实,这样的做法是很不明智的。”

--------------arch-v32/cache.h------------------
#ifndef _ASM_CRIS_ARCH_CACHE_H
#define _ASM_CRIS_ARCH_CACHE_H


#define L1_CACHE_BYTES 32
#define L1_CACHE_SHIFT 5
#define L1_CACHE_SHIFT_MAX 5

#endif
分析:
也可用#define L1_CACHE_BYTES (1UL<<L1_CACHE_SHIFT)来实现

-------------asm-i386/cache.h--------------------

#ifndef __ARCH_I386_CACHE_H
#define __ARCH_I386_CACHE_H


#define L1_CACHE_SHIFT (CONFIG_X86_L1_CACHE_SHIFT)
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)

//largest L1 which this arch supports
#define L1_CACHE_SHIFT_MAX 7

#endif
分析:cache行在32位平台上多为32字节,但在I386平台上也有128字节的。

----------/linux/prefetch.h--------------------
这里是内核中的解释:(含有自己的分析)

#define _LINUX_PREFETCH_H

#ifndef ARCH_HAS_PREFETCH
static inline void prefetch(const void *x) {;}
#endif

#ifndef ARCH_HAS_PREFETCHW
static inline void prefetchw(const void *x) {;}
#endif

#ifndef ARCH_HAS_SPINLOCK_PREFETCH
#define spin_lock_prefetch(x) prefetchw(x)
#endif

#ifndef PREFETCH_STRIDE
#define PREFETCH_STRIDE (4*L1_CACHE_BYTES)
#endif //PREFETCH_STRIDE

static inline void prefetch_range(void *addr, size_t len)
{
#ifdef ARCH_HAS_PREFETCH
char *cp;
char *end = addr + len;

for (cp = addr; cp < end; cp += PREFETCH_STRIDE)
prefetch(cp);
#endif //ARCH_HAS_PREFETCH
}

#endif //_LINUX_PREFETCH_H

-----asm-x86_64/processor.h---prefetch()---------
static inline void prefetch(void *x)
{
asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x));
}
分析:
将x指针作强制类型转换为unsigned long *型,然后取出该内存操作数,送入高速缓存。

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

----------------__list_for_each()-----------------
Linux Kernel 2.6.14中的解释中的精华部分:

#define __list_for_each(pos, head) /
for (pos = (head)->next; pos != (head); pos = pos->next)
list_for_each()有prefetch()用于复杂的表的遍历,而__list_for_each()无prefetch()
用于简单的表的遍历.
注意:head在宏定义中用了括号将其括起来.

----------------list_for_each_prev()-------------
#define list_for_each_prev(pos, head) /
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); /
pos = pos->prev)
解释类似上面的list_for_each()。

----------------list_for_each_safe()--------------
内核中解释的精华部分:

#define list_for_each_safe(pos, n, head) /
for (pos = (head)->next, n = pos->next; pos != (head); /
pos = n, n = pos->next)
这是说你可以边遍历边删除,这就叫safe。十分精彩。刚开始时,我也一直不理解safe的
意思,后来在www.linuxforum.net论坛上搜索list_for_each_safe找到了解答。

----------------list_entry()--------------------
#define list_entry(ptr, type, member) /
container_of(ptr, type, member)
分析:
list_entry()函数用于将指向某链表结点成员的指针调整到该链表的开始处,并指针转换
为该链表结点的类型。

-------------list_for_each_entry()---------------
#define list_for_each_entry(pos, head, member) /
for (pos = list_entry((head)->next, t ypeof(*pos), member); /
prefetch(pos->member.next), &pos->member != (head); /
pos = list_entry(pos->member.next, typeof(*pos), member))
分析:
1.杨沙洲--国防科技大学计算机学院--2004年8月指出:
大多数情况下,遍历链表的时候都需要获得链表节点数据项,也就是说list_for_each()和
list_entry()总是同时使用。对此Linux给出了一个list_for_each_entry()宏。

2.这是用于嵌套的结构体中的宏:(这个程序例子来自:《Linux内核分析及编程》作者:
倪继利 电子工业出版社)
struct example_struct
{
struct list_head list;
int priority;
... //其他结构体成员
};
struct example_struct *node = list_entry(ptr,struct example_struct,list);

自己分析:对比list_entry(ptr,type,member)可知有以下结果:
其中list相当于member成员,struct example_struct相当于type成员,ptr相当于ptr成员
。而list{}成员嵌套于example_struct{}里面。ptr指向example_struct{}中的list成员变
量的。在list_entry()作用下,将ptr指针回转指向struct example_struct{}结构体的
开始处。

3.pos当指向外层结构体,比如指向struct example_struct{}的结点,最开始时候,pos当
指向第一个结点。而head开始时候也是指向第一个外层结点的里面的这个内嵌的链表结构
体struct list_head{},(head)->next则指向后继的一个外层结点的内嵌的链表结点
struct list_head{} list。member即是指出该list为其内嵌的结点。
思路:用pos指向外层结构体的结点,用head指向内层嵌入的结构体的结点。用(head)->
next,pos->member.next(即:ptr->list.next)来在内嵌的结构体结点链表中遍历。每遍历
一个结点,就用list_entry()将内嵌的pos->member.next指针回转为指向该结点外层结构
体起始处的指针,并将指针进行指针类型转换为外层结构体型pos。&pos->member! = (
head)用pos外层指针引用member即:list成员,与内层嵌入的链表之头结点比较来为循环
结束条件。

-------------list_for_each_entry_reverse()-------
#define list_for_each_entry_reverse(pos, head, member) /
for (pos = list_entry((head)->prev, typeof(*pos), m+ember); /
prefetch(pos->member.prev), &pos->member != (head); /
pos = list_entry(pos->member.prev, typeof(*pos), member))
分析类似上面。

---------------list_prepare_entry()---------------
1.函数背景:来自杨沙洲.国防科技大学计算机学院.2004年8月.www.linuxforum.net
Linux 内核技术论坛:
杨在贴子中指出:如果遍历不是从链表头开始,而是从已知的某个pos结点开始,则可以使
用list_for_each_entry_continue(pos,head,member)。有时还会出现这种需求,即经过一
系列计算后,如果pos有值,则从pos开始遍历,如果没有,则从链表头开始,为此,
Linux专门提供了一个list_prepare_entry(pos,head,member)宏,将它的返回值作为list
_for_each_entry_continue()的pos参数,就可以满足这一要求。

2.内核中的list_prepare_entry()的注释及代码:


内核源代码:
#define list_prepare_entry(pos, head, member) /
((pos) ? : list_entry(head, typeof(*pos), member))
分析:
:前面是个空值,即:若pos不为空,则pos为其自身。等效于:
(pos)? (pos): list_entry(head,typeof(*pos),member)
注意内核格式::前后都加了空格。

------------list_for_each_entry_continue()--------
3.内核中的list_for_each_entry_continue()的注释及代码:


内核源代码:
#define list_for_each_entry_continue(pos, head, member) /
for (pos = list_entry(pos->member.next, typeof(*pos), member); /
prefetch(pos->member.next), &pos->member != (head); /
pos = list_entry(pos->member.next, typeof(*pos), member))
分析见list_prepare_entry()中的函数背景。

-------------list_for_each_entry_safe()-----------
1.函数背景:来自杨沙洲.国防科技大学计算机学院.2004年8月.www.linuxforum.net
Linux 内核技术论坛:
杨在贴子中指出:list_for_each_entry_safe(pos, n, head,member),它们要求调用者另
外提供一个与pos同类型的指针n,在for循环中暂存pos下一个节点的地址,避免因pos节点
被释放而造成的断链。

2.内核中的注释与源代码:


#define list_for_each_entry_safe(pos, n, head, member) /
for (pos = list_entry((head)->next, typeof(*pos), member), /
n = list_entry(pos->member.next, typeof(*pos), member); /
&pos->member != (head); /
pos = n, n = list_entry(n->member.next, typeof(*n), member))
分析类似上面。容易明白。

--------list_for_each_entry_safe_continue()-------
#define list_for_each_entry_safe_continue(pos, n, head, member) /
for (pos = list_entry(pos->member.next, typeof(*pos), member), /
n = list_entry(pos->member.next, typeof(*pos), member); /
&pos->member != (head); /
pos = n, n = list_entry(n->member.next, typeof(*n), member))

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/njuhufan/archive/2009/10/17/4686334.aspx

poll为什么使用poll_list链表结构而不是数组 - 深入内核源码分析 动态扩展性:链表可以根据实际需要动态分配和释放内存,避免了固定大小数组带来的内存浪费或不足问题。高效的插入和删除操作:链表在插入和删除节点时具有常数时间复杂度,而数组需要移动大量元素,导致性能下降。优化内存管理:页对齐的内存分配可以简化内存管理操作,提高内存管理的效率。 阅读详情

相关推荐

Linux内核源码list.h解读

#list.h源码阅读 此文章是我阅读list.h后的一些见解,有问题且理解不到位的地方希望大家批评指正。 本次我们阅读的内核版本为4.18.7 在Linux内核中,list.h是内核为了方便使用链表而自己建立的链表头文件。 以__开头的是指内核函数。 ##链表 ##宏定义 除了调用其它头文件外,首先看到的是两个宏定义。 #define LIST_HEAD_INIT(name) { &amp;amp;...

weixin_43122409的博客 3934

Linux内核2.6.14源码分析-双向循环链表代码分析

Linux内核源码分析-链表代码分析分析人:余旭分析时间:2005年11月17日星期四 11:40:10 AM雨 温度:10-11度编号:1-4 类别:准备工作Email:yuxu9710108@163.com时代背景:开始在www.linuxforum.net Linux内核技术论坛上面发贴,在网友的帮忙下,解决了一些问题。版权声明:版权保留。本文用作其他用途当经作者本人同意,转载请注明作者姓名

njuhufan的专栏 2430

OO Programing in C

最近看c版很多人讨论c的面向对象,前天突然在别的论坛看到这个帖子,又因为今天早上醒得早,就copy过来了 http://www.javaeye.com/topic/150575http://www.javaeye.com/topic/150578http://www.javaeye.com/topic/150581作者:rubynroll -----------------------------

tianyu的专栏 - Linux site:blog.youkuaiyun.com/wishfly 1770

一句话技术——barrier内存屏蔽 (转)

#define barrier()  __asm__ __volatile__("": : :"memory") 1)__asm__用于指示编译器在此插入汇编语句<br />2)__volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。<br />3)memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据

1459

深入解析Linux内核源码注释与核心机制

Linux内核源码采用高度模块化的分层架构,其根目录下核心子系统各司其职:init/包含启动初始化代码,在此执行系统启动关键流程;kernel/承载进程调度、时间管理等核心逻辑;mm/负责内存管理机制;fs/实现VFS及各类文件系统接口;net/组织网络协议栈;drivers/涵盖设备驱动;arch/按架构划分底层代码(如x86、ARM)。顶层Makefile通过Kconfig构建配置体系,控制编译选项与模块裁剪。以5.10+版本为例,源码树通过scripts/和。

weixin_35756624的博客 1092

02_Linux内核分析及编程高清pdf版(2-2)

本书作者在整理自己多年研发笔记的基础上,以精心挑选的典型开发实例,向读者详细讲述了Linux内核源代码的各部分结构、原理及组成框架,主要分析了 Linux最新版本(2.6.11)的内核源代码,帮助读者深入理解Linux内核,精通Linux内核编程。全书分为20章,内容包括进程管理、进程间通信、内存管理、文件系统、I/O接口及资源管理、内核的编译及调试原理、网络通信、内核安全、USB驱动程序等。 对于想了解Linux开发,以及从事Linux内核编程的开发人员来说,本书是一本集大成之作,它既有讲解透彻的原理,也有详细实用的示例,更有作者多年从事实际开发工作的心得。本书主要针对从事Linux内核编程的中高级读者及软件工程师,也很合适作为大学教材和参考书。

详解Linux内核之双向循环链表

详解Linux内核之双向循环链表 本文详解了内核中面向对象的list结构的原理,以及如何以list为内嵌对象来构造自己的链表结构,如何从内嵌list对象获得自定义的对象指针;探讨了各种宏或者函数的详细使用方法及怎样以通用list结构来操作自定义对象。 【关键字】双向循环链表,list,list_entry,typeof,containerof,list_for_each,list_for_ea

shuyong1999的专栏 1520

Linux源码List.h文件详细分析(链表、队列、堆栈、哈希表等)

对Linux内核源码中list.h文件自己分析总结,主要是一些双向循环链表的操作及一些数据结构的实现,方便借鉴引用。

linux内核中链表代码分析---list.h头文件分析(一)【转】

转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17:13:14 在学习数据结构时,有一个重要的知识点就是链表。对于链表的一些基本操作,它的最好学习资料就是内核中的list.h头文件,内核中大量的使用...

weixin_34343689的博客 90

SoC验证新思路:除了UVM仿真,你还可以试试Cadence JasperGold的形式化验证

本文深入探讨了Cadence JasperGold形式化验证在SoC设计中的创新应用,对比传统UVM仿真验证,详细解析了其数学穷尽性验证优势。通过实战案例展示了JasperGold在总线协议、时钟域交叉等关键场景的验证方法,并提供了混合验证流程设计与效能优化方案,帮助工程师提升验证效率30%以上。

weixin_30566063的博客 120

Figma入门-自动布局

Figma原型图制作内置组件友好,自动布局类似CSS flex,通过布局网格控制对齐、分布、换行等,前端熟悉的同学易上手,参数设置基本一致。

我码玄黄的博客 2174

linux内核源码分析笔记

一、内核源码目录结构 1、Linux 内核源代码包括三个主要部分 1)内核核心代码:包括linux内核整体架构分析笔记描述的各子系统和子模块,以及其他支撑子系统,如:电源管理、linux初始化等。 2)非核心代码:例如库文件(因为 Linux 内核是一个自包含的内核,即内核不依赖其它的任何软件,自己就可以编译通过)、固件集合、 KVM(虚拟机技术)等。 3)编译脚本、配置文件、帮助文档、版权说明等辅助性文件。 2、内核源代码顶层目录结构: include/ :内核头文件,需要提供给外部模块(例..

locahuang的博客 6532

别再凭感觉了!手把手教你用Excel搞定激光器MPE与NOHD安全计算(附模板下载)

本文详细介绍了如何利用Excel自动化计算激光器的MPE(最大允许照射量)和NOHD(标称眼危害距离),提升激光安全评估的效率和准确性。通过实战案例和模板下载,帮助工程师快速掌握GB/T 7247标准中的关键计算,避免常见错误,适用于激光测距仪等设备的安评工作。

weixin_30954265的博客 466

Linux内核源码学习之链表

链表是Linux内核中最简单、最普通的数据结构,运用及其广泛。链表是一种存放和操作可变数量元素(常称为节点)的数据结构。链表和静态数组的不同之处在于,它所包含的元素都是动态创建并插入链表的。在编译时不必知道具体需要创建多少个元素,另外由于每个元素的创建时间都不相同,所以它们在内存中无需占用连续内存区。由于元素是不连续存放,所以各元素需要通过某种方式被连接在一起,于是每个元素都包含一个...

Enjoy working的博客 549

YOLOv8损失函数升级:从CIoU到Wise-IoU v3的实战调优与避坑指南

本文详细介绍了如何将YOLOv8的损失函数从CIoU升级到Wise-IoU v3,以提升目标检测性能,特别是在小目标检测和样本不均衡场景下的表现。通过实战调优步骤和避坑指南,帮助开发者有效应用Wise-IoU v3的动态聚焦机制和单调性控制,显著提升模型精度和训练稳定性。

weixin_33737774的博客 590

借助生成式 AI 加快创作速度,通过快速构建 Omniverse 扩展程序,您可以直接在 USD Composer 中连接首选的生成式 AI 智能体并与之交互。或者,您也可以使用由 LLM 提供支持的

通过快速构建 Omniverse 扩展程序,您可以直接在 USD Composer 中连接首选的生成式 AI 智能体并与之交互。或者,您也可以使用由 LLM 提供支持的 OpenUSD 开发方法,为您的工作流程提供强劲助力。

pvmsmfchcs的博客 683

linux内核源码“双向链表list_head”

linux内核源码“双向链表list_head”

北京开源人Linux运维实战 897

华为od面试手撕真题【分解质因数】

华为od面试手撕真题【分解质因数】

MISAYAONE的博客 1133

Linux内核2.6.14源码分析-双向循环链表代码分析(巨详细)

<!--google_ad_client = "pub-2132256014190581";/* 160x600, 创建于 09-1-4 */google_ad_slot = "4461398019";google_ad_width = 160;google_ad_height = 600;//-->Linux内核源码分析-链表代码分析 分析人:余旭 分

嵌入式 Linux ARM ADS ADHOC C C++ PLC 单片机 6166

uniapp(全端兼容)-最新详细实现接入腾讯云IM即时通讯教程,uniapp在安卓苹果app端/小程序/h5移动端等集成使用腾讯云im插件,提供消息列表与聊天对话示例,支持发图片/私聊/实时监听消息

uni-app,腾讯云IM,安卓app,苹果app,微信小程序,移动端,H5网页,手机网站,微信小程序、支付宝小程序,手机app端,nvue,im即时通讯,腾讯云IM详细教程,如何接入腾讯IM即时通讯,使用腾讯im有没有插件,怎么使用腾讯云IM插件,tim-wx-sdk,聊天sdk,即时通讯im uniapp私聊/群聊/聊天室,实现IM即时通讯仿微信聊天功能,对接腾讯云IM+音视频,调用腾讯即时通信IM的示例,卡顿很卡不流畅,音视频,全局监听,仿微信IM,uni-huanxin-im,sdk,实现多端实时通

✅ 精选推荐文章 1万+

YOLOv11改进-卷积-引入小波卷积WTConv 解决多尺度小目标问题

1.首先,WTConv利用二维Haar小波变换对输入图像进行多级分解。低频分量 (LL):捕捉图像的低频信息,如整体形状或轮廓。水平高频分量 (LH):捕捉图像中的水平边缘信息。垂直高频分量 (HL):捕捉图像中的垂直边缘信息。对角线高频分量 (HH):捕捉图像的对角线细节。在每一级的小波变换中,图像被下采样(空间分辨率减半),但频率信息得到了更细的分解。递归地执行小波变换(称为多级分解)可以得到不同尺度下的频率分量。

qq_64693987的博客 1万+

Linux 2.6 内核源码中的循环双链表结构

文章目录内核源码分析双链表数据结构定义双链表头节点定义宏双链表头节点初始化函数用于循环双链表结构处理的函数1. __list_add() 函数2. list_add() 函数3. list_add_tail() 函数4. __list_del()5. list_del() 和 list_del_init()6. list_replace() 和 list_replace_init()7. list_move 和 list_move_tail 内核源码分析 双链表数据结构定义 循环双链表定义位于 linu

啸铁 480

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

linux内核源码“双向链表list_head”续

linux内核源码“双向链表list_head”续

北京开源人Linux运维实战 767

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

链表与它在 Linux 内核中的实现

在系统学习链表之前,我们先回顾一下链表的相关知识,这里我不是用教科书上的枯燥定义,而是使用通俗易懂的语言描述一下。经过上面分析,我们知道了,数组是依靠位置连续和偏移量计算来实现快速定位一个元素的。而链表靠的是每个节点记住自己相邻节点的地址来实现将所有节点串联起来的。所以即使Node A和Node B的地址差了十万八千里,只要它们互相指着对方,逻辑上它们就是相邻的节点,遍历、插入、删除时都不会有任何问题。这正是链表最核心的的思想,也是它能做到任意位置高效增删的根本原因,当然,代价就是随机访问变得非常慢。

分享一些嵌入式Linux相关知识 1267

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

linux内核数据结构分析之链表

但是,这里还有个问题需要解决,那就是,怎么能通过结构体的某个成员来获取另一个成员的值呢?这就需要内核中定义的一个container_of宏定义:container_of 的主要作用是通过给定的结构体成员的地址来获取整个结构体的起始地址。这在处理复杂的数据结构,如链表时非常有用。

qq_28576837的博客 1332
上一篇: 浅谈linux内核中的list.h[2]
下一篇: VC18个技巧
tedious
博客等级 码龄21年 22粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值