rest_init函数分析(续)

copy from:https://yq.aliyun.com/articles/559628

本文主要阐述,内核态,怎么样启动到用户态的;

代码在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启动失败

原文地址:https://www.cnblogs.com/Oude/p/12588325.html

时间: 2024-10-19 23:13:35

rest_init函数分析(续)的相关文章

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)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

start_kernel中的rest_init函数到init进程

copy from :https://blog.csdn.net/challen537/article/details/6120130 tart_kernel ,是用来启动内核的主函数,我想大家都知道这个函数啦,而在该函数的最后将调用一个函数叫 rest_init() ,它执行完,内核就起来了, asmlinkage void __init start_kernel(void) { ...... /* Do the rest non-__init'ed, we're now alive */ r

linux C函数之strdup函数分析

一.函数分析 1.函数原型: #include <string.h>char *strdup(const char *s); 2.功能: strdup()函数主要是拷贝字符串s的一个副本,由函数返回值返回,这个副本有自己的内存空间,和s没有关联.strdup函数复制一个字符串,使用完后,要使用delete函数删除在函数中动态申请的内存,strdup函数的参数不能为NULL,一旦为NULL,就会报段错误,因为该函数包括了strlen函数,而该函数参数不能是NULL. 3.strdup函数实现 c

如何验证一个地址可否使用—— MmIsAddressValid函数分析

又是一篇内核函数分析的博文,我个人觉得Windows的内核是最好的老师,当你想实现一个功能之前可以看看Windows内核是怎么做的,说不定就有灵感呢:) 首先看下官方的注释说明: /*++ Routine Description: For a given virtual address this function returns TRUE if no page fault will occur for a read operation on the address, FALSE otherwis

page_address()函数分析--如何通过page取得虚拟地址

由于X86平台上面,内存是划分为低端内存和高端内存的,所以在两个区域内的page查找对应的虚拟地址是不一样的. 一. x86上关于page_address()函数的定义 在include/linux/mm.h里面,有对page_address()函数的三种宏定义,主要依赖于不同的平台: 首先来看看几个宏的定义:CONFIG_HIGHMEM:顾名思义,就是是否支持高端内存,可以查看config文件,一般推荐内存超过896M的时候,才配置为支持高端内存.WANT_PAGE_VIRTUAL:X86平台

Oracle官网JNI简介和接口函数分析

第一章 概述 本章主要介绍JNI(Java Native Interface),JNI是一种本地编程接口.它允许运行在JAVA虚拟机中的JAVA代码和用其他编程语言,诸如C语言.C++.汇编,写的应用和库之间的交互操作. JNI的最大优势在于没有强加任何限制在JAVA虚拟机的下层实现上,因此,JAVA虚拟机供应商能够提供JNI的支持而不影响虚拟机的其他部分,程序员只需写出一个版本的本地应用和库,就可使之运行在一切支持JNI的JAVA虚拟机上. 本章包含了以下的要点: ? JNI概述 ? 目标 ?

如何验证一个地址可否使用——MmIsAddressValid函数分析

又是一篇内核函数分析的博文,我个人觉得Windows的内核是最好的老师,当你想实现一个功能之前可以看看Windows内核是怎么做的,说不定就有灵感呢:) 首先看下官方的注释说明: /*++ Routine Description: For a given virtual address this function returns TRUE if no page fault will occur for a read operation on the address, FALSE otherwis

Linux-0.11内核内存管理get_free_page()函数分析

/* *Author : DavidLin*Date : 2014-11-11pm*Email : [email protected] or [email protected]*world : the city of SZ, in China*Ver : 000.000.001*history : editor time do 1)LinPeng 2014-11-11 created this file! 2)*/Linux-0.11内存管理模块是源代码中比较难以理解的部分,现在把笔者个人的理解