1. Linux 系统启动过程

linux启动时我们会看到许多启动信息。
Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段:

  • 内核的引导。
  • 运行 init。
  • 系统初始化。
  • 建立终端 。
  • 用户登录系统。

    init程序的类型:

    • SysV: init, CentOS 5之前, 配置文件: /etc/inittab。
    • Upstart: init,CentOS 6, 配置文件: /etc/inittab, /etc/init/*.conf。
    • Systemd: systemd, CentOS 7,配置文件: /usr/lib/systemd/system、 /etc/systemd/system。

1. 内核引导

当计算机打开电源后,首先是BIOS开机自检,按照BIOS中设置的启动设备(通常是硬盘)来启动。
操作系统接管硬件以后,首先读入 /boot 目录下的内核文件。
3. Linux 系统启动过程 - 图1


2. 运行init

init 进程是系统所有进程的起点,你可以把它比拟成系统所有进程的老祖宗,没有这个进程,系统中任何进程都不会启动。
init 程序首先是需要读取配置文件 /etc/inittab。
3. Linux 系统启动过程 - 图2

2.1 运行级别

许多程序需要开机启动。它们在Windows叫做”服务”(service),在Linux就叫做”守护进程”(daemon)。
init进程的一大任务,就是去运行这些开机启动的程序。
但是,不同的场合需要启动不同的程序,比如用作服务器时,需要启动Apache,用作桌面就不需要。
Linux允许为不同的场合,分配不同的开机启动程序,这就叫做”运行级别”(runlevel)。也就是说,启动时根据”运行级别”,确定要运行哪些程序。
3. Linux 系统启动过程 - 图3
Linux系统有7个运行级别(runlevel):

  • 运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
  • 运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆
  • 运行级别2:多用户状态(没有NFS)
  • 运行级别3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式
  • 运行级别4:系统未使用,保留
  • 运行级别5:X11控制台,登陆后进入图形GUI模式
  • 运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动

3. 系统初始化

在init的配置文件中有这么一行: si::sysinit:/etc/rc.d/rc.sysinit 它调用执行了/etc/rc.d/rc.sysinit,而rc.sysinit是一个bash shell的脚本,它主要是完成一些系统初始化的工作,rc.sysinit是每一个运行级别都要首先运行的重要脚本。
它主要完成的工作有:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。
l5:5:wait:/etc/rc.d/rc 5
这一行表示以5为参数运行/etc/rc.d/rc,/etc/rc.d/rc是一个Shell脚本,它接受5作为参数,去执行/etc/rc.d/rc5.d/目录下的所有的rc启动脚本,/etc/rc.d/rc5.d/目录中的这些启动脚本实际上都是一些连接文件,而不是真正的rc启动脚本,真正的rc启动脚本实际上都是放在/etc/rc.d/init.d/目录下。
而这些rc启动脚本有着类似的用法,它们一般能接受start、stop、restart、status等参数。
/etc/rc.d/rc5.d/中的rc启动脚本通常是K或S开头的连接文件,对于以 S 开头的启动脚本,将以start参数来运行。
而如果发现存在相应的脚本也存在K打头的连接,而且已经处于运行态了(以/var/lock/subsys/下的文件作为标志),则将首先以stop为参数停止这些已经启动了的守护进程,然后再重新运行。
这样做是为了保证是当init改变运行级别时,所有相关的守护进程都将重启。
至于在每个运行级中将运行哪些守护进程,用户可以通过chkconfig或setup中的”System Services”来自行设定。
3. Linux 系统启动过程 - 图4


4. 建立终端

rc执行完毕后,返回init。这时基本系统环境已经设置好了,各种守护进程也已经启动了。
init接下来会打开6个终端,以便用户登录系统。在inittab中的以下6行就是定义了6个终端:
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
从上面可以看出在2、3、4、5的运行级别中都将以respawn方式运行mingetty程序,mingetty程序能打开终端、设置模式。
同时它会显示一个文本登录界面,这个界面就是我们经常看到的登录界面,在这个登录界面中会提示用户输入用户名,而用户输入的用户将作为参数传给login程序来验证用户的身份。


5. 用户登录系统

一般来说,用户的登录方式有三种:

  • (1)命令行登录
  • (2)ssh登录
  • (3)图形界面登录

3. Linux 系统启动过程 - 图5
对于运行级别为5的图形方式用户来说,他们的登录是通过一个图形化的登录界面。登录成功后可以直接进入 KDE、Gnome 等窗口管理器。
而本文主要讲的还是文本方式登录的情况:当我们看到mingetty的登录界面时,我们就可以输入用户名和密码来登录系统了。
Linux 的账号验证程序是 login,login 会接收 mingetty 传来的用户名作为用户名参数。
然后 login 会对用户名进行分析:如果用户名不是 root,且存在 /etc/nologin 文件,login 将输出 nologin 文件的内容,然后退出。
这通常用来系统维护时防止非root用户登录。只有/etc/securetty中登记了的终端才允许 root 用户登录,如果不存在这个文件,则 root 用户可以在任何终端上登录。
/etc/usertty文件用于对用户作出附加访问限制,如果不存在这个文件,则没有其他限制。


6. 图形模式与文字模式的切换方式

Linux预设提供了六个命令窗口终端机让我们来登录。
默认我们登录的就是第一个窗口,也就是tty1,这个六个窗口分别为tty1,tty2 … tty6,你可以按下Ctrl + Alt + F1 ~ F6 来切换它们。
如果你安装了图形界面,默认情况下是进入图形界面的,此时你就可以按Ctrl + Alt + F1 ~ F6来进入其中一个命令窗口界面。
当你进入命令窗口界面后再返回图形界面只要按下Ctrl + Alt + F7 就回来了。
如果你用的vmware 虚拟机,命令窗口切换的快捷键为 Alt + Space + F1~F6. 如果你在图形界面下请按Alt + Shift + Ctrl + F1~F6 切换至命令窗口。
3. Linux 系统启动过程 - 图6


7. Linux 关机

在linux领域内大多用在服务器上,很少遇到关机的操作。毕竟服务器上跑一个服务是永无止境的,除非特殊情况下,不得已才会关机。
正确的关机流程为:sync > shutdown > reboot > halt
关机指令为:shutdown ,你可以man shutdown 来看一下帮助文档。
例如你可以运行如下命令关机:
sync 将数据由内存同步到硬盘中。

shutdown 关机指令,你可以man shutdown 来看一下帮助文档。例如你可以运行如下命令关机:

shutdown –h 10 ‘This server will shutdown after 10 mins’ 这个命令告诉大家,计算机将在10分钟后关机,并且会显示在登陆用户的当前屏幕中。

shutdown –h now 立马关机

shutdown –h 20:25 系统会在今天20:25关机

shutdown –h +10 十分钟后关机

shutdown –r now 系统立马重启

shutdown –r +10 系统十分钟后重启

reboot 就是重启,等同于 shutdown –r now

halt 关闭系统,等同于shutdown –h now 和 poweroff
最后总结一下,不管是重启系统还是关闭系统,首先要运行 sync 命令,把内存中的数据写到磁盘中。
关机的命令有 shutdown –h now halt poweroff init 0 , 重启系统的命令有 shutdown –r now reboot init 6

2. 系统初始化配置

设置静态IP

  1. [root@localhost ~]$ cd /etc/sysconfig/network-scripts
  2. [root@localhost ~]$ vim ifcfg-ens33
  3. TYPE=Ethernet #网络类型
  4. PROXY_METHOD=none
  5. BROWSER_ONLY=no
  6. DEFROUTE=yes
  7. IPV4_FAILURE_FATAL=no
  8. NAME=ens33
  9. UUID=59ab8840-ef2e-441b-97d1-f9f7fde61d7b
  10. DEVICE=ens33
  11. ONBOOT=yes #开机自启
  12. BOOTPROTO=static #修改此处,改为static
  13. IPADDR=192.168.88.100 #ip地址
  14. NETMASK=255.255.255.0 #子网掩码
  15. GATEWAY=192.168.88.2 #网关,可在虚拟机中查看
  16. DNS1=114.114.114.114 #dns地址

关闭SELINUX

[root@localhost ~]$ swapoff -a && sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab 
[root@localhost ~]$ setenforce 0 && sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config && reboot

关闭防火墙

#centos6
[root@localhost ~]$ iptables -F  &&  service iptables save 

#centos7
[root@localhost ~]$ systemctl stop firewalld &&  systemctl disable firewalld

系统初始化脚本

[root@localhost ~]$ vim init.sh
#!/bin/bash

# Description: system init script
# Date: 2021-01-01
# Author: lurenjia

# Network
ping -c 1 -W 3 114.114.114.114 &> /dev/null
if [ ! $? = 0 ];then
  echo "Cannot be networked"
  exit 1
fi

#提取本机的ip
#ip=$(ifconfig|grep "inet"|grep -v "127.0.0.1"|cut -d: -f2|awk '{print $2}')


# Set PATH Variables
export PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/root/bin
export LANG="en_US.UTF-8"

# Set output color
COLUMENS=80
SPACE_COL=$[ $COLUMENS-15 ]
#VERSION=`uname -r | awk -F'.' '{print $1}'`
VERSION=`uname -r | awk -F'.' '{print $4}' | awk -F 'l' '{print $2}'`  #根据是6还是7来判断

RED='\033[1;5;31m'
GREEN='\033[1;32m'
NORMAL='\033[0m'


success() {
  REAL_SPACE=$[ $SPACE_COL - ${#1} ]
  for i in `seq 1 $REAL_SPACE`; do
      echo -n " "
  done
  echo -e "[ ${GREEN}SUCCESS${NORMAL} ]"
}

failure() {
  REAL_SPACE=$[ $SPACE_COL - ${#1} ]
  for i in `seq 1 $REAL_SPACE`; do
      echo -n " "
  done
  echo -e "[ ${RED}FAILURE${NORMAL} ]"
  exit 1
}

#echo -e "\033[34m当前ip $ip \033[0m"


# 01
Data="01) 关闭selinux..."
echo -n $Data
setenforce 0
/bin/cp /etc/selinux/config /etc/selinux/config.bak
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config &> /dev/null
[ `grep "SELINUX=enforcing" /etc/selinux/config|wc -l` -eq 0 ] && success "$Data" || failure "$Data"


# 02
Data="02) 关闭iptables或者firewalld..."
echo -n $Data
if [ $VERSION = 6 ];then
    service iptables stop &> /dev/null
    chkconfig iptables off &> /dev/null
    [ `chkconfig --list | grep iptables| grep 3:on | wc -l` -eq 0 ] && success "$Data" || failure "$Data"
else
    systemctl stop firewalld &> /dev/null
    systemctl disable firewalld &> /dev/null
    [ `systemctl list-unit-files | grep firewalld | grep enabled | wc -l` -eq 0 ] && success "$Data" || failure "$Data"
fi


# 03
Data="03) 设置公网DNS..."
echo -n $Data
cat << EOF >> /etc/resolv.conf
options timeout:1 attempts:1 rotate single-request-reopen
nameserver 114.114.114.114
nameserver 114.114.114.115
EOF
[ `grep '114.114.114.114' /etc/resolv.conf | wc -l` -ne 0 ] && success "$Data" || failure "$Data"


# 04
Data="04) 安装常用基础命令..."
echo -n $Data
yum -y install vim expect screen lrzsz tree openssl openssh-clients openssl-devel openssh-server telnet iftop iotop sysstat wget ntpdate dos2unix lsof net-tools mtr gcc gcc-c++ cmake zip unzip git sudo psmisc &> /dev/null
if [ ! $? = 0 ];then
    failure "$Data"
else
    success "$Data"
fi


# 05
Data="05) 配置阿里云yum源..."
echo -n $Data
cd /etc/yum.repos.d
mkdir -p /etc/yum.repos.d/repo_bak
mv *.repo /etc/yum.repos.d/repo_bak/
wget http://mirrors.aliyun.com/repo/Centos-$VERSION.repo &> /dev/null 
wget http://mirrors.aliyun.com/repo/epel-$VERSION.repo &> /dev/null
if [ $VERSION = 6 ];then
     sed -i "s|enabled=1|enabled=0|g" /etc/yum/pluginconf.d/fastestmirror.conf
     mv *.repo /etc/yum.repos.d/repo_bak/
     curl -o /etc/yum.repos.d/CentOS-Base.repo https://www.xmpan.com/Centos-6-Vault-Aliyun.repo 
fi

yum clean all &> /dev/null && yum makecache &> /dev/null
[ `grep aliyun.com /etc/yum.repos.d/Centos-$VERSION.repo | wc -l` -ne 0 -a `grep aliyun.com /etc/yum.repos.d/epel-$VERSION.repo | wc -l` -ne 0 ] && success "$Data" || failure "$Data"


# 06
Data="06) 与阿里云时间同步服务器进行时间同步..."
echo -n $Data
/usr/sbin/ntpdate ntp1.aliyun.com &> /dev/null &&  hwclock --systohc &> /dev/null
echo "*/5 * * * * /usr/sbin/ntpdate ntp1.aliyun.com &&  hwclock --systohc" >> /var/spool/cron/root
if [ $VERSION = 6 ];then
    service crond restart &> /dev/null
else
    systemctl restart crond &> /dev/null
fi
[ `grep ntpdate /var/spool/cron/root |wc -l` -ne 0 ] && success "$Data" || failure "$Data"


# 07
Data="07) 调整用户级别的文件描述符数量..."
echo -n $Data
/bin/cp /etc/security/limits.conf /etc/security/limits.conf.bak
echo "* - nofile 65535">> /etc/security/limits.conf
[ `grep nofile /etc/security/limits.conf | grep -v ^# | awk -F 'nofile' '{print $2}'` -ge  60000 ] && success "$Data" || failure "$Data"


# 08
Data="08) 调整用户级别的进程数量..."
echo -n $Data
if [ $VERSION = 6 ];then
    /bin/cp /etc/security/limits.d/90-nproc.conf  /etc/security/limits.d/90-nproc.conf.bak
    echo -e '*          soft    nproc     65535\nroot       soft    nproc     unlimited' > /etc/security/limits.d/90-nproc.conf
    [ `grep '*' /etc/security/limits.d/90-nproc.conf | grep -v ^# | awk -F ' ' '{print $4}'` -ge  60000 ] && success "$Data" || failure "$Data"
else
    /bin/cp /etc/security/limits.d/20-nproc.conf  /etc/security/limits.d/20-nproc.conf.bak
    echo -e '*          soft    nproc     65535\nroot       soft    nproc     unlimited' > /etc/security/limits.d/20-nproc.conf
    [ `grep '*' /etc/security/limits.d/20-nproc.conf | grep -v ^# | awk -F ' ' '{print $4}'` -ge  60000 ] && success "$Data" || failure "$Data"
fi


# 09
Data="09) 修改字符集..."
echo -n $Data
if [ $VERSION = 6 ];then
    /bin/cp /etc/sysconfig/i18n /etc/sysconfig/i18n.bak
    echo 'LANG="en_US.UTF-8"' > /etc/sysconfig/i18n
    source /etc/sysconfig/i18n
    [ `echo $LANG | grep 'en_US.UTF-8' | wc -l` -ne 0 ] && success "$Data" || failure "$Data"
else
    /bin/cp /etc/locale.conf /etc/locale.conf.bak
    echo 'LANG="en_US.UTF-8"' > /etc/locale.conf
    source /etc/locale.conf
    [ `echo $LANG | grep 'en_US.UTF-8' | wc -l` -ne 0 ] && success "$Data" || failure "$Data"
fi


# 10
Data="10) 精简开机自启服务..."
echo -n $Data
if [ $VERSION = 6 ];then
    for cgt in `chkconfig --list | grep 3:on | awk '{print $1}'`;do chkconfig --level 3 $cgt off &> /dev/null;done
    for cgt in {crond,sshd,network,rsyslog};do chkconfig --level 3 $cgt on &>/dev/null;done
    [ `chkconfig --list|grep 3:on|wc -l` -eq 4 ] && success "$Data" || failure "$Data"
else
    systemctl list-unit-files|grep service| grep enable | awk '{print $1}'|xargs -i systemctl disable {} &> /dev/null
    for cgt in {crond,sshd,network,rsyslog,NetworkManager};do systemctl enable $cgt &>/dev/null;done
    [ `systemctl list-unit-files | grep enabled | wc -l` -lt 20 ] && success "$Data" || failure "$Data"
fi

# 11
Data="11) 内核参数优化..."
echo -n $Data
[ -f /etc/sysctl.conf.bak ] && /bin/cp /etc/sysctl.conf.bak /etc/sysctl.conf.bak.$(date +%F-%H%M%S) || /bin/cp /etc/sysctl.conf /etc/sysctl.conf.bak
cat >> /etc/sysctl.conf <<EOF
net.ipv4.ip_forward = 1   #开启路由转发 
net.ipv4.conf.all.rp_filter = 1  
net.ipv4.conf.default.rp_filter = 1 #启用源路由核查功能
net.ipv4.conf.all.accept_source_route = 0 
net.ipv4.conf.default.accept_source_route = 0  #禁用所有IP源路由 
kernel.sysrq = 0   #使用sysrq组合键是了解系统目前运行情况,为安全起见设为0关闭
kernel.core_uses_pid = 1  #控制core文件的文件名是否添加pid作为扩展
net.ipv4.tcp_syncookies = 1 #开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理
kernel.msgmnb = 65536 #每个消息队列的大小(单位:字节)限制
kernel.msgmax = 65536  #整个系统最大消息队列数量限制
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

###调整系统级别的文件描述符的数量
fs.file-max = 6553500 

###调整系统级别的允许线程的数量
kernel.pid_max=1000000

###内存资源使用相关设定
vm.vfs_cache_pressure = 100000
vm.max_map_count = 262144
vm.swappiness = 0
net.core.wmem_default = 8388608   
net.core.rmem_default = 8388608  
net.core.rmem_max = 16777216 
net.core.wmem_max = 16777216 
net.ipv4.tcp_rmem = 4096 8192 4194304 
net.ipv4.tcp_wmem = 4096 8192 4194304    
##应对DDOS攻击,TCP连接建立设置
net.ipv4.tcp_syncookies = 1 
net.ipv4.tcp_synack_retries = 1  
net.ipv4.tcp_syn_retries = 1   
net.ipv4.tcp_max_syn_backlog = 262144 
##应对timewait过高,TCP连接断开设置
net.ipv4.tcp_max_tw_buckets = 6000  
net.ipv4.tcp_tw_recycle = 1  
net.ipv4.tcp_tw_reuse = 1 
net.ipv4.tcp_timestamps = 0   
net.ipv4.tcp_fin_timeout = 30 
net.ipv4.ip_local_port_range = 1024 65000
###TCP keepalived 连接保鲜设置
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_keepalive_intvl = 15 
net.ipv4.tcp_keepalive_probes = 5
###其他TCP相关调节
net.core.somaxconn = 8192 
net.core.netdev_max_backlog = 262144  
net.ipv4.tcp_max_orphans = 3276800    
net.ipv4.tcp_sack = 1  
net.ipv4.tcp_window_scaling = 1
EOF
sysctl -p &> /dev/null
[ `grep "net.ipv4.ip_forward = 1" /etc/sysctl.conf|wc -l` -ne 0 ] && success "$Data" || failure "$Data"


# 12
Data="12) 禁止空密码连接..."
echo -n $Data
/bin/cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
#sed -i 's/\#Port 22/Port 13888/' /etc/ssh/sshd_config
#sed -i 's/\#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/\#PermitEmptyPasswords no/PermitEmptyPasswords no/' /etc/ssh/sshd_config
sed -i 's/\#UseDNS yes/UseDNS no/' /etc/ssh/sshd_config
[ `grep "PermitEmptyPasswords no" /etc/ssh/sshd_config | wc -l` -ne 0 -a `grep "UseDNS no" /etc/ssh/sshd_config|wc -l` -ne 0 ] && success "$Data" || failure "$Data"


# 13
Data="13) 优化history记录..."
echo -n $Data
cat << EOF >> /etc/profile
export HISTSIZE=10000
USER_IP=\`who -u am i | awk '{print \$NF}'|sed -e 's/[()]//g'\`
if [ -z \$USER_IP  ]
then
  USER_IP="NO_client_IP"
fi
export HISTTIMEFORMAT="<%Y.%m.%d %H:%M:%S-\$USER_IP-\$USER> : "
EOF
source /etc/profile
[ `grep "HISTTIMEFORMAT" /etc/profile | wc -l` -ne 0 ] && success "$Data" || failure "$Data"