本质上也是个 Linux 基础知识速查手册

Linux的诞生

Linux 简介

通过以下三点可以概括 Linux 到底是什么:

  • 类 Unix 系统:Linux 是一种自由、开放源码的类似 Unix 的操作系统

  • Linux 本质是指 Linux 内核:严格来讲,Linux 这个词本身只表示 Linux 内核,单独的 Linux 内核并不能成为一个可以正常工作的操作系统。所以,就有了各种 Linux 发行版。

  • Linux 之父(林纳斯·本纳第克特·托瓦兹 Linus Benedict Torvalds):一个编程领域的传奇式人物,真大佬!我辈崇拜敬仰之楷模。他是 Linux 内核 的最早作者,随后发起了这个开源项目,担任 Linux 内核的首要架构师。他还发起了 Git 这个开源项目,并为主要的开发者。

Linux 诞生

1989 年,Linus Torvalds 进入芬兰陆军新地区旅,服 11 个月的国家义务兵役,军衔为少尉,主要服务于计算机部门,任务是弹道计算。服役期间,购买了安德鲁·斯图尔特·塔能鲍姆所著的教科书及 minix 源代码,开始研究操作系统。1990 年,他退伍后回到大学,开始接触 Unix。

Minix 是一个迷你版本的类 Unix 操作系统,由塔能鲍姆教授为了教学之用而创作,采用微核心设计。它启发了 Linux 内核的创作。

1991 年,Linus Torvalds 开源了 Linux 内核。

常见的 Linux 发行版本

Linus Torvalds 开源的只是 Linux 内核,我们上面也提到了操作系统内核的作用。一些组织或厂商将 Linux 内核与各种软件和文档包装起来,并提供系统安装界面和系统配置、设定与管理工具,就构成了 Linux 的发行版本。

内核主要负责系统的内存管理,硬件设备的管理,文件系统的管理以及应用程序的管理。

Linux 的发行版本可以大体分为两类:

  • 商业公司维护的发行版本:比如 Red Hat 公司维护支持的 Red Hat Enterprise Linux (RHEL)。
  • 社区组织维护的发行版本:比如基于 Red Hat Enterprise Linux(RHEL)的 CentOS、基于 Debian 的 Ubuntu。

Linux 系统的组成

kernel 内核:是 Linux 的核心程序,用于管理硬件设备、系统的线程进程、内存、交换空间、文件系统、精灵进程(守护进程)等。主要实现系统程序与硬件之间的控制管理功能。

Shell:包裹在内核之外的人机交互界面,用于用户和内核之间打交道的功能,类似于windows的cmd。通过Shell将输入的命令与内核通讯,好让内核可以控制硬件开正确无误的操作工作。

  • 而且 Shell 有着不同的分类,比如 Bourne shell(sh),Korn shell(ksh)、C shell (csh)、Bourne-again shell(bash)、tcsh。其中最常用的有 csh 和 bash。

User Application:Linux根据程序的运行,分为用户空间和内核空间

  • 普通的应用程序运行在用户空间,一些涉及到系统核心资源的操作的程序运行在内核空间
  • 而且一些应用程序当需要访问系统资源的时候,必须通过系统调用,通过一些内核函数将系统资源由内核空间拷贝到用户空间。

Files System:文件系统,管理文件和目录。

Linux文件系统

Linux 文件系统简介

一切皆为文件

在 Linux 操作系统中,一切被操作系统管理的资源,如网络接口卡、磁盘驱动器、打印机、输入输出设备、普通文件或目录等,都被视为文件。这是 Linux 系统中一个重要的概念,即”一切都是文件”。

这种概念源自 UNIX 哲学,即将所有资源都抽象为文件的方式来进行管理和访问。Linux 的文件系统也借鉴了 UNIX 文件系统的设计理念。这种设计使得 Linux 系统可以通过统一的文件接口来管理和操作不同类型的资源,从而实现了一种统一的文件操作方式。例如,可以使用类似于读写文件的方式来对待网络接口、磁盘驱动器、设备文件等,使得操作和管理这些资源更加统一和简便。

这种文件为中心的设计理念为 Linux 系统带来了灵活性和可扩展性,使得 Linux 成为一种强大的操作系统。同时,这也是 Linux 系统的一大特点,深受广大用户和开发者的喜欢和推崇

inode 介绍

inode为索引节点,是 Unix/Linux 文件系统中最核心、最基础的概念之一

  • 每个文件或目录(包括设备文件、符号链接等)在创建时,文件系统都会为其分配一个唯一的 inode。
  • inode 本身不包含文件名,它只存储了与该文件相关的所有元信息(metadata)和指向文件实际数据块的指针。

核心思想是通过 inode,把文件名和文件内容分离,文件名存储在 目录(Directory)中,文件内容和属性 存储在 inode 和数据块 中,目录的本质就是一个特殊的文件,其内容是一张 文件名 → inode 编号 的映射表

通过以下五点可以概括 inode 到底是什么:

  1. 硬盘以扇区 (Sector) 为最小物理存储单位,而操作系统和文件系统以块 (Block) 为单位进行读写,块由多个扇区组成。文件数据存储在这些块中。现代硬盘扇区通常为 4KB,与一些常见块大小相同,但操作系统也支持更大的块大小,以提升大文件读写性能。文件元信息(例如权限、大小、修改时间以及数据块位置)存储在 inode(索引节点)中。每个文件都有唯一的 inode。inode 本身不存储文件数据,而是存储指向数据块的指针,操作系统通过这些指针找到并读取文件数据。 固态硬盘 (SSD) 虽然没有物理扇区,但使用逻辑块,其概念与传统硬盘的块类似。
  2. inode 是一种固定大小的数据结构,其大小在文件系统创建时就确定了,并且在文件的生命周期内保持不变。
  3. inode 的访问速度非常快,因为系统可以直接通过 inode 号码定位到文件的元数据信息,无需遍历整个文件系统。
  4. inode 的数量是有限的,每个文件系统只能包含固定数量的 inode。这意味着当文件系统中的 inode 用完时,无法再创建新的文件或目录,即使磁盘上还有可用空间。因此,在创建文件系统时,需要根据文件和目录的预期数量来合理分配 inode 的数量
  5. 可以使用 stat 命令可以查看文件的 inode 信息,包括文件的 inode 号、文件类型、权限、所有者、文件大小、修改时间。

简单来说:inode 就是用来维护某个文件被分成几块、每一块在的地址、文件拥有者,创建时间,权限,大小等信息。block 是实际文件的内容,如果一个文件大于一个块时候,那么将占用多个 block,但是一个块只能存放一个文件。

因为数据是由 inode 指向的,如果有两个文件的数据存放在同一个块中,就会乱套了

image-20260214121546731

可以看出,Linux/Unix 操作系统使用 inode 区分不同的文件。这样做的好处是,即使文件名被修改或删除,文件的 inode 号码不会改变,从而可以避免一些因文件重命名、移动或删除导致的错误。同时,inode 也可以提供更高的文件系统性能,因为 inode 的访问速度非常快,可以直接通过 inode 号码定位到文件的元数据信息,无需遍历整个文件系统。

不过,使用 inode 号码也使得文件系统在用户和应用程序层面更加抽象和复杂,需要通过系统命令或文件系统接口来访问和管理文件的 inode 信息。

查看文件的 inode 号和详细 inode 信息的命令如下

1
2
3
4
5
6
7
8
# 文件的 inode 号
ls -i filename

# 输出包含 inode 号、大小、权限、三个时间戳等所有元数据
stat filename

# 查看磁盘分区的 inode 使用情况
df -i

硬链接和软链接

在 Linux/类 Unix 系统上,文件链接(File Link)是一种特殊的文件类型,可以在文件系统中指向另一个文件。常见的文件链接类型有两种:

  1. 硬链接:

    硬链接是为同一个 inode 创建的另一个(或多个)文件名。可以把它理解成给同一个物理文件起了多个名字。

    • 在 Linux/类 Unix 文件系统中,每个文件和目录都有一个唯一的索引节点(inode)号,用来标识该文件或目录。硬链接通过 inode 节点号建立连接,硬链接和源文件的 inode 节点号相同,两者对文件系统来说是完全平等的(可以看作是互为硬链接,源头是同一份文件),删除其中任何一个对另外一个没有影响,可以通过给文件设置硬链接文件来防止重要文件被误删。

    • 当你创建一个硬链接时,系统会在目标目录中添加一个新的目录项,即一个新的文件名,这个新的文件名指向源文件的同一个 inode,同时,该 inode 的链接计数(Link Count)会增加 1。

    • 所有硬链接拥有完全相同的 inode 号。它们是同一个文件的不同名字。修改任何一个硬链接的内容,其他所有硬链接看到的内容都会实时、完全同步地改变。删除任何一个硬链接,只是将 inode 的链接计数减 1。只要链接计数不为 0,文件的数据就不会被删除。

    • 硬链接具有一些限制,不能对目录以及不存在的文件创建硬链接,并且,硬链接也不能跨越文件系统。

      我们之前提到过,硬链接是通过 inode 节点号建立连接的,而硬链接和源文件共享相同的 inode 节点号。

      然而,每个文件系统都有自己的独立 inode 表,且每个 inode 表只维护该文件系统内的 inode。如果在不同的文件系统之间创建硬链接,可能会导致 inode 节点号冲突的问题,即目标文件的 inode 节点号已经在该文件系统中被使用。

  2. 软链接:

    软链接是一个独立的特殊文件,它的内容仅仅是另一个文件或目录的路径字符串。它类似于 Windows 中的 快捷方式。

    • 当你访问一个软链接时,系统会先读取软链接文件本身的内容,即那个路径字符串,然后解析这个路径,再去访问真正的目标文件。

    • 软链接有自己的 inode,与目标文件的 inode 完全不同。软链接文件的内容就是目标文件的绝对路径或相对路径。因为它只存储路径,所以可以轻松跨越不同的磁盘分区或文件系统。

    • 源文件删除后,软链接依然存在,但是指向的是一个无效的文件路径。不同于硬链接,可以对目录或者不存在的文件创建软链接

    • 创建软链接的命令如下:

      1
      ln -s target_path symbolic_link_name

对于跨越文件系统,它的核心含义是操作的目标位于与源不同的、独立的文件系统实例上

文件系统在这里不仅仅指代 NTFS、ext4、APFS 这样的格式类型,而是指一个被挂载到目录树上的、独立的存储单元。

例如。一个物理磁盘可以有多个分区。每个分区都可以格式化为一个独立的文件系统,一个网络存储卷被挂载到本地目录后,也构成了一个独立的文件系统。

你可以通过 mount 命令查看所有已挂载的文件系统。每个挂载点通常都代表一个独立的文件系统实例。

简单来说:只要两个路径的最顶层挂载点不同,它们就属于不同的文件系统。

Linux文件类型

对于Linux,一切皆为文件

Linux 支持很多文件类型,其中非常重要的文件类型有:普通文件,目录文件,链接文件,设备文件,管道文件,Socket 套接字文件 等

  • 普通文件(-):用于存储信息和数据, Linux 用户可以根据访问权限对普通文件进行查看、更改和删除。比如:图片、声音、PDF、text、视频、源代码等等。
  • 目录文件(d,directory file):目录也是文件的一种,用于表示和管理系统中的文件,目录文件中包含一些文件名和子目录名。打开目录事实上就是打开目录文件。
    • Linux 中的目录也是文件,目录文件中保存着该目录下其他文件的 inode 号和文件名等信息,目录文件中的每个数据项都是指向某个文件 inode 号的链接,删除文件名就等于删除与之对应的链接。
    • 目录文件的字体颜色是蓝色
  • 符号链接文件(l,symbolic link):保留了指向文件的地址而不是文件本身。
    • 链接文件一般指的是一个文件的软连接,文件名为浅蓝色
  • 字符设备(c,char):用来访问字符设备比如键盘。
  • 设备文件(b,block):用来访问块设备比如硬盘、软盘,即设备文件。设备文件一般存放在 /dev/ 目录下,文件名为黄色
    • 设备文件分两种:
      • 块设备文件,也就是支持以块为单位的访问方式的设备文件。应用程序可以随机访问块设备文件的数据,程序也可以自行确定数据的位置
      • 字符设备文件以字节流的方式进行访问,由字符设备驱动程序来实现这种特性,由于字符设备文件是以文件流的方式进行访问的,因此可以顺序读取,但通常不支持随机存取。
  • 管道文件(p,pipe) : 一种特殊类型的文件,用于进程之间的通信。
  • 套接字文件(s,socket):用于进程间的网络通信,也可以用于本机之间的非网络通信。

每种文件类型都有不同的用途和属性,可以通过命令如ls、file等来查看文件的类型信息。

Linux 目录树

Linux 使用一种称为目录树的层次结构来组织文件和目录。

目录树由根目录(/)作为起始点,向下延伸,形成一系列的目录和子目录。每个目录可以包含文件和其他子目录。结构层次鲜明,就像一棵倒立的树。

Linux的目录结构

常见目录说明:

  • /bin: 存放二进制可执行文件(ls、cat、mkdir 等),常用命令一般都在这里:
    • 系统有很多放置执行档的目录,但/bin比较特殊。因为/bin放置的是在单人维护模式下能够被操作的指令,常用命令指的是在/bin底下的指令可以被root与一般帐号所使用
  • /etc: 存放系统管理和配置文件;
    • 系统主要的设定档几乎都放置在这个目录内,例如人员的帐号密码档、各种服务的启始档等等
  • /home: 存放所有用户文件的根目录,是用户主目录的基点,比如用户 user 的主目录就是/home/user,可以用~user 表示;
    • 这是系统预设的使用者家目录,在你新增一个一般使用者帐号时,预设的使用者家目录都会到这里来。
  • /usr: 用于存放系统应用程序;一般情况下我们在这里面进行操作
  • /opt: 额外安装的可选应用程序包所放置的位置。一般情况下,我们可以把 tomcat 等都安装到这里;
  • /proc: 虚拟文件系统目录,是系统内存的映射。可直接访问这个目录来获取系统信息;
  • /root: 超级用户(系统管理员)的主目录;
  • /sbin: 存放二进制可执行文件,只有 root 才能访问。这里存放的是系统管理员使用的系统级别的管理命令和程序。如 ifconfig 等;
  • /dev: 用于存放设备文件;在Linux系统上,任何装置与周边设备都是以档案的型态存在于这个目录当中。
  • /mnt: 系统管理员安装临时文件系统的安装点,系统提供这个目录是让用户临时挂载其他的文件系统;
  • /boot: 存放用于系统引导时使用的各种文件;主要放置开机会使用到的档案
  • /lib 和/lib64: 存放着和系统运行相关的库文件;系统的函式库非常的多,而/lib放置的则是在开机时会用到的函式库
  • /tmp: 用于存放各种临时文件,是公用的临时文件存储点;
    • 这是让一般使用者或者是正在执行的程序暂时放置档案的地方。这个目录是任何人都能够存取的,所以你需要定期的清理一下。
  • /var: 用于存放运行时需要改变数据的文件,也是某些大文件的溢出区,比方说各种服务的日志文件(系统启动日志等。)等;
  • /lost+found: 这个目录平时是空的,系统非正常关机而留下“无家可归”的文件(windows 下叫什么.chk)就在这里
  • /mnt:如果想要暂时挂在某些额外的内容,一般建议可以放置到这个目录中

Linux 挂载磁盘

对于 Linux,如果你为此添加了一个磁盘,差不多需要经历以下步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌─────────────────────────────────────────────────────────────┐
│ 新磁盘使用完整流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 查看磁盘信息 │
│ lsblk 或 fdisk -l │
│ ↓ │
│ 2. 对磁盘分区 │
│ fdisk /dev/sdb 或 cfdisk /dev/sdb │
│ ↓ │
│ 3. 格式化分区 │
│ mkfs.ext4 /dev/sdb1 │
│ ↓ │
│ 4. 创建挂载点 │
│ mkdir /mnt/data │
│ ↓ │
│ 5. 挂载分区 │
│ mount /dev/sdb1 /mnt/data │
│ ↓ │
│ 6. 设置开机自动挂载 │
│ 编辑 /etc/fstab │
│ ↓ │
│ 7. 验证挂载 │
│ df -h 或 mount | grep sdb1 │
│ │
└─────────────────────────────────────────────────────────────┘

Linux 环境变量

在 Linux 系统中,环境变量是用来定义系统运行环境的一些参数,比如每个用户不同的主目录(HOME),比如 Maven 的本地仓库位置。

环境变量分类

按照作用域来分,环境变量可以简单的分成:

  • 用户级别环境变量:~/.bashrc、~/.bash_profile。
  • 系统级别环境变量:/etc/bashrc、/etc/environment、/etc/profile、/etc/profile.d。

上述配置文件执行先后顺序为:/etc/environment –> /etc/profile –> /etc/profile.d –> ~/.bash_profile –> /etc/bashrc –> ~/.bashrc

如果要修改系统级别环境变量文件,需要管理员具备对该文件的写入权限。

建议用户级别环境变量在 ~/.bash_profile中配置,系统级别环境变量在 /etc/profile.d 中配置。

按照生命周期来分,环境变量可以简单的分成:

  • 永久的:需要用户修改相关的配置文件,变量永久生效。
  • 临时的:用户利用 export 命令,在当前终端下声明环境变量,关闭 shell 终端失效。

读取环境变量

通过 export 命令可以输出当前系统定义的所有环境变量

1
2
# 列出当前的环境变量值
export -p
image-20260214123452914

除了 export 命令之外, env 命令也可以列出所有环境变量。

echo 命令可以输出指定环境变量的值。

1
2
3
4
# 输出当前的PATH环境变量的值
echo $JAVA_HOME
# 输出当前的HOME环境变量的值
echo $HOME
image-20260214123537763

指定环境变量

通过 export命令可以修改指定的环境变量。不过,这种方式修改环境变量仅仅对当前 shell 终端生效,关闭 shell 终端就会失效。修改完成之后,立即生效。

1
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib

通过 vim 命令修改环境变量配置文件。这种方式修改环境变量永久有效。

1
vim ~/.bash_profile

如果修改的是系统级别环境变量则对所有用户生效,如果修改的是用户级别环境变量则仅对当前用户生效。

修改完成之后,需要 source 命令让其生效或者关闭 shell 终端重新登录。

1
source /etc/profile

Linux进程与系统管理

Linux进程相关概念

程序(Program):是磁盘上的一个静态文件,只包含代码和数据,不能执行。一般对应的是操作系统中的一个可执行文件,一个程序可以同时启动多个进程。

进程(Process):是程序的一次运行实例,是动态的、有生命周期的实体。运行着的,要占用系统运行资源的程序,系统给每一个进行都分配一个唯一的进程标识符。进程之间默认不能互相访问内存,状态和资源相互隔离,非常安全,但是如果需要交换信息就需要进程间文件传输等内容。

线程(Thread):操作系统调度的最小单元。可在同一个进程中共享资源的一个执行单位。一个进程内可以启动多个线程。Linux 内核中,线程本质也是轻量级进程

进程状态分类

运行:进程占用CPU正在运行。

等待:进程正在等待某一事件或某一资源。

挂起:正在运行的进程,因为某个原因失去CPU而暂停运行。

终止:进程已结束。

休眠:进程主动暂时停止运行。

僵死:进程已停止运行,但是相关控制信息仍保留。

img

对于休眠,

  • 可中断睡眠 (S - Interruptible Sleep): 进程在等某个事件(比如你敲键盘)。它可以被信号(如 Ctrl+C)随时唤醒。
  • 不可中断睡眠 (D - Uninterruptible Sleep): 进程通常在等待 I/O 设备(如磁盘读取)。在这种状态下,进程不响应任何信号,甚至连 kill -9 都杀不死它。如果系统出现大量 D 状态进程,通常意味着硬件或文件系统出故障了。

进程的核心属性

每个进程都有:

  1. PID

    进程 ID,唯一标识。

  2. PPID

    父进程 ID(谁启动了它)。

  3. USER

    是哪个用户运行的。

  4. 状态

    运行、休眠、僵尸、停止等。

  5. 占用资源

    CPU%、MEM%、VSZ(虚拟内存)、RSS(物理内存)。

进程的父子关系

而且 Linux 中所有进程都有父进程,形成进程树。

最顶层的进程是:

  • systemd(PID=1)

这是所有进程的祖先。

PID 1 (systemd/init): 它是所有进程的“始祖”。系统启动时,内核会创建第一个用户态进程。在现代 Linux 中,它通常是 systemd。所有其他进程都是由它或它的子孙进程 fork(派生)出来的。

孤儿进程 (Orphan Process): 如果父进程先死了,而子进程还在运行,子进程就会变成“孤儿”。此时,PID 1 会领养这些孤儿,负责在它们结束时回收它们的资源。

僵尸进程 (Zombie Process): 进程已停止,但其父进程还没有调用 wait() 来读取它的退出状态。此时进程虽死,但在进程表中仍占个坑位(即 task_struct 还没释放)。而且僵尸进程无法被 kill,只能通过处理其父进程来清理。

进程控制块

每个进程都有如下内容作为核心属性

在 Linux 中,PCB 的具体实现是 task_struct 结构体(定义在内核源码中)。它被加载到内存中,记录了进程的一切信息:

它存储了进程的核心属性,包括每个进程在系统中都有一个唯一的进程PID,PPID,所属用户 组 ID 等进程标识,还存有进程状态,调度信息,内存管理信息,上下文信息等(多线程)

而且,在 Linux 内核眼中,进程和线程都由 task_struct 表示。区别在于,同一个进程下的多个线程会共享相同的虚拟内存空间、文件描述符和信号处理函数。

也就是,对于线程,他们的多个 task_struct 共享同一个进程的虚拟内存、文件描述符、信号处理函数,仅保留自己的 PID、上下文、栈等私有信息。

内核调度器并不关心你是进程还是线程,它只认 task_struct,只管调度这些执行流,调度的最小单位是线程,进程是资源分配的最小单位。

进程信号

进程之间是相互隔离的,它们需要特定的机制来进行通信

一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件

信号是多种多样的,并且一个信号对应一个事件,这样才能做到收到并且识别一个信号后,知道到底是一个什么事件,应该如何处理

信号是异步的,发送信号的进程不需要等接收方回应,接收方也不知道信号什么时候来

Linux 有 64 种信号,前 31 种是普通信号,只说一下重要的信号

信号编号 信号名 含义 默认行为
1 SIGHUP 终端断开(比如关闭 SSH) 终止进程
2 SIGINT 中断(Ctrl+C) 终止进程
9 SIGKILL 强制杀死进程(不能被忽略 / 捕获) 终止进程(必执行)
15 SIGTERM 正常终止(kill 命令默认) 终止进程
17 SIGCHLD 子进程结束 / 暂停 忽略
19 SIGSTOP 暂停进程(不能被忽略 / 捕获) 暂停进程

每个进程收到信号后,有 3 种处理方式

  • 默认处理:按系统预设规则(比如 SIGINT 终止进程);
  • 忽略信号:收到后不做任何处理(除了 SIGKILL/SIGSTOP,这两个不能忽略);
  • 自定义处理:注册信号处理函数,收到信号时执行自己的逻辑(比如收到 SIGINT 时先保存数据再退出)。

系统管理

所有 Linux 管理,本质就是管这三样:

  1. 进程:是否运行、是否卡死、占用多少资源
  2. 资源:CPU、内存、磁盘、网络
  3. 服务:开机自启、重启、状态

而且对于服务,在 Linux 里通常被称为 Daemon,也就是守护线程,它们在后台运行,没有控制终端,通常以 d 结尾

程序 → 进程 到底发生了什么

程序只是硬盘上的二进制文件,本身不占 CPU、不占内存。

当你运行它时,操作系统会做这几件事:

  1. 把程序代码从硬盘读到内存
  2. 然后准备启动一个进程来运行它,先给进程分配PID
  3. 再分配虚拟内存空间
  4. 分配文件描述符、打开标准输入输出
  5. 把进程交给调度器,准备运行

Linux 系统启动

Linux 系统启动过程

linux启动时我们会看到许多启动信息。

但是Linux系统的启动过程并不是大家想象中的那么复杂,它是一个很快的过程,其过程可以分为5个阶段:

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

内核引导

同样,Linux 也需要进行内核引导,当计算机打开电源后,首先是BIOS开机自检,按照BIOS中设置的启动设备(通常是硬盘)来启动。

操作系统接管硬件以后,首先读入 /boot 目录下的内核文件。

image-20260219112502376

然后上面也提到了 init 进程是系统所有进程的起点,是所有进程的老祖宗,没有这个进程,系统中任何进程都不会启动。

然后自检,内核引导都完成之后,就会启动 init 程序

image-20260219112605238

运行级别

许多程序需要开机启动。它们在 Windows 叫做”服务”(service),在Linux就叫做”守护进程”(daemon)。

init进程的一大任务,就是去运行这些开机需要自己启动的程序。

但是,不同的场合需要启动不同的程序,比如用作服务器时,需要启动 Apache,用作桌面就不需要。

Linux 允许为不同的场合,分配不同的开机启动程序,这就叫做”运行级别”(runlevel)。也就是说,启动时根据”运行级别”,确定要运行哪些程序。

image-20260219112652062

Linux系统有7个运行级别(runlevel):

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

系统初始化

传统SysV init系统初始化涉及到的一个 init 的配置文件如下,

1
2
3
4
5
6
7
8
9
# 典型inittab配置内容
si::sysinit:/etc/rc.d/rc.sysinit # 系统初始化脚本
l0:0:wait:/etc/rc.d/rc 0 # 运行级别0(关机)
l1:1:wait:/etc/rc.d/rc 1 # 运行级别1(单用户)
l2:2:wait:/etc/rc.d/rc 2 # 运行级别2(多用户,无网络)
l3:3:wait:/etc/rc.d/rc 3 # 运行级别3(多用户,有网络)
l4:4:wait:/etc/rc.d/rc 4 # 运行级别4(保留)
l5:5:wait:/etc/rc.d/rc 5 # 运行级别5(图形界面)
l6:6:wait:/etc/rc.d/rc 6 # 运行级别6(重启)

可以看到,在上面展示的init的配置文件中有这么一行:si::sysinit:/etc/rc.d/rc.sysinit 

它调用执行了/etc/rc.d/rc.sysinit,而rc.sysinit是一个bash shell的脚本,sysinit是系统初始化的相关动作含义,它主要是完成一些系统初始化的工作,rc.sysinit是每一个运行级别都要首先运行的重要脚本。

它主要完成的工作有:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。

image-20260219122153821

是它们负责了系统的初始化

终端建立

rc执行完毕后,返回init。这时基本系统环境已经设置好了,各种守护进程也已经启动了。

init接下来会打开6个终端,以便用户登录系统。在inittab中的以下6行就是定义了6个终端:

1
2
3
4
5
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程序来验证用户的身份。

用户登录

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

  1. 命令行登录
  2. ssh登录
  3. 图形界面
image-20260219122325543

对于运行级别为5的图形方式用户来说,他们的登录是通过一个图形化的登录界面。登录成功后可以直接进入 KDE、Gnome 等窗口管理器。

而本文主要讲的还是文本方式登录的情况:当我们看到 mingetty 的登录界面时,我们就可以输入用户名和密码来登录系统了。

Linux 的账号验证程序是 login,login 会接收 mingetty 传来的用户名作为用户名参数。

然后 login 会对用户名进行分析:如果用户名不是 root,且存在 /etc/nologin 文件,login 将输出 nologin 文件的内容,然后退出。

/etc/nologin 是系统维护时的”禁止登录标志文件”,存在时候,非 root 无法登录,不存在时,所有用户都可以正常登录

这通常用来系统维护时防止非root用户登录。只有/etc/securetty中登记了的终端才允许 root 用户登录,如果不存在这个文件,则 root 用户可以在任何终端上登录。

/etc/usertty文件用于对用户作出附加访问限制,如果不存在这个文件,则没有其他限制。

Linux用户、权限与安全

用户

首先,每个用户都有唯一的ID(UID),系统通过UID判断用户的权限,而非用户名。所以说,即使你改了用户名,只要 UID 不变,系统依然认为你是同一个人。

同时,用户会归属于不同的“组”,通过组可以批量管理权限

而且 Linux 的用户分为三类:

  • root:系统管理员,拥有最高权限,相当于Windows的Administrator。而且,登录root后,命令行提示符为#(普通用户为$)
  • 普通用户:供人日常使用的账号,权限受限,默认只能操作自己的/home/用户名下的文件,无法修改系统配置,一般情况下,需由root或有sudo权限的用户创建
  • 系统用户:供系统服务或程序使用的用户,不能登陆系统,一般是用于维持服务运行,通常情况下是系统或软件安装时自动或者你自己创建的。

组

组是用户的集合,目的是批量管理权限。每个用户至少属于1个组,而且可属于多个组。

主组: 用户创建时默认所属的组(通常与用户名同名)。用户创建的任何文件,默认所属组就是这个主组。

附加组: 用户可以加入多个其他的组。

  • 就假如你是一个程序员(主组:dev),但你有时需要重启 Web 服务,于是管理员把你加入了 webadmin 附加组。这样你就获得了操作 Web 服务的权限,而不需要切换账号。

户口本

这是个人戏称,一般是指用户与组的核心配置文件

用户和组实实在在记录在几个关键文件中

  • /etc/passwd(用户基本信息库): 每一行代表一个用户,格式为 用户名:密码占位符:UID:GID:备注:家目录:登录Shell。
  • /etc/shadow(影子文件): 专门存储加密后的密码。只有 root 权限能读,这是为了防止普通用户通过暴力破解获取系统密码。
  • /etc/group(组信息库): 记录组名、组 ID(GID)以及该组包含哪些成员。

Linux 权限结构

Linux 中每个文件 / 目录的权限都分为 3 类对象:

  • 所有者(user, u):文件的创建者 / 所属用户;
  • 所属组(group, g):文件所属的用户组;
  • 其他用户(other, o):既不是所有者也不在所属组的所有用户;
  • 所有用户(all, a):包含 u+g+o。

每类对象都有 3 种权限:

  • 读(r):数字表示 4,文件可查看内容,目录可 ls 列出内容;
  • 写(w):数字表示 2,文件可修改内容,目录可增删文件;
  • 执行(x):数字表示 1,文件可运行(如脚本 / 程序),目录可 cd 进入;
  • 无权限(-):数字表示 0。

查看权限的命令是ls -l,可以使用 chmod 指令来改变文件的权限。

Linux文件权限

以普通文件为例,使用 ls -l 命令,可以看到结果的第一列是一大堆的形式image-20260216004553891

这是什么意思呢?我们来分析一下

  • 首先,第一部分就是权限与文件类型

    1
    -rw-r--r--

    一般情况下我们分为 4 组来看

    • 首先第一位为文件类型,至于具体的字母看上面文件类型部分,这里是 -,说明这是一个普通文件。

    • 第 2-4 位是所有者权限user, u,rw- 对应:

      • r:读权限(4)
      • w:写权限(2)
      • -:无执行权限(0)

      合计:4 + 2 = 6,即所有者拥有读、写权限,无执行权限。这个数字很有用,我们chmod的时候很多用这个数字来代替

    • 第 5-7 位是所属组权限group, g,r-- 对应:

      • r:读权限(4)
      • -:无写权限(0)
      • -:无执行权限(0)

      合计:4 + 0 + 0 = 4,即所属组只有读权限。

    • 第 8-10 位是其他用户权限other, o,r-- 对应:

      • r:读权限(4)
      • -:无写权限(0)
      • -:无执行权限(0)

      合计:4 + 0 + 0 = 4,即其他用户只有读权限。

  • 然后,对于那个 1,就是硬链接数,这个数字表示该文件在文件系统中的硬链接数量。

    • 对于普通文件,通常是 1,表示只有一个文件名指向它。
    • 对于目录,这个数字通常是 2 + 子目录数,因为目录本身和它的 . 条目是两个链接,每个子目录又会通过 .. 增加一个链接。
  • 然后是所有者和所属组,都是root,然后是文件大小和最后修改时间,最后是文件名

而且一般来说,你新建一个文件,它的权限默认是 644 而不是 777?

umask(权限掩码) 在起作用,它是个过滤器,会在在创建文件时“扣除”掉一部分权限,以确保初始状态是安全的。

sudo

我们极少直接登录 root

嗯,真的是这样

sudo 是 SuperUser DO,这是一个允许普通用户以特定身份,一般是 root,执行命令的工具。

这种方式比直接给 root 密码更安全,因为所有 sudo 操作都会被记录在日志中,且权限可以随时收回。

特殊的权限位

除了 r/w/x 之外,Linux 还有三个特殊的权限位

SUID (Set User ID)

  • 表现: 权限位显示为 rws------。
  • 作用: 当普通用户执行该文件时,会临时获得该文件所有者的权限。
  • 这个比较抽象,了解一下就行,就好像普通用户没权限改 /etc/shadow,但通过 passwd 命令(带有 SUID),他们就能以 root 身份修改自己的密码。

SGID (Set Group ID)也是一样

  • 作用: 在目录下设置 SGID 后,任何人在该目录下创建的新文件,其所属组会自动继承该目录的所属组,而不是创建者的主组。这在团队协作开发中极其好用。

Sticky Bit (粘滞位)

  • 表现: 权限位最后一位为 t(如 rwxrwxrwt)。
  • 作用: 防止用户删除别人的文件。
  • 典型例子: /tmp 目录。大家都能在里面写文件,但你不能删掉我创建的文件。

Vim

Vim是从 vi 发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。

vim优势在于,所有Unix Like系统都会内置vi文本编辑器,其他的文本编辑器则不一定会存在,而所以,大部分Linux系统的发行版都自带Vim

基本上 vi/vim 共分为三种模式,分别是命令模式(Command mode),输入模式(Insert mode)和底线命令模式(Last line mode)。 这三种模式的作用分别是:

  • 命令模式:用户刚刚启动 vi/vim,便进入了命令模式。此状态下敲击键盘动作会被 Vim 识别为命令,而非输入字符,比如我们此时按下 i,并不会输入一个字符,i 被当作了一个命令。

    • 以下是普通模式常用的几个命令:
      • i – 切换到输入模式,在光标当前位置开始输入文本。
      • x – 删除当前光标所在处的字符。
      • : – 切换到底线命令模式,以在最底一行输入命令。
      • a– 进入插入模式,在光标下一个位置开始输入文本。
      • u – 撤销上一次操作。
      • Ctrl + r – 重做上一次撤销的操作。
      • :w – 保存文件。
      • :q – 退出 Vim 编辑器。
      • :q! – 强制退出Vim 编辑器,不保存修改。

    命令模式只有一些最基本的命令,因此仍要依靠底线命令行模式输入更多命令。

  • 输入模式:在命令模式下按下 i 就进入了输入模式,使用 Esc 键可以返回到普通模式。

    • 在输入模式中,可以使用以下按键:
      • 字符按键以及Shift组合,输入字符
      • ENTER,回车键,换行
      • BACK SPACE,退格键,删除光标前一个字符
      • DEL,删除键,删除光标后一个字符
      • 方向键,在文本中移动光标
      • HOME/END,移动光标到行首/行尾
      • Page Up/Page Down,上/下翻页
      • Insert,切换光标为输入/替换模式,光标将变成竖线/下划线
      • ESC,退出输入模式,切换到命令模式
  • 底线命令模式:在命令模式下按下:(英文冒号)就进入了底线命令模式。底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。基本的命令有

    • :w:保存文件。
    • :q:退出 Vim 编辑器。
    • :wq:保存文件并退出 Vim 编辑器。
    • :q!:强制退出Vim编辑器,不保存修改

附上 vim 键盘图

image-20260219164653422