linux 内核移植(七)——rest_init函数分析

简介:

代码在start_kernel函数运行的最后到了rest_init()函数中


1:rest_init()函数分析

  (1)rest_init中调用kernel_thread函数启动了2个内核线程,分别是:kernel_init和kthreadd

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

  (3)rest_init最终调用cpu_idle函数结束了整个内核的启动。也就是说linux内核最终结束了一个函数cpu_idle。这个函数里面肯定是死循环。

  (4)简单来说,linux内核最终的状态是:有事干的时候去执行有意义的工作(执行各个进程任务),实在没活干的时候就去死循环(实际上死循环也可以看成是一个任务)。

  (5)之前已经启动了内核调度系统,调度系统会负责考评系统中所有的进程,这些进程里面只有有哪个需要被运行,调度系统就会终止cpu_idle死循环进程(空闲进程)转而去执行有意义的干活的进程。这样操作系统就转起来了。


2.1:什么是内核线程

  (1)进程和线程。简单来理解,一个运行的程序就是一个进程。所以进程就是任务、进程就是一个独立的程序。

独立的意思就是这个程序和别的程序是分开的,这个程序可以被内核单独调用执行或者暂停。

  (2)在linux系统中,线程和进程非常相似,几乎可以看成是一样的。实际上我们当前讲课用到的进程和线程的概念就是

一样的。

  (3)进程/线程就是一个独立的程序。应用层运行一个程序就构成一个用户进程/线程,那么内核中运行

一个函数(函数其实就是一个程序)就构成了一个内核进程/线程。

  (4)所以我们kernel_thead函数运行一个函数,其实就是把这个函数变成了一个内核线程去运行起来,然后他可以被内核调度系统去调度。说白了就是去调度器注册了一下,以后人家调度的时候会考虑你。


2.2:进程0、进程1、进程2

 (1)操作系统是用一个数字来表示/记录一个进程/线程的,这个数字就被称为这个进程的进程号。这个号码是从0开始分配的。因此这里涉及到的三个进程分别是linux系统的进程0、进程1、进程2.

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

(4)我们在ubuntu下ps -aux可以看到当前系统运行的所有进程,可以看出进程号是从1开始的。为什么不从0开始,因为进程0不是一个用户进程,而属于内核进程。

  进程0:进程0其实就是刚才讲过的idle进程,叫空闲进程,也就是死循环。

  进程1:kernel_init函数就是进程1,这个进程被称为init进程。

  进程2:kthreadd函数就是进程2,这个进程是linux内核的守护进程。它的作用是管理调度其他内核进程这个进程是用来保证linux内核自己本身能正常工作的。


3:init进程分析

  需要注意的一点是这个进程刚开始运行的时候是内核态,是属于内核进程,然后它自己运行了一个用户太下面的程序后把自己强行转成了用户态,因为init进程自身完成了从内核态到用户态的过渡,所以后续的其他进程都可以工作在用户态下面了


3.1:init进程在内核态下做了什么

  重要的点就挂载根文件系统,并试图找到用户态下的那个init程序,原因是init进程要完成从内核态到用户态的转变就必须去运行一个用户态的应用程序,而内核源代码中的程序都是属于内核态的,所以这个应用程序必须不属于内核源代码,这样才能保证自己是用户态,所以这个应用程序就的是由另外一份文件提供,即根文件系统

3.2: init进程在用户态下做了什么

  init进程大部分有意义的工作都是在用户态下进行的,原因是用户态下的所有进程都是直接或者间接由init进程生成的。

3.3:如何从内核态跳跃到用户态?还能回来不?

  init进程在内核态下面时,通过调用kernel_execve函数来执行一个用户空间编译链接的应用程序就跳跃到了用户态下面了,需要注意的是,这个跳跃的过程进程号并没有改变还是进程1,并且这个跳跃是单向的,以后要从用户态回到内核态只有走API这一条路了

kernel_execve函数被调用的路径start_kernel->rest_init->kernel_thread->kernel_init->init_post->run_init_process->kernel_execve


4:init进程在内核态下的分析(也就是kernel_init函数)

4.1:打开控制台,代码如下:

1
2
3
4
5
/* Open the /dev/console on the rootfs, this should never fail */
if  (sys_open(( const  char  __user *)  "/dev/console" , O_RDWR, 0) < 0)
printk(KERN_WARNING  "Warning: unable to open an initial console.\n" );
( void ) sys_dup(0);
( void ) sys_dup(0);

  (1)linux系统中每个进程都有自己的一个文件描述符表,表中存储的是本进程打开的文件。

  (2)linux系统中有一个设计理念:一切届是文件。所以设备也是以文件的方式来访问的。我们要访问一个设备,就要去打开这个设备对应的文件描述符。譬如/dev/fb0这个设备文件就代表LCD显示器设备,/dev/buzzer代表蜂鸣器设备,/dev/console代表控制台设备。打开一个设备的文件就会得到这个设备的文件描述符(或者是文件描述符的编号),这个编号就代表这个设备,以后操作这个设备就用这个文件描述符来操作它

  (3)这里我们打开了/dev/console文件,并且复制了2次文件描述符,一共得到了3个文件描述符。这三个文件描述符分别是0、1、2.这三个文件描述符就是所谓的:标准输入、标准输出、标准错误。

  (4)进程1打开了三个标准输出输出错误文件,因此后续的进程1衍生出来的所有的进程默认都具有这3个三件描述符


4.2:挂载根文件系统,代码如下

1
2
3
4
if  (sys_access(( const  char  __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}

  (1)prepare_namespace函数中挂载根文件系统

  (2)根文件系统在哪里?根文件系统的文件系统类型是什么? uboot通过传参来告诉内核这些信息。uboot传参中的root=/dev/mmcblk0p2 rw 这一句就是告诉内核根文件系统在哪里uboot传参中的rootfstype=ext3这一句就是告诉内核rootfs的类型。

  (3)如果内核挂载根文件系统成功,则会打印出:VFS: Mounted root (ext3 filesystem) on device 179:2.如果挂载根文件系统失败,则会打印:No filesystem could mount root, tried:  yaffs2

  (4)如果内核启动时挂载rootfs失败,则后面肯定没法执行了。内核中设置了启动失败休息5s自动重启的机制,因此这里会自动重启,所以有时候大家会看到反复重启的情况。

  (5)如果挂载rootfs失败,可能的原因有:最常见的错误就是uboot的bootargs设置不对。rootfs烧录失败(fastboot烧录不容易出错,以前是手工烧录很容易出错)rootfs本身制作失败的。(尤其是自己做的rootfs,或者别人给的第一次用)


5:执行用户态下的进程1程序

  (1)上面一旦挂载rootfs成功,则进入rootfs中寻找应用程序的init程序,

这个程序就是用户空间的进程1.找到后用run_init_process(里面的kernel_execve函数)去执行他

  (2)我们如果确定init程序是谁?方法是:先从uboot传参cmdline中看有没有指定,如果有指定先执行cmdline中指定的程序。cmdline中的init=/linuxrc这个就是指定rootfs中哪个程序是init程序。这里的指定方式就表示我们rootfs的根目录下面有个名字叫linuxrc的程序,这个程序就是init程序。如果uboot传参cmdline中没有init=xx或者cmdline中指定的这个xx执行失败,还有备用方案。

第一备用:/sbin/init,第二备用:/etc/init,第三备用:/bin/init,第四备用:/bin/sh。

如果以上都不成功,则kernel启动失败




本文转自 菜鸟养成记 51CTO博客,原文链接:http://blog.51cto.com/11674570/1840899
相关文章
|
2月前
|
安全 网络协议 Linux
深入理解Linux内核模块:加载机制、参数传递与实战开发
本文深入解析了Linux内核模块的加载机制、参数传递方式及实战开发技巧。内容涵盖模块基础概念、加载与卸载流程、生命周期管理、参数配置方法,并通过“Hello World”模块和字符设备驱动实例,带领读者逐步掌握模块开发技能。同时,介绍了调试手段、常见问题排查、开发规范及高级特性,如内核线程、模块间通信与性能优化策略。适合希望深入理解Linux内核机制、提升系统编程能力的技术人员阅读与实践。
248 1
|
14天前
|
安全 Linux iOS开发
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
160 53
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
2月前
|
Ubuntu Linux
Ubuntu 23.04 用上 Linux 6.2 内核,预计下放到 22.04 LTS 版本
Linux 6.2 带来了多项内容更新,修复了 AMD 锐龙处理器设备在启用 fTPM 后的运行卡顿问题,还增强了文件系统。
|
14天前
|
Linux API iOS开发
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
112 14
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
24天前
|
数据管理 Linux iOS开发
Splunk Enterprise 9.4.5 (macOS, Linux, Windows) - 机器数据管理和分析
Splunk Enterprise 9.4.5 (macOS, Linux, Windows) - 机器数据管理和分析
68 0
|
2月前
|
监控 Ubuntu Linux
什么Linux,Linux内核及Linux操作系统
上面只是简单的介绍了一下Linux操作系统的几个核心组件,其实Linux的整体架构要复杂的多。单纯从Linux内核的角度,它要管理CPU、内存、网卡、硬盘和输入输出等设备,因此内核本身分为进程调度,内存管理,虚拟文件系统,网络接口等4个核心子系统。
221 0
|
2月前
|
Web App开发 缓存 Rust
|
2月前
|
Ubuntu 安全 Linux
Ubuntu 发行版更新 Linux 内核,修复 17 个安全漏洞
本地攻击者可以利用上述漏洞,攻击 Ubuntu 22.10、Ubuntu 22.04、Ubuntu 20.04 LTS 发行版,导致拒绝服务(系统崩溃)或执行任意代码。