本文大量内容引用自孟宁老师在《LINUX操作系统分析》课程中的内容
《Linux内核分析》MOOC课程

http://www.xuetangx.com/courses/course-v1:ustcX+USTC001+_/about

环境的搭建参见上面的链接

在编译源码的过程中,gcc报错,error: linux/compiler-gcc6.h: 没有那个文件或目录
百度之后,发现是因为我的gcc太新导致了这个问题
解决方案见下面连接
http://blog.chinaunix.net/uid-20680966-id-5322921.html

linux源码根目录/init/main.c为linux内核启动文件
启动函数为start_kernel();

代码分析

asmlinkage __visible void __init start_kernel(void)
{char *command_line; /*命令行,用来存放bootloader传递过来的参数*/char *after_dashes;/** Need to run as early as possible, to initialize the* lockdep hash:*/lockdep_init(); /*lockdep是一个内核调试模块,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题。建立一个哈希表(hash tables),就是一个前后指向的指针结构体数组。 (函数的主要作用是初始化锁的状态跟踪模块。由于内核大量使用锁来进行多进程多处理器的同步操作,死锁就会在代码不合理的时候出现,但是要定位哪个锁比较困难,用哈希表可以跟踪锁的使用状态。死锁情况:一个进程递归加锁同一把锁;同一把锁在两次中断中加锁;几把锁形成闭环死锁)*/set_task_stack_end_magic(&init_task); /*init_task即手工创建的PCB,0号进程即最终的idle进程*/smp_setup_processor_id(); /*针对SMP处理器,用于获取当前CPU的硬件ID,如果不是多核,函数为空 (判断是否定义了CONFIG_SMP,如果定义了调用read_cpuid_mpidr读取寄存器CPUID_MPIDR的值,就是当前正在执行初始化的CPU ID,为了在初始化时做个区分,初始化完成后,所有处理器都是平等的,没有主从)*/debug_objects_early_init(); /*初始化哈希桶(hash buckets)并将static object和pool object放入poll列表,这样堆栈就可以完全操作了 (这个函数的主要作用就是对调试对象进行早期的初始化,就是HASH锁和静态对象池进行初始化,执行完后,object tracker已经开始完全运作了)*//** Set up the the initial canary ASAP:*/boot_init_stack_canary(); /*canary值的是用于防止栈溢出攻击的堆栈的保护字。初始化堆栈保护的值,防止栈溢出*/cgroup_init_early(); /*在系统启动时初始化cgroups,同时初始化需要early_init的子系统 (这个函数作用是控制组(control groups)早期的初始化,控制组就是定义一组进程具有相同资源的占有程度,比如,可以指定一组进程使用CPU为30%,磁盘IO为40%,网络带宽为50%。目的就是为了把所有进程分配不同的资源)*/local_irq_disable(); /*关闭当前CPU的所有中断响应,操作CPSR寄存器。对应后面的*/early_boot_irqs_disabled = true; /*系统中断关闭标志,当early_init完毕后,会恢复中断设置标志为false。*//** Interrupts are still disabled. Do necessary setups, then* enable them*/boot_cpu_init(); /*设置当前引导系统的CPU在物理上存在,在逻辑上可以使用,并且初始化准备好,即激活当前CPU (在多CPU的系统里,内核需要管理多个CPU,那么就需要知道系统有多少个CPU,在内核里使用cpu_present_map位图表达有多少个CPU,每一位表示一个CPU的存在。如果是单个CPU,就是第0位设置为1。虽然系统里有多个CPU存在,但是每个CPU不一定可以使用,或者没有初始化,在内核使用cpu_online_map位图来表示那些CPU可以运行内核代码和接受中断处理。随着移动系统的节能需求,需要对CPU进行节能处理,比如有多个CPU运行时可以提高性能,但花费太多电能,导致电池不耐用,需要减少运行的CPU个数,或者只需要一个CPU运行。这样内核又引入了一个cpu_possible_map位图,表示最多可以使用多少个CPU。在本函数里就是依次设置这三个位图的标志,让引导的CPU物理上存在,已经初始化好,最少需要运行的CPU。)*/page_address_init();  /*初始化高端内存的映射表 (在这里引入了高端内存的概念,那么什么叫做高端内存呢?为什么要使用高端内存呢?其实高端内存是相对于低端内存而存在的,那么先要理解一下低端内存了。在32位的系统里,最多能访问的总内存是4G,其中3G空间给应用程序,而内核只占用1G的空间。因此,内核能映射的内存空间,只有1G大小,但实际上比这个还要小一些,大概是896M,另外128M空间是用来映射高端内存使用的。因此0到896M的内存空间,就叫做低端内存,而高于896M的内存,就叫高端内存了。如果系统是64位系统,当然就没未必要有高端内存存在了,因为64位有足够多的地址空间给内核使用,访问的内存可以达到10G都没有问题。在32位系统里,内核为了访问超过1G的物理内存空间,需要使用高端内存映射表。比如当内核需要读取1G的缓存数据时,就需要分配高端内存来使用,这样才可以管理起来。使用高端内存之后,32位的系统也可以访问达到64G内存。在移动操作系统里,目前还没有这个必要,最多才1G多内存)*/pr_notice("%s", linux_banner); /*输出各种信息(Linux_banner是在kernel/init/version.c中定义的,这个字符串是编译脚本自动生成的)*/setup_arch(&command_line); /*很重要的一个函数arch/arm/kernel/setup.c
(内核架构相关初始化函数,是非常重要的一个初始化步骤。其中包含了处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、内存子系统的早期初始化(bootmem分配器))*/mm_init_cpumask(&init_mm); /*每一个任务都有一个mm_struct结构来管理内存空间,init_mm是内核的mm_struct*/setup_command_line(command_line); /*对cmdline进行备份和保存*/setup_nr_cpu_ids(); /*设置最多有多少个nr_cpu_ids结构*/setup_per_cpu_areas(); /*为系统中每个CPU的per_cpu变量申请空间,同时拷贝初始化段里数据(.data.percpu)*/smp_prepare_boot_cpu();    /* arch-specific boot-cpu hooks */ /*为SMP系统里引导CPU(boot-cpu)进行准备工作。在ARM系统单核里是空函数*/build_all_zonelists(NULL, NULL); /*设置内存管理相关的node(节点,每个CPU一个内存节点)和其中的zone(内存域,包含于节点中,如)数据结构,以完成内存管理子系统的初始化,并设置bootmem分配器*/page_alloc_init(); /*设置内存页分配通知器*/pr_notice("Kernel command line: %s\n", boot_command_line);parse_early_param(); /*解析cmdline中的启动参数*/after_dashes = parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,-1, -1, &unknown_bootoption); /*这行代码主要对传入内核参数进行解释,如果不能识别的命令就调用最后参数的函数*/if (!IS_ERR_OR_NULL(after_dashes))parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,set_init_arg);jump_label_init();/** These use large bootmem allocations and must precede* kmem_cache_init()*/setup_log_buf(0); /*使用bootmeme分配一个记录启动信息的缓冲区*/pidhash_init(); /*进程ID的HASH表初始化,用bootmem分配并初始化PID散列表,由PID分配器管理空闲和已指派的PID,这样可以提供通PID进行高效访问进程结构的信息。LINUX里共有四种类型的PID,因此就有四种HASH表相对应。*/vfs_caches_init_early(); /*前期虚拟文件系统(vfs)的缓存初始化*/sort_main_extable(); /*对内核异常表(exception table)按照异常向量号大小进行排序,以便加速访问*/trap_init(); /*对内核陷阱异常进行初始化,在ARM系统里是空函数,没有任何的初始化。内有set_system_trap_gate(SYSCALL_VECTOR, &SYSTEM_CALL)*/mm_init();
/*标记哪些内存可以使用,并且告诉系统有多少内存可以使用,当然是除了内核使用的内存以外
(初始化内核内存分配器,包括六个子函数
1、page_cgroup_init_flatmem();获取page_cgroup所需内存
2、mem_init;关闭并释放bootmem分配器,打印内存信息,内核启动时看到Virtual kernel memory layout:的信息就是这个函数的3、kmem_cache_init();初始化slab分配器
4、percpu_init_late();PerCPU变量系统后期初始化
5、pgtable_cache_init();也表缓存初始化,arm中是个空函数 6、vmalloc_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(); /*对进程调度器的数据结构进行初始化,创建运行队列,设置当前任务的空线程,当前任务的调度策略为CFS调度器 *//** Disable preemption - early bootup scheduling is extremely* fragile until we cpu_idle() for the first time.*/preempt_disable(); /*关闭优先级调度。由于每个进程任务都有优先级,目前系统还没有完全初始化,还不能打开优先级调度。*/if (WARN(!irqs_disabled(),"Interrupts were enabled *very* early, fixing it\n"))local_irq_disable(); /*这段代码主要判断是否过早打开中断,如果是这样,就会提示,并把中断关闭*/idr_init_cache(); /*为IDR机制分配缓存,主要是为 structidr_layer结构体分配空间*/rcu_init(); /*初始化直接读拷贝更新的锁机制。 Read-Copy Update (RCU主要提供在读取数据机会比较多,但更新比较的少的场合,这样减少读取数据锁的性能低下的问题。)*/context_tracking_init();radix_tree_init(); /*内核radis 树算法初始化*//* init some links before init_ISA_irqs() */early_irq_init(); /*前期外部中断描述符初始化,主要初始化数据结构*/init_IRQ(); /*对应架构特定的中断初始化函数,在ARM中就是machine_desc->init_irq(),就是运行设备描述结构体中的init_irq函数[arch/arm/mach-msm/board-xxx.c]*/tick_init(); /*初始化内核时钟系统,tick control,调用clockevents_register_notifier,就是监听时钟变化事件 (这个函数主要作用是初始化时钟事件管理器的回调函数,比如当时钟设备添加时处理。在内核里定义了时钟事件管理器,主要用来管理所有需要周期性地执行任务的设备)*/rcu_init_nohz();init_timers(); /*初始化引导CPU的时钟相关的数据结构,注册时钟的回调函数,当时钟到达时可以回调时钟处理函数,最后初始化时钟软件中断处理*/hrtimers_init(); /*初始化高精度的定时器,并设置回调函数。*/softirq_init(); /*初始化软件中断,软件中断与硬件中断区别就是中断发生时,软件中断是使用线程来监视中断信号,而硬件中断是使用CPU硬件来监视中断。*/timekeeping_init(); /*初始化系统时钟计时,并且初始化内核里与时钟计时相关的变量。*/time_init(); /*初始化系统时钟。开启一个硬件定时器,开始产生系统时钟就是system_timer的初始化,arch/arm/mach-msm/board-*.c */sched_clock_postinit();perf_event_init(); /*CPU性能监视机制初始化,此机制包括CPU同一时间执行指令数,cache miss数,分支预测失败次数等性能参数*/profile_init(); /*分配内核性能统计保存的内存,以便统计的性能变量可以保存到这里*/call_function_init(); /*初始化所有CPU的call_single_queue,同时注册CPU热插拔通知函数到CPU通知链中*/WARN(!irqs_disabled(), "Interrupts were enabled early\n"); /*对应前面的local_irq_disable() (打开本CPU的中断,也即允许本CPU处理中断事件,在这里打开引CPU的中断处理。如果有多核心,别的CPU还没有打开中断处理。)*/early_boot_irqs_disabled = false;local_irq_enable();kmem_cache_init_late(); /*这是内核内存缓存(slab分配器)的后期初始化,当初始化完成之后,就可以使用通用内存缓存了*//** 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("Too many boot %s vars at `%s'", 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(); /*测试锁的API是否使用正常,进行自我测试。比如测试自旋锁、读写锁、一般信号量和读写信号量。*/#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {pr_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;}
#endif /*检查initrd的位置是否符合要求,也就是判断传递进来initrd_start对应的物理地址是否正常,如果有误就打印错误信息,并清零initrd_start。*/page_cgroup_init(); /*初始化容器组的页面内存分配,mem_cgroup是cgroup体系中提供的用于memory隔离的功能,*/debug_objects_mem_init(); /*这个函数是创建调试对象内存分配初始化,所以紧跟内存缓存初始化后面*/kmemleak_init(); /*内核内存泄漏检测机制初始化;*/setup_per_cpu_pageset(); /*创建每个CPU的高速缓存集合数组并初始化,此前只有启动页组。因为每个CPU都不定时需要使用一些页面内存和释放页面内存,为了提高效率,就预先创建一些内存页面作为每个CPU的页面集合。*/numa_policy_init(); /*初始化NUMA的内存访问策略。所谓NUMA,它是NonUniform Memory AccessAchitecture(非一致性内存访问)的缩写,主要用来提高多个CPU访问内存的速度。因为多个CPU访问同一个节点的内存速度远远比访问多个节点的速度来得快。*/if (late_time_init)late_time_init(); /*主要运行时钟相关后期的初始化功能。*/sched_clock_init(); /*对每个CPU进行系统进程调度时钟初始化*/calibrate_delay(); /*主要计算CPU需要校准的时间,这里说的时间是CPU执行时间。如果是引导CPU,这个函数计算出来的校准时间是不需要使用的,主要使用在非引导CPU上,因为非引导CPU执行的频率不一样,导致时间计算不准确。BogoMIPS值,也是衡量cpu性能的标志*/pidmap_init(); /*进程位图初始化,一般情况下使用一页来表示所有进程占用情况。*/anon_vma_init(); /*初始化反向映射的匿名内存,提供反向查找内存的结构指针位置,快速地回收内存。*/acpi_early_init(); /*这个函数是初始化ACPI电源管理。高级配置及电源接口(Advanced Configuration and Power Interface)ACPI规范介绍ACPI能使软、硬件、操作系统(OS),主机板和外围设备,依照一定的方式管理用电情况,系统硬件产生的Hot-Plug事件,让操作系统从用户的角度上直接支配即插即用设备,不同于以往直接通过基于BIOS 的方式的管理。*/
#ifdef CONFIG_X86if (efi_enabled(EFI_RUNTIME_SERVICES))efi_enter_virtual_mode();
#endif
#ifdef CONFIG_X86_ESPFIX64/* Should be run before the first non-init thread is created */init_espfix_bsp();
#endif /*初始化EFI的接口,并进入虚拟模式。EFI是ExtensibleFirmware Interface的缩写,就是INTEL公司新开发的BIOS接口。*/thread_info_cache_init(); /*线程信息(thread_info)的缓存初始化。*/cred_init(); /*任务信用系统初始化 */fork_init(totalram_pages); /*根据当前物理内存计算出来可以创建进程(线程)的最大数量,并进行进程环境初始化,为task_struct分配空间。*/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(); /*页面写机制初始化 */proc_root_init(); /*初始化系统进程文件系统,主要提供内核与用户进行交互的平台,方便用户实时查看进程的信息。*/cgroup_init(); /*进程控制组正式初始化,主要用来为进程和其子程提供性能控制。比如限定这组进程的CPU使用率为20% */cpuset_init(); /*初始化CPUSET,CPUSET主要为控制组提供CPU和内存节点的管理的结构。*/taskstats_init_early(); /*任务状态早期初始化,为结构体获取高速缓存,并初始化互斥机制。任务状态主要向用户提供任务的状态信息。*/delayacct_init(); /*任务延迟机制初始化,初始化每个任务延时计数。当一个任务等CPU运行,或者等IO同步时,都需要计算等待时间。*/check_bugs(); /*检查CPU配置、FPU等是否非法使用不具备的功能,检查CPU BUG,软件规避BUG*/sfi_init_late(); /*SFI 初始程序晚期设置函数*/if (efi_enabled(EFI_RUNTIME_SERVICES)) {efi_late_init();efi_free_boot_services();}ftrace_init(); /*功能跟踪调试机制初始化,初始化内核跟踪模块,ftrace的作用是帮助开发人员了解Linux 内核的运行时行为,以便进行故障调试或性能分析 function trace.*//* Do the rest non-__init'ed, we're now alive */rest_init(); /*剩余的初始化,至此,内核已经开始工作了。在rest_init()中,有kernel_init(),通过调用run_init_process,产生了第一个用户态进程,1号进程,默认在根目录下。rest_init()中,cpu_starup_entry函数调用cpu_idle_loop使得init_task空闲为idle进程,即0号进程*/
}

该代码分析复制于如下博客
https://www.shiyanlou.com/courses/reports/982406
原文作者好厉害,我等弱鸡只有拜服

Linux内核分析实验3——分析linux内核启动过程相关推荐

  1. 【CentOS Linux 7】实验2【Linux多用户管理】

    [CentOS Linux 7]实验1[Linux文件目录管理] [CentOS Linux 7]实验2[Linux多用户管理] 目   录 一.实验目的 二.实验内容 三.实验步骤和结果

  2. linux系统从开机到登陆界面的启动过程

    简述: 1.开机BIOS自检 2.MBR引导 3.grub引导菜单 4.加载内核kernel 5.启动init进程 6.读取inittab文件,执行rc.sysinit,rc等脚本 7.启动minge ...

  3. Linux用户程序的编译链接与加载启动过程

    Linux用户程序的编译链接与加载启动过程 rtoax 2021年3月 1. 程序的编译链接 1.1. 介绍 如果我们打开维基百科的 链接器 页,我们将会看到如下定义: 在计算机科学中,链接器(英文: ...

  4. linux系统使用实验报告操作系统,linux操作系统实验报告1.doc

    linux操作系统实验报告1.doc 实验LINUXSHELL基本命令使用实验目的熟悉LINUX操作系统环境,掌握LINUX的基本命令.实验提示1.LINUX命令行的语法结构COMMANDOPTION ...

  5. linux服务器搭建实验4报告,LINUX实验四报告

    <LINUX实验四报告>由会员分享,可在线阅读,更多相关<LINUX实验四报告(6页珍藏版)>请在人人文库网上搜索. 1.一. 实验目的掌握Linux系统中常用命令的作用和命令 ...

  6. 嵌入式linux操作系统的移植 实验报告,嵌入式linux系统移植试题

    [A] 内核不支持当前处理器 [B] 内核不支持当前开发平台 [C ] 内核获取不到bootloader传递来的参数 [D ] 内核初始化终端出现错误 18. 内核启动过程中,如果终端出现" ...

  7. 计算机操作系统实验指导linux版,操作系统实验指导书(linux版).doc

    操作系统实验指导书(linux版) <操作系统>实验指导书 实验学时:16 适用专业:计算机科学与技术 实验一:进程和线程的创建 1. 在linux下编写一个应用程序,命名为an_ch2_ ...

  8. Android内核开发:图解Android系统的启动过程

    本文是<Android内核开发>系列的第六篇文章,前面的几篇文章介绍了Android内核开发相关的基础知识,包括:Android源码的下载.版本和分支介绍.编译和烧写等等,从本文起就要开始 ...

  9. 菜鸟 Linux系统学习1简介 安装 云服务器 启动过程

    Linux 是一种 自由和开放源码的类 UNIX 操作系统. Linux 英文解释为 Linux is not Unix. Linux 是在 1991 由 林纳斯·托瓦兹 在赫尔辛基大学上学时创立的, ...

  10. Linux必知必会的目录与启动过程

    第1章 /etc/目录 1.1 /etc/sysconfig/network-scripts/ifcfg-eth0 linux第一块网卡的配置文件 [root@znix ~]# cat /etc/sy ...

最新文章

  1. 如何优雅的设计java异常
  2. Ajax缓存解决办法
  3. Groovy里使用Runnable实现多线程
  4. Andorid之Log图文详解(Log.v,Log.d,Log.i,Log.w,Log.e)的用法总结
  5. activexobject mysql_ActiveXObject函数详解(转)
  6. Puppet安装部署篇(一)
  7. ionic 环境安装
  8. [ZJOI 2012]灾难
  9. 框架学习之Spring 第四节 Spring集成JDBC组件开发
  10. 内存cgroup---CGroup中参数由来篇
  11. hdu 1983(BFS+DFS) 怪盗Kid
  12. 拍一拍身上的土(2009-12-12 01:17:43韩寒博客)
  13. java分页之页面分页—@易小川
  14. 网络连接状态指示器(NCSI ,Network Connectivity Status Indicator)
  15. centos 添加路由命令
  16. python字符串转负数_python 字符串 步进 负数
  17. centos6 yum 阿里源 配置
  18. 创新洞察|为什么中国企业更擅长用户导向创新
  19. Android Activity中实现Fragment切换功能效果
  20. java 与数据库连接失败_java链接不上数据库,怎么解决!

热门文章

  1. python正则表达式匹配网址中的域名_匹配 IP 地址与域名的正则表达式
  2. mysql查询前5000条记录分页显示_数据库 完整的查询语句,查询顺序及分页显示功能...
  3. ANSYS ICEM CFD三维非结构网格生成实例——教室吊顶空调送回风
  4. matlab的sparse,matlab的
  5. 什么是分布式架构,有什么优势?
  6. Windows文件自动增量备份工具,解放你的双手!
  7. PDA手持移动POS销售开单软件(网络版)、PDA手持设备小票机
  8. Hbuilder 打开iOS真机调试操作
  9. Halcon之感兴趣的区域
  10. 25 个你需要知道的人工智能术语