您尚未登录。

楼主 #1 2018-08-21 20:22:10

xinxiaoci
会员
注册时间: 2018-04-18
已发帖子: 71
积分: 71

Linux_kernel 简单跟踪分析001

内核启动流程分析

  1. 内核打补丁、配置、编译、烧写、实验
    a. 解压缩、打补丁
    tar xjf linux-2.6.22.6.tar.bz2
    cd linux-2.6.22.6
    patch -p1 <../linux-2.6.22.6_jz2440.patch
    b. 配置
    . make menuconfig // 自己根据菜单配置
    配置项很多,比较麻烦。
    . 使用默认配置,并在上面修改
    find -name "defconfig" // 查找所有默认配置文件
    cd ./arch/arm/configs // ARM 架构
    找到相似的配置文件 s3c2410_defconfig
    cd linux-2.6.22.6/
    make s3c2410_defconfig //所有的配置项被写入 .config 文件中去
    make menuconfig // 读取 .config 以菜单形式显示
    教程中用的Ubuntu9.10
    实际用的ubuntu 16.04 make s3c2410_defconfig 报错

Makefile:416: *** mixed implicit and normal rules: deprecated syntax
Makefile:1449: *** mixed implicit and normal rules: deprecated syntax
make: *** No rule to make target ‘s3c2410_defconfig’。 停止。

https://blog.csdn.net/u013944565/article/details/77686569

. 使用厂家提供的配置文件
将厂家提供的配置文件 命名为 .config
make menuconfig

对应开发板 cp config_ok .config 即可
make menuconfig 报错
`Symbol ‘acs_map’ has different size in shared object, consider re-linking
解决方法:
sudo apt-get install libncurses5-dev libncursesw5-dev
https://blog.csdn.net/czg13548930186/article/details/79851149

make menuconfig 菜单操作

上下左右移动光标。
回车进入子菜单
Y 选中,编译进内核
N 不选中
M 编译为模块
加粗字体为相应的快捷键
? 为帮助
/ 搜索
esc 两次退出
c. 编译
make uImage
1> 根据.config 生成 autoconfig.h 文件,供原代码使用
2> 根据.config 生成 auto.config 文件,供子目录 Makefile 使用,在顶层Makefile中包含。

make uImage时出现错误:
UIMAGE arch/arm/boot/uImage
“mkimage” command not found - U-Boot images will not be built
Image arch/arm/boot/uImage is ready

解决方法:
安装u-boot-tools软件包:
sudo apt-get install u-boot-tools

d. 下载内核
在uboot菜单选择k
在电脑上用dnw.exe -> USB Poart -> Transmit 选择编译好的内核文件 uImage
生成的内核文件在 /work/kernel/linux-2.6.22.6/arch/arm/boot 文件下
根文件系统的删除:
在uboot 下执行 nand erase root

没有根文件时,内核初始化后就卡死了。

  1. 内核功能、结构,结合Makefile、Kconfig 进行分析

因为配置完后会生成一个.config 文件,所以我们分析 .config 文件中去

.config 里面都是配置项,我们以CONFIG_DM9000 为例分析。

grep "CONFIG_DM9000" * -nwR

1> arch/arm/plat-s3c24xx/common-smdk.c 源码用到 来自头文件 autoconfig.h
2> drivers/net/Makefile 子目录 Makefile 用到 // y 或 m 在 Makefile 体现 obj-$(CONFIG_DM9000) += dm9dev9000c.o Makefile中的CONFIG_DM9000 来自于 auto.config
如果是 y 则被编译进内核
如果是 m 则被编译成 .ko 模块
3> include/config/auto.conf // 来自.config自动生成 CONFIG_DM9000=y auto.conf 中的内容要被别的Makefile使用,所以它被包含在顶层的Makefile中
在顶层Makefile中查找 /auto.conf
-include include/config/auto.conf Makefile:443
4> include/linux/autoconfig.h // 来自.config自动生成 CONFIG_DM9000 = 1 不管是y 或 m 在头文件里都是1

分析Makefile和链接脚本
Makefile的介绍看韦老师书本 16.2.2 表 16.3
关于Makefile的文档请看: 源码下的Documentation/kbuid/Makefile.txt

子目录下的Makefile很简单
obj-y += a.o
obj-m += b.o

a.c b.c
怎么将两个源文件编译进内核
obj-y += a.o b.o
怎么将两个文件编译成模块 Makefile.txt->Line:190
obj-m += ab.o
ab-obj := a.o b.o

我们编译内核是使用 make uImage

所以我们查找 uImage
grep "uImage" * -nwR

uImage 在架构相关的文件中定义
arch/arm/Makefile:227:zImage Image xipImage bootpImage uImage: vmlinux

uImage 并没有在顶层Makefile中定义,所以 顶层文件应该包含 arch/arm/Makefile 文件

在顶层文件中搜索 include

/include n 下一个

Makefile:413
include $(srctree)/arch/$(ARCH)/Makefile

arch/arm/Makefile:227:zImage Image xipImage bootpImage uImage: vmlinux
继续分析 uImage 依赖于 vmlinux uImage = 头 + 真正的内核(vmlinux)

继续搜索 vmlinux 不在架构 Makefile 中,在顶层 Makefile 中有定义
grep "vmlinux" Makefile -nwR
745:vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE

展开:
$(vmlinux-lds) 是 链接脚本 arch/arm/kernel/vmlinux.lds

608:vmlinux-init := $(head-y) $(init-y)
/ 第一个文件 /
grep "head-y" arch/arm/Makefile -nwR // 架构 Makefile 中
94:head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o // 最初始的代码 第一个链接的应该是 arch/arm/kernel/head$(MMUEXT).o
/ 初始化 /
grep "init-y" Makefile -nwR // 顶层 Makefile 中
434:init-y := init/
573:init-y := $(patsubst %/, %/built-in.o, $(init-y)) // init-y = init/built-in.o 最终init 目录下的文件会被编译成一个built-in.o文件 (patsubst 是一个Makefile 函数)
609:vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
/ 核心 /
grep "core-y" Makefile -nwR
438:core-y := usr/
562:core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
574:core-y := $(patsubst %/, %/built-in.o, $(core-y))
最终 core-y := kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o

//
grep "libs-y" Makefile -nwR
437:libs-y := lib/
577:libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
578:libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
579:libs-y := $(libs-y1) $(libs-y2)
最终 libs-y := lib/lib.a lib/built-in.o

/ 驱动 /
grep "drivers-y" Makefile -nwR
435:drivers-y := drivers/ sound/
575:drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
最终 drivers-y := drivers/built-in.o sound/built-in.o

/ 网络 /
grep "net-y" Makefile -nwR
436:net-y := net/
576:net-y := $(patsubst %/, %/built-in.o, $(net-y))
最终 net-y := net/built-in.o

610:vmlinux-all := $(vmlinux-init) $(vmlinux-main)
611:vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds

手工展开太过复杂,所以我查看编译的最后一条信息:
rm vmlinux // 删除 vmlinux 重新编译
make uImage V=1 // V=1 显示详细的编译信息

arm-linux-ld -EL -p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o --start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/mach-s3c2410/built-in.o arch/arm/mach-s3c2400/built-in.o arch/arm/mach-s3c2412/built-in.o arch/arm/mach-s3c2440/built-in.o arch/arm/mach-s3c2442/built-in.o arch/arm/mach-s3c2443/built-in.o arch/arm/nwfpe/built-in.o arch/arm/plat-s3c24xx/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o
通过Makefile的分析我们大概能知道内核的结构,看韦老师书本 16.2.2 表 16.2

确定链接脚本:arch/arm/kernel/vmlinux.lds // 根据 vmlinux.lds.S 生成的 链接脚本中的链接地址是虚拟地址
确定第一个文件:arch/arm/kernel/head.o
然后就可以根据第一个文件,一路跟踪下去了。

  1. 内核的启动流程分析 详细请看 韦老师书本 16.3.1 图 16.7
    a. __lookup_processor_type 确定内核是否支持该架构
    b. __lookup_machine_type 确定内核时候支持该单板 u-boot 传入的第二个参数
    c. __create_page_tables 建立一级页表
    d. _arm920_setup 禁止ICache、DCache
    e. __enable_mmu 使能MMU
    f. __mmap_switched 复制数据段,清除BSS段,设置栈指针,保存CPU ID 到processor_id 变量、保存机器类型ID到_machine_arch_type变量,调用start_kernel

分析 arch/arm/kernel/head.S 文件

bl __lookup_processor_type @ r5=procinfo r9=cpuid // 协处理器获取处理器类型
bl __lookup_machine_type @ r5=machinfo // 判断传入的机器ID 在head-common.S 文件中去

__lookup_machine_type: @ 查找机器类型 u-boot 传入的di 2 个参数
adr r3, 3b @ r3 = address of 3b ,real address, phy address 3: .long . qian mian
ldmia r3, {r4, r5, r6} @ r4 ="." virtual address of 3b, r5 = __arch_info_begin , r6 = __arch_info_end r5 r6 在链接脚本中定义,中间是架构信息初始化段 .arch.info.init
sub r3, r3, r4 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space 转换虚拟地址到物理地址(当前的虚拟地址只是链接时候用的虚拟地址,因为MMU还没有启动)
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
teq r3, r1 @ matches loader number? r1 di 2 个参数
beq 2f @ found 2f hou mian 2: mov pc, lr
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
cmp r5, r6
blo 1b
mov r5, #0 @ unknown machine
2: mov pc, lr

在链接脚本中:
arch/arm/kernel/vmlinux.lds
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
搜索 .arch.info.init

grep ".arch.info.init" * -nR

Binary file arch/arm/mach-s3c2412/mach-smdk2413.o matches
Binary file arch/arm/mach-s3c2412/built-in.o matches
Binary file arch/arm/mach-s3c2412/mach-vstms.o matches
Binary file arch/arm/mach-s3c2410/mach-qt2410.o matches
Binary file arch/arm/mach-s3c2410/mach-smdk2410.o matches
Binary file arch/arm/mach-s3c2410/built-in.o matches
Binary file arch/arm/mach-s3c2440/mach-smdk2440.o matches
Binary file arch/arm/mach-s3c2440/built-in.o matches
Binary file arch/arm/mach-s3c2443/mach-smdk2443.o matches
Binary file arch/arm/mach-s3c2443/built-in.o matches // 内核也支持上面这些单板
arch/arm/kernel/vmlinux.lds.S:39: *(.arch.info.init) // 生成的链接脚本
arch/arm/kernel/vmlinux.lds:306: *(.arch.info.init) // 真实的链接脚本
include/asm-arm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = { \ // 文件夹链接
include/asm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = {\ // 真实的文件

arch.h 找到定义

#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,

#define MACHINE_END \
};

搜索这个宏 MACHINE_START 谁在使用 ,选择其中一个单板 Mach-smdk2440.c (arch\arm\mach-s3c2440):MACHINE_START(S3C2440, "SMDK2440")

MACHINE_START(S3C2440, "SMDK2440")
/ Maintainer: Ben Dooks <ben@fluff.org> /
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,

.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END

展开:

static const struct machine_desc __mach_desc_S3C2440 \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_S3C2440, \
.name "SMDK2440",
/ Maintainer: Ben Dooks <ben@fluff.org> /
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,

.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
};

相当于定义了一个结构体 machine_desc 这个结构体被放在 .arch.info.init 段,查看结构体 machine_desc

struct machine_desc {
/*

  • Note! The first four elements are used

  • by assembler code in head-armv.S
    */
    unsigned int nr; / architecture number /
    unsigned int phys_io; / start of physical io /
    unsigned int io_pg_offst; /* byte offset for io

  • page tabe entry */

const char name; / architecture name */
unsigned long boot_params; / tagged list /

unsigned int video_start; / start of video RAM /
unsigned int video_end; / end of video RAM /

unsigned int reserve_lp0 :1; / never has lp0 /
unsigned int reserve_lp1 :1; / never has lp1 /
unsigned int reserve_lp2 :1; / never has lp2 /
unsigned int soft_reboot :1; / soft reboot /
void (fixup)(struct machine_desc ,
struct tag *, char **,
struct meminfo *);
void (map_io)(void);/ IO mapping function */
void (*init_irq)(void);
struct sys_timer timer; / system tick timer */
void (*init_machine)(void);
};

继续分析 :arch/arm/kernel/head.S

bl __create_page_tables // 创建一级页表
ldr r13, __switch_data @ address to jump to after mmu has been enabled 当MMU使能之后 跳转到 __mmap_switched
adr lr, __enable_mmu @ return (PIC) address 使能MMU

ldr r13, __switch_data 的意思:
r13 = __switch_data的地址 查看 __switch_data:head-common.S (arch\arm\kernel):__switch_data:
__switch_data:
.long __mmap_switched // 相当于 r13 中保存了 __mmap_switched 函数的入口地址,这个地址是运行时地址 (虚拟地址,因为链接脚本中用的是虚拟地址)
.long __data_loc @ r4
.long __data_start @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
.long __machine_arch_type @ r5
.long cr_alignment @ r6
.long init_thread_union + THREAD_START_SP @ sp

adr lr, __enable_mmu 的意思:
相对地址adr 当前地址 与链接位置无关 。查看 __enable_mmu

__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
orr r0, r0, #CR_A
#else
bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I
#endif
mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT))
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
b __turn_mmu_on // MMU结尾调用了 __turn_mmu_on

分析 __turn_mmu_on 代码:

__turn_mmu_on:
mov r0, r0
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
mov r3, r3
mov r3, r3
mov pc, r13 // 将PC 指针指向 R13 的运行地址 即 __mmap_switched 函数的入口地址

再分析__mmap_switched 代码:
__mmap_switched:
adr r3, __switch_data + 4

ldmia r3!, {r4, r5, r6, r7}
cmp r4, r5 @ Copy data segment if needed
1: cmpne r5, r6
ldrne fp, [r4], #4
strne fp, [r5], #4
bne 1b

mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b

ldmia r3, {r4, r5, r6, sp}
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
bic r4, r0, #CR_A @ Clear 'A' bit
stmia r6, {r0, r4} @ Save control register values
b start_kernel // 最终调用 start_kernel C 函数 到这个位置,第一阶段代码就算完毕了,更复杂的功能在C语言中实现。

第一阶段并没有使用到u-boot 传递进来的参数,所以应该是在C函数中实现的,我们继续分析 start_kernel

一些初始化函数
printk(linux_banner); // 打印内核信息
setup_arch(&command_line);// u-boot 传入进来的命令行
setup_command_line(command_line);

分析 setup_arch(&command_line) 函数:

struct tag tags = (struct tag )&init_tags; // 标签
struct machine_desc *mdesc; // 架构信息的结构 machine_desc
char *from = default_command_line; // 默认命令行参数 , 如果u-boot 没有传入参数会执行默认命令行参数。

// 物理地址转虚拟地址
tags = phys_to_virt(mdesc->boot_params); // mdesc->boot_params = S3C2410_SDRAM_PA + 0x100 = 物理地址+0x100 刚好等于 u-boot 参数的存放地址 0x30000100 theKernel (0, bd->bi_arch_number, bd->bi_boot_params)
parse_tags(tags); // 解析 tags

parse_cmdline(cmdline_p, from); // 单独解析命令行
....

返回 start_kernel 函数继续分析:
start_kernel()
...
setup_arch(&command_line); // 解析 u-boot 传入进来的参数
setup_command_line(command_line); // 解析 u-boot 传入进来的参数
...
parse_early_param();
do_early_param
从 __setup_start 扫描到 __setup_end 调用 early 函数
unknown_bootoption
obsolete_checksetup
从 __setup_start 扫描到 __setup_end 调用 非 early 函数
...
rest_init();
创建 kernel_init 线程 :kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
prepare_namespace();
mount_root();// 我们的最终目的是挂接根文件系统
init_post();
sys_open((const char __user *) "/dev/console", O_RDWR, 0)// 打开控制台
// 执行应用程序
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");

挂载根文件系统是哪个盘/分区?

传递进来的参数是 char *commandline = getenv ("bootargs");// 来自于环境变量 bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
root=/dev/mtdblock3 根文件放在第四个分区上
prepare_namespace(); 函数中 根文件挂载 mount_root(); 之前肯定要确定挂接的是哪个文件系统

进入 prepare_namespace
if (saved_root_name[0]) {
root_device_name = saved_root_name; // 设备名字在 saved_root_name[0] 数组中
if (!strncmp(root_device_name, "mtd", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name); // ROOT_DEV 根文件设备 root_device_name 设备名字
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}

搜索 saved_root_name

Do_mounts.c (init):static char __initdata saved_root_name[64];
Do_mounts.c (init): strlcpy(saved_root_name, line, sizeof(saved_root_name));
Do_mounts.c 中 下面这两段代码与 u-boot 中的命令行相似 一个结构对应一个函数,我们可以猜测,先检测到 "root=" 这个字符串,然后执行对应的函数 root_dev_setup

static int __init root_dev_setup(char *line)
{
strlcpy(saved_root_name, line, sizeof(saved_root_name));// root_dev_setup 把/dev/mtdblock3 保存到 saved_root_name 中去
return 1;
}

__setup("root=", root_dev_setup); // 这里有 "root="

分析 __setup 宏,这个宏是一个结构体变量,它被强制定义在某一个段内,然后会有其他程序从这个段的起始地址轮训到结束地址去执行这些 __setup 命令。
搜索 __setup 搜索结果中搜索 define

Init.h (include\linux):#define __setup(str, fn) \

#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)

查找 __setup_param 的定义

#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata = str; \
static struct obs_kernel_param __setup_##unique_id \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }

根据上面的信息对 __setup("root=", root_dev_setup) 进行展开

#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_root_dev_setup[] __initdata = "root="; \ // 定义一个字符串 __setup_str_root_dev_setup
static struct obs_kernel_param __setup_root_dev_setup \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_root_dev_setup, root_dev_setup, 0 } // __setup_str_root_dev_setup 对应的函数 root_dev_setup

.init.setup 在链接脚本的定义 :
__setup_start = .;
*(.init.setup)
__setup_end = .;

搜索 __setup_start __setup_end 看谁调用扫描了这些段,以及执行了这些对应函数

Main.c (init):extern struct obs_kernel_param __setup_start[], __setup_end[]; // 链接脚本地址声明
Main.c (init): p = __setup_start; // static int __init obsolete_checksetup(char *line) 函数
Main.c (init): for (p = __setup_start; p < __setup_end; p++) { // static int __init do_early_param(char param, char val) 函数

分析这两个函数
--------------------------------------------------

static int __init obsolete_checksetup(char *line)
{
struct obs_kernel_param *p;
int had_early_param = 0;

p = __setup_start;
do {
int n = strlen(p->str);
if (!strncmp(line, p->str, n)) {
if (p->early) { // p->early = 1
/* Already done in parse_early_param?

  • (Needs exact match on param part).

  • Keep iterating, as we can have early

  • params and __setups of same names 8( */
    if (line[n] == '\0' || line[n] == '=')
    had_early_param = 1;
    } else if (!p->setup_func) { // 对应的函数为空
    printk(KERN_WARNING "Parameter %s is obsolete,"
    " ignored\n", p->str);
    return 1;
    } else if (p->setup_func(line + n)) // 执行 __setup 中字符串对应的函数 p->early = 0 且 对应的函数不为空 上面 __setup("root=", root_dev_setup) 展开中 p->early = 0 所以 __setup("root=", root_dev_setup) 在这里面调用
    return 1;
    }
    p++;
    } while (p < __setup_end);

return had_early_param;
}

obsolete_checksetup 谁来调用的

static int __init unknown_bootoption(char param, char val)
{
/ Change NUL term back to "=", to make "param" the whole string. /
if (val) {
/ param=val or param="val"? /
if (val == param+strlen(param)+1)
val[-1] = '=';
else if (val == param+strlen(param)+2) {
val[-2] = '=';
memmove(val-1, val, strlen(val)+1);
val--;
} else
BUG();
}

/ Handle obsolete-style parameters /
if (obsolete_checksetup(param)) // 传入参数,调用 obsolete_checksetup
return 0;
...... // 略
......
}

unknown_bootoption 由 C 入口函数 asmlinkage void __init start_kernel(void) 调用

parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption); // 调用 unknown_bootoption parse_args 不在细分 应该是给 unknown_bootoption 传递参数 并调用

--------------------------------------------------
/ Check for early params. /
static int __init do_early_param(char param, char val)
{
struct obs_kernel_param *p;

for (p = __setup_start; p < __setup_end; p++) {
if (p->early && strcmp(param, p->str) == 0) { // 判断传入的参数 param 是否 等于 p->str 等于是在.init.setup 段里面扫描匹配字符串 且 p->early = 1 执行 上面 __setup("root=", root_dev_setup) 展开中 p->early = 0 所以 __setup("root=", root_dev_setup) 并不在这里面调用
if (p->setup_func(val) != 0) // p->setup_func(val) 执行对应的函数
printk(KERN_WARNING
"Malformed early option '%s'\n", param);
}
}
/ We accept everything at this stage. /
return 0;
}

do_early_param 谁来调用的

void __init parse_early_param(void)
{
static __initdata int done = 0;
static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];

if (done)
return;

/ All fall through to do_early_param. /
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
parse_args("early options", tmp_cmdline, NULL, 0, do_early_param); // 这里调用 do_early_param parse_args 有时间自己分析 应该是给 do_early_param 传递参数并调用
done = 1;
}

parse_early_param 由 C 入口函数 asmlinkage void __init start_kernel(void) 调用
parse_early_param();

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

分区的概念:

root=/dev/mtdblock3 根文件放在第四个分区上,我们的NAND Flash 没有分区表,所以怎么确认第四个分区的?

由于没有分区表,所以只能在代码里面写死了
以u-boot 为例

bootloader||环境变量(参数)||kernel||文件系统(root)
可以输入 u-boot 命令 mtd 查看
#: name size offset mask_flags
0: boot_loader 0x00040000 0x00000000 0
1: params 0x00020000 0x00040000 0
2: kernel 0x00200000 0x00060000 0
3: root 0x0fda0000 0x00260000 0

怎么修改分区,参考韦老师书本 16.3.3 MTD 分区

如果换一个单板,或内核,不知道这些分区信息怎么办?
可以查看内核启动时的分区打印信息
然后在代码中搜索对应的字符串如:
grep "\"bootloader\"" * -nR

arch/arm/plat-s3c24xx/common-smdk.c:120: .name = "bootloader",

static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND, // 表示接着上一个分区
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};

更多请看 韦老师文本第 16 章

离线

#2 2018-08-21 21:26:25

晕哥
管理员
所在地: wechat: whycan_cn
注册时间: 2017-09-06
已发帖子: 9,473
积分: 9207

Re: Linux_kernel 简单跟踪分析001

https://whycan.cn/t_1075.html

xiaoci 真是一个努力的朋友,挖坑网见证小白到大神的过程!





离线

楼主 #3 2018-08-22 08:28:21

xinxiaoci
会员
注册时间: 2018-04-18
已发帖子: 71
积分: 71

Re: Linux_kernel 简单跟踪分析001

晕哥 wrote:

https://whycan.cn/t_1075.html

xiaoci 真是一个努力的朋友,挖坑网见证小白到大神的过程!

谢谢晕哥!最近的事情有点忙,耽误太多时间,前同事离职,几个烂尾项目都压在我手上,老板一直也在催进度!大女儿刚幼儿园报名要适应节奏,小孩要适应,家长也要适应。二孩还有一个月左右出生,老婆的脾气也是被现实生活折磨的非常敏感易怒,我也非常能理解她的心情,感觉自己在感情上成熟了不少(怕老婆了!!)。总之一言难尽,生活本就如此,有苦有甜吧。然后就是自己时间也安排的不是太合理,学习被一些乱七八糟的事情打乱,有点浮躁。但我不会放弃,最近可能会优先清理手头的烂尾工作,晚上抽时间出来看书学习,论坛进度更新。死磕到底,不会放弃。

离线

#4 2018-08-22 08:34:06

jumiao
会员
注册时间: 2018-08-22
已发帖子: 2
积分: 2

Re: Linux_kernel 简单跟踪分析001

刚刚百度到这里,看到楼主的发言,感觉一把辛酸泪,和我太相似了,都快哭出来了,生活不易, 且行且珍惜!

离线

#5 2018-08-22 08:38:28

jumiao
会员
注册时间: 2018-08-22
已发帖子: 2
积分: 2

Re: Linux_kernel 简单跟踪分析001

面对这已经不再年轻的自己,现实中各种压力,再加上今年第一批00后上大学,三年后面临着和第一批00后抢饭碗, 心中也是五味杂陈.

离线

#6 2018-08-22 08:48:45

晕哥
管理员
所在地: wechat: whycan_cn
注册时间: 2017-09-06
已发帖子: 9,473
积分: 9207

Re: Linux_kernel 简单跟踪分析001

xiaoxiaoci 的压力我感同身受, 不过现在两个孩子都大了,
比以前轻松很多了, 但是仍然面临各种问题和压力,
我想只要自己不放弃自己,未来就会充满希望.

未来你和你的家庭, 一定会感谢现在努力的你!





离线

#7 2018-08-22 09:23:54

Jin劲
会员
注册时间: 2018-04-06
已发帖子: 217
积分: 217

Re: Linux_kernel 简单跟踪分析001

刚出来工作 迷茫中...

离线

#8 2018-08-22 09:26:08

晕哥
管理员
所在地: wechat: whycan_cn
注册时间: 2017-09-06
已发帖子: 9,473
积分: 9207

Re: Linux_kernel 简单跟踪分析001

还要面临和小劲这样的实力派竞争 ...





离线

#9 2019-01-19 20:40:05

jw__liu
会员
注册时间: 2019-01-18
已发帖子: 40
积分: 40

Re: Linux_kernel 简单跟踪分析001

mark一下

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn


东莞哇酷科技有限公司开发