栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

【linux kernel】start

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【linux kernel】start

一、开篇

(注)本文源码基于linux内核版本:4.1.15。


在start_kernel()函数的开始处,定义了两个变量:

	char *command_line;
	char *after_dashes;

第一个表示指向内核命令行的指针,第二个用于包含parse_args()函数的结果,该函数解析带有name=value形式参数的输入字符串,查找特定的关键字并调用正确的处理程序。

接着,start_kernel()函数会调用set_task_stack_end_magic()函数。该函数获取init任务的地址并设置STACK _END_MAGIC(0x57AC6E9D)作为它的“魔术号”,以防止堆栈溢出。定义如下:

void set_task_stack_end_magic(struct task_struct *tsk)
{
	unsigned long *stackend;

	stackend = end_of_stack(tsk);
	*stackend = STACK_END_MAGIC;	
}

在start_kenrel()调用这个函数时传入的参数是init_task全局变量的地址,init task表示初始任务结构,其定义如下(/init/init_task.c):

struct task_struct init_task = INIT_TASK(init_task);

struct task_struct任务结构体存储了关于进程的所有信息,该结构体非常大。定义在(/include/linux/sched.c)文件中,该结构体包含100多个字段,堪称linux内核中的最大结构定义啦。

在set_task_stack_end_magic()函数中,使用end_of_stack()函数获取到init_task代表的线程堆栈上最后一个可用的地址,然后将STACK _END_MAGIC(0x57AC6E9D)设置到该地址中。end_of_stack()函数定义如下:

static inline unsigned long *end_of_stack(struct task_struct *p)
{
#ifdef CONFIG_STACK_GROWSUP
	return (unsigned long *)((unsigned long)task_thread_info(p) + THREAD_SIZE) - 1;
#else
	return (unsigned long *)(task_thread_info(p) + 1);
#endif
}

从以上代码可见,end_of_stack()的返回值由CONFIG_STACK_GROWSUP宏来管控,本文以ARM架构为例,其堆栈是向下生长,故而没有定义CONFIG_STACK_GROWSUP宏,所以end_of_stack()函数将执行如下代码:

	return (unsigned long *)(task_thread_info(p) + 1);

task_thread_info()返回用INIT_TASK填充的堆栈:

#define task_thread_info(task)  ((struct thread_info *)(task)->stack)

接下来,start_kernel()函数将调用smp_setup_processor_id()函数,这是一个架构相关函数,在ARM架构下,其内容如下(/arch/arm/kernel/setup.c):

void __init smp_setup_processor_id(void)
{
	int i;
	u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
	u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);

	cpu_logical_map(0) = cpu;
	for (i = 1; i < nr_cpu_ids; ++i)
		cpu_logical_map(i) = i == cpu ? 0 : i;

   
	set_my_cpu_offset(0);
	
    
	pr_info("Booting Linux on physical CPU 0x%xn", mpidr);
}

start_kernel()的下一个函数是debug_objects_early_init()。定义如下(/lib/debugobjects.c):

void __init debug_objects_early_init(void)
{
	int i;
	
    
	for (i = 0; i < ODEBUG_HASH_SIZE; i++)
		raw_spin_lock_init(&obj_hash[i].lock);
	
    
	for (i = 0; i < ODEBUG_POOL_SIZE; i++)
		hlist_add_head(&obj_static_pool[i].node, &obj_pool);
}

该函数在linux内核早期引导期间调用,用于初始化哈希表结构并将静态对象池对象(obj_static_pool)链接到obj_pool中。在debug_objects_early_init()调用之后,对象跟踪器就完全可以运行了。obj_static_pool定义如下:

static struct debug_obj		obj_static_pool[ODEBUG_POOL_SIZE] __initdata;

在debug_objects_early_init()函数之后,将调用boot_init_stack_canary()函数(注:该函数是一个架构相关函数),它用-fstack-protector gcc特性的canary值填充当前任务的->stack_canary值。这个操作依赖于CONFIG_CC_STACKPROTECTOR配置选项,如果该选项被禁用,boot_init_stack_canary()不做任何事情。如下代码(/arch/arm/include/asm/stackproctector.h):

static __always_inline void boot_init_stack_canary(void)
{
	unsigned long canary;

	get_random_bytes(&canary, sizeof(canary));
	canary ^= LINUX_VERSION_CODE;

	current->stack_canary = canary;
	__stack_chk_guard = current->stack_canary;
}

接着,start_kernel()函数将调用boot_cpu_init()函数了,该函数定义如下:

static void __init boot_cpu_init(void)
{
	int cpu = smp_processor_id();
	
	set_cpu_online(cpu, true);
	set_cpu_active(cpu, true);
	set_cpu_present(cpu, true);
	set_cpu_possible(cpu, true);
}

boot_cpu_init()函数用于激活第一个处理器,在该函数中,首先调用smp_processor_id()获取当前CPU的ID,然后, 将启动cpu标记为“present”、“online”、“active”、“possible”四种状态。

接着,会调用:

pr_notice("%s", linux_banner);

打印出linux内核的banner信息。pr_notice()本质是printk()函数的封装,如下代码:

#define pr_notice(fmt, ...) 
    printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)

在linux内核启动过程中,会打印出下图所示的信息,正是由这行代码完成。

二、文末总结

文本描述了start_kernel()函数开始的环节,由于这些函数较短,故把他们都写在了一起。有些函数与具体架构相关,因此在不同架构和不同linux版本下可能会有不同情况,故而需要结合着linux内核源码来分析。


搜索/关注【嵌入式小生】wx公众号,获取更多精彩内容>>>>

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/882031.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号