SRE-第二周作业

  1. Linux 系统中,用户和组的定义以及密码信息存放于哪四个核心配置文件中?请用表格详细说明每个文件各字段的含义,并解答:为什么有了 /etc/passwd 还需要 /etc/shadow?/etc/shadow 中用户的密码字段以 $6$ 开头,这代表什么加密算法?后面的字符串是如何构成的?

    用户和组的核心文件:

    [root@Rocky-01 ~]# ll /etc/passwd                       //用于存放用户基本信息
    -rw-r--r--. 1 root root 2028 May 30 16:04 passwd
    [root@Rocky-01 ~]# ll /etc/shadow                       //用于存放用户密码及安全属性相关信息
    ----------. 1 root root 1104 May 30 16:04 shadow
    [root@Rocky-01 ~]# ll /etc/group                        //用于存放用户组基本信息
    -rw-r--r--. 1 root root 790 May 30 16:04 group
    [root@Rocky-01 ~]# ll /etc/gshadow                      //用于存放用户组组密码、组管理员信息
    ----------. 1 root root 633 May 30 16:04 gshadow

    各字段详细信息说明:

    /etc/passwd 、 /etc/shadow:

    分开存放是为了保护密码密文,拆分敏感数据,提升系统安全性。/etc/passwd 默认全局可读(权限 644),所有用户都能查看密文,暴力破解风险极高。/etc/shadow,该文件权限为 000仅 root 用户可读取,普通用户无法访问。shadow 额外提供密码有效期、过期提醒、账号失效等安全策略,单纯 passwd 无法实现复杂密码安全管控。

    [root@Rocky-01 ~]# ll /etc/passwd
    -rw-r--r--. 1 root root 2028 May 30 16:04 passwd
    [root@Rocky-01 ~]# ll /etc/shadow
    ----------. 1 root root 1104 May 30 16:04 shadow

    $6$ 表示使用 SHA-512 哈希算法(Linux 默认主流密码加密算法)。

    常见标识:

    $1$:MD5

    $5$:SHA-256

    $6$:SHA-512

    构成:

    $6$xx$xxxx $算法标识$盐值$加密密文

  2. 详细说明 r (读)、w (写)、x (执行) 权限在文件和目录上的本质区别?如果一个普通用户对某个目录仅有 w 权限,但没有 x 权限,他能在这个目录下创建文件吗?为什么?

    针对文件以及目录的区别:

    文件:权限控制文件内容的访问、修改、运行

    目录:权限控制目录本身的查看、增删、进入

    普通用户只有w权限时无法在目录下创建文件,经过测试只有对目录同时具有wx权限时才能创建文件,目录的 x 权限是路径访问权限。没有 x,系统不允许 cd 进入该目录,也无法定位、操作该目录内部。哪怕有 w 权限,系统也无法完成 “在该目录下写入新文件条目” 的底层寻址,创建操作直接失败。

    [g@Rocky-01 tmp]$ ll /tmp/ | grep test
    drwxrw-rw-. 2 root root 19 Jun 10 15:27 test
    [g@Rocky-01 tmp]$ touch /tmp/test/b.txt
    touch: cannot touch '/tmp/test/b.txt': Permission denied
    [g@Rocky-01 tmp]$ ll /tmp/ | grep test
    drwx--x--x. 2 root root 19 Jun 10 15:27 test
    [g@Rocky-01 tmp]$ touch /tmp/test/b.txt
    touch: cannot touch '/tmp/test/b.txt': Permission denied
    [g@Rocky-01 tmp]$ ll /tmp/ | grep test
    drwxr-xr-x. 2 root root 19 Jun 10 15:27 test
    [g@Rocky-01 tmp]$ touch /tmp/test/b.txt
    touch: cannot touch '/tmp/test/b.txt': Permission denied
    [g@Rocky-01 tmp]$ ll /tmp/ | grep test
    drwx-wx-wx. 2 root root 19 Jun 10 15:27 test
    [g@Rocky-01 tmp]$ touch /tmp/test/b.txt
  3. 什么是 umask?说明其计算逻辑。若系统全局默认 umask 为 022,为什么新创建的文件默认权限是 644,而新建目录是 755?如果是 umask 023 呢?

    umask是用户文件创建掩码,是Linux系统的默认权限,采用减法规则。当新建目录或文件时,用最大权限-umask=最终权限 ,系统默认最大权限文件为777,目录为666

    新建文件:777-022=755
    新建目录:666-022=644

    如果umask为023时:

    新建文件:777-023=754
    新建目录:666-023=643
  4. 详解 Linux 的三种特殊权限:SUID、SGID、Sticky(粘滞位)的作用、表现形式(在 ls -l 输出中的字符位置及大小写区别)以及它们各自的生产应用场景。

    SUID:Set User ID 让普通用户在执行时临时拥有属主的权限,主要用于需要临时提升权限的可执行文件,如passwdsusudo等,表现形式体现在 文件属主的 x 执行权限位,有x权限时小写s,无x权限时大写S,如下:

    -rwsr-xr-x (有SUID,且有x权限)
    -rwSr--r-- (有SUID,但无x权限)

    SGID:Set Group ID 让文件执行时临时拥有所属组的权限,目录下创建文件或目录自动继承目录所属组,主要作用于需要团队共享目录、协作开发目录、公共数据目录等场景,表现形式体现在 所属组的 x 执行权限位,有x权限时小写s,无x权限时大写S,如下:

    drwxr-sr-x (有SGID,且有x权限)
    drwxr-Sr-x (有SGID,但无x权限)

    Sticky Bit:当设置粘滞位权限时,目录下的文件或文件夹只允许所有者、目录所有者、root 能删除 / 重命名,其他用户即使有w权限也不能删除别人的文件,主要用于需要多人可写但不能互删文件的公共目录,如/tmp/var/tmp。表现形式体现在其他人的 x 权限位,有x权限时小写t,无x权限时大写T,如下:

    drwxrwxrwt (有Sticky,且有x权限)
    drwxrwxrwT (有Sticky,且无x权限)
  5. 对比 locate 和 find 命令的工作机制、优缺点及适用场景。为什么新建一个文件后,立刻用 locate 搜不到?如何解决?

    对比项find 命令locate 命令
    工作机制实时遍历目录 / 磁盘,逐个文件检查读取系统预生成的文件路径数据库,不查真实磁盘
    速度慢(遍历磁盘)极快(查数据库)
    准确性100% 准确,查到就是真实存在不一定实时,数据库没更新就搜不到新文件
    搜索范围可指定任意目录、条件(权限、大小、时间)只能搜文件名 / 路径
    功能强弱强(可按权限、类型、时间、大小搜索)弱(仅文件名模糊匹配)
    系统资源高(大量 I/O)极低
    是否实时实时非实时

    因为 locate 不搜索真实磁盘,只搜索系统数据库。新建的文件还没被收录到数据库里,所以搜不到。系统默认每天自动更新一次数据库,未更新前,新文件都查不到。手动执行updatedb,执行完再用 locate,就能搜到刚创建的文件了。

  6. 在 ubuntu 环境中,如何使用 cat 和 EOF 标志,非交互式地向系统配置文件 /etc/hosts.bak 中追加以下三行内容?

    192.168.1.100 k8s-master
    192.168.1.101 k8s-node1
    192.168.1.102 k8s-node2
    cat  << EOF >>/etc/hosts.bak
    192.168.1.100 k8s-master
    192.168.1.101 k8s-node1
    192.168.1.102 k8s-node2
    EOF

  7. 创建一个名为 sysadm 的系统用户组,指定 GID 为 2000。创建一个名为 ops_user 的系统用户,要求:其主组为 sysadm,附加组为 wheel(或 sudo)。其家目录指定为 /data/home/ops_user,且在创建时自动建立家目录。指定其登录 Shell 为 /bin/bash。生产中需要临时禁用和启用用户 ops_user 的登录权限,分别写出锁定和解锁的命令。如何利用非交互式命令(如 chpasswd)将用户 ops_user 的密码修改为 Ops@2026#Sec?

    groupadd -g 2000 sysadm  ///创建用户组
    useradd -g sysadm -G wheel -m -d  /data/home/ops_user -s /bin/bash ops_user  ///创建用户
    passwd -l ops_user  ///临时锁定该用户
    passwd -u ops_user  ///解除用户锁定
    echo "ops_user:Ops@2026#Sec" | chpasswd  ///设置用户密码
  8. 现有一个目录 /data/shares。为了防止被普通用户误删,我们需要保证:所有用户都可以在该目录下创建文件 and 目录。用户只能删除自己创建的文件,不能删除其他用户的文件。请写出创建该目录并设置相应权限的完整命令。

    [root@Rocky-01 /]# mkdir -p /date/shares
    [root@Rocky-01 /]# cd /date/
    [root@Rocky-01 date]# ll
    total 0
    drwxr-xr-x. 2 root root 6 Jun 11 16:09 shares
    [root@Rocky-01 date]# chmod +t shares/
    [root@Rocky-01 date]# ll
    total 0
    drwxr-xr-t. 2 root root 6 Jun 11 16:09 shares
  9. 为了防止恶意软件或人为失误修改系统核心文件 /etc/resolv.conf,我们需要将其设置为“即使是 root 用户也无法修改和删除,只能读取”。请写出设置和撤销此状态的命令。

    [root@Rocky-01 ~]# lsattr /etc/resolv.conf
    ---------------------- /etc/resolv.conf
    [root@Rocky-01 ~]# chattr +i /etc/resolv.conf
    [root@Rocky-01 ~]# lsattr /etc/resolv.conf 
    ----i----------------- /etc/resolv.conf
    [root@Rocky-01 ~]# chattr -i /etc/resolv.conf
  10. 很多时候在编写 Python 脚本或 YAML 配置文件时,我们希望将 Vim 的 Tab 键自动转换成 4 个空格,并且显示行号。请写出要在当前用户的家目录下的配置文件中添加哪些属性行?

    cat <<EOF >> ~/.vimrc
    " 显示行号
    set number
    " Tab 转为空格
    set expandtab
    " 一个 Tab 对应 4 个空格
    set tabstop=4
    " 缩进时也使用 4 个空格
    set shiftwidth=4
    " 智能缩进对齐
    set softtabstop=4
    EOF
  11. 在 Vim 中,如何非交互地将当前打开的文件中,第 10 行到第 50 行之间的所有 http:// 字符串替换为 https://?

    :10,50s/http:\/\//https:\/\//g
  12. 生产环境磁盘空间告急,请写出一条命令:在 /var/log/目录中,查找所有文件名以 .log 结尾、大小大于 50MB 且在 7 天以前修改过的普通文件,并将它们打包压缩备份至 /tmp/log_backup.tar.gz,随后将原文件安全删除。

    find /var/log/ -type f -name "*.log" -size +50M -mtime +7 -print0 |tar -czf /tmp/log_backup.tar.gz --null -T - && find /var/log/ -type f -name "*.log" -size +50M -mtime +7 -delete
  13. 演示如何使用 tar 工具打包 /etc 目录,但是排除掉 /etc/udev 和 /etc/selinux 这两个子目录。

    tar -czf etc_backup.tar.gz --exclude=/etc/udev --exclude=/etc/selinux /etc   ///打包/etc目录
    tar -tzf etc_backup.tar.gz | grep -E "udev|selinux"     ///检查打包文件是否含有排除目录
  14. 假设有一个名为 user_info.txt 的文件,每行格式如下:username:department:phone_number:address。请分别使用以下工具提取:

    1. 使用 cut 提取第一列的用户名。

      [root@Rocky-01 etc]# cat /home/user_info.txt
      alice:Engineering:555-1234:123 Maple St, Springfield
      bob:Sales:555-5678:456 Oak Ave, Metropolis
      carol:Human Resources:555-8765:789 Pine Rd, Gotham
      dave:Engineering:555-4321:101 Elm Dr, Star City
      eve:Marketing:555-9999:202 Birch Ln, Smallville
      [root@Rocky-01 etc]# cut -d ':' -f 1 /home/user_info.txt
      alice
      bob
      carol
      dave
      eve
      [root@Rocky-01 etc]# cut -d ':' -f 1,4 /home/user_info.txt
      alice:123 Maple St, Springfield
      bob:456 Oak Ave, Metropolis
      carol:789 Pine Rd, Gotham
      dave:101 Elm Dr, Star City
      eve:202 Birch Ln, Smallville
    2. 使用 tr 将文本中的 : 替换为空格。

      [root@Rocky-01 etc]# tr ':' ' ' < /home/user_info.txt
      alice Engineering 555-1234 123 Maple St, Springfield
      bob Sales 555-5678 456 Oak Ave, Metropolis
      carol Human Resources 555-8765 789 Pine Rd, Gotham
      dave Engineering 555-4321 101 Elm Dr, Star City
      eve Marketing 555-9999 202 Birch Ln, Smallville
  15. 有些同学在删除查找到的千万级临时文件时,直接运行 find /tmp -name "*.tmp" -exec rm -f {} \;,请解答:

    1. 该命令有什么性能弊端?

      • 每个文件都启动一个 rm 进程 -exec ... {} \; 会为每一个匹配到的文件单独执行一次 rm 命令。这意味着要创建、运行并销毁千万次进程,系统调用开销(fork()/exec())极大,CPU 上下文切换频繁,执行时间会非常长(可能数小时甚至更久)。

      • 无法利用批量删除 rm 本身可以一次删除多个文件,但该写法强制一次只处理一个文件,完全放弃了批量化操作的优势。

      • 内存与 I/O 压力 find 在遍历大型目录树时本身会消耗 I/O 和目录项缓存,加上逐文件启动 rm,会使磁盘 I/O 模式变得碎片化,进一步降低效率。

    2. 如何使用 xargs 进行优化?其运行原理是什么?

      find /tmp -name "*.tmp" -print0 | xargs -0 rm -f
      • find -print0 将匹配的文件路径以 null 字符(\0 分隔输出,而非默认的换行符。这样可以避免文件名中特殊字符(空格、换行等)导致的解析歧义。

      • xargs -0 表示从标准输入读取以 null 分隔的参数列表,并按批次构建命令行。

      • xargs 会尽量将多个文件名填入一个命令行(受系统 ARG_MAX 限制,通常可达几 MB),然后一次性执行 rm -f 文件1 文件2 ...

      • 对于千万级文件,xargs 会分批执行多次 rm,但执行的次数仅为 (文件总数) / (每批数量),相比每个文件一次 rm 减少了 千万倍 的进程创建开销,因此效率大幅提升。

    3. 若文件名包含空格,优化命令会有何安全隐患?如何彻底规避?

      • find 默认以换行符分隔文件名输出,而 xargs 默认以空白字符(空格、换行、制表符)作为分隔符。

      • my tmp file.tmp 为例,文件名会被 xargs 拆分成 三个独立参数mytmpfile.tmp

      • 结果:rm 会尝试删除当前目录下名为 mytmp 的文件(很可能不存在),而真正的文件 my tmp file.tmp 不会被删除。这不仅导致删除失败,还可能误删其他同名文件(如果恰好存在名为 mytmp 的文件),造成数据丢失或遗漏

      • -print0 保证文件名内的任何字符(包括空格、换行、引号、反斜杠)都原样输出,并以 \0 作为唯一分隔符。

      • xargs -0 采用相同的 \0 分隔方式解析输入,确保每个文件名作为一个完整的参数传递给 rm

  16. 研发部要求在 Rocky 环境下,导入 1000 个格式为 testuser001 到 testuser1000 的测试账号,并要求设置不同的初始随机密码。请回答:

    1. 为什么不能直接写 for 循环调用 useradd + passwd?

      • 性能极低:循环体内每次调用 useraddpasswd 都会启动一个独立的进程。创建 1000 个用户需要执行 2000 次外部命令,频繁的 fork()/exec() 系统调用和进程上下文切换会消耗大量 CPU 时间,尤其是在虚拟机或容器环境下可能耗时数分钟甚至更久。

      • 密码设置非交互不便passwd 命令默认需要交互式输入,虽然 --stdin 选项可以接受管道输入,但不同发行版支持情况不同(Rocky Linux 中 passwd 默认不支持 --stdin,需使用 chpasswdopenssl 配合)。强行使用管道还可能暴露密码在命令行参数中(通过 /proc 可被其他用户窥探)。

      • 错误处理困难:若循环中某个用户创建失败(例如用户名已存在),后续用户仍会尝试创建,且难以回滚。

    2. 请使用 newusers 和 chpasswd 给出一种高效率的一体化解决方案。

      #!/bin/bash
      ​
      PASSWD_FILE="/tmp/user_passwords.txt"
      NEWUSERS_FILE="/tmp/newusers_input.txt"
      ​
      > "$PASSWD_FILE"
      > "$NEWUSERS_FILE"
      ​
      #for u in $(seq -f "%03g" 1 1000); do
      #    userdel -r "testuser$u" 2>/dev/null
      #done
      ​
      for i in $(seq -f "%03g" 1 1000); do
          username="testuser$i"
          # 生成 12 位随机密码
           password=$(tr -dc 'A-Za-z0-9!@#$%^&*' < /dev/urandom | head -c 12)
          echo "$username:$password" >> "$PASSWD_FILE"
          # 正确格式:用户名:密码:UID:GID:GECOS:家目录:shell
          echo "$username:x::::/home/$username:/bin/bash" >> "$NEWUSERS_FILE"
      done
      ​
      newusers < "$NEWUSERS_FILE" || { echo "newusers 失败"; exit 1; }
      chpasswd < "$PASSWD_FILE" || { echo "chpasswd 失败"; exit 1; }
      ​
      rm -f "$NEWUSERS_FILE"
      echo "成功创建 1000 个用户,密码保存在 $PASSWD_FILE"

[5]

  1. 基础正则表达式(BRE)与扩展正则表达式(ERE)在元字符支持上有何本质区别?请举例说明在 grep 和 sed 命令中,若使用 ERE 应如何加选项或进行转义?

    基础正则表达式 (BRE)扩展正则表达式 (ERE) 的核心区别在于:部分元字符需要反斜杠 \ 来激活其特殊含义

    元字符BRE 中的含义ERE 中的含义
    ?字面问号(需转义 \? 表示“零次或一次”)直接表示“零次或一次”
    +字面加号(需转义 \+ 表示“一次或多次”)直接表示“一次或多次”
    {n,m}字面花括号(需转义 \{n,m\} 表示重复次数)直接表示重复次数
    ( )字面括号(需转义 \( \) 用于分组)直接用于分组
    |字面竖线(需转义 | 表示“或”)直接表示“或”

    总结

    • BRE 将大部分扩展元字符视为普通字符,需要用反斜杠转义才能获得特殊功能。

    • ERE 将上述字符视为内建元字符,直接使用即可,反斜杠反而会取消其特殊含义(使其变成字面字符)。

    注:*^ $ . [] 等基础元字符在两种模式中无需转义,始终有特殊含义。

    tar -tzf etc_backup.tar.gz | grep  "udev\|selinux"    //grep默认使用BRE
    tar -tzf etc_backup.tar.gz | grep -E "udev|selinux"   //-E参数使用ERE
    echo "hello world" | sed 's/\([a-z]\+\) \([a-z]\+\)/\2 \1/' 
    echo "hello world" | sed -E 's/([a-z]+) ([a-z]+)/\2 \1/'
  2. 简述 sed 命令的模式空间(Pattern Space)和保持空间(Hold Space)的概念,它是如何实现流编辑的?并简述 awk 的工作循环(即 BEGIN、主输入循环、END 的执行顺序和优先级),并列出其至少 5 个常用内置变量的含义。

    • 模式空间(Pattern Space) sed 读取输入文件的每一行(去掉尾部换行符)后,将其存放在一个内存缓冲区中,该缓冲区称为“模式空间”。sed 的所有编辑命令(如 s/old/new/dp 等)都默认作用于模式空间中的内容。处理完成后,模式空间的内容会被打印到标准输出(除非使用 -n 抑制),然后清空,再读取下一行。

    • 保持空间(Hold Space) 另一个独立的、可持久化存储的缓冲区,用于暂存数据。默认情况下保持空间是空的。通过特定的命令(hHgGx 等)可以在模式空间和保持空间之间复制或追加数据,从而实现跨行、多行模式匹配或数据暂存。

      流编辑:

      1. 从输入流中读取一行,去掉尾随换行符,存入模式空间。

      2. 依次执行脚本中所有的编辑命令(这些命令可能根据条件修改模式空间,也可能与保持空间交互)。

      3. 若未使用 -n 选项,则将模式空间的内容打印到输出(自动补回换行符);若使用了 -n,则仅当遇到 p 命令时才打印。

      4. 清空模式空间,重复上述步骤,直到文件结束。

      awk 的工作循环:

      • BEGIN 最先且只一次 → 主循环对每一行执行 → END 最后且只一次。

      • 如果没有 BEGINEND,对应部分直接跳过。

      • 主循环中的“模式”可以是正则表达式、关系表达式、范围模式或 BEGIN/END 保留字。

      变量名含义
      NR已经读取的记录数(默认记录以换行符分隔,即当前行号),从 1 开始累计所有文件。
      FNR当前文件的记录数(当前文件内的行号),每个文件重新从 1 开始。
      NF当前记录中字段的数量(即当前行被分隔符切分出的列数)。
      $0当前记录的整行内容(原始文本,未切分)。
      $1, $2, ...当前记录的第 1、2 … 个字段(按 FS 分隔符切分)。
      FS输入字段分隔符(默认为空格或制表符,可自定义为任意正则表达式,如 FS=":")。
      OFS输出字段分隔符(默认为空格,print 打印多个字段时用此连接)。
      RS输入记录分隔符(默认为换行符 \n,可修改为其他字符实现以空行等为记录分隔)。
      ORS输出记录分隔符(默认为换行符,print 在每个输出后追加此字符)。
      FILENAME当前正在处理的输入文件名。
  3. 对比 MBR 分区方案与 GPT 分区方案,并解答:在生产环境中面对 4TB 大容量磁盘,应该选择哪种分区方案?为什么?简述 Ext4 文件系统与 XFS 文件系统的差异,它们在进行逻辑卷缩减时有什么重大区别?

    MBR 分区方案与 GPT 分区方案对比:

    对比维度MBR (主引导记录)GPT (GUID 分区表)
    最大容量支持2.2 TB(受 32 位 LBA 地址限制)18 EB(受 64 位 LBA 地址限制,约 1800 万 TB)
    分区数量最多 4 个主分区,或 3 个主分区 + 1 个扩展分区(其下可再分逻辑分区)理论无限制(Windows 限制为 128 个),Linux 下可更多
    数据结构位于磁盘首部扇区(0 扇区),无冗余备份磁盘首部和尾部各保存一份分区表副本,具有 CRC 校验 功能,抗损坏能力更强
    引导模式依赖 BIOS + Legacy 引导依赖 UEFI 引导(兼容模式除外)
    适用场景老旧系统、小于 2TB 的启动盘现代系统、所有大于 2TB 的数据盘、高可靠性服务器

    选择GPT分区方案,原因如下:

    1. 硬性容量限制:MBR 的 32 位地址空间最多只能寻址 2.2TB 空间。在 4TB 磁盘上使用 MBR,系统只能识别并使用约 2TB 的容量,剩余空间完全浪费。

    2. 数据安全性:生产环境数据无价。GPT 在磁盘末尾备份了分区表,且带有 CRC 校验,当磁盘头部出现坏道或数据损坏时,GPT 能自动从备份恢复,避免了整个磁盘分区表丢失导致的数据灾难。

    ext4 文件系统与 XFS 文件系统的差异:

    对比维度Ext4XFS
    最大文件系统容量1 EB8 EB
    最大单文件大小16 TB8 EB
    元数据操作中等(使用日志,但目录查找随文件数增加线性增长)极快(使用 B+ 树索引,在大目录下性能衰减极小)
    数据一致性默认 有序日志(ordered),兼顾性能与安全高级日志,支持增量备份和在线碎片整理
    在线扩容支持(resize2fs支持(xfs_growfs
    适用场景通用场景、中小型文件数、系统盘大型数据库、大数据存储、大量小文件或超大文件、高并发读写的服务器
    恢复工具e2fsck 恢复能力较强xfs_repair 恢复较慢且依赖日志

    逻辑卷缩减时的区别:

    • Ext4(支持缩减)

      • resize2fs 工具支持将文件系统在线(已挂载)扩容,但缩减必须卸载(umount)文件系统

      • 特点:支持在线缩减,但操作步骤复杂且必须停机卸除。

    • XFS(完全不支持缩减)

      • XFS 文件系统设计的核心理念是 “只能扩大,不能缩小”xfs_growfs 命令仅支持扩容。

      • 特点绝对禁止对 XFS 逻辑卷执行缩减操作。

  4. 物理卷(PV)、卷组(VG)、逻辑卷(LV)和物理盘区(PE)这四个概念是如何层层关联的?相比传统物理分区,LVM 的核心优势是什么?说明 RAID-0, RAID-1, RAID-5, RAID-10 的区别,并从“容错性、磁盘利用率、读写性能、最少盘数”这几个维度进行横向对比。

    LVM:

    物理卷(PV,Physical Volume):最底层,是物理磁盘(或磁盘分区、RAID阵列)被 LVM 初始化处理后的标记。执行 pvcreate 时,会在磁盘头部写入 LVM 元数据。

    物理盘区(PE,Physical Extent):PV 被分割成的固定大小块(默认为 4MB)。PE 是 LVM 中最小的物理存储单元,类似于文件系统中的 Block。

    卷组(VG,Volume Group)资源池。由一个或多个 PV 组合而成的存储仓库。VG 将所有加入的 PV 的 PE 汇集在一起,形成可分配的存储空间池。

    逻辑卷(LV,Logical Volume)最终卷。从 VG 的 PE 池中按需分配一定数量的 PE 组成 LV。用户最终在 LV 上格式化文件系统并挂载使用。

    磁盘(/dev/sdb)→ PV(转换成 PE 块)→ VG(将多个 PV 的 PE 汇聚成池)→ LV(从 VG 中分配若干 PE 块组成逻辑连续空间)→ 用户文件系统

    LVM支持在线扩容(Ext4/XFS)不用停机,LV 可以跨多块物理磁盘,将多块小盘合并成一个大容量逻辑空间。可动态调整 LV 大小(XFS只能扩不能缩,Ext4支持双向调整),支持 LVM 快照(Snapshot),可以在秒级创建系统状态镜像,动态灵活,易于管理大规模服务器

    RAID:

    RAID 级别容错性磁盘利用率读写性能最少磁盘数
    RAID-0(任意一块盘坏,全部数据丢失)100%(N块盘容量全部可用)读写最高(并发 I/O)2 块
    RAID-1优秀(允许坏 1 块,仅镜像组内损坏不超过 1 块)50%(N块盘仅能使用 N/2 容量)写性能略降(需写两份),读性能提升(可并发读)2 块
    RAID-5良好(允许坏 1 块)(N-1)/N(N块盘损失一块容量存校验位,利用率约 66%~94%)读快写性能较差(每次写需要读取旧数据+计算异或+写入新校验,有“写惩罚”)3 块
    RAID-10极佳(最多可坏 N/2 块盘,但镜像组内不能同时坏两块)50%(同 RAID-1,容量浪费一半)读写性能均为最优(镜像提供读并发,条带提供写并行)4 块
  5. 现有一个网页服务器访问日志文件,请编写一个 sed 语句:将文件中被注释的行(以 # 开头)和空行全部过滤掉,直接在终端打印剩下的内容。并写出安全的 sed 命令(要求原地修改并生成备份 .bak 文件),将配置文件 /etc/ssh/sshd_config 中的 #Port 22 替换为 Port 2222。

    sed -n '/^$/!{ /^#/!p }' file.log
    sed -i.bak 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
  6. 假设 Nginx 的日志文件 /var/log/nginx/access.log 中,第一列为客户端的 IP 地址,第九列为 HTTP 状态码。请写出 awk 一行命令:

    [root@Rocky-01 nginx]# cat access.log
    192.168.1.101 - - [17/Jun/2024:10:15:32 +0000] "GET /index.html HTTP/1.1" 200 6123 "-" "Mozilla/5.0"
    192.168.1.102 - - [17/Jun/2024:10:16:45 +0000] "POST /api/login HTTP/1.1" 404 1024 "-" "curl/7.68.0"
    192.168.1.101 - - [17/Jun/2024:10:17:01 +0000] "GET /about.html HTTP/1.1" 200 4532 "-" "Mozilla/5.0"
    192.168.1.103 - - [17/Jun/2024:10:18:22 +0000] "GET /images/logo.png HTTP/1.1" 200 8912 "-" "Mozilla/5.0"
    192.168.1.104 - - [17/Jun/2024:10:19:33 +0000] "GET /products/ HTTP/1.1" 500 0 "-" "Mozilla/5.0"
    192.168.1.101 - - [17/Jun/2024:10:20:10 +0000] "GET /contact.html HTTP/1.1" 404 512 "-" "Mozilla/5.0"
    192.168.1.105 - - [17/Jun/2024:10:21:55 +0000] "GET /index.html HTTP/1.1" 200 6123 "-" "Mozilla/5.0"
    192.168.1.102 - - [17/Jun/2024:10:22:18 +0000] "GET /favicon.ico HTTP/1.1" 200 1500 "-" "Mozilla/5.0"
    192.168.1.103 - - [17/Jun/2024:10:23:44 +0000] "POST /api/order HTTP/1.1" 502 0 "-" "curl/7.68.0"
    192.168.1.106 - - [17/Jun/2024:10:24:09 +0000] "GET /index.html HTTP/1.1" 200 6123 "-" "Mozilla/5.0"
    192.168.1.101 - - [17/Jun/2024:10:25:30 +0000] "GET /admin/ HTTP/1.1" 403 0 "-" "Mozilla/5.0"
    192.168.1.104 - - [17/Jun/2024:10:26:15 +0000] "GET /about.html HTTP/1.1" 200 4532 "-" "Mozilla/5.0"
    192.168.1.107 - - [17/Jun/2024:10:27:41 +0000] "GET /products/ HTTP/1.1" 404 1024 "-" "Mozilla/5.0"
    192.168.1.102 - - [17/Jun/2024:10:28:03 +0000] "GET /index.html HTTP/1.1" 200 6123 "-" "Mozilla/5.0"
    192.168.1.108 - - [17/Jun/2024:10:29:22 +0000] "GET /images/banner.jpg HTTP/1.1" 200 15678 "-" "Mozilla/5.0"
    192.168.1.105 - - [17/Jun/2024:10:30:11 +0000] "GET /contact.html HTTP/1.1" 200 512 "-" "Mozilla/5.0"
    192.168.1.109 - - [17/Jun/2024:10:31:54 +0000] "GET /index.html HTTP/1.1" 200 6123 "-" "Mozilla/5.0"
    192.168.1.101 - - [17/Jun/2024:10:32:20 +0000] "GET /assets/css/style.css HTTP/1.1" 200 2048 "-" "Mozilla/5.0"
    192.168.1.110 - - [17/Jun/2024:10:33:45 +0000] "GET /index.html HTTP/1.1" 304 0 "-" "Mozilla/5.0"
    192.168.1.102 - - [17/Jun/2024:10:34:12 +0000] "GET /test.html HTTP/1.1" 404 0 "-" "Mozilla/5.0"

    1.统计并输出每个 IP 的请求总次数,按降序排列,取前 10 名。

    awk '{print $1}'  /var/log/nginx/access.log | sort  | uniq -c | sort -nr | head -10
    5 192.168.1.101
    4 192.168.1.102
    2 192.168.1.105
    2 192.168.1.104
    2 192.168.1.103
    1 192.168.1.110
    1 192.168.1.109
    1 192.168.1.108
    1 192.168.1.107
    1 192.168.1.106
    awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' /var/log/nginx/access.log | sort -nr | head -10
    {count[$1]++} //对第一行的IP进行计数
    for (ip in count) //将索引赋值给ip变量
    print count[ip], ip //输出计数值,再输出ip
    疑问:ip值从哪儿来?为什么能输出对应得ip?
        awk主循环对$1进行计数并将计数结果进行封装,外层索引使用$1的数值,for (ip in count) 将count的索引依次赋值给ip变量,count[ip]等于前面的计数次数,ip等于count的索引

    2.统计并输出每个 HTTP 状态码(如 200, 404, 500 等)出现的次数。

    awk '{print $9}'  /var/log/nginx/access.log | sort  | uniq -c
         12 200
          1 304
          1 403
          4 404
          1 500
          1 502
    awk '{status[$9]++} END {for (s in status) print status[s], s}' /var/log/nginx/access.log | sort -nr
  7. 生产环境中新加入了一块物理硬盘 /dev/sdb,要求对其使用 GPT 方案,并划分一个容量为 100GB 的主分区 /dev/sdb1。请分别给出使用:

    1. parted 命令行的一体化分区命令。

      parted -s /dev/sdb mklabel gpt mkpart primary 0% 100GB
    2. cat << EOF 配合 fdisk / gdisk 的非交互式分区方案。 并写出将新建的 /dev/sdb1 格式化为 xfs 文件系统,并指定卷标(Label)为 DATA_PART 的命令。

      gdisk /dev/nvme0n2  <<EOF
      o
      y
      n
      1
      ​
      +100GB
      ​
      w
      y
      EOF
      mkfs.xfs -L DATA_PART /dev/nvme0n2
  8. 写出将 /dev/sdb1 挂载到 /data 目录下的命令。获取 /dev/sdb1 的 UUID,并写出在 /etc/fstab 中新增一行以实现“系统开机时自动将其以 defaults 选项挂载到 /data” 的配置字段。

    [root@Rocky-01 ~]# mkdir /data
    [root@Rocky-01 ~]# mount /dev/nvme0n2 /data/
    [root@Rocky-01 ~]# df -Th
    Filesystem          Type      Size  Used Avail Use% Mounted on
    devtmpfs            devtmpfs  4.0M     0  4.0M   0% /dev
    tmpfs               tmpfs     872M     0  872M   0% /dev/shm
    tmpfs               tmpfs     349M  5.7M  344M   2% /run
    /dev/mapper/rl-root xfs        17G  4.7G   13G  28% /
    /dev/nvme0n1p1      xfs       960M  304M  657M  32% /boot
    tmpfs               tmpfs     175M   52K  175M   1% /run/user/42
    tmpfs               tmpfs     175M   36K  175M   1% /run/user/0
    /dev/nvme0n2        ext4       20G   24K   19G   1% /data
    [root@Rocky-01 ~]# blkid /dev/nvme0n2
    /dev/nvme0n2: UUID="a4a319e3-5ec9-4ae4-bad7-2dfec37a6443" TYPE="ext4"
    [root@Rocky-01 ~]# vim /etc/fstab
    [root@Rocky-01 ~]# grep -vE '^$|^#' /etc/fstab
    /dev/mapper/rl-root     /                       xfs     defaults        0 0
    UUID=157ff871-a0a6-4c92-a3fd-a02e2c302848 /boot                   xfs     defaults        0 0
    /dev/mapper/rl-swap     none                    swap    defaults        0 0
    UUID=a4a319e3-5ec9-4ae4-bad7-2dfec37a6443 /data ext4    defaults        0 0
  9. 现系统内存不足,需要紧急扩容交换空间。请写出以下操作命令:

    1. 在单盘上新建一个物理分区 /dev/sdc1,大小为 4GB。

      echo -e "n\np\n1\n\n+4G\nt\n82\nw" | fdisk /dev/sdc
    2. 将该分区格式化为交换分区类型并启用它。

      mkswap /dev/sdc1
      swapon /dev/sdc1
    3. 通过配置文件 /etc/fstab 实现开机自动启用,并设置其优先级为 10。

      vim /etc/fstab
      /dev/sdc1 swap swap defaults,pri=10 0 0
      # 查看当前 swap 设备及优先级
      swapon --show
      # 查看总内存
      free -h
  10. 现有一个卷组 vg_data,其中已包含物理卷 /dev/sdb1。在该卷组中有一个名为 lv_store 的逻辑卷,采用 xfs 文件系统,挂载在 /store。由于空间爆满,需要从物理盘 /dev/sdc2 处对其进行扩容。请写出:

    1. 将 /dev/sdc2 初始化为 PV 并扩展到卷组 vg_data。

      pvcreate /dev/sdc2
      vgextend vg_data /dev/sdc2
    2. 扩展逻辑卷 lv_store,将卷组剩余所有空间全部加给它。

      lvextend -l +100%FREE /dev/vg_data/lv_store
    3. 在线扩容其 xfs 文件系统。

      xfs_growfs /dev/vg_data/lv_store
    # 查看卷组剩余空间
    vgdisplay vg_data
    # 查看逻辑卷新大小
    lvdisplay /dev/vg_data/lv_store
    # 查看文件系统大小
    df -h /store
  11. 写出为 /dev/vg_data/lv_store 创建一个只读快照 lv_store_snap` 的命令,快照预留大小为 500MB。在逻辑卷上进行测试后发现数据损毁,请写出卸载原逻辑卷、恢复快照并重新挂载验证的完整命令。

    lvcreate -s -n lv_store_snap -L 500M /dev/vg_data/lv_store  #创建快照
    umount /store   #逻辑卷故障,先卸载逻辑卷
    lvconvert --merge /dev/vg_data/lv_store_snap    #恢复快照
    mount /dev/vg_data/lv_store /store  #重新挂载
    ls -l /store   # 检查数据是否恢复
    df -h /store   # 查看挂载情况
  12. 在对新磁盘配置持久挂载时,某管理员将 /etc/fstab 中的 UUID 输错了一个字符,重启后系统直接卡在了 Emergency Mode。此时根分区变为了只读。请回答:

    1. 在此终端下如果直接编辑 /etc/fstab 会遇到什么报错?(提示:只读文件系统)、

      E212: Can't open file for writing 或 Read-only file system
    2. 请给出能够解除只读限制、恢复系统挂载并修复该配置文件的完整命令步骤。

      mount -o remount,rw /
      cp /etc/fstab /etc/fstab.bak
      vim /etc/fstab
    3. 在修改 /etc/fstab 之后重启前,应运行哪条命令验证配置正确性?

      mount -a 
  13. 开发部门在清理旧分区时,要求运维将一个容量为 500GB 的 LVM 逻辑卷缩减到 200GB。该卷上挂载的是 xfs 文件系统。请回答:

    1. 这个任务在 xfs 文件系统上可行吗?如果执行 lvreduce 会发生什么灾难?

      • XFS 文件系统设计上不支持缩容(shrink),只支持在线扩容(grow)。

      • 如果直接对承载 XFS 的逻辑卷执行 lvreduce(缩小 LV),逻辑卷的大小会被强行截断至 200GB,但文件系统元数据仍认为整个 LV 为 500GB。这会导致:

        • 文件系统元数据与底层存储空间不匹配。

        • 后续任何读写操作都可能访问到被截断的物理区域,造成文件系统元数据损坏、数据丢失、甚至卷无法挂载

        • 即使使用 xfs_repair 也无法恢复,因为 XFS 没有设计收缩功能,损坏通常是永久性的

    2. 如果文件系统是 ext4,缩减逻辑卷的正确安全步骤是怎样的?为什么缩容必须“先缩减文件系统,再缩减逻辑卷”?

      逻辑卷(LV)是底层块设备,文件系统是上层抽象。如果先缩减 LV,文件系统还认为自己占据 500GB 的空间,就会尝试访问已被裁剪掉的区域,导致元数据损坏和数据丢失。先缩文件系统,是让文件系统知道自己即将变小,并安全地移动数据,然后告诉 LV 可以回收空间。

      # 1. 卸载文件系统(必须卸载,ext4 不支持在线缩减)
      umount /store
      ​
      # 2. 强制检查文件系统,确保一致性
      e2fsck -f /dev/vg_data/lv_store
      ​
      # 3. 缩小文件系统到目标大小(先缩文件系统)
      #    大小可以指定为 200G 或更小,但必须小于等于最终 LV 大小
      resize2fs /dev/vg_data/lv_store 200G
      ​
      # 4. 缩小逻辑卷(再缩 LV)
      #    -L 指定新大小,注意单位与 resize2fs 保持一致
      lvreduce -L 200G /dev/vg_data/lv_store
      ​
      # 5. 重新检查文件系统(可选但推荐)
      e2fsck -f /dev/vg_data/lv_store
      ​
      # 6. 重新挂载并验证
      mount /dev/vg_data/lv_store /store
      df -h /store

[6]

  1. 解释 ABI 与 API 的概念与区别。静态链接与动态链接有何异同?在排查动态链接库丢失引起的软件运行报错时,应如何使用 ldd 命令?

    ABI 与 API 的概念与区别:

    对比维度API(应用程序编程接口)ABI(应用程序二进制接口)
    定义层级源代码级别的约定。定义了“如何写代码”来调用某个功能(函数名、参数类型、返回类型)。二进制机器码级别的约定。定义了“编译后的代码如何交互”(调用约定、寄存器使用、系统调用号、内存对齐、符号命名规则)。
    关注对象开发者(人类可读)。编译器和操作系统内核(机器可读)。
    破坏性变更修改 API(如改函数名、加参数)会导致编译错误(编译阶段报错)。修改 ABI(如改结构体成员顺序、改系统调用号)不会报编译错,但会导致运行时崩溃(Segfault)或奇怪的未定义行为
    跨平台性标准 API(如 POSIX)可跨操作系统,只需重新编译即可运行。ABI 严格绑定操作系统发行版、CPU 架构(x86_64 vs ARM)和编译器版本。即便 API 相同,ABI 不同,二进制也无法互通(如 Windows .exe 无法在 Linux 运行)。

    静态链接与动态链接有何异同:

维度静态链接(Static Linking)动态链接(Dynamic Linking)
时机发生在编译/构建阶段ld 链接器将库代码打包进可执行文件)。发生在程序加载或运行时(由动态链接器 /lib/ld-linux.so 加载 .so 库)。
可执行文件大小巨大(整个 libc.a 都复制进去)。极小(仅包含重定位和符号表)。
内存占用(同一库被多个进程重复加载到物理内存)。(共享库在内存中只存一份,通过页表映射给多进程)。
更新升级极难(必须重新编译链接整个程序)。极方便(只需替换 .so 文件,重启程序即可,如 OpenSSL 漏洞修复)。
环境依赖无依赖(打包即运行,移植性强)。强依赖(缺少 .so 或版本不匹配直接报错 cannot open shared object file)。
启动速度(无需外部解析)。稍慢(运行时需符号查找与重定位,但通常可忽略)。

动态链接库丢失引起的软件运行报错:

ldd /path/to/your_program   //诊断
linux-vdso.so.1 (0x00007ffe...)
libssl.so.1.1 => not found   <-- 这就是缺失的库!
libc.so.6 => /lib64/libc.so.6 (0x00007f...)
find /usr -name "libssl.so*"   //检查系统是否有该文件
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH   //若库在 /usr/local/lib,需加入环境变量
./your_program
ls -l /usr/lib64/libssl.so*  //查看动态库是否为软连接文件,并坚持指向文件是否被删除

  1. 简述 Rocky 环境下 yum/dnf 客户端与软件源服务端的工作机制。自建私有 YUM 仓在企业内部有何实际意义?说明 Ubuntu 下的 dpkg 和 apt 的层级关系。在卸载软件时,apt remove、apt purge 与 apt autoremove 三者之间有何区别?

    yum/dnf 客户端与软件源服务端的工作机制:

    1. 客户端配置/etc/yum.repos.d/*.repo 文件中定义仓库的 baseurl(或 mirrorlist)和 gpgkey

    2. 元数据下载:执行 dnf updatednf install 时,客户端会下载仓库中的 repodata/repomd.xml 文件,该文件包含所有软件包的元数据(名称、版本、依赖关系、文件列表、校验和等)。

    3. 本地缓存:客户端将元数据缓存在 /var/cache/dnf/ 下,后续操作优先使用缓存(可设置过期时间)。

    4. 依赖解析:客户端根据用户请求(安装/更新/删除)和本地元数据,利用 libsolv 库计算完整的依赖树,确保无冲突。

    5. 包下载与事务执行:确定需下载的包列表后,从 baseurl 或镜像下载 .rpm 包到本地,校验 GPG 签名和校验和,然后执行 RPM 事务(安装/升级/删除)。

    6. 服务端:通常是 HTTP 服务器(如 nginx)提供静态文件服务,目录结构为 repodata/ 子目录和 .rpm 包。管理员使用 createrepodnf reposync 生成并更新 repodata

      自建私有 YUM 仓在企业内部有何实际意义

    7. 内网加速:避免数百台服务器同时访问公网源,节省带宽,提升部署速度。

    8. 版本锁定与合规:可冻结某个稳定版本的软件包,防止生产环境因外部源更新导致意外不兼容。

    9. 定制化包分发:可放置企业内部开发的 .rpm 包(如定制版的 nginx、监控 agent),统一分发。

    10. 安全可控:只允许内部仓库,可做漏洞扫描和安全审核,避免使用未授权的第三方源。

    11. 离线环境支持:物理隔离网络下,私有仓库是唯一可行的软件安装来源。

    12. 简化运维:结合 dnf update 和内网仓库,实现统一版本管理和补丁发布。

      dpkg 和 apt 的层级关系

    层级工具职责
    底层dpkgDebian 包管理器的核心工具,直接处理 .deb 包文件(安装、卸载、查询元数据、解包等),但不自动处理依赖关系。它操作本地数据库(/var/lib/dpkg/status)。
    上层apt(含 apt-get, apt-cache基于 dpkg 构建的高级包管理工具,自动处理依赖解析,从远程仓库下载包,然后调用 dpkg 执行安装/卸载。还提供搜索、更新、升级等高级功能。
    统一入口apt 命令(新版)整合了 apt-getapt-cache 的常用子命令,提供更友好的进度条和输出,但功能子集略小。

    关系apt 是前端,dpkg 是后端。apt 负责网络、仓库、依赖,dpkg 负责实际安装/卸载包文件。操作 .deb 文件时直接使用 dpkg -i,但需要自己解决依赖;推荐用 apt install ./package.deb,会自动处理依赖。

    apt remove、apt purge 与 apt autoremove 的区别

    命令行为保留配置文件?处理依赖典型场景
    apt remove <pkg>删除软件包本身(二进制文件、库等),但保留该软件包的配置文件(位于 /etc 下的配置)。(保留)删除包后,若其他包依赖它,会阻止删除;若该包依赖的其他包未被其他包使用,则会一并标记为自动依赖,但不会自动删除。临时移除软件,但保留配置以便日后重装恢复原配置。
    apt purge <pkg>删除软件包,并同时删除其所有配置文件(包括 /etc 下的配置和用户家目录中的配置)。(彻底清除)同上,但会彻底清理残留。彻底卸载软件,消除所有痕迹,适合清理不用的服务。
    apt autoremove不指定包名,此命令用于删除不再被任何已安装包所依赖的自动安装的包(即作为依赖引入但现已多余的包)。不涉及自动计算依赖树,清理孤儿包。定期清理系统,释放磁盘空间。常与 remove/purge 配合使用,在删除主包后执行 autoremove 清理其依赖。
  2. 什么是 systemd?它相比于传统的 SysV init 机制,最显著的优势是什么?说明服务控制单元文件中,[Unit]、[Service]、[Install] 三个配置段的核心作用。

    systemd 是 Linux 系统中 PID 为 1 的初始化系统(init 进程),也是系统的服务管理器。它不仅负责系统内核启动后的第一个用户态进程,还承担着管理所有系统服务(daemon)、挂载点、设备、套接字、定时任务等资源的全生命周期管理。目前,Rocky Linux、CentOS、Ubuntu、Debian 等主流发行版均已将 systemd 作为默认 init 系统。

    维度SysV initsystemd
    启动方式串行顺序启动:严格按照 /etc/rc?.d/ 中的数字序号(S01, S02...)逐一执行,速度极慢。并行按依赖启动:根据单元文件中的 After/Before/Requires 构建依赖关系图,同时启动无依赖的服务,启动速度极快(可缩短数十秒)。
    进程管理进程组管理松散,孤儿进程易失控(必须依赖 pidfile 追踪)。集成 cgroups:每个服务独立拥有 cgroup,可精确控制资源(CPU/内存),并可自动重启崩溃进程Restart=always)。
    启动方式灵活性仅支持“启动时运行或完全禁用”。支持 Socket 激活总线激活设备激活路径激活(按需启动),例如:sshd 可在第一个请求到达时才启动。
    日志管理日志分散在 /var/log/ 下多个文件中,格式不一。集成 journald:统一收集所有服务日志(journalctl),支持结构化存储、过滤和远程转发。
    统一管理繁杂的 shell 脚本(start/stop/status 需自定义)。标准化声明式单元文件.service),所有服务状态查询(systemctl)统一,支持快照、目标(target)管理等高级特性。
    1. [Unit] 段 —— 通用元数据与依赖定义
    • 作用:定义服务的描述信息、与其他单元的依赖关系启动顺序,适用于所有类型的单元(服务、挂载、设备等)。

    • 核心指令

      • Description:人类可读的描述(如 Description=My Custom Web Server)。

      • After启动顺序不强制依赖,只保证本服务在该单元之后启动(如 After=network.target)。

      • Before:本服务在该单元之前启动。

      • Requires强依赖,若依赖的单元启动失败,本服务也会失败。

      • Wants弱依赖,依赖单元启动失败不影响本服务启动。

    2. [Service] 段 —— 进程运行与生命周期控制(仅针对服务类型)
    • 作用:定义服务如何启动、运行、重启以及运行环境

    • 核心指令

      • Type:服务类型(simple(默认,主进程在前台)、forking(主进程后台化,需配合PIDFile)、oneshot(一次性任务)、notify(通知 systemd 已就绪))。

      • ExecStart启动命令(绝对路径,如 /usr/bin/python3 /app/server.py)。

      • ExecStop停止命令(通常缺省时 systemd 发送 SIGTERM,手动指定可做清理)。

      • ExecReload重载命令(如 kill -HUP)。

      • Restart:重启策略(noon-failurealwayson-success 等)。

      • User / Group:指定运行进程的用户和组(安全加固必须设置)。

      • WorkingDirectory:工作目录。

      • EnvironmentFile:加载环境变量文件(如 /etc/sysconfig/myservice)。

    3. [Install] 段 —— 启用(Enable)与开机自启关联
    • 作用不决定服务当前是否运行,仅定义在 systemctl enabledisable 时,服务与系统启动目标的链接关系

    • 核心指令

      • WantedBy最关键。指定该服务被哪个系统目标(target)所“想要”。例如 WantedBy=multi-user.target,表示当系统进入多用户模式时,该服务会被自动启动(即 enable 后会在 /etc/systemd/system/multi-user.target.wants/ 下创建软链接)。

      • RequiredBy:强依赖目标,较少用。

      • Alias:为单元设置别名(systemctl alias)。

  3. Linux 中执行 Shell 脚本有 ./script.sh、bash script.sh、source script.sh。请说明它们在“是否启动子进程”和“对脚本文件的执行权限要求”上的异同。说明 set -e、set -u、set -o pipefail 起到了什么作用?

    执行方式是否启动子进程(子Shell)对脚本文件的执行权限要求环境变量影响典型使用场景
    ./script.sh。内核根据 shebang(#!/bin/bash)调用对应解释器,启动一个全新的子进程(子Shell)来运行脚本。必须有执行权限(chmod +x script.sh)。不影响父Shell。脚本内导出的变量、切换的目录在脚本结束后全部失效。执行普通应用程序或独立脚本,最标准的运行方式。
    bash script.sh。显式指定 bash 命令,bash 本身作为一个子进程启动,并读取脚本文件作为参数。不需要执行权限(只需读权限 r)。不影响父Shell。与 ./ 效果相同(环境隔离)。快速执行脚本而无需修改权限,或用于绕过 shebang 使用不同解释器(如 shbash -x 调试)。
    source script.sh(或 . script.sh不启动子进程,脚本在当前Shell进程中逐行执行。不需要执行权限(只需读权限 r)。会影响父Shell。脚本中所有变量、函数、cd 目录变更都会保留在当前终端会话中。加载配置文件(如 ~/.bashrc)、设置环境变量、切换工作目录。
    • -e:防止低级错误蔓延。

    • -u:防止变量未定义导致逻辑漏洞。

    • -o pipefail:防止管道中的错误被静默吞噬

  4. 什么是全局变量?简述 /etc/profile、/etc/bashrc、~/.bash_profile、~/.bashrc 这四个配置文件的加载顺序以及在登录与非登录场景下的差异。

    • 环境变量(Environment Variables):由父进程导出(export)并被子进程继承的变量。它对当前 Shell 及其所有子进程可见。例如 PATHHOME

    • Shell 变量(本地变量):仅在当前 Shell 进程中有效,不会被传递给子进程。

    场景加载文件(按顺序)是否读 ~/.bashrc
    登录 Shell(SSH / tty)/etc/profile~/.bash_profile(→ 通常内含 source ~/.bashrc(间接通过 profile)
    非登录交互式 Shell(终端标签)/etc/bashrc~/.bashrc(直接)
    非交互式 Shell(脚本执行)不加载(除非指定 BASH_ENV
  1. 某台机器上运行着一个未知服务,其二进制程序路径为 /usr/sbin/httpd。请写出一条命令:查询这个二进制文件是由哪个具体的包(RPM 或 DEB)安装生成的。在未联网的情况下,需要查询本地一个尚未安装的软件包 vsftpd-3.0.3-49.el9.x86_64.rpm 内包含了哪些文件和目录,请写出对应的查询命令。

    whereis httpd
        httpd: /usr/sbin/httpd /usr/lib64/httpd /etc/httpd /usr/share/httpd /usr/share/man/man8/httpd.8.gz
    rpm -qf /usr/sbin/httpd
        httpd-core-2.4.62-13.el9_8.1.x86_64
    dpkg -S " /usr/sbin/httpd"
    rpm -qpl vsftpd-3.0.3-49.el9.x86_64.rpm
    dpkg -c vsftpd_3.0.5-0ubuntu1.1_amd64.deb
  2. 现需要通过源码编译安装 Nginx(以 nginx-1.24.0 为例),请写出其核心的编译部署步骤。编译安装完成在 /usr/local/nginx 目录后,如何在不使用绝对路径的情况下,让系统所有用户都可以直接运行 nginx 命令?请写出环境变量配置的命令。

    yum install -y gcc pcre-devel zlib-devel openssl-devel make
    wget https://nginx.org/download/nginx-1.24.0.tar.gz
    tar -xzf nginx-1.24.0.tar.gz
    cd nginx-1.24.0
    ./configure --prefix=/usr/local/nginx \
                --with-http_ssl_module \
                --with-http_v2_module \
                --with-stream
    make -j$(nproc)
    make install
    /usr/local/nginx/sbin/nginx -v 
    echo 'export PATH=/usr/local/nginx/sbin:$PATH' > /etc/profile.d/nginx.sh
    source /etc/profile.d/nginx.sh
    which nginx
    nginx -v
  3. 在 /usr/local/bin/backup.sh 有一个自动备份的脚本。现在要求将其封装为系统服务 backup.service,满足:

    1. 服务说明为 Daily Backup Service。

    2. 在 network.target 后启动。

    3. 类型为 simple。

    4. 异常退出 5s 内自动重启。

    5. 允许开机自启。 请完整写出 /etc/systemd/system/backup.service 的配置文件内容,并写出重载配置、开机自启并立刻启动该服务的命令。

      cat << EOF >> /etc/systemd/system/backup.service
      [Unit]
      Description=Daily Backup Service
      After=network.target
      ​
      [Service]
      Type=simple
      ExecStart=/usr/local/bin/backup.sh
      Restart=on-failure
      RestartSec=5s
      ​
      [Install]
      WantedBy=multi-user.target
      EOF
      ​
      systemctl daemon-reload && systemctl enable backup && systemctl start backup && systemctl status backup
  4. 定义一个普通本地变量 URL="https://www.sswang.com/index.html",请写出表达式:

    1. 获取此变量字符串的总长度。

      URL="https://www.sswang.com/index.html"
      echo ${#URL}
    2. 提取出域名部分的子串 "www.sswang.com"。

      echo $URL | cut -d'/' -f3
      # 或
      echo $URL | awk -F '/' '{print $3}'
    3. 并说明如何定义只读变量 PORT 且值为 8080,如果修改或删除会发生什么?

      readonly PORT=8080
      # 或
      declare -r PORT=8080
      PORT=9090
      # 报错:bash: PORT: readonly variable
      unset PORT
      # 报错:bash: unset: PORT: cannot unset: readonly variable
  5. 编写一个 Shell 脚本片段,提示用户输入管理员账号及密码,要求:

    1. 提示信息 "Please input admin username: "。

    2. 接收密码时,不回显输入。

    3. 用户在 10 秒内未输入,则脚本自动超时退出。

      #!/bin/bash
      ​
      # 输入用户名
      read -t 10 -p "Please input admin username: " username
      if [ $? -ne 0 ] || [ -z "$username" ]; then
          echo -e "\nTimeout or empty username. Exiting." >&2
          exit 1
      fi
      ​
      # 输入密码
      read -t 10 -s -p "Please input admin password: " password
      ret=$?
      echo >&2   # 输出换行到stderr,但可能不显示,最好用echo
      echo
      if [ $ret -ne 0 ] || [ -z "$password" ]; then
          echo -e "Timeout or empty password. Exiting." >&2
          exit 1
      fi
  6. 编写代码演示三种不同的整数加法运算方法。并在生产环境需要进行浮点数计算:计算当前磁盘利用率(已用空间 45GB 除以总空间 120GB 的百分比),并保留两位小数。请给出命令。另外,演示逻辑测试 [[]] 表达式:判断用户输入的变量 IP_ADDR 格式是否为有效的 IPv4 地址。

    sum1=$((1 + 2)) ; echo "$sum1"
    let sum2=1+2 ; echo $sum2
    sum3=$(expr 1 + 2) ; echo "$sum3"
    echo "scale=2; 45 * 100 / 120" | bc
    #!/bin/bash
    ​
    read -p "请输入一个IPv4地址: " IP_ADDR
    ​
    # 正则:匹配 0-255 的四组数字
    ipv4_regex='^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$'
    ​
    if [[ $IP_ADDR =~ $ipv4_regex ]]; then
        echo "有效的IPv4地址: $IP_ADDR"
    else
        echo "无效的IPv4地址: $IP_ADDR"
    fi
  7. 在 Shell 脚本中,位置参数 $* 与 $@ 到底有什么本质区别?请回答:

    1. 在没有被双引号括住时行为不同吗?

      • 不加引号时$*$@ 裸写):两者都会将位置参数展开成多个独立的单词,然后对每个单词进行路径名扩展(通配符)和单词分割(分词)

      • 由于它们都被拆散了,如果参数中包含空格或通配符(如 *),它们会被错误地拆分成多个参数或展开成文件名。

      • 假设脚本参数为 "hello world""*.txt"。不加引号时,无论是 $* 还是 $@,都会被拆成 3 个甚至更多个词(因为空格分词和 * 被展开了),行为几乎一致。

    2. 当被双引号括住为 "$*" 和 "$@" 时分别是什么逻辑?

      • "$\*":将所有位置参数合并成一个单一的字符串,并用 IFS 环境变量的第一个字符作为分隔符连接它们(默认是空格)。

        • 例如:参数为 abc 时,"$*" 等价于 "a b c"(一个字符串,3个字符)。

      • "$@":将每个位置参数展开为独立的、被双引号包裹的字符串,保持每个参数的原始边界。

        • 例如:参数为 abc 时,"$@" 等价于 "a" "b" "c"(三个独立的字符串)。

    3. 在处理含有不确定字符或空格的参数传递时选用哪一个?

      无引号的 $\*helloworld*.txt(如果当前目录有 txt 文件,*.txt 会被展开成文件名,严重错误)。

      "$\*":输出 [hello world *.txt](合并成一个字符串,丢失了参数边界,通常用于拼接字符串,而非传递参数)。

      "$@":输出 [hello world][*.txt](完美保留了参数原样,不会展开通配符,也不会拆分空格)。

      #!/bin/bash
      echo "====== 使用 \$* (不加引号) ======"
      for arg in $*; do
          echo "[$arg]"
      done
      ​
      echo "====== 使用 \$@ (不加引号) ======"
      for arg in $@; do
          echo "[$arg]"
      done
      ​
      echo "====== 使用 \"\$*\" (加引号) ======"
      for arg in "$*"; do
          echo "[$arg]"
      done
      ​
      echo "====== 使用 \"\$@\" (加引号,标准做法) ======"
      for arg in "$@"; do
          echo "[$arg]"
      done
      ​
      ​
      bash test.args.sh  "hello world" "*.txt"
      ====== 使用 $* (不加引号) ======
      [hello]
      [world]
      [1.txt]
      ====== 使用 $@ (加引号) ======
      [hello]
      [world]
      [1.txt]
      ====== 使用 "$*" (加引号) ======
      [hello world *.txt]
      ====== 使用 "$@" (加引号,标准做法) ======
      [hello world]
      [*.txt]
  8. 编译安装软件时遇到依赖缺失,除了去互联网搜索,如何通过包管理器查找依赖文件所归属的软件?

    1. 请写出 Rocky 环境下,根据文件路径或库文件名称反向搜索对应 RPM 软件包的命令;

      rpm -pl /usr/sbin/httpd
      rpm -qf /usr/sbin/httpd
      httpd-core-2.4.62-13.el9_8.1.x86_64
      yum provides sos
      yum deplist httpd
    2. 如果是在 Ubuntu 环境下,又该使用什么命令?

      dpkg -S /usr/sbin/sshd
      apt-cache depends nginx
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值