CentOS系统启动流程图文详解.

原文:http://www.linuxidc.com/Linux/2017-03/141966.htm

熟悉系统启动流程对于我们学习Linux系统是非常有帮助的,虽然基础,但能帮助我们更加理解Linux系统的工作机制。以下将以CentOS发行版为例来介绍Linux系统的启动流程,因为在CentOS 5、CentOS 6以及CentOS 7使用的初始化程序init各不相同,虽然CentOS 6和CentOS 7都有向后兼容,但在工作机制上仍有一些差异,因此以下主要介绍CentOS 5/6系统启动流程。

CentOS的启动流程总体顺序如下(以CentOS 6为例):

POST --> Boot Sequence --> bootloader(MBR) --> Kernel --> 加载rootfs --> switchroot --> /sbin/init --> (配置文件:/etc/inittab, /etc/init/*.conf) --> 根据init配置文件设置默认运行级别 --> 运行系统初始化脚本/etc/rc.d/rc.sysinit,完成系统初始化 --> 开启或关闭用户选定的对应运行级别下所对应的服务 --> 启动终端,打印登录提示符。

注意:红色部分代表内核空间的系统启动流程,紫色部分代表用户空间的系统启动流程。

接下来逐一详解:

第一步:POST加电自检

主要实现的功能是检测各个外围硬件设备是否存在而且能够正常运行起来,实现这一自检功能的是固化在主板上的ROM(主要代表为CMOS)芯片上的BIOS(Basic Input/Output System)程序;例如BIOS会检测CPU、Memory以及I/O设备是否能够正常运行,如果是个人计算机的话可能还会检测一下显示器。只要一通电,CPU就会自动去加载ROM芯片上的BIOS程序,是这样来实现的。而检测完成之后就进行硬件设备的初始化。

第二步:Boot Sequence(选择启动设备以加载MBR)

主要实现的功能是选择要启动的硬件设备,选择了之后就可以读取这个设备上位于MBR里头的bootloader了。这一步的实现是这样的:根据BIOS中对启动顺序的设定,BIOS自己会依次扫描各个引导设备,然后第一个被扫描到具有引导程序(bootloader)的设备就被作为要启动的引导设备。

第三步:加载bootloader(MBR)

这一步实现起来的步骤比较多,前面的BIOS通过读取并执行启动设备的MBR中的bootloader,而bootloader要实现的功能就是提供一个菜单给用户,让用户去选择要启动的系统或不同的内核版本,然后把用户选择的内核版本加载至RAM中的特定空间,接着在RAM中解压、展开,而后把系统控制权移交给内核。

    grub是bootloader中的一种,就grub来说,为了打破在MBR中只有446Bytes用于存放bootloader这一限制,所以这一步的实现是这样的:grub是通过分成三个阶段来实现加载内核这一功能的,这三个阶段分别是:stage1, stage1.5以及stage2。其中:

stage1:存放于MBR的前446Bytes,用于加载stage1.5阶段,目的是为了识别并驱动stage2(或者/boot)所在分区的文件系统;

stage1.5:存放于MBR之后的扇区,加载stage2所在分区的文件系统驱动,让stage1中的bootloader能识别stage2所在分区的文件系统;

stage2:存放于磁盘分区之上,具体存放于/boot/grub目录之下,主要用于加载内核文件(vmlinuz-VERSION-RELEASE)以及ramdisk这个临时根文件系统(initrd-VERSION-RELEASE.img或initramfs-VERSION-RELEASE.img)。

概述:假如要启动的是硬盘设备,首先我们的硬件平台主板BIOS必须能够识别硬盘,然后BIOS才能加载硬盘中的bootloader,而bootloader自身加载后就能够直接识别当前主机上的硬盘设备了;不过,能够识别硬盘设备不代表能够识别硬盘设备中的文件系统,因为文件系统是额外附加的一层软件组织的文件结构,所以要对接一种文件系统,就必须要有对应的能够识别和理解这种文件系统的驱动,这种驱动就称为文件系统驱动。而stage1.5就是向grub提供文件系统驱动的,这样stage1就能访问stage2及内核所在的分区(/boot)了。

演示:

[root@localhost ~]# ls -1F /boot/
config-2.6.32-642.el6.x86_64    //此版本内核被编译时选择的功能与模块配置文件;
efi/
grub/    //就是引导装载程序grub相关配置文件的目录;
initramfs-2.6.32-642.el6.x86_64.img    //临时根文件系统,提供根文件系统所在分区的驱动;
initrd-2.6.32-642.el6.x86_64kdump.img
lost+found/
symvers-2.6.32-642.el6.x86_64.gz
System.map-2.6.32-642.el6.x86_64    //内核功能放置到内存地址的映射表;
vmlinuz-2.6.32-642.el6.x86_64*    //就是内核文件;
[root@localhost ~]
[root@localhost ~]
[root@localhost ~]
[root@localhost ~]# ls -1F /boot/grub/
device.map
e2fs_stage1_5
fat_stage1_5
ffs_stage1_5
grub.conf
iso9660_stage1_5
jfs_stage1_5
menu.lst@
minix_stage1_5
reiserfs_stage1_5
splash.xpm.gz
stage1
stage2
ufs2_stage1_5
vstafs_stage1_5
xfs_stage1_5
//可以看到stage1, 各种文件系统对应的stage1.5及stage2这几个阶段的文件都存放于此目录下;

查看grub的配置:

[root@localhost ~]# vim /boot/grub/grub.conf
default=0    //设定默认启动第一个菜单项(title);
timeout=5    //等待用户选择菜单项的时长;
splashimage=(hd0,0)/grub/splash.xpm.gz    //指明菜单项的背景图片的文件路径;
hiddenmenu    //是否隐藏菜单的具体内容;
title CentOS (2.6.18-398.el5)    //此处为菜单项的标题;
    root (hd0,0)   //指定grub查找stage2及Kernel文件所在设备的分区;可理解为grub的"根"
    kernel /vmlinuz-2.6.18-398.el5 ro root=/dev/VolGroup00/LogVol00    //启动的内核;
    initrd /initrd-2.6.18-398.el5.img    //与内核匹配的ramdisk文件;
//注意:kernel和initrd的文件路径均以grub的"根"作为起始目录,且存放于stage2所在分区上;

需要注意的是,stage2、内核以及ramdisk文件通常放置于一个基本磁盘分区之上,因为grub无法驱动lvm、高级软raid等复杂逻辑设备,除非提供一个复杂的驱动接口,否则如果stage2及内核等文件都存放在lvm等复杂逻辑设备上将无法被stage1所识别,更别说加载了!

第四步:Kernel自身初始化

Kerenl在得到系统控制权之后,首先要进行自身初始化,而初始化的主要作用是:

    (1)探测可识别到的所有硬件设备;

bootloader将系统控制权移交给内核就好比如后朝推翻前朝,统治者(内核)当权之后,首先要检查一下有哪些是前朝所留下的,例如有哪些领土、人力、财力、兵力可用等等。

    (2)加载硬件驱动程序,即加载真正的根文件系统所在设备的驱动程序(有可能会借助于ramdisk加载驱动);

这就像统治者(内核)在得知底下存在的人力、财力之后,开始将可以“为我所用”的人力纳入麾下,听自己使唤,而不听使唤的杀掉;

    (3)以只读方式挂载根文件系统;

如果有借助于ramdisk这个临时文件系统(虚根),则在这一步之后会执行根切换;否则不执行根切换。

    (4)运行用户空间的第一个应用程序:/sbin/init.

到这里内核空间的启动流程就结束了,而接下来是用户空间完成后续的系统启动流程。

注意:

ramdisk和内核是由bootloader一同加载到内存当中的,ramdisk是用于实现系统初始化的、基于内存的磁盘设备,即加载至内存(的某一段空间)后把内存当磁盘使用,并在内存中作为临时根文件系统提供给内核使用,帮助内核挂载真正的根文件系统。而之所以能够帮助内核挂载根文件系统是因为在ramdisk这个临时文件系统的/lib/modules目录下有真正的根文件系统所在设备的驱动程序;除此之外,这个临时文件系统也遵循FHS,例如有这些固定目录结构:/bin, /sbin, /lib, /lib64, /etc, /mnt, /media, ...

因为Linux内核有一个特性就是通过使用缓冲/缓存来达到加速对磁盘上文件的访问的目的,而ramdisk是加载到内存并模拟成磁盘来使用的,所以Linux就会为内存中的“磁盘”再使用一层缓冲/缓存,但是我们的ramdisk本来就是内存,它只不过被当成硬盘来使用罢了,这就造成双缓冲/缓存了,而且不会起到提速效果,甚至影响了访问性能;CentOS 5系列以及之前版本的ramdisk文件为initrd-VERSION-RELEASE.img,就会出现上述所说到的问题;而为了解决一问题,CentOS 6/7系列版本就将其改为initramfs-VERSION-RELEASE.img,使用文件系统的方式就可以避免双缓冲/缓存了,我们可以说这是一种提速机制。

需要注意的是,系统发行商为了适应于各个不同的硬件接口,因此将各个不同的硬件接口的驱动组装打包起来,例如在用户第一次使用光盘安装完系统之后,会动态探测当前主机上的硬件设备并调用与之对应的设备驱动再做成ramdisk文件的。所以,ramdisk文件并非必须的,如果只是为了将Linux安装于特定硬件平台上,就可以直接把对应的驱动编译进内核即可,而不需要去使用ramdisk文件了。

第五步:init管理用户空间服务进程

init可以理解成是内核派来管理用户空间的使者,就好像天使代表上帝来访问人间一样。init这个初始化程序会根据其配置文件执行一系列操作。虽然CentOS 5、CentOS 6以及CentOS 7的init配置 文件各不相同,但总体的启动流程是不变的。

这一步的流程是:/sbin/init --> 根据init配置文件设置默认运行级别 --> 运行系统初始化脚本/etc/rc.d/rc.sysinit,完成系统初始化 --> 关闭或启动用户选定的默认运行级别所对应的服务 --> 启动终端,打印登录提示符

(1)根据init配置文件设置默认运行级别

对于CentOS 5来说,初始化程序init是SysV init,其配置文件为:/etc/inittab;

对于CentOS 6来说,初始化程序init是upstart,其配置文件为:/etc/inittab, /etc/init/*.conf,也就是upstart将配置文件拆分成多个,在/etc/init/目录下以conf结尾的都是upstart风格的配置文件,而/etc/inittab仅用于设置默认运行级别;

对于CentOS 7来说,初始化程序init是systemd,其配置文件为:/usr/lib/system/systemd/*, /etc/systemd/system/*;

注意:

    运行级别是为了系统的运行和维护等目的而设定的一种机制,对于CentOS 5/6来讲,一共有7种运行级别,即0-6,它们的意义如下:

0:关机模式,shutdown;

1:单用户模式(single user):不需要通过认证,登录进去之后为root用户身份,是维护模式;

2:多用户模式(multi user):会启动网络功能,但不会启动NFS,是维护模式;

3:多用户模式(multi user):为完全功能模式,提供文本界面;

4:预留级别,目前无特别使用目的,但习惯上以同3级别功能来使用;

5:多用户模式(multi user):为完全功能模式,提供图形界面;

6:重启模式,reboot;

CentOS 5配置文件详解:

每行定义一种action以及与之对应的process:
格式:id:runlevel:action:process
各字段解释:
    id:一个任务的标识符;
    runlevel:在哪些级别下启动该任务;格式可以是#,###,也可以为空(表示所有级别);
    action:在什么条件下启动该任务;
    process:任务;
action种类:
    wait:等待切换至该任务所在的级别时执行一次;
    respawn:一旦该任务终止,就会重新启动这个任务;
    initdefault:设定默认运行级别;此时process是省略掉的;
    sysinit:设定系统初始化方式,这里一般指定为/etc/rc.d/rc.sysinit脚本文件;

CentOS 6配置文件/etc/inittab如图:

CentOS 7配置文件/etc/inittab如图:

注意:CentOS 7使用target代替了runlevel,也就是在CentOS 7系列已经没有了运行等级这一概念了。而如果需要设置target则可参考该文件的后两行注释;

代码分析:

以下为CentOS 5的/etc/inittab文件

id:3:initdefault:    //设置默认运行级别,此处设置为3;
# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit    //指定系统初始化脚本;
//根据前面获取到的运行级别信息,运行/etc/rc.d/rc脚本;
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
//因为默认运行级别为3,因此只会开启或关闭/etc/rc.d/rc3.d/目录下的脚本所控制的服务;而其它
/etc/rc.d/rc#.d/目录则别略过;
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

(2)运行系统初始化脚本/etc/rc.d/rc.sysinit,完成系统初始化

运行初始化脚本是为了初始化系统环境,这一步初始化包括:

①设置主机名;

②设置欢迎信息;

③激活udev和selinux;

④挂载/etc/fstab文件中定义的所有文件系统;

⑤检测根文件系统,以读写方式重新挂载根文件系统;

⑥设置系统时钟;

⑦根据/etc/sysctl.conf文件来设置内核参数;

⑧激活lvm及软raid设备;

⑨激活swap设备;

⑩加载额外设备的驱动程序(因为内核只加载根文件系统所在分区的驱动程序);

清理操作;

注意:CentOS 7的systemd可能没有这一步,而是通过其他机制来实现。

(3)关闭或启动用户选定的默认运行级别下所对应的服务

要实现的功能是:根据前面用户通过init配置文件对默认运行级别的设定,关闭或启动这个运行级别下的服务。(注意:关闭在前,启动在后)

具体实现方式:运行/etc/rc.d/rc这个脚本文件,而初始化程序init根据前面获取的默认运行级别信息,将这个运行级别数字以参数方式传递给/etc/rc.d/rc脚本中的变量$runlevel;然后,这个脚本会以glob方式去把/etc/rc.d/rc$runlevel.d/S*匹配到的脚本所控制的服务开启,而把/etc/rc.d/rc$runlevel.d/K*匹配到的脚本所控制的服务开启。

/etc/rc.d/rc#.d/目录下的脚本文件命名格式:

(1)K##*:表示要停止的服务;'##'表示关闭优先级,数字越小,越是优先关闭;依赖到其他服务的服
         务会优先关闭,而被依赖的服务则后关闭;
          
(2)S##*:表示要启动的服务;'##'表示启动优先级,数字越小,越是优先启动;被依赖的服务会优先
         启动,而依赖到其他服务的服务则后启动;

例如,runlevel为3时,则匹配到的是/etc/rc.d/rc3.d/目录下的脚本文件;runlevel为5时,则匹配到的是/etc/rc.d/rc5.d/目录下的脚本文件等;而这些目录下的脚本有些是以S打头的,有些是以K打头的,这样的控制机制很容易想明白:其中以S打头的代表要开启的服务,以K打头的代表要关闭的服务。这就是我们的主机启动之后为什么有些服务会自动启动,而有些服务不会自动启动的原因了。

需要注意的是,在这些/etc/rc.d/rc#.d/目录下,除了S99local这个服务脚本之外,其他服务都链接到/etc/init.d/目录下对应的服务脚本,而S99local则链接到/etc/rc.d/rc.local(或/etc/rc.local)。所以,如果我们有不方便或者没必要写成服务脚本的程序,但又希望这样的程序能够在开机自动开启时,可以直接写进/etc/rc.d/rc.local这个脚本文件中就行了!

以下为/etc/rc.d/rc脚本文件代码节选:

# First, run the KILL scripts.
for in /etc/rc$runlevel.d/K* ; do
//这是一个for循环,根据前面获取的运行级别来匹配并关闭/etc/rc#.d/目录下所有以K打头的脚本
所控制的服务;
    check_runlevel "$i" || continue
    # Check if the subsystem is already up.
    subsys=${i#/etc/rc$runlevel.d/K??}
    [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
        || continue
    # Bring the subsystem down.
    if LC_ALL=C egrep -q "^..*init.d/functions" $i ; then
        $i stop        //stop,关闭服务!
    else
        action $"Stopping $subsys: " $i stop
    fi
done
# Now run the START scripts.
for in /etc/rc$runlevel.d/S* ; do
//这是一个for循环,根据前面获取的运行级别信息,匹配并开启/etc/rc#.d/目录下所有以S打头的
脚本所控制的服务;
    check_runlevel "$i" || continue
    # Check if the subsystem is already up.
    subsys=${i#/etc/rc$runlevel.d/S??}
    [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
        && continue
    # If we're in confirmation mode, get user confirmation
    if [ -f /var/run/confirm ]; then
        confirm $subsys
        test $? = 1 && continue
    fi
    update_boot_stage "$subsys"
    # Bring the subsystem up.
    if "$subsys" "halt" -o "$subsys" "reboot" ]; then
        export LC_ALL=C
        exec $i start
    fi
    if LC_ALL=C egrep -q "^..*init.d/functions" $i \
            || [ "$subsys" "single" -o "$subsys" "local" ]; then
        $i start        //start,开启服务!
    else
        action $"Starting $subsys: " $i start
    fi
done

演示:

查看/etc/rc.d/目录下的所有S99local脚本:

[root@localhost ~]# find /etc/rc.d/ -name S99local -exec ls -l {} \;
lrwxrwxrwx. 1 root root 11 Feb 20 06:11 /etc/rc.d/rc2.d/S99local -> ../rc.local
lrwxrwxrwx. 1 root root 11 Feb 20 06:11 /etc/rc.d/rc5.d/S99local -> ../rc.local
lrwxrwxrwx. 1 root root 11 Feb 20 06:11 /etc/rc.d/rc3.d/S99local -> ../rc.local
lrwxrwxrwx. 1 root root 11 Feb 20 06:11 /etc/rc.d/rc4.d/S99local -> ../rc.local
//所有S99local服务脚本都链接至/etc/rc.d/rc.local
[root@localhost ~]# ll /etc/rc.local 
lrwxrwxrwx. 1 root root 13 Feb 20 06:11 /etc/rc.local -> rc.d/rc.local
//服务脚本/etc/rc.local链接至/etc/rc.d/rc.local

查看/etc/rc.d/目录下的文件及目录:

[root@localhost ~]# ls -1F /etc/rc.d/
init.d/
rc*
rc0.d/
rc1.d/
rc2.d/
rc3.d/
rc4.d/
rc5.d/
rc6.d/
rc.local*
rc.sysinit*
//可以看到多个运行级别多对应的/etc/rc.d/rc#目录;

查看/etc/rc.d/rc0.d/目录下的服务脚本:

[root@localhost ~]# ll /etc/rc.d/rc0.d/
total 320
lrwxrwxrwx 1 root root 17 Feb 20 06:08 K01dnsmasq -> ../init.d/dnsmasq
lrwxrwxrwx 1 root root 16 Feb 20 06:09 K01smartd -> ../init.d/smartd
lrwxrwxrwx 1 root root 22 Feb 20 06:20 K02avahi-daemon -> ../init.d/avahi-daemon
lrwxrwxrwx 1 root root 24 Feb 20 06:20 K02avahi-dnsconfd -> ../init.d/avahi-dnsconfd
.....(中间省略).....
lrwxrwxrwx 1 root root 25 Feb 20 06:09 K99readahead_early -> ../init.d/readahead_early
lrwxrwxrwx 1 root root 25 Feb 20 06:09 K99readahead_later -> ../init.d/readahead_later
lrwxrwxrwx 1 root root 17 Feb 20 06:08 S00killall -> ../init.d/killall
lrwxrwxrwx 1 root root 14 Feb 20 06:08 S01halt -> ../init.d/halt
//因为运行级别0代表关机模式,因此统统都为以K打头的链接文件;

注意:CentOS 7采用的是systemd这个初始化程序,在系统启动过程中不会启动服务,而是在用户需要用到时则立即启动对应的服务。这是为了提高系统启动速度的一种非常精巧的设计。

(4)启动终端,打印登录提示符

根据前面获取的运行级别来启动终端,mingetty程序是用于启动终端的,它会调用登录程序login,这样就能显示出登录提示符了,类似mingetty这种用于打开终端的程序还有getty等。

而如果默认运行级别为5,则会打开图形界面。

以下为CentOS 5的/etc/inittab文件中部分代码:

# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
//在配置文件这里指明打开留个文本终端,分别对应于[Ctrl]+[Alt]+F1至F6;
# Run xdm in runlevel 5
x:5:respawn:/etc/X11/prefdm -nodaemon

第六步:用户登录

系统启动结束!

说了这么多,还是绘制成图更为直观吧!

转载于:https://www.cnblogs.com/liujiacai/p/8068144.html

(转)CentOS系统启动流程图文详解相关推荐

  1. ebay注册流程_2017年eBay注册开店流程图文详解

    原标题:2017年eBay注册开店流程图文详解 今天跟大家分享最新的eBay注册开店流程,希望对卖家小伙伴们有所帮助.我们先简单了解一下eBay注册条件. 企业注册eBay需满足以下条件: 合法登记的 ...

  2. [转]《战地3》寒霜2引擎渲染流程图文详解

    一直一来受制于技术.预言和环境,中国国内玩家.开发者对于国外先进游戏引擎的渲染流程知之甚少,虽然没有技术封锁缺更似自我封锁,在GDC上很少看到中国开发者的身影,无法学习到先进的开发经验. 首先来回味一 ...

  3. U盘安装CentOS 7.0图文详解教程

    背景: 阅读新闻 [日期:2014-10-14] 来源:Linux社区 作者:Linux [字体:大 中 小] 记录下U盘安装CentOS 7.0安装过程,供Linux新手参考.U盘安装Linux简单 ...

  4. 无显示器u盘安装centos_U盘安装CentOS 7.0图文详解教程

    记录下U盘安装CentOS 7.0安装过程,供Linux新手参考.U盘安装Linux简单又环保,推荐大家使用. 安装前准备: 1.CentOS 7.0 64位系统:CentOS-7.0-1406-x8 ...

  5. 微信公众平台开发(2)--微信认证流程图文详解

    点此查看 微信公众号/微信网页/微信支付/企业微信/小程序开发合集及源代码下载 本文目录 1. 微信认证的作用 2. 登录 3. 开始认证 4. 认证流程 4.1 准备资料 4.2 同意协议 4.3 ...

  6. iOS 上架流程图文详解2022版 (上)

    到了2021年,虽然网上也有大牛写过很多IOS App上架流程资料,但随着苹果发布机制的微调 有些已经过时了.我就趁着这次刚刚发布成功的鲜活经验,记录下来,做一下补充. 1.首先得注册Apple De ...

  7. 游戏美术次世代制作流程图文详解

    [流程1--原画分析] 项目上通常都会给到你一个原画,然后根据原画进行制作.也不能全信原画.如果你有足够的想象力又有空间意识的话你可以对原画进行一定的改进. [流程2--用3DsMax 或Maya制作 ...

  8. 游戏美术3D建模次世代制作流程图文详解

    [流程1--原画分析] 项目上通常都会给到你一个原画,然后根据原画进行制作.也不能全信原画.如果你有足够的想象力又有空间意识的话你可以对原画进行一定的改进. [流程2--用3DsMax 或Maya制作 ...

  9. ebay注册流程_【eBay新手开店】2020年eBay注册开店流程图文详解

    今天跟大家分享最新的eBay注册开店流程,希望对卖家小伙伴们有所帮助.我们先简单了解一下eBay注册条件. 企业注册eBay需满足以下条件 ·合法登记的企业用户,并且能提供eBay要求的所有相关文件 ...

  10. iOS AppStore上架流程图文详解​

    1.首先得注册Apple Developer的开发者账号,最后如果要上架苹果商店,这个账号是要交年费的,核算下来大概600多元人民币.​ 2.接下来要登录Apple Developer网站,点击&qu ...

最新文章

  1. 服务没有报告任何错误。 请键入 NET HELPMSG 3534 以获得更多的帮助。
  2. Spring ORM数据訪问——Hibernate
  3. Unable to load the Wrapper's native library because none of the following files及解决方法
  4. matlab把图片转为base64
  5. Scala print语句格式打印
  6. Spring MVC 异步处理请求,提高程序性能
  7. ant-design table 分页(tableProps)
  8. 跑外卖仅靠吃苦可以吗?
  9. 9.linux ntp服务器搭建
  10. Zabbix---4 监控mysql服务并配置动作
  11. js new到底干了什么,new的意义是什么?
  12. 在 Docker 中使用 mysql 的一些技巧 1
  13. android 播放资源mp4,Android播放assets文件里视频文件相关问题分析
  14. 网易,抖音音乐人认证教程
  15. VM 虚拟机屏幕大小的调整
  16. python 修改图片尺寸_Python实现更改图片尺寸大小的方法(基于Pillow包)
  17. 不小心把java文件删除了_如何使用Java恢复已删除的文件?
  18. Mysql主从复制之异步与半同步以及主从切换(实验)
  19. faiss-cpu安装及基础使用
  20. python标准库math中用来计算平方根的函数_python考试复习题库

热门文章

  1. 中国电力电子行业前景方向预测及投资规划建议报告2022-2028年版
  2. Inpaint 9 简体中文【订阅版+Win/Mac】
  3. 淘宝千万级并发架构的十四次演进
  4. html5效果案例,10个优秀HTML5网站案例赏析
  5. linux运行程音乐软件,在Linux系统下用Wine 5.0运行酷狗音乐的使用体验
  6. 歌谷服务套件gms_谷歌gms框架安装器下载-安卓9谷歌服务框架app安装-游戏大玩家...
  7. javapython提取PDF中的表格——PDF转Excel
  8. 【基础知识】【模块介绍】0.96寸OLED显示屏(SSD1306)基础命令和寻址方法
  9. 林子雨版《大数据技术原理与应用》实验五 信息挖掘详解
  10. Icode编程>>>图形化编程>>>1级训练场>>>重复执行练习【1】