init函数详解

本文介绍了Android设备启动过程中的关键步骤,包括配置DRAM内存大小、fastboot模式的启用与禁用、splashscreen的显示控制等内容,并深入探讨了这些功能在bootloader阶段的实现细节。
1.主要功能,红色部分是android特有的一些功能,如fastboot,recovery模式等:
* Variety of nand devices for bootup
* USB driver to enable upgrading images over usb during development
* Keypad driver to enable developers enter ‘fastboot’ mode for image upgrades
* Display driver for debugging and splash screen
* Enable Android recovery image and image upgrades

2.配置dram内存大小,供linux kernel使用
The memory tags can be customized inlk/target/<target_name>/atags.c

3.fastboot模式,可以自行打开或者关闭
如,在boot中关闭按键或者usb 驱动,都可以达到此目的
相关文件
lk/app/aboot/fastboot.c
lk/app/aboot/aboot.c


4.MTD block setting
可以配置各个mtd image 分区在如下 文件中
lk\target\tcc8900_evm\init.c
static struct ptentry board_part_list[]

5.打开或者关闭splash screen in the bootloader
DISPLAY_SPLASH_SCREEN功能可以来打开关闭
开机时候,boot会从’splash’ MTD分区中读取原始的文件到framebuffer中显示,所以也需要加载display 的驱动
入口函数在 kernel/main.c 中的 kmain(), 以下就来读读这一段 code.

void kmain(void)
{
// get us into some sort of thread context
thread_init_early();
// early arch stuff
arch_early_init();
// do any super early platform initialization
platform_early_init();
// do any super early target initialization
target_early_init();
dprintf(INFO, "welcome to lk/n/n");

// deal with any static constructors
dprintf(SPEW, "calling constructors/n");
call_constructors();
// bring up the kernel heap
dprintf(SPEW, "initializing heap/n");
heap_init();
// initialize the threading system
dprintf(SPEW, "initializing threads/n");
thread_init();
// initialize the dpc system
dprintf(SPEW, "initializing dpc/n");
dpc_init();
// initialize kernel timers
dprintf(SPEW, "initializing timers/n");
timer_init();
#if (!ENABLE_NANDWRITE)
// create a thread to complete system initialization
dprintf(SPEW, "creating bootstrap completion thread/n");
thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
// enable interrupts
exit_critical_section();
// become the idle thread
thread_become_idle();
#else
bootstrap_nandwrite();
#endif
}

In include/debug.h: 我们可以看到 dprintf ()的第一个参数是代表 debug level.

/* debug levels */
#define CRITICAL 0
#define ALWAYS 0
#define INFO 1
#define SPEW 2
In include/debug.h:

view plainprint?
#define dprintf(level, x...) do { if ((level) <= DEBUGLEVEL) { _dprintf(x); } } while (0)

所以 dprintf 会依 DEBUGLEVEL 来判断是否输出信息.

来看第一个 call 的函数: thread_init_early, define in thread.c

view plainprint?
void thread_init_early(void)
{
int i;
/* initialize the run queues */
for (i=0; i < NUM_PRIORITIES; i++)
list_initialize(&run_queue[i]);
/* initialize the thread list */
list_initialize(&thread_list);
/* create a thread to cover the current running state */
thread_t *t = &bootstrap_thread;
init_thread_struct(t, "bootstrap");
/* half construct this thread, since we're already running */
t->priority = HIGHEST_PRIORITY;
t->state = THREAD_RUNNING;
t->saved_critical_section_count = 1;
list_add_head(&thread_list, &t->thread_list_node);
current_thread = t;
}

#define NUM_PRIORITIES 32 in include/kernel/thread.h

list_initialize() defined in include/list.h: initialized a list

view plainprint?
static inline void list_initialize(struct list_node *list)
{
list->prev = list->next = list;
}

run_queue 是 static struct list_node run_queue[NUM_PRIORITIES]

thread_list 是 static struct list_node thread_list

再来要 call 的函数是: arch_early_init() defined in arch/arm/arch.c

view plainprint?
void arch_early_init(void)
{
/* turn off the cache */
arch_disable_cache(UCACHE);
/* set the vector base to our exception vectors so we dont need to double map at 0 */
#if ARM_CPU_CORTEX_A8
set_vector_base(MEMBASE);
#endif
#if ARM_WITH_MMU
arm_mmu_init();
platform_init_mmu_mappings();
#endif
/* turn the cache back on */
arch_enable_cache(UCACHE);
#if ARM_WITH_NEON
/* enable cp10 and cp11 */
uint32_t val;
__asm__ volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (val));
val |= (3<<22)|(3<<20);
__asm__ volatile("mcr p15, 0, %0, c1, c0, 2" :: "r" (val));
/* set enable bit in fpexc */
val = (1<<30);
__asm__ volatile("mcr p10, 7, %0, c8, c0, 0" :: "r" (val));
#endif
}

现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要处理器中的MMU(Memory Management Unit,
内存管理单元)提供支持。
CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Address,以下简称VA),而MMU将这个地
址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将VA映射成PA
MMU将VA映射到PA是以页(Page)为单位的,32位处理器的页尺寸通常是4KB。例如,MMU可以通过一个映射项将VA的一页
0xb7001000~0xb7001fff映射到PA的一页0x2000~0x2fff,如果CPU执行单元要访问虚拟地址0xb7001008,则实际访问到的物理地
址是0x2008。物理内存中的页称为物理页面或者页帧(Page Frame)。虚拟内存的哪个页面映射到物理内存的哪个页帧是通过页
表(Page Table)来描述的,页表保存在物理内存中,MMU会查找页表来确定一个VA应该映射到什么PA。

操作系统和MMU是这样配合的:

1. 操作系统在初始化或分配、释放内存时会执行一些指令在物理内存中填写页表,然后用指令设置MMU,告诉MMU页表在物理内存中
的什么位置。
2. 设置好之后,CPU每次执行访问内存的指令都会自动引发MMU做查表和地址转换操作,地址转换操作由硬件自动完成,不需要用指令
控制MMU去做。

MMU除了做地址转换之外,还提供内存保护机制。各种体系结构都有用户模式(User Mode)和特权模式(Privileged Mode)之分,
操作系统可以在页表中设置每个内存页面的访问权限,有些页面不允许访问,有些页面只有在CPU处于特权模式时才允许访问,有些页面
在用户模式和特权模式都可以访问,访问权限又分为可读、可写和可执行三种。这样设定好之后,当CPU要访问一个VA时,MMU会检查
CPU当前处于用户模式还是特权模式,访问内存的目的是读数据、写数据还是取指令,如果和操作系统设定的页面权限相符,就允许访
问,把它转换成PA,否则不允许访问,产生一个异常(Exception)

常见的 segmentation fault 产生的原因:
用户程序要访问一段 VA, 经 MMU 检查后无权访问, MMU 会产生异常, CPU 从用户模式切换到特权模式, 跳转到内核代码中执行异常服务程序.
内核就会把这个异常解释为 segmentation fault, 将引发异常的程序终止.
### 关于 `sem_init` 函数的详细解释和用法 #### 1. 功能概述 `sem_init` 是 POSIX 标准中定义的一个函数,主要用于初始化一个信号量(Semaphore),该信号量可用于进程间或线程间的同步机制。通过协调多个线程或进程对共享资源的访问,信号量能够有效防止竞争条件的发生。 #### 2. 函数原型 以下是 `sem_init` 的函数声明: ```c #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); ``` - **参数说明** - `sem_t *sem`: 指向要初始化的信号量对象的指针。 - `int pshared`: 表示信号量的作用范围。如果值为 `0`,则表示此信号量仅限于当前进程内的线程共享;如果是非零值,则表示此信号量可以在不同进程之间共享[^2]。 - `unsigned int value`: 设置信号量的初始值。通常用来控制可并发访问某个资源的最大数量[^1]。 - **返回值**: 成功时返回 `0`,失败时返回 `-1` 并设置相应的错误码到 `errno` 中。 #### 3. 使用场景 - 当需要在线程内部实现互斥锁功能时,可以通过将 `pshared` 设定为 `0` 来创建一个线程局部的信号量。 - 如果希望跨进程通信并管理公共资源,则需设定 `pshared` 不等于 `0`,并将信号量置于共享内存区段内[^4]。 #### 4. 示例代码 下面展示如何利用 `sem_init` 实现基本的线程同步: ```c #include <pthread.h> #include <semaphore.h> #include <stdio.h> #include <stdlib.h> #define NUM_THREADS 5 // 定义全局信号量变量 sem_t my_semaphore; void* thread_function(void* arg) { // 线程尝试获取信号量 if (sem_wait(&my_semaphore) != 0) { // 尝试减少信号量计数器 perror("sem_wait failed"); exit(EXIT_FAILURE); } printf("Thread %ld is running\n", (long)arg); // 执行一些工作... // 增加信号量计数器 if (sem_post(&my_semaphore) != 0) { // 发布信号给其他等待者 perror("sem_post failed"); exit(EXIT_FAILURE); } pthread_exit(NULL); } int main() { pthread_t threads[NUM_THREADS]; long i; // 初始化信号量,允许最多只有一个线程进入临界区 if (sem_init(&my_semaphore, 0, 1) != 0) { // 这里我们只让单一线程运行 perror("sem_init failed"); exit(EXIT_FAILURE); } for(i = 0; i < NUM_THREADS; ++i){ if(pthread_create(&threads[i], NULL, thread_function, (void*)i)){ fprintf(stderr,"Error creating thread %ld\n", i); exit(EXIT_FAILURE); } } for(i = 0; i < NUM_THREADS; ++i){ pthread_join(threads[i], NULL); } // 销毁信号量 if (sem_destroy(&my_semaphore) != 0){ perror("sem_destroy failed"); } return EXIT_SUCCESS; } ``` #### 5. 注意事项 - 调用 `sem_init` 后,在不再需要使用信号量之前应调用 `sem_destroy` 来销毁它,以释放相关联的任何系统资源[^3]。 - 对同一个已经被初始化过的信号量再次调用 `sem_init` 可能会产生未定义行为。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值