Skip to content

Latest commit

 

History

History
375 lines (243 loc) · 23.7 KB

File metadata and controls

375 lines (243 loc) · 23.7 KB

二、Unix 操作系统组件

本章将重点介绍 Unix 操作系统以及用户将与之交互的组件:文件和文件系统、进程、用户和权限,等等。它还将解释一些基本的过程通信以及系统程序错误处理的工作原理。在创建系统应用程序时,我们将与操作系统的所有这些部分进行交互。

本章将介绍以下主题:

  • 内存管理
  • 文件和文件系统
  • 过程
  • 用户、组和权限
  • 过程通信

技术要求

与前一章相同,本章不需要安装任何软件:任何其他兼容 POSIX 的 shell 就足够了。

例如,您可以选择 Bash(https://www.gnu.org/software/bash/ ),建议使用 Zsh(http://www.zsh.org/ 或鱼(https://fishshell.com/ )。

内存管理

操作系统处理应用程序的主内存和辅助内存使用。它跟踪使用了多少内存、哪个进程以及哪些部分是空闲的。它还处理来自进程的新内存分配以及进程完成时的内存取消分配。

管理技术

有不同的内存处理技术,包括:

  • 单次分配:除了为操作系统保留的部分外,所有内存都可用于应用程序。这意味着一次只能有一个应用程序在执行,如在微软磁盘操作系统MS-DOS中)。
  • 分区分配:将内存划分为不同的块,称为分区。每个进程使用其中一个块可以一次执行多个进程。分区可以重新定位和压缩,以便为下一个进程获得更多的连续内存空间。
  • 分页内存:内存分为帧,帧大小固定。一个进程的内存被分成大小相同的部分,称为。页面和帧之间有一个映射,使进程将自己的虚拟内存视为连续的。这个过程也称为分页

虚拟内存

Unix 使用分页内存管理技术,将每个应用程序的内存抽象为连续的虚拟内存。它还使用一种称为交换的技术,使用交换文件将虚拟内存扩展到辅助内存(硬盘驱动器或固态驱动器SSD)。

当内存不足时,操作系统会将休眠进程的页面放入交换分区,以便为请求更多内存的活动进程腾出空间,执行一个名为交换出的操作。当执行中的进程需要交换文件中的页面时,会将其加载回主内存以执行它。这在中称为交换。

交换的主要问题是与辅助内存交互时的性能下降,但它对于扩展多任务处理能力和处理比物理内存大的应用程序非常有用,只加载在给定时间实际需要的部分。创建内存高效的应用程序是通过避免或减少交换来提高性能的一种方法。

top命令显示每个进程的可用内存、交换和内存消耗的详细信息:

  • RES是进程使用的物理主内存。
  • VIRT是进程使用的总内存,包括交换的内存,因此等于或大于RES
  • SHRVIRT中实际可共享的部分,例如加载的库。

了解文件和文件系统

文件系统是用于在磁盘中构造数据的方法,而文件是用于指示一段自包含信息的抽象。如果文件系统是分层的,这意味着文件被组织在目录树中,目录树是用于排列存储文件的特殊文件。

操作系统和文件系统

在过去的 50 年中,已经发明和使用了大量的文件系统,每种文件系统在空间管理、文件名和目录、元数据和访问限制方面都有自己的特点。每个现代操作系统主要使用单一类型的文件系统。

Linux

Linux 选择的文件系统FS扩展文件系统EXT系列),但也支持其他的,包括 XFS、日志文件系统JFS)和B-tree 文件系统Btrfs). 它还兼容较旧的文件分配****表FAT)系列(FAT16 和 FAT32)和新技术文件系统NTFS)。最常用的文件系统仍然是 EXT(EXT4)的最新版本,该版本于 2006 年发布,并扩展了其前身的容量,包括对更大磁盘的支持。

马科斯

macOS 使用苹果文件系统APFS),支持 Unix 权限,并有日志记录。它还具有丰富的元数据和保留大小写的特性,同时是一个不区分大小写的文件系统。它支持其他文件系统,包括 HFS+和 FAT32,支持 NTFS 进行只读操作。要写入这样的文件系统,我们可以使用实验性功能或第三方应用程序。

窗户

Windows 使用的主要文件系统是 NTFS。除了不区分大小写之外,Windows FS 区别于其他的签名功能是使用字母后跟冒号来表示路径中的分区,并使用反斜杠作为文件夹分隔符,而不是正斜杠。驱动器号以及主分区使用的 C 来自 MS-DOS,其中 A 和 B 是软盘驱动器的保留驱动器号。

Windows 本机还支持其他文件系统,例如 FAT,这是一个在 70 年代末和 90 年代末非常流行的文件系统系列,以及扩展文件分配表exFAT),这是微软在 FAT 基础上为可移动设备开发的一种格式。

文件和硬链接和软链接

大多数文件都是常规文件,包含一定数量的数据。例如,文本文件包含由特定编码表示的人类可读字符序列,而位图包含关于每个像素使用的大小和位的一些元数据,后跟每个像素的内容。

文件被安排在目录中,这样就可以使用不同的名称空间来重用文件名。它们以名称、人类可读的标识符引用,并以树结构组织。路径是表示目录的唯一标识符,它由目录的所有父目录的名称组成,并由分隔符(Unix 中为/,Windows 中为\)连接,从根目录到所需的叶。例如,如果一个名为a的目录位于另一个名为b的目录下,该目录位于一个名为c的目录下,它将有一个从根目录开始并连接所有目录的路径,直到文件:/c/b/a

当多个文件指向同一内容时,我们有一个硬链接,但这在所有文件系统(例如 NTFS 和 FAT)中都是不允许的。软链接是指向其他软链接或硬链接的文件。硬链接可以在不破坏原始链接的情况下删除,但软链接不是这样。符号链接是一个常规文件,其自身的数据是另一个文件的路径。它还可以链接其他文件系统或不存在的文件和目录(这将是一个断开的链接)。

在 Unix 中,一些实际上不是文件的资源被表示为文件,与这些资源的通信是通过写入或读取相应的文件来实现的。例如,/dev/sda文件代表整个磁盘,/dev/stdoutdev/stdin/dev/stderr是标准的输出、输入和错误。Everything is a file的主要优点是,可以用于文件的工具也可以与其他设备(网络和管道)或实体(进程)交互。

Unix 文件系统

本节中包含的原则特定于 Linux 使用的文件系统,如 EXT4。

根和 inodes

在 Linux 和 macOS 中,每个文件和目录都由一个索引节点表示,它是一种特殊的数据结构,存储除文件名和实际数据之外的所有文件信息。

Inode0用于空值,表示没有 Inode。Inode1用于记录磁盘上的任何坏块。文件系统层次结构的根使用 inode2。它由/表示。

从最新的 Linux 内核源代码中,我们可以看到如何保留第一个 inode。具体情况如下:

#define EXT4_BAD_INO 1 /* Bad blocks inode */
#define EXT4_ROOT_INO 2 /* Root inode */
#define EXT4_USR_QUOTA_INO 3 /* User quota inode */
#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */
#define EXT4_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */
#define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */
#define EXT4_JOURNAL_INO 8 /* Journal inode */

此链接是前面代码块的源:https://elixir.bootlin.com/linux/latest/source/fs/ext4/ext4.h#L212

目录结构

在 Unix 文件系统中,根目录下还有一系列其他目录,每个目录都用于特定目的,因此可以在不同的操作系统之间保持一定的互操作性,并使编译后的软件能够在不同的操作系统上运行,从而使二进制文件具有可移植性。

这是目录及其范围的综合列表:

| 目录 | 说明 | | /bin | 所有用户的可执行文件 | | /boot | 用于引导系统的文件 | | /dev | 设备驱动程序 | | /etc | 应用程序和系统的配置文件 | | /home | 用户的主目录 | | /kernel | 内核文件 | | /lib | 共享库文件和其他与内核相关的文件 | | /mnt | 临时文件系统,从软盘和 CD 到闪存驱动器 | | /proc | 带有活动进程的进程编号的文件 | | /sbin | 管理员可执行文件 | | /tmp | 可以安全删除的临时文件 | | /usr | 管理命令、共享文件、库文件和其他 | | /var | 可变长度文件(日志和打印文件) |

导航和交互

当使用 shell 时,当路径是相对的(例如,file.shdir/subdir/file.txt时,其中一个目录将是工作目录。工作目录用作前缀以获取绝对目录。这通常显示在命令行的提示中,但可以使用pwd命令(打印工作目录)打印。

cd(更改目录)命令可用于更改当前工作目录。要创建一个新目录,需要使用mkdir(生成目录)命令。

为了显示目录的文件列表,有一个ls命令,它接受一系列选项,包括更多信息(-l),显示隐藏的文件和目录(-a),以及按时间(-t)和大小(-S排序。

还有一系列其他命令可用于与文件交互:touch命令使用给定名称创建一个新的空文件,要编辑其内容,您可以使用一系列编辑器,包括 vi 和 nano,catmoreless是一些可以读取文件的命令。

安装和拆卸

操作系统将硬盘分割为称为分区的逻辑单元,每个逻辑单元可以是不同的文件系统。当操作系统启动时,它使用/etc/fstab文件的每一行的mount命令使一些分区可用,大致如下所示:

# device # mount-point # fstype # options # dumpfreq # passno
/dev/sda1     /           ext4    defaults     0            1

此配置使用ext4文件系统和默认选项将/dev/sda1装载到*/*disk,无需备份(0)和根完整性检查(1)。mount命令可随时用于公开文件系统中的分区。从主文件系统中删除这些分区需要它的对应项umount。用于该操作的空目录称为装入点,它表示文件系统连接的根目录。

过程

当应用程序启动时,它将成为一个进程:一个由操作系统提供的特殊实例,其中包含运行中的应用程序使用的所有资源。此程序必须采用可执行和可链接格式ELF),以便操作系统解释其指令。

工艺特性

每个流程都是一个五位标识符流程 IDPID),代表流程的整个生命周期。这意味着不能有两个进程同时具有相同的 PID。它们的唯一性使得通过了解特定进程的 PID 来访问该进程成为可能。一旦一个进程终止,其 PID 可以在必要时重新用于另一个进程。

与 PID 类似,还有其他一些特性可以描述一个过程。详情如下:

  • P****PID:启动此流程的流程的父流程 ID
  • 好数字:该流程对其他流程的友好程度
  • 端子或 TTY:流程所连接的端子
  • RUID/EUID:真实有效的用户 ID,属于流程所有者
  • RGID/EGID:真实/有效的组所有者,流程的组所有者

要查看活动进程的列表,可以使用ps(进程状态)命令,该命令显示活动用户当前正在运行的进程列表:

> ps -f
UID    PID  PPID  C  STIME  TTY    TIME      CMD
user   8    4     0  Nov03  pts/0  00:00:00  bash -l -i
user   43   8     0  08:53  pts/0  00:00:00  ps -f

过程生命周期

新流程的创建可以通过两种不同的方式进行:

  • 使用fork:重复调用过程。子进程(新进程)是父进程(调用进程)的精确副本(内存),以下情况除外:
    • PID 是不同的。
    • 子对象的 PPID 等于父对象的 PID。
    • 子项不会从父项继承以下内容:
      • 内存锁
      • 信号量调整
      • 出色的异步 I/O 操作
      • 异步 I/O 上下文
  • 使用一个exec:这将用一个新的进程映像替换当前进程映像,将程序加载到当前进程空间并从其入口点运行。

前景和背景

当一个进程启动时,它通常处于前台,这将阻止与外壳的通信,直到作业完成或中断。在命令(cat file.txt &结尾处使用&符号启动进程将在后台中启动进程,从而可以继续使用 shell。SIGTSTP信号可通过C**trl+Z发送,允许用户从外壳暂停前台进程。可以使用fg命令恢复,也可以在后台使用bg命令恢复。

jobs命令报告正在运行的作业及其编号。在输出中,方括号中的数字是过程控制命令使用的作业编号,如fgbg

**# 杀掉一份工作

前台进程可以使用C**trl+ZSIGINT信号终止。为了终止后台进程,或者向进程发送任何信号,可以使用kill命令。

kill命令接收的参数可以是以下任一参数:

  • 发送到进程的信号
  • PID 或作业编号(带%前缀)

使用的更显著的信号如下所示:

  • SIGINT:表示用户输入导致的终止,可以通过kill命令发送-2
  • SIGTERM:表示非用户生成的通用终止请求,以及具有-6值的kill命令的默认信号
  • SIGKILL:由操作系统直接处理的终止,它立即终止进程并具有-9

用户、组和权限

用户和组以及权限是 Unix 操作系统中用于控制资源访问的主要实体。

用户和组

文件和其他资源的授权由用户和组提供。用户具有唯一的用户名,这些用户名是人类友好的标识符,但从操作系统的角度来看,每个用户都由一个唯一的正整数表示:用户 IDUID)。组是另一种授权机制,作为用户,它们有一个名称和一个组 IDGID)。在操作系统中,每个进程都与一个用户关联,每个文件和目录都属于一个用户和一个组。

/etc/passwd文件包含所有这些信息以及更多信息:

# username : encrypted password : UID : GID : full name : home directory : login shell
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
user:x:1000:1000:"User Name":/home/user:/bin/bash

用户不直接使用 UID;他们使用用户名和密码的组合来启动第一个进程,即交互式 shell。第一个 shell 的子对象从中继承 UID,因此它们仍然属于同一个用户。

UID0是为名为 root 的用户保留的,该用户具有特权,可以在系统上执行几乎任何操作,例如读取/写入/执行任何文件、终止任何进程以及更改正在运行的进程 UID。

组是用户的逻辑集合,用于在用户之间共享文件和目录。每个组独立于其他组,它们之间没有特定的关系。对于当前用户所属的组的列表,有groups命令。要更改文件的组所有权,有chgrp

所有者、组和其他人

Unix 文件属于用户和组。这将创建三个级别的授权层次结构:

  • 所有者:与文件关联的 UID
  • :属于与文件关联的 GID 的 UID
  • 其他人其他人

可以为这些组中的每个组指定不同的权限,并且这些权限通常会随着所有者的不同而减少。一个文件的所有者对它的权限比它自己的组或该组之外的用户少,这是没有意义的。

读、写和执行

用户和组用作访问文件的前两层保护。拥有文件的用户具有一组不同于文件组的权限。不是所有者且不属于该组的人具有不同的权限。这三组权限称为所有者其他

对于集合的每个元素,可以执行三个操作:读取、写入和执行。这对于文件来说非常简单,但对于目录来说却有不同的含义。Read 可以列出内容,write 用于在其中添加新链接,execute 用于导航。

三个权限由八进制值表示,其中第一位为读取权限,第二位为写入权限,第三位为执行权限。它们也可以依次用字母rwx表示,可能的值如下:

  • 0 or ---:无权限
  • 1 or --x:执行权限(执行文件或导航到目录)
  • 2 or -w-:写入权限(在目录中写入文件或添加新文件)
  • 3 or -wx:写入并执行
  • 4 or r--:读取权限(读取文件或列出目录内容)
  • 5 or r-x:读取并执行
  • 6 or rw-:读写
  • 7 or rwx:读、写、执行

三个八进制值的顺序表示用户、组和其他用户的权限:

  • 777:人人都能读、写、执行。
  • 700:所有者可以读、写、执行。
  • 664:所有者和组可以读写。
  • 640:所有者可以读写,组可以读。
  • 755:所有者可以读、写、执行,组和其他人可以读、执行。

带有-l标志(或其别名ll)的ls命令显示当前目录的文件列表和文件夹及其权限。

更改权限

chmod命令可以更改文件或目录的权限。这可用于覆盖当前权限或修改它们:

  • 为了替换权限,必须发出chmod xxx file命令。*xxx*可以是表示各层权限的三个八进制值,也可以是指定权限的字符串,例如u=rwxg=rxo=r
  • 要添加或删除一个或多个权限,可以使用chmod +x filechmod -x file

有关更多信息,请使用带有帮助标志(chmod --helpchmod命令。

过程通信

操作系统负责进程之间的通信,并具有不同的机制来交换信息。这些过程是单向的,例如导出代码、信号和管道,或者是双向的,例如套接字。

导出代码

应用程序通过返回一个名为退出状态的值,将其结果传达给操作系统。这是进程结束时传递给父进程的整数值。常见退出代码列表可在/usr/include/sysexits.h文件中找到,如下所示:

#define EX_OK 0 /* successful termination */
#define EX__BASE 64 /* base value for error messages */
#define EX_USAGE 64 /* command line usage error */
#define EX_DATAERR 65 /* data format error */
#define EX_NOINPUT 66 /* cannot open input */
#define EX_NOUSER 67 /* addressee unknown */
#define EX_NOHOST 68 /* host name unknown */
#define EX_UNAVAILABLE 69 /* service unavailable */
#define EX_SOFTWARE 70 /* internal software error */
#define EX_OSERR 71 /* system error (e.g., can't fork) */
#define EX_OSFILE 72 /* critical OS file missing */
#define EX_CANTCREAT 73 /* can't create (user) output file */
#define EX_IOERR 74 /* input/output error */
#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
#define EX_PROTOCOL 76 /* remote error in protocol */
#define EX_NOPERM 77 /* permission denied */
#define EX_CONFIG 78 /* configuration error */
#define EX__MAX 78 /* maximum listed value */

其来源如下:https://elixir.bootlin.com/linux/latest/source/fs/ext4/ext4.h#L212

最后一个命令的退出代码存储在$?变量中,可以对其进行测试,以控制操作流程。常用的运算符是&&(双与号),它仅在第一条指令的退出代码为0时执行下一条指令,例如stat file && echo something >> file,它仅在文件存在时才将某个内容追加到文件中。

信号

退出代码连接进程及其父进程,但通过信号可以将任何进程与另一进程(包括其自身)连接起来。它们也是异步的和单向的,但它们表示来自进程外部的通信。

最常见的信号是SIGINT,它告诉应用程序终止,并且可以通过Ctrl+C组合键发送到 shell 中的前台进程。但是,还有更多选项,如下表所示:

| 名称 | 编号 | 说明 | | SIGHUP | 1. | 控制终端关闭 | | SIGINT | 2. | 中断信号(Ctrl+C) | | SIGQUIT | 3. | 退出信号(Ctrl+D) | | SIGFPE | 8. | 试图进行非法的数学运算 | | SIGKILL | 9 | 立即退出应用程序 | | SIGALRM | 14 | 闹钟信号 |

kill命令允许您向任何应用程序发送信号,可用信号的综合列表可通过-l标志显示:

管道是进程之间最后一种单向通信方法。顾名思义,管道连接两端——一个进程输入和另一个进程输出——使同一主机上的进程能够通信以交换数据。

这些分类为匿名或命名:

  • 匿名管道将一个流程标准输出链接到另一个流程标准输入。通过|操作符,可以很容易地在外壳内完成,将管道前的命令输出链接为管道后的命令输入。ls -l | grep "user"获取ls命令的输出,并将其用作grep的输入。
  • 命名管道使用特定文件来执行重定向。可以使用>(较大)运算符将输出重定向到文件,而<(较小)符号允许您将文件用作另一个进程的输入。ls -l > file.txt将命令的输出保存到文件中。cat < file.txt将文件内容发送到命令的标准输入,标准输入将其复制到标准输出。

还可以使用>>(双更大)操作符将内容附加到命名管道,该操作符将从文件末尾开始写入。

插座

Unix 域套接字是同一台计算机上的应用程序之间的双向通信方法。它们是由内核处理并管理数据交换的逻辑端点。

套接字的性质允许将其用作面向流或面向数据报的套接字。面向流的协议确保在移动到下一个数据块之前传递消息,以保持消息的完整性。相反,面向消息的协议会忽略未接收到的数据,并不断发送以下消息,从而使其成为一种速度更快但可靠性较低的协议,延迟非常低。

插座分类如下:

  • SOCK_STREAM:面向连接、有序、可靠的数据流传输
  • SOCK_SEQPACKET:面向连接、有序、可靠地传输具有记录边界的消息数据
  • SOCK_DGRAM:信息传输无序、不可靠

总结

本章概述了主要 Unix 组件以及它们之间的交互方式。我们从内存管理和它在 Unix 中的工作原理开始,理解了分页交换等概念。

然后,我们分析了文件系统,查看了现代操作系统的支持,并解释了现有文件类型之间的区别:文件、目录以及硬链接和软链接。

在学习了 inode 的概念之后,我们了解了 Unix 操作系统中目录的结构,并解释了如何导航和与文件系统交互,以及如何装载和卸载其他分区。

我们继续讨论在 Unix 中运行应用程序的进程及其结构和属性。我们分析了流程生命周期,从它的创建到forkexec,到它的结束或用kill命令终止。

另一个重要主题是用户、组和权限。我们了解了什么是用户,什么是组,如何加入它们,以及如何使用这些概念将权限划分为三个组:用户、组和其他组。这有助于更好地理解 Unix 权限模型,以及如何更改文件和目录的权限。

最后,我们了解了进程之间的通信是如何使用单向通道(如信号和导出代码)或双向通信(如套接字)工作的。

在下一章中,我们将快速概述 Go 语言。

问题

  1. 现代操作系统使用哪个文件系统?
  2. 什么是 inode?Unix 中的 inode0是什么?
  3. PID 和 PPID 之间有什么区别?
  4. 如何终止在后台运行的进程?
  5. 用户和组之间的区别是什么?
  6. Unix 权限模型的范围是什么?
  7. 你能解释一下信号和导出代码的区别吗?
  8. 什么是交换文件?**