大家周末好,今天给大家继续分享linux内核学习。上次讲解到linux内核启动分析的前期准备,还没有去分析linux内核具体启动分析过程,这里我换一种方式来分享,在linux启动后,linux系统接下来会如何进行工作?然后再反过来具体分析linux内核启动过程,启动过程会设计到一些汇编语言,以及这个时候去具体分析c语言代码的话,就真正考验c语言的功底的时候到了;同时c语言的基本功,大家可以去看我以前学习c语言的一些常用c语言用法,我把它搞成了专辑,方便大家查看。
一、init进程完成了从内核态向用户态的转变:
1、一个进程2种状态:
这里所说的一个进程两种状态,说的是进程状态的转换;首先在介绍这种状态的转换之前,我们来了解一下什么是init进程,它其实是linux系统在启动后运行的第一个进程(这里关于进程的学习,可以去看我之前分享的linux应用编程专辑,有很详细的介绍);而init进程刚开始运行的时候是内核态,它属于一个内核线程,然后他自己运行了一个用户态下面的程序后把自己强行转成了用户态。因为init进程自身完成了从内核态到用户态的过度,因此后续的其他进程都可以工作在用户态下面了。
2、内核态下做了什么?
内核状态下重点就做了一件事情,就是挂载根文件系统并试图找到用户态下的那个init程序。init进程要把自己转成用户态就必须运行一个用户态的应用程序(这个应用程序名字一般也叫init),要运行这个应用程序就必须得找到这个应用程序,要找到它就必须得挂载根文件系统,因为所有的应用程序都在文件系统中。内核源代码中的所有函数都是内核态下面的,执行任何一个都不能脱离内核态。应用程序必须不属于内核源代码,这样才能保证自己是用户态。也就是说我们这里执行的这个init程序和内核不在一起,他是另外提供的。提供这个init程序的那个人就是根文件系统。
打个不恰当的比喻,比如大家都知道的建房子,在这之前,你必须打好地基,打好了地基之后,你才能开始动工在地基上砌砖头了,也就是各种操作了。
3、用户态下做了什么?
init进程大部分有意义的工作都是在用户态下进行的。init进程对我们操作系统的意义在于:其他所有的用户进程都直接或者间接派生自init进程。
4、如何从内核态跳跃到用户态?还能回来不?
init进程在内核态下面时,通过一个函数kernel_execve来执行一个用户空间编译连接的应用程序就跳跃到用户态了。注意这个跳跃过程中进程号是没有改变的,所以一直是进程1.这个跳跃过程是单向的,也就是说一旦执行了init程序转到了用户态下整个操作系统就算真正的运转起来了,以后只能在用户态下工作了,用户态下想要进入内核态只有走API这一条路了。这就是大家经常看操作系统大致框架都是这样描述的:
上层:表示我们的应用程序,在linux里面我们会有相应的api(或者自己写的)
中间层:就是我们的内核了,也就是操作系统了
底层:就是实实在在的硬件电路(当然在os和硬件之间有一个启动程序,也就是我们常说的uboot)。
具体kernel_execve函数如下(这里赞不分析,暂时让大家理性的感受一下第一次看linux内核代码的感受,这里主要面向第一次接触linux代码的小伙伴哦。):
int kernel_execve(const char *filename, char *const argv[], char *const envp[])
{
struct pt_regs regs;
int ret;
memset(®s, 0, sizeof(struct pt_regs));
ret = do_execve((char *)filename, (char __user * __user *)argv,
(char __user * __user *)envp, ®s);
if (ret < 0)
goto out;
/*
* Save argc to the register structure for userspace.
*/
regs.ARM_r0 = ret;
/*
* We were successful. We won't be returning to our caller, but
* instead to user space by manipulating the kernel stack.
*/
asm("addr0, %0, %1\n\t"
"movr1, %2\n\t"
"movr2, %3\n\t"
"blmemmove\n\t"/* copy regs to top of stack */
"movr8, #0\n\t"/* not a syscall */
"movr9, %0\n\t"/* thread structure */
"movsp, r0\n\t"/* reposition stack pointer */
"bret_to_user"
:
: "r" (current_thread_info()),
"Ir" (THREAD_START_SP - sizeof(regs)),
"r" (®s),
"Ir" (sizeof(regs))
: "r0", "r1", "r2", "r3", "ip", "lr", "memory");
out:
return ret;
}
二、init进程构建了用户交互界面:
在上面也说了,在init进程切换到用户状态后,以后对操作系统操作的话就能只能在用户状态下操作了,而这各种操作也就是我们的进程操作了,和windows里面的实际应用程序一样,一个程序就是一个进程,比如我们在windows任务管理器里面就可以看到如下图所示:
在我们linux系统里面的话,在init进程转换为用户状态下后,后面有一些我们比较熟悉的进程操作:login进程、命令行进程、shell进程(shell,我们都很熟悉,人机交互图像话界面),并且shell进程又会启动了其他用户进程;然后在命令行和shell进程一旦工作了,用户就可以在命令行下通过./xx的方式来执行其他应用程序,每一个应用程序的运行就是一个进程。
三、总结:
为啥要把init进程状态转换到用户状态(这就好像一张纸一样,你可以在纸上随便涂鸦,但是当你的纸张破了等,那可能就操作不了。),其实操作系统内核里面的东西不能随便更改,这样做也是在保护操作系统内核。就比如说,你房子不能说,把地基给搞垮了,把地基搞垮了,那你这房子就完蛋了;如果你把操作系统给搞挂了,那你电脑也玩完了,最为常见的就是,在我们windows系统下,经常遇到蓝屏问题。好了上面的理解纯属个人理解,如有误,还望指出。