在
2.4(具体哪个版本记不清了)
以后的
Linux
内核中引入了一种新的向内核传递参数的方法
tag
标记。内核参数通过一个静态的
tag
链表在启动的时候传递到内核。每个
tag
的结构为
+-----------+
tag_header
+-----------+
tag_xxx
+-----------+
其中
tag_header
为
tag
头,表明
tag_xxx
的类型和大小,之所以要标识
tag_xxx
的类型是因为不同的
tag
需要不同的处理函数(下文讲
tagtable
的时候会分析到)。
tag_header
的结构为
|
struct
tag_header
{
int
size
;
int
tag;
}
|
size
表示
tag
的结构大小,
tag
为表示
tag
类型的常量。这个静态的链表必须以
tag_header.tag = ATAG_CORE
开始,并以
tag_header.tag = ATAG_NONE
结束。由于不同的
tag
所使用的格式可能不尽相同,所以内核又定义了一个结构
tagtable
来把
tag
和相应的操作函数关联起来
|
struct
tagtable
{
u32 tag;
int
(
*
parse)
(
const
struct
tag*
)
;
}
|
其中
tag
为标识入
ATAG_NONE,ATAG_CORE
等。
parse
为处理函数。
Linux
内核将
tagtable
也组成了一个静态的链表放入
.taglist.init
节中,这是通过
__tagtable
宏来实现的
|
#
define
__tag __attribute_used__ __attribute__(
(
__section__ (
“.
taglist.
init”)
)
)
#
define
__tagble(
tag,
fn)
static
struct
tagtable __tagtable_#
#
fn __tag =
{
tag,
fn}
|
以处理命令行参数为例
:
|
static
int
__init parse_tag_cmdline(
const
struct
tag*
tag)
{
strlcpy(
default_command_line,
tag-
>
u.
cmdline.
cmdline,
COMMAND_LINE_SIZE)
;
}
__tagtable(
ATAG_CMDLINE,
parse_tag_cmdline)
|
可以看到
parse_tag_cmdline
将命令行参数拷贝到
default_command_line
里,
__tagtable
将
ATAG_CMDLINE
和
parse_tag_cmdline
挂钩。
以上已经分析了内核和
tag
相关的两个重要结构。现在分析具体的实现。内核中定义了一些默认的
tags
|
static
struct
init_tags
{
struct
tag_header hdr1;
struct
tag_core core;
struct
tag_header hdr2;
struct
tag_mem32 mem;
struct
tag_header hdr3;
}
init_tags __initdata =
{
{
tag_size(
tag_core)
,
ATAG_CORE }
,
{
1,
PAGE_SIZE,
0xff }
,
{
tag_size(
tag_mem32)
,
ATAG_MEM }
,
{
MEM_SIZE,
PHYS_OFFSET }
,
{
0,
ATAG_NONE }
}
|
上述结构中一个
tag_header
和
tag_xxx
形成了
tag
的完整描述,
tag_size
返回
tag_head
和
tag_xxx
的总大小,在
tag_size
中我们要注意的是
u32*
指针加
1
地址值实际上地址加了
4
#define tag_next(t) ((struct tag*)((u32*)(t)
+(t)->hdr.size))
#define tag_size(type) ((sizeof(struct tag_header)+sizeof(struct type)) >> 2
tag_size
实际上计算的是
(tag_head+tag_xxx)/4
。经过进一步的分析还发现每个
tag
在内存中的大小并不是相同的,这一点可以从
tag_next
看出,
tag_next
只是将指针移到了下一个
tag
的
tag_header
处,这种内存布局更加紧凑。对
tag
的处理代码在
arch/arm/setup.c setup_arch
里面。以下是一部分的关键代码
|
struct
tag *
tags =
(
struct
tag*
)
&
init_tags;
//tags指向默认的tag链表
……
mdesc =
setup_machine(
machine_arch_type)
;
// mdesc包含启动参数在内存中的地址
if
(
mdesc-
>
boot_params )
tags =
phys_to_vert(
mdesc-
>
boot_params)
;
// bootloader有传递启动参数到内核
if
(
tags-
>
hdr.
tag !
=
ATAG_CORE )
convert_to_tag_list(
tags)
;
//如果是旧的启动参数结构,将其转成新的tag链表的形式
if
(
tags-
>
hdr.
tags !
=
ATAG_CORE )
tags =
(
struct
tag*
)
&
init_tags;
//转换失败,使用内置的启动参数
if
(
tags-
>
hdr.
tag =
=
ATAG_CORE )
{
if
(
meminfo.
nr_banks !
=
0 )
squash_mem_tags(
tags)
;
//如果在meminfo中有配置内存tag则跳过对内存tag的处理
parse_tags(
tags)
;
}
|
*
注
:2.6.18内核smdk2410
的
meminfo
没有设置
nr_banks
,所以必须在内核的启动参数里面传递
mem=”memory size”@”memory base address”
,否则系统识别内存错误,这点从系统的启动信息就可以看出来,而且在加载
initrd
的时候也会遇到内存溢出的错误
|
static
void
__init parse_tags(
const
struct
tag*
t)
{
for
(
;
t-
>
hdr.
size;
t=
tag_next(
t)
)
{
if
(
!
parse_tag(
t)
)
printk(
…)
;
}
}
|
parse_tags
遍历
tag
链表调用
parse_tag
对
tag
进行处理。
parse_tags
在
tabtable
中寻找
tag
的处理函数(通过
tag_header
结构中的
tag
)。