内核源码——C语言阶段的start_kernel函数

以下内容源于朱有鹏嵌入式课程的学习,如有侵权请告知删除。

前言

1、内容总结

C语言阶段的start_kernel函数位于内核源码/init/main.c文件,主要完成以下内容:

(1)打印一些输出信息。

(2)初始化内核工作所需的模块(比如内存管理、调度系统、异常处理等)。


2、学习方法

(1)学习思路

  • 抓大放小,不深究;感兴趣可以就某个话题去网上搜索资料学习。
  • 重点内容局部深入分析。

(2)具体学习方法

  • 顺着代码执行路径抓全,这是我们的学习主线。
  • 对照内核启动的打印信息进行分析。(本文章根据打印信息来分析源代码)

(3)几条学习线路

  • 分析uboot给kernel传参的影响和实现。
  • 硬件初始化与驱动加载。
  • 内核启动后的结局与归宿。

(4)重点内容

  • 函数setup_arch()完成的两件事情,即机器码架构的查找并且执行架构相关的硬件的初始化,以及uboot给内核的传参cmdline。
  • 内核启动后的稳定状态。

3、start_kernel函数代码

asmlinkage void __init start_kernel(void)
{char * command_line;extern struct kernel_param __start___param[], __stop___param[];smp_setup_processor_id();/** Need to run as early as possible, to initialize the* lockdep hash:*/lockdep_init();debug_objects_early_init();/** Set up the the initial canary ASAP:*/boot_init_stack_canary();cgroup_init_early();local_irq_disable();early_boot_irqs_off();early_init_irq_lock_class();/** Interrupts are still disabled. Do necessary setups, then* enable them*/lock_kernel();tick_init();boot_cpu_init();page_address_init();printk(KERN_NOTICE "%s", linux_banner);setup_arch(&command_line);mm_init_owner(&init_mm, &init_task);setup_command_line(command_line);setup_nr_cpu_ids();setup_per_cpu_areas();smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */build_all_zonelists(NULL);page_alloc_init();printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);parse_early_param();parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);/** These use large bootmem allocations and must precede* kmem_cache_init()*/pidhash_init();vfs_caches_init_early();sort_main_extable();trap_init();mm_init();/** Set up the scheduler prior starting any interrupts (such as the* timer interrupt). Full topology setup happens at smp_init()* time - but meanwhile we still have a functioning scheduler.*/sched_init();/** Disable preemption - early bootup scheduling is extremely* fragile until we cpu_idle() for the first time.*/preempt_disable();if (!irqs_disabled()) {printk(KERN_WARNING "start_kernel(): bug: interrupts were ""enabled *very* early, fixing it\n");local_irq_disable();}rcu_init();radix_tree_init();/* init some links before init_ISA_irqs() */early_irq_init();init_IRQ();prio_tree_init();init_timers();hrtimers_init();softirq_init();timekeeping_init();time_init();profile_init();if (!irqs_disabled())printk(KERN_CRIT "start_kernel(): bug: interrupts were ""enabled early\n");early_boot_irqs_on();local_irq_enable();/* Interrupts are enabled now so all GFP allocations are safe. */gfp_allowed_mask = __GFP_BITS_MASK;kmem_cache_init_late();/** HACK ALERT! This is early. We're enabling the console before* we've done PCI setups etc, and console_init() must be aware of* this. But we do want output early, in case something goes wrong.*/console_init();if (panic_later)panic(panic_later, panic_param);lockdep_info();/** Need to run this when irqs are enabled, because it wants* to self-test [hard/soft]-irqs on/off lock inversion bugs* too:*/locking_selftest();#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - ""disabling it.\n",page_to_pfn(virt_to_page((void *)initrd_start)),min_low_pfn);initrd_start = 0;}
#endifpage_cgroup_init();enable_debug_pagealloc();kmemtrace_init();kmemleak_init();debug_objects_mem_init();idr_init_cache();setup_per_cpu_pageset();numa_policy_init();if (late_time_init)late_time_init();sched_clock_init();calibrate_delay();pidmap_init();anon_vma_init();
#ifdef CONFIG_X86if (efi_enabled)efi_enter_virtual_mode();
#endifthread_info_cache_init();cred_init();fork_init(totalram_pages);proc_caches_init();buffer_init();key_init();security_init();dbg_late_init();vfs_caches_init(totalram_pages);signals_init();/* rootfs populating might need page-writeback */page_writeback_init();
#ifdef CONFIG_PROC_FSproc_root_init();
#endifcgroup_init();cpuset_init();taskstats_init_early();delayacct_init();check_bugs();acpi_early_init(); /* before LAPIC and SMP init */sfi_init_late();ftrace_init();/* Do the rest non-__init'ed, we're now alive */rest_init();
}

一、内核的解压缩与版本信息打印

1、零碎内容

(1)smp。smp就是对称多处理器(即多核心CPU)。

(2)lockdep。锁定依赖,是一个内核调试模块,与处理内核自旋锁死锁问题有关。

(3)cgroup。control group,内核提供的一种来处理进程组的技术。


2、内核的解压缩

在内核源码/arch/arm/boot/compressed/head.S文件中,调用了decompress_kernel()函数来解压内核,而decompress_kernel()函数位于/arch/arm/boot/compressed/misc.c文件中,其内容如下:


3、打印内核版本信息

(1)printk函数是内核用来向控制台打印信息的函数,类似于应用层编程中的printf。

  • 内核不能使用标准库函数,因此不能使用printf,其实printk就是内核实现的一个printf。

(2)printk函数可以定义消息输出的级别。

  • 因为linux代码量太多,里面的printk打印信息太多。如果所有的printk都能打印出来而不加任何限制,则最终内核启动后得到海量的输出信息。
  • linux内核给每一个printk添加一个打印级别。级别定义0-7(注意编程的时候要用相应的宏定义,不要直接用数字)分别代表8种输出的重要性级别,0表示最重要,7表示最不重要。根据自己的消息的重要性去设置打印级别。
  • linux的控制台有一个消息过滤显示机制,控制台只会显示级别比控制台定义的级别高的消息。比如控制台的消息显示级别设置为4,那么只有printk中消息级别为0-3(也可能是0-4)的才可以显示看见。

(3)linux_banner的内容解析

  • 在编译后的kernel目录下,使用“ grep "UTS_RELEASE" * -nR ”搜索得知:#define UTS_RELEASE "2.6.35.7"。
  • 同理得知:#define LINUX_COMPILE_BY "root"
  • 同理得知:#define LINUX_COMPILE_HOST "ubuntu"
  • 同理得知:#define LINUX_COMPILER "gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67) "
  • 同理得知:#define UTS_VERSION "#7 PREEMPT Mon Aug 22 16:50:01 CST 2022"

二、内核对架构信息的处理

1、setup_arch函数简介

 (1)setup_arch函数主要用来确定当前内核对应的硬件平台(CPU与机器码)。

  • linux内核会支持一种CPU的运行,而CPU+开发板就确定了一个硬件平台。
  • 机器码是硬件平台的一个固定的编码,用来表征这个硬件平台。
  • 经过配置的内核可以在此平台上运行。

(2)当前内核支持的机器码以及硬件平台相关的一些定义,都在这个函数中处理。


2、setup_arch函数内部的setup_processor函数

(1)此函数用来查找CPU信息,可以结合串口打印的信息来分析。


3、setup_arch函数内部的setup_machine函数

已知setup_arch函数部分内容截图如下:

setup_arch函数内部所调用的setup_machine函数内容如下:

setup_machine函数内部所调用的lookup_machine_type函数内容如下:

(1)setup_machine函数的参数

  • 由代码可知,setup_arch函数传给setup_machine函数的参数是机器码编号,即machine_arch_type。
  • machine_arch_type在include/generated/mach-types.h的32039-32050行定义,经过分析后确定它的值是2456。

(2)setup_machine函数的作用

  • 通过传入的机器码编号,找到对应这个机器码的machine_desc描述符,并且返回这个描述符的指针。

(3)真正干活的函数是lookup_machine_type(__lookup_machine_type函数)

  • 此函数在head-common.S中,分析得知真正干活的函数是__lookup_machine_type。

(4)__lookup_machine_type函数的工作原理

1)首先,内核把各种CPU架构的信息组织成若干machine_desc结构体的实例,然后设置段属性.arch.info.init。如下:

 在/arch/arm/include/mach/arch.h文件中,有如下内容:

 比如在/arch/arm/mach-s5pv210/mach-x210.c文件中,有如下内容:

比如在/arch/arm/mach-s3c2410/mach-qt2410.c文件中,有如下内容:

2)其次,链接的时候,这些描述符就会被连接在一起。

kernel编译后生成的链接脚本/arch/arm/kernel/vmlinux.lds.S部分截图如下:

3)最后,__lookup_machine_type函数到描述符所在处,依次遍历各个描述符,比对看机器码哪个相同。

三、内核对bootargs的处理

​​​​​​​1、setup_arch函数进行基本的cmdline处理

​​​​​​

 (1)可以从串口输出看到对应的信息: 

 (2)cmd_line,指uboot给kernel传参时传递的命令行启动参数,即uboot的bootargs。

​​​​​​​

(3)变量default_command_line是一个字符数组(是全局变量),表示默认的命令行参数。

在/arch/arm/kernel/setup.c文件中定义如下:

static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

而CONFIG_CMDLINE,在内核配置后生成的.config文件中定义如下:

 

(4)内核对cmdline的处理思路

  • 内核中设置了一个默认的cmdline(即.config文件中配置的那个),uboot也可以通过tag给kernel再传递一个cmdline。
  • 如果uboot给内核传cmdline成功,则内核会优先使用uboot所传递的;如果uboot没有给内核传cmdline或者传参失败,则内核会使用默认的cmdline。
  • 内核为什么要这样设计?因为希望内核是灵活的,可以通过传参对内核进行配置。
  • 这个处理思路在setup_arch函数中实现。

2、setup_command_line函数

处理和命令行参数cmdline有关的任务。略。


3、parse_early_param函数、parse_args函数

(1)这两个函数用来解析cmdline传参和其他传参。

比如将cmdline解析,得到一个字符串数组,这个数组中依次存放了一个个设置项目信息。

(2)注意,这两个函数只是进行解析,没有去处理。换言之,这两个函数只是把长字符串解析成短字符串,最多和内核里控制这个相应功能的变量挂钩了,但是并没有去执行。真正执行的代码在各自模块初始化的代码部分。

四、start_kernel函数中的xxx_init函数

start_kernel函数中还调用了很多的xxx_init函数,都是内核工作需要的模块的初始化函数。

(1)trap_init:设置异常向量表

(2)mm_init:内存管理模块初始化

(3)sched_init:内核调度系统初始化

(4)early_irq_init、init_IRQ:中断初始化

(5)console_init:控制台初始化

以上只列出部分xxx_init函数。这些初始化之后,内核就具备了基本的工作条件。如果把内核比喻成一个复杂机器,那么start_kernel函数的作用,就是把众多零部件组装在一起形成这个机器,让它具备工作的基本条件。

五、start_kernel函数中的rest_init函数

1、rest_init函数简介

此函数之前内核的基本组装已经完成,剩下的工作放在一个叫rest_init的函数中完成。

rest_init函数内容如下:

(1)rest_init函数调用kernel_thread函数,启动了2个内核线程:

1)kernel_init

2)kthreadd

(2)rest_init函数调用schedule函数开启内核的调度系统,从此linux系统开始运转起来。


(3)rest_init函数最终调用cpu_idle函数结束整个内核的启动,cpu_idle函数里面是死循环。而调度系统负责考评系统中所有的进程。只要有哪个需要被运行,调度系统就会终止cpu_idle死循环进程(空闲进程)转而去执行有意义的干活的进程。


 2、什么是内核线程?

  • 一个运行的程序就是一个进程,这个程序和别的程序是分开的,这个程序可以被内核单独调用执行或者暂停。
  • 应用层运行一个程序就构成一个用户进程/线程;
  • 内核中运行一个函数(函数其实就是一个程序)就构成一个内核进程/线程。
  • 因此kernel_thead函数运行一个函数,就是把这个函数变成一个内核线程,可以被内核调度系统去调度,即去调度器注册,将来调度的时候会进行考虑。

3、进程0、进程1、进程2

(1)截至目前为止一共涉及到3个内核进程/线程。操作系统用数字来表示/记录一个进程/线程,此数字就被称为这个进程的进程号。这个号码是从0开始分配的。

  • 进程0:也就是那个死循环,也叫idle进程(空闲进程)。
  • 进程1:也就是kernel_init函数,这个进程被称为init进程
  • 进程2:也就是kthreadd函数,这个进程是linux内核的守护进程。这个进程是用来保证linux内核自己本身能正常工作的。

(2)在linux命令行下,使用ps命令可以查看当前linux系统中运行的进程情况。

(3)在ubuntu下ps -aux可以看到当前系统运行的所有进程,可以看出进程号是从1开始的。

  • 为什么不从0开始,因为进程0属于内核进程,而不是一个用户进程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461771.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Excel2007数据透视表学习(四)改变数据透视表布局

通过在【数据透视表字段列表】对话框内将【列标签】、【行标签】和【报表筛选】区域的字段转换即可创建不同分析角度的数据透视表。 如果还是习惯Excel2003版本字段拖拽方式创建数据透视表&#xff0c;可以在数据透视表中单击鼠标右键→【数据透视表选项】→【显示】→勾选【…

经典基础算法之面试题(系列一)(转)

1. 打靶问题的递归解法 Technorati 标记: 递归算法,字典法,二分法,打靶问题,日期问题,求幂,多项式求值伦敦奥运会火热进行中&#xff0c;让我们来看个打靶的问题&#xff1a;一个射击运动员打靶&#xff0c;靶一共有10环&#xff0c;求连开10枪打中90环的可能行有多少种&#x…

2013年11月份我国网络不良与垃圾短信息分析报告

IDC评述网&#xff08;idcps.com&#xff09;12月17日报道&#xff1a;近日&#xff0c;12321网络不良与垃圾信息举报受理中心最新播报了11月份我国不良与垃圾短信息发送情况。根据报告显示&#xff0c;11月份垃圾邮件发送IP地址与上月大体相同&#xff0c;主要集中在湖北、北京…

SCOM Rule 介绍 [SCOM中文系列之六]

SCOM的Rule主要用来收集数据&#xff0c;我们看到的EventView&#xff0c;PerformanceData&#xff0c;Report所需数据大部分都是通过Rule来收集的。 SCOM Rule的基础结构是触发指定的条件&#xff0c;就执行指定的动作。这个我们通过Rule的属性界面可以看的清楚点&#xff1a;…

init进程的详解

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 内核源码——C语言阶段的start_kernel函数_天糊土的博客-CSDN博客 分析根文件系统中的/linuxrc文件_天糊土的博客-CSDN博客 linux内核sys_mount()分析_kai_ding的博客-CSDN博客_sys_mount…

跟着石头哥哥学cocos2d-x(四)--cocos2dx中的动画以及TexturePacker使用

2019独角兽企业重金招聘Python工程师标准>>> 之前向Andreas Loew申请了一枚TexturePacker注册码&#xff0c;很快都下来了&#xff0c;作为回报我打算还是写一篇关于TexturePacker的使用博客吧&#xff0c;有兴趣的可以在这里申请密钥,http://www.codeandweb.com&am…

头像和Karma汽车

Powered by Zoundry RavenTechnorati : Karma, 头像, 汽车 Del.icio.us : Karma, 头像, 汽车 转载于:https://www.cnblogs.com/bsmagic/archive/2008/08/06/1261858.html

All-In-One Code Framework [一站式示例代码库] 【转】

All-In-One Code Framework [一站式示例代码库]2010 对一站式示例代码库&#xff0c;对奋战在一站式示例代码库上的每一位工程师来说都是不同寻常的一年。 在我们共同努力和开发社区的支持下&#xff0c;该项目从一年前的草根雏形&#xff0c;成长为CodePlex排名前三&#xff0…

Ioc容器Autofac介绍

Autofac是轻量级的开源Ioc容器&#xff0c;在这里可以下载http://code.google.com/p/autofac/。如果你用过其他的Ioc容器&#xff0c;那么学习Autofac使用也会比较容易&#xff0c;下面将通过一些例子来讲解其用法。 先看一个例子&#xff1a; 首先新建一个工程&#xff0c;添加…

用代码实现Sharepoint2010的个人信息的照片上传(2)(原创)

用代码从AD读取照片信息并同步到SharePoint2010的个人照片信息中 前言 上篇我们讲到用代码实现了照片信息同步到SharePoint2010的个人照片信息中&#xff0c;但是做的是从文件夹读取照片信息到SharePoint2010,今天我们要做的从AD的thumbnailPhoto属性中读取照片信息到SharePoin…

HTML5实现Word中文字全环绕图片效果

实现这个功能是当时看见数字报的展示出来的编排方式&#xff0c;就是图片热点&#xff0c;而且下载打开还是PDF的&#xff0c;让我这个WP用户咋个看。想实现Word中的编排方式&#xff0c;也需要浏览器支持Word插件&#xff0c;火狐、谷歌、Opera这些都要另外弄插件&#xff0c;…

Bzoj 2127 happiness 最小割

happiness 题解: 将图转换成最小割. 将割完的图中与S相连的点看做选文科, 与T相连的点看做选理科. flow(s, u) 文科值 flow(u,t) 理科值 假设u 和 v 一起选文科有奖励值z, flow(s,u) z/2 flow(s,v) z/2, flow(u,v) z/2 假设u 和 v 一起选理科有奖励值z, flow(u,t) z/2…

POJ 1149 最大流建图 PIGS

题意&#xff1a; 给出猪圈个数 m 和买家人数 n 然后给出m个猪圈的猪的头数.. 接下来 n 行.. 给出mm a1 a2 .. a(mm) k 例如 2 1 5 3 表示第i1个用户 有mm(2) 个猪圈的钥匙 就是有第 a1(1) 个和第 a2(5) 个猪圈的钥匙..还想要买得k头猪.. 问最多能够卖出多少头.. 思路&#xff…

ARM通识——ARM的7种基本工作模式、37个通用寄存器

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、ARM采用32位架构 ARM约定一个Byte是8 bits&#xff0c;一个Halfword是16 bits (2 byte)&#xff0c;一个Word是32 bits (4 byte)。 大部分ARM core 提供ARM 指令集&#xff08;32 bit&#xff0…

以NFS方式挂载rootfs的设置方法

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 更多详细内容&#xff0c;见Linux中NFS服务器的配置(/etc/export)_天糊土的博客-CSDN博客 网络文件系统&#xff08;NFS&#xff0c;network file system&#xff09;&#xff0c;是一种网络通讯协…

分析根文件系统中的/linuxrc文件(附带busybox的介绍)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 init进程的详解_天糊土的博客-CSDN博客_init进程 busybox详解_linuxarmsummary的博客-CSDN博客 前言 因为uboot给内核传参的bootargs中有“init/linuxrc”这个项目&#xff0c;而由前面的…

CentOS Linux搭建SVN Server配置详解

PS&#xff1a;今天给开发的同事搭建一个公网的SVN Server&#xff0c;google一下&#xff0c;发现下面这篇最详细&#xff0c;就记录下来&#xff0c;重新排版一下。顺便根据自己的使用经验&#xff0c;补充了几个细节和总结。本文配置是基于CentOS 5.x的&#xff0c;但一样适…

405 Method Not Allowed

当jquery easyui的datagrid插件使用.json文件为数据源&#xff0c;通过非http协议访问【就是直接拖进浏览器查看效果】&#xff0c;能正常加载.json文件并且生成datagrid数据源。但是通过http协议访问示例代码时&#xff0c;无法加载json文件生成datagrid的数据源。 这个是由于…

Eclipse之ANT使用

Ant是Java平台下非常棒的批处理命令执行程序&#xff0c;能非常方便地自动完成编译&#xff0c;测试&#xff0c;打包&#xff0c;部署等等一系列任务&#xff0c;大大提高开发效率。如果你现在还没有开始使用Ant&#xff0c;那就要赶快开始学习使用&#xff0c;使自己的开发水…

idea修改maven项目版本号

1 先安装插件 2 控制台执行命令 mvn build-helper:parse-version versions:set -DnewVersion1.1-SNAPSHOT versions:commit 1.1-SNAPSHOT 为你要修改的版本号 技术交流群&#xff1a; 816227112 有时候命令执行完看不到改变&#xff0c;需要刷新下。 转载于:https://www.cnblo…