【日常积累】Linux之init系统学习

init系统简介:

Linux 操作系统的启动首先从 BIOS 开始,接下来进入 boot loader,由 bootloader 载入内核,进行内核初始化。内核初始化的最后一步就是启动 pid 为 1 的 init 进程,这个进程是系统的第一个进程,它负责产生其他所有用户进程。

init 的一些特点

  • init是Linux系统操作中不可缺少的程序之一。
  • 所谓的init进程,它是由内核启动的第一个用户级进程。内核自行启动(已被装入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。所以,init始终是第一个进程(其进程编号始终为1)。
  • 仅仅将内核运行起来是毫无实际用途的,必须由 init 系统将系统代入可操作状态。比如启动外壳 shell 后,便有了人机交互,这样就可以让计算机执行一些预订程序完成有实际意义的任务(这其实就是就是后面所说的内核态到用户态的转变)。内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。

Linux下的三个特殊进程

Linux下有三个特殊的进程idle进程(PID=0),init进程(PID=1),和kthreadd(PID=2)

  • idle进程由系统自动创建,运行在内核态。idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个没有通过fork或者kernel_thread产生的进程。完成加载系统后,演变为进程调度、交换。
  • kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间,负责所有内核进程的调度和管理。它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthread的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程 。
  • init进程由idle通过kernel_thread创建,在内核空间完成初始化后,加载init程序。在这里我们就主要了解下init进程,init进程由0进程创建,完成系统的初始化,是系统中所有其他用户进程的祖先进程
    Linux中的所有进程都是由init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成后,init将变成为守护进程监视系统其他进程。(内核态转变为用户态)

大致过程为:0号进程->1号内核进程->1号用户进程(init进程)->getty进程->shell进程

init进程完成从内核态向用户态的转变

一个进程先后两种状态

init进程刚开始运行的时候是内核态,它属于一个内核线程,然后运行一个用户态下面的程序后(如/sbin/init),把自己转成用户态(后面的进程需要工作在用户态下)。

init进程完成了从内核态到用户态的过渡,因此后续的其他进程都可以工作在用户态。

init进程在内核态下的工作内容

主要是挂载根文件系统,并试图找到用户态下的那个init程序。(这句话看出,init进程是早于init程序运行的。)

init进程要把自己转成用户态就必须运行一个用户态的应用程序,要运行这个应用程序就必须得找到这个应用程序,要找到这个应用程序就必须得挂载根文件系统,因为所有的应用程序都在文件系统中。

内核源代码中的所有函数都处于内核态,执行其中任何一个都不能脱离内核态。而应用程序必须不属于内核源代码,这样才能保证应用程序处于用户态。这里执行的init程序和内核不在一起,由根文件系统另外提供。

init进程在用户态下的工作内容

init进程大部分有意义的工作都是在用户态下进行的。init进程对操作系统的意义在于,其他所有的用户进程都直接或者间接派生自init进程。

init进程如何从内核态跳跃到用户态 ?还能回来不?

init进程处于内核态时,通过函数do_execve来执行一个用户空间编译链接的应用程序就跳跃到用户态了。

  • 跳跃过程中进程号没有改变,一直是进程1。
  • 跳跃过程是单向的,一旦执行init程序转到用户态,整个操作系统就算真正运转起来了,以后只能在用户态下工作,用户态下想要进入内核态只能通过调用API。

1、init进程挂载了根文件系统

(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本身制作失败的。

2、init进程执行init程序完成内核态到用户态的转变

(1)一旦挂载rootfs成功,则进入rootfs中寻找应用程序的init程序(在init_post()函数中),找到后用run_init_process去执行。

(2)如果确定init程序是谁?

先从uboot传参cmdline中看有没有指定,如果有指定先执行cmdline中指定的程序。比如init=/linuxrc表示rootfs的根目录下的linuxrc程序就是init程序。

如果uboot传参cmdline中没有init=xx或者cmdline中指定的这个xx执行失败,还有备用方案。第一备用:/sbin/init,第二备用:/etc/init,第三备用:/bin/init,第四备用:/bin/sh。如果以上都不成功,则没有办法了。

init=/linuxrc一般指向busybox。

3、init进程构建了用户交互界面

(1)init进程是其他用户进程的祖先。

linux系统中一个进程的创建是通过其父进程创建出来的。根据这个理论只要有一个父进程就能生出一堆子孙进程了。

(2)init启动了login进程(用户登录进程)、命令行进程(提供命令行环境)、shell进程(提供命令解释和执行)。

(3)shell进程启动了其他用户进程。

命令行和shell一旦工作,用户就可以在命令行下通过./xx的方式来执行其他应用程序,每一个应用程序的运行就是一个进程。

4、init进程打开了控制台

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

(2)linux系统中一切皆是文件,因此设备也是以文件的方式来访问的。要访问一个设备,就要打开此设备对应的文件描述符。譬如/dev/fb0这个设备文件就代表LCD显示器设备,/dev/buzzer代表蜂鸣器设备,/dev/console代表控制台设备。

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

(4)进程1打开了这3个文件描述符,因此进程1衍生出来的所有的进程默认都具有这3个文件描述符。

运行级别

简单的说,运行级就是操作系统当前正在运行的功能级别。这个级别有多种,以centos为例,6和7版本的设置方法不同,具体对应关系如下。

init level (centos 6 /etc/inittab) systemctl target(centos 7) 说明
0 poweroff.target 停机(千万不能把initdefault 设置为0 )
1 rescure.target 单用户(救援)模式
2 multi-user.target 多用户,没有 NFS
3 multi-user.target 完整的多用户文本模式级别,登录后进入到控制台命令行模式
4 未使用
5 graphical.target X11 (xwindow,能够正常切换的前提是系统支持)
6 reboot.target 重新启动
bash 复制代码
[root@k8s-m1 ~]# ll /usr/lib/systemd/system/runlevel*target
lrwxrwxrwx 1 root root 15 Feb 27 16:14 /usr/lib/systemd/system/runlevel0.target -> poweroff.target
lrwxrwxrwx 1 root root 13 Feb 27 16:14 /usr/lib/systemd/system/runlevel1.target -> rescue.target
lrwxrwxrwx 1 root root 17 Feb 27 16:14 /usr/lib/systemd/system/runlevel2.target -> multi-user.target
lrwxrwxrwx 1 root root 17 Feb 27 16:14 /usr/lib/systemd/system/runlevel3.target -> multi-user.target
lrwxrwxrwx 1 root root 17 Feb 27 16:14 /usr/lib/systemd/system/runlevel4.target -> multi-user.target
lrwxrwxrwx 1 root root 16 Feb 27 16:14 /usr/lib/systemd/system/runlevel5.target -> graphical.target
lrwxrwxrwx 1 root root 13 Feb 27 16:14 /usr/lib/systemd/system/runlevel6.target -> reboot.target

运行级别的配置

centos6设置默认的启动级别

bash 复制代码
# 查看运行级别: centos7 也可以使用该命令。
	[root@k8s-m1 ~]# runlevel 
	N 3
# 修改运行级别:
	临时修改: init level(对应的级别即可)
	永久修改:需要修改 /etc/inittab 文件。
在最下面的一行中的语句 id:5:initdefault 中的数字5改成需要的启动程度就可以了,一般是命令行模式 3

centos7设置默认的启动级别

bash 复制代码
#centos7修改设置默认的系统级别
[root@k8s-m1 ~]#  systemctl set-default multi-user.target 
Removed symlink /etc/systemd/system/default.target.
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target.
#centos7查看默认的系统级别
[root@k8s-m1 ~]#  systemctl get-default 
 multi-user.target

参考:https://blog.csdn.net/m0_45406092/article/details/130660743

更多关于Linux的知识请前往博客主页查看。

相关推荐
e***74952 小时前
Spring Security 官网文档学习
java·学习·spring
哈里谢顿3 小时前
使用kvm创建一台虚拟机
linux
hanyi_qwe4 小时前
文本三剑客--awk
linux·运维·服务器
Caven774 小时前
【Linux 技巧】如何在登录时自动激活 Conda Base 环境
linux·运维·conda
凌寒115 小时前
Linux(Debian)安装、卸载 MySQL
linux·运维·mysql·debian
IT小哥哥呀5 小时前
如何从 Windows SSH 进入 VirtualBox Ubuntu 虚拟机——密码认证(逐步指南)
linux·windows·ubuntu·ssh·教程·虚拟机
山河亦问安5 小时前
Spring原理编码学习
java·学习·spring
思成不止于此6 小时前
【C++ 数据结构】二叉搜索树:原理、实现与核心操作全解析
开发语言·数据结构·c++·笔记·学习·搜索二叉树·c++40周年
怀旧,6 小时前
【Linux系统编程】7. 进程的概念(上)
linux·运维·服务器
7***n756 小时前
API网关设计模式
linux·服务器·设计模式