对于这个知识点,自己以前一直不了解,今天特意总结下,作为自己的一个学习记录
一. init是Linux系统操作中不可缺少的程序之一。
所谓的init进程,它是一个由内核启动的用户级进程。
内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。
所以,init始终是第一个进程(其进程编号始终为1)。
内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。
如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。
二. init一共分为7个级别,这7个级别的所代表的含义如下
0:停机或者关机(千万不能将initdefault设置为0)
1:单用户模式,只root用户进行维护
2:多用户模式,不能使用NFS(Net File System)
3:完全多用户模式(标准的运行级别)
4:安全模式
5:图形化(即图形界面)
6:重启(千万不要把initdefault设置为6)
其实,可以通过查看/etc/rc.d/中的rc*.d的文件来对比理解。
init 0,对应的系统会运行,/etc/rc.d/rc0.d里指定的程序。我们来看下名称
[root@localhost ~]# ls /etc/rc.d/rc0.d
K01dnsmasq K15ksmtuned K35nmb K60crond K74lm_sensors K83portreserve K85rpcgssd K88iscsi K90network S00killall
K10cups K16ksm K35smb K66gpsd K75netfs K84NetworkManager K85rpcidmapd K88rsyslog K92ip6tables S01halt
K10saslauthd K20nfs K36mysqld K69rpcsvcgssd K75udev-post K84wpa_supplicant K86nfslock K89iscsid K92iptables
K10xfs K25sshd K50haldaemon K70vboxdrv K76openvpn K85mdmonitor K87alsasound K89netplugd K98qemu
K15gpm K30sendmail K50netconsole K74acpid K83bluetooth K85messagebus K87rpcbind K89rdisc K99lvm2-monitor
开机会执行的两个进程是killall和halt,这两个都表示为终止进程。
故init 0是用于表示关机的。
init 1,对应的系统会运行,/etc/rc.d/rc1.d里指定的程序。
[root@localhost ~] # ls /etc/rc.d/rc1.d
K01dnsmasq K15ksmtuned K35nmb K60crond K74lm_sensors K84NetworkManager K85rpcidmapd K88rsyslog K92ip6tables S99single
K10cups K16ksm K35smb K66gpsd K75netfs K84wpa_supplicant K86nfslock K89iscsid K92iptables
K10saslauthd K20nfs K36mysqld K69rpcsvcgssd K76openvpn K85mdmonitor K87alsasound K89netplugd K98qemu
K10xfs K25sshd K50haldaemon K70vboxdrv K83bluetooth K85messagebus K87rpcbind K89rdisc S02lvm2-monitor
K15gpm K30sendmail K50netconsole K74acpid K83portreserve K85rpcgssd K88iscsi K90network S26udev-post
这个级别启动的服务有三个,udev、lvm相关的和single(单用户模式的服务)。
故此级别是单用户模式,只有root能用,不支持其他用户。
init 2,对应的系统会运行,/etc/rc.d/rc2.d里指定的程序。
[root@localhost ~ ]# ls /etc/rc.d/rc2.d/
K01dnsmasq K20nfs K36mysqld K74lm_sensors K85rpcgssd K89netplugd S08iptables S23NetworkManager S30vboxdrv S99local
K10saslauthd K25sshd K50haldaemon K75netfs K85rpcidmapd K89rdisc S12rsyslog S24portreserve S35qemu
K10xfs K30sendmail K50netconsole K76openvpn K86nfslock K90network S13rpcbind S25cups S85gpm
K15ksmtuned K35nmb K66gpsd K83bluetooth K88iscsi S02lvm2-monitor S15mdmonitor S26acpid S90crond
K16ksm K35smb K69rpcsvcgssd K84wpa_supplicant K89iscsid S08ip6tables S22messagebus S26udev-post S99alsasound
这个级别启动的服务多了,NetworkManager/iptables/acpid/alsa都已经开启,但是nfs,smb,openvpn 相关服务没有开启,这个级别不支持nfs。
init 3 , 对应的系统运行/etc/rc.d/rc3.d
[root@localhost ~] # ls /etc/rc.d/rc3.d/
K01dnsmasq K30sendmail K74lm_sensors K89rdisc S08iptables S18rpcidmapd S25cups S35qemu S85ksmtuned S99local
K10saslauthd K36mysqld K76openvpn K90network S12rsyslog S19rpcgssd S25netfs S50bluetooth S90crond
K10xfs K50netconsole K84wpa_supplicant K99lvm2-monitor S13iscsi S22messagebus S26acpid S50haldaemon S91nmb
K20nfs K66gpsd K85mdmonitor S07iscsid S13rpcbind S23NetworkManager S26udev-post S84ksm S91smb
K25sshd K69rpcsvcgssd K89netplugd S08ip6tables S14nfslock S24portreserve S30vboxdrv S85gpm S99alsasound
这个级别nfs服务是开启的,被成为完全多用户模式。
init 4
[root@localhost ~ ]# ls /etc/rc.d/rc4.d/
K01dnsmasq K30sendmail K66gpsd K85mdmonitor S07iscsid S13rpcbind S23NetworkManager S26udev-post S84ksm S99local
K10saslauthd K35nmb K69rpcsvcgssd K89netplugd S08ip6tables S14nfslock S24portreserve S30vboxdrv S85gpm
K10xfs K35smb K74lm_sensors K89rdisc S08iptables S18rpcidmapd S25cups S35qemu S85ksmtuned
K20nfs K36mysqld K76openvpn K90network S12rsyslog S19rpcgssd S25netfs S50bluetooth S90crond
K25sshd K50netconsole K84wpa_supplicant K99lvm2-monitor S13iscsi S22messagebus S26acpid S50haldaemon S99alsasound
此模式被称为安全模式。
init 5
[root@localhost ~ ]# ls /etc/rc.d/rc5.d/
K01dnsmasq K25sshd K66gpsd K84wpa_supplicant K87rpcbind K90network S22messagebus S26udev-post S84ksm S99local
K10saslauthd K30sendmail K69rpcsvcgssd K85mdmonitor K88iscsi K99lvm2-monitor S23NetworkManager S30vboxdrv S85ksmtuned
K10xfs K36mysqld K74lm_sensors K85rpcgssd K89iscsid S08ip6tables S25cups S35qemu S91nmb
K15gpm K50netconsole K76openvpn K85rpcidmapd K89netplugd S08iptables S25netfs S50bluetooth S91smb
K20nfs K60crond K83portreserve K86nfslock K89rdisc S12rsyslog S26acpid S50haldaemon S99alsasound
完全的图形化界面模式
init 6
[root@localhost ~ ]# ls /etc/rc.d/rc6.d/
K01dnsmasq K15ksmtuned K35nmb K60crond K74lm_sensors K83portreserve K85rpcgssd K88iscsi K90network S00killall
K10cups K16ksm K35smb K66gpsd K75netfs K84NetworkManager K85rpcidmapd K88rsyslog K92ip6tables S01reboot
K10saslauthd K20nfs K36mysqld K69rpcsvcgssd K75udev-post K84wpa_supplicant K86nfslock K89iscsid K92iptables
K10xfs K25sshd K50haldaemon K70vboxdrv K76openvpn K85mdmonitor K87alsasound K89netplugd K98qemu
K15gpm K30sendmail K50netconsole K74acpid K83bluetooth K85messagebus K87rpcbind K89rdisc K99lvm2-monitor
这个级别里,只有两个服务,一个为killall,一个是reboot,即,关闭现在的系统,重启。故此级别是重启。
不同的系统版本,可能里面的文件会不同,如果要查看,可以通过ll来看,其实他们都是软连接。
扩展分析:
一、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( "add r0, %0, %1\n\t" "mov r1, %2\n\t" "mov r2, %3\n\t" "bl memmove\n\t" /* copy regs to top of stack */ "mov r8, #0\n\t" /* not a syscall */ "mov r9, %0\n\t" /* thread structure */ "mov sp, r0\n\t" /* reposition stack pointer */ "b ret_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系统下,经常遇到蓝屏问题。好了上面的理解纯属个人理解,如有误,还望指出。