操作系统原理

存储设备

磁盘分区

MBR最多有四个主分区,使用扩展分区和逻辑分区最多可以划分15个分区,单个分区最大2TB。

GPT是UEFI标准的一部分,最多提供128个分区,使用GUID来识别每个磁盘和分区。与MBR存在单一故障 点不同,GPT 提供分区表信息的冗余。主GPT位于磁盘头部,而备份副本(次要GPT)位于磁盘尾部。GPT 使用校验和来检测GPT头和分区表中的错误与损坏。

磁盘阵列

RAID 0:breaking up data into chunks and striping it across the available disks. This means that each disk contains a portion of the data and that multiple disks will be referenced when retrieving information.

RAID 1:mirroring data across all available disks.

RAID 5:N strips + 1 parity

RAID 6:N strips + 2 parity

RAID10:RAID 0 + RAID 1

文件系统

文件系统类型

vfat
ext4
btrfs
xfs

无法调整已有分区。

tmpfs

https://www.kernel.org/doc/html/latest/filesystems/tmpfs.html

devtmpfs

Linux目录树

目录简介用途
/home用户主目录用户个人文件、用户配置文件
/root超级管理员目录保留
/usr系统资源Unix System Resources
/usr/binbinary可执行程序(/bin
/usr/sbinsystem binary系统配置、维护和管理相关可执行程序
/sbin
/usr/lib[xx]library库文件(/lib[xx]),
/bin/sbin中的可执行程序共享的库文件
/usr/local==自编译或第三方程序==
/usr/src内核源代码、头文件和文档
/usr/include用于编译用户空间源代码的头文件
/usr/share与系统架构无关的共享文件
/optoptional==第三方系统软件==
/etc系统范围配置文件
/media自动挂载的外部可移动设备(取代/cdrom
/mntmount手动挂载外部设备的位置
/devdevice通过其中的文件访问设备
/dev/sda硬盘
/dev/sda1硬盘分区
/dev/lp0打印机
/dev/mem内存
/dev/tty终端
/bootboot loadervmlinuz, grub
/sys与内核交互的虚拟文件系统;系统与组件信息
/procprocess虚拟文件系统:系统进程和资源信息
/run系统启动早期守护进程的运行数据
/srvservice服务数据
/varvariables系统运行时可变数据,例如系统日志、缓存、临时文件
/var/log系统和应用日志
/tmptemporary临时文件(重启系统时清空)
/snapsnap packages*

*:snap packages do not depend on any other packages, dependencies, or libraries. They are bundled with everything they need for proper functioning, and completely isolated from the rest of the system.

Linux File System 101. Linux file system follows a tree-like… | by Saeed Mohajeryami | The Startup | Medium

单独挂载分区到某些目录:linux - Why put things other than /home to a separate partition? - Unix & Linux Stack Exchange

权限

文件可以设置三种权限:

  1. 读(r)权限:可读取文件的内容;对于目录则是列举其中的内容;

  2. 写(w)权限:可修改文件的内容;对于目录则是创建或删除其中的文件;

  3. 执行(x)权限:对普通文件,文件内容可被装入内存直接运行或逐条解释执行;对于目录,则是可以读其中文件。

文件的用户分为三组:owner(所有者)、group(文件所在的用户组中的其他用户)、其他用户。文件在创建时被给定所有者(通常是当前用户)和用户组(文件所在的目录的用户组);每组中都包含了rwx三种权限声明。不同的文件系统还可能具有其他一些权限,例如ACL,可压缩,可修改等。

权限描述

新创建的文件及目录的缺省权限umask分别为:

  • 文件:-rw-r--r-- 644

  • 目录:drwxr-xr-x 755

操作系统的启动

操作系统的启动分为两个阶段:

  • 引导阶段开始于打开电源开关,结束于内核初始化完成和 systemd进程成功运行。
  • 启动阶段接管剩余工作,直到操作系统进入可操作状态。

引导(boot)

  1. BIOS上电自检:检验电脑硬件基本功能是否正常。

    自检成功后,BIOS产生一个中断INT 13H,指向某个接入的可引导设备的引导扇区。它所找到的包含有效的引导记录(主引导记录,MBR)的第一个引导扇区将被装载到内存中,并且控制权也将从BIOS转移到引导扇区代码

    引导扇区是引导加载器的第一阶段。大多数 Linux 发行版本使用的引导加载器有三种:GRUB、GRUB2 和 LILO。GRUB2 是最新的,也是相对于其他老的同类程序使用最广泛的。

  2. GRUB2(GRand Unified BootLoader,Version 2)

    寻找操作系统内核并加载其到内存的程序。

    • 阶段1:执行引导代码(引导镜像,boot.img)。

      引导代码必须非常小,因为它必须连同分区表放到硬盘的第一个 512 字节的扇区中,其中不包含设备的分区信息。由于引导记录必须非常的小,不能理解文件系统结构,因此阶段 1 的唯一功能就是定位并加载阶段1.5的代码,且该段代码必须位于引导记录与设备第一个分区之间的位置

    • 阶段1.5:执行文件系统驱动程序

      在第一个分区的开始位置(扇区63)和MBR(扇区 0)之间有 62 个 512 字节的扇区(共 31744 字节),用于存储阶段1.5的代码镜像core.img文件。

      该空间足够容纳一些通用的文件系统驱动程序,如EXT、FAT、NTFS等。因此GRUB2的阶段2能够放在EXT文件系统内(/boot/grub,但不能放在逻辑卷内)。

    • 阶段2:定位和加载 Linux 内核到内存,并转移控制权到内核

      内核的相关文件位于 /boot目录下(其文件名均带有前缀 vmlinuz),以一种自解压的压缩格式存储以节省空间。

      ls -la vmlinuz*
      

    GRUB2 通过 /boot/grub/grub.cfg 进行配置。该配置文件通常是利用工具自动生成的( 例如在Ubuntu中,grub-update基于/etc/default/grub生成配置文件)。

  3. 内核

    内核加载到内存后,首先必须从压缩格式解压自身。

    内核自解压完成后,则加载 systemd进程,并转移控制权到 systemd,结束引导过程。

启动(startup)

引导过程结束后,Linux 内核和 systemd 处于运行状态,但是由于没有其他任何程序在执行,故其不能执行任何有关用户的功能性任务。

systemd是所有进程的父进程(代替旧式init程序),其功能包括:

  1. 挂载文件系统

    systemd挂载在/etc/fstab 中配置的文件系统,包括内存交换文件或分区。

  2. 决定系统启动的目标态

    目标态(target)对应旧式的运行级别(runlevel),包括graphical.target(桌面系统,对应runlevel 5)、multi-user.target(服务器系统,对应runlevel 3)。

    根据配置文件/etc/systemd/system/default.target决定系统应该进入的目标态,default.target是一个链接文件,通常链接到描述文件graphical.targetmulti-user.target

    每个目标态有一个在其配置文件中描述的依赖集systemd需要首先启动其所需依赖。systemd也会查看老式的systemV init目录中是否存在相关启动文件,若存在,则systemd根据这些配置文件的内容启动对应的服务。

    图 1:systemd 的启动流程

用户管理

用户

群组

The Primary group – When a user creates a file, the file’s group is set to the user’s primary group.

Secondary or supplementary group - Useful when you want to grant certain file permissions to a set of users who are members of the group.

Each user can belong to exactly one primary group and zero or more secondary groups.

Only root or users with sudo access can add a user to a group.

用户信息存储

群组ID(Group ID,GID

帐号信息存储在/etc/passwd 文件中,每一行代表一个帐号,每行包含7部分内容:

用户名:密码(x):UID:GID:用户信息说明:家目录:shell

UID为用户编号(User ID),其分配规则为:

  • 0:系统管理员;

  • 1~499:系统帐号;

  • 500~65535:一般用户。

账户信息中的密码使用“x”代替,没有任何密码。

密码信息储存在/etc/shadow文件中,密码以加密后的密文存储。

用户名:经过加密的密码:密码改动的日期:密码不可被改动的天数:密码需要重新更改的天数:密码更改前进行提示的天数:密码过期后的宽限时间:帐号失效时间:保留

群组信息存储在/etc/group中,每一行代表一个群组,分为4个字段:

组名:群组密码(x):GID:此群组包含的用户名称

用户名用","隔开,不能有额外的空格。

/etc/passwd中的GID是用户的初始群组,当用户登入系统后,就拥有了这个群组的权限。使用groups命令查看用户支持的群组,第一个输出的群组即为有效群组,在创建新文件时,档案的群组属性就是有效群组。使用newfrp切换有效群组,这时会在原本的shell中重新启动一个shell,其中用户的有效群组就是切换后的。当退出新的shell后,有效群组就恢复了。

群组管理员信息保存在/etc/shadow中,

组名:密码:群管理员帐号:群包含的用户帐号

密码栏开头为“!”表示无合法密码,所以无群组管理员。

群组管理员可以将帐号加入自己管理的群组中,而不许要root来进行管理,减少了root的工作量。现在由于有sudo命令,所以群管理员已经很少使用了。

有效身份与真实身份

用户登录过程

用户输入用户名和密码后,系统在/etc/passwd里面寻找帐号。如果没有则跳出;如果有,则将对应的UIDGID一起读出来,另外该帐号的家目录和shell设定也一并读出。

核对密码,系统进入/etc/shadow找到对应帐号与UID,然后核对输入的密码和里面的(加密)密码是否相同。

如果密码正确,那么就启动用户的shell环境。

进程

每个会话(session)拥有一个或者多个进程组(process group),每个进程组拥有一个或多个进程(process)。会话中的第一个进程就是这个会话的领导(session leader),会话标识(session id)就用它的进程标识(process id)。

用户态和内核态

同步原语

非递归锁

non-recursive lock:当一个进程获取到一个非递归锁后,尝试获取该锁的其他进程将阻塞直到该锁被释放。

Locks are associated with processes. A process can only have one kind of lock set for each byte of a given file. When any file descriptor for that file is closed by the process, all of the locks that process holds on that file are released, even if the locks were made using other descriptors that remain open. Likewise, ==locks are released when a process exits==, and are ==not inherited by child processes== created using fork (see Creating a Process).

递归锁

recursive lock:同一个进程可多次获取一个递归锁,但也需要释放同样多次数。

信号量

semaphore

网络

内核防火墙

netfilter/iptables组成Linux中的包过滤防火墙,完成数据包过滤、修改、重定向和网络地址转换等功能。netfilter位于内核空间,iptables位于用户空间。iptables将用户的安全设定执行到安全框架netfilter中。

netfilter

By Jan Engelhardt - Own work, Origin SVG PNG, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=8575254

处理位置-链

网络层对分组处理可分为五个模块(Chain,链)。每个链上设置了一系列规则,匹配规则的数据包按对应的处理方式进行处理。

  • PREROUTING
  • FORWARD
  • POSTROUTING
  • INPUT
  • OUTPUT
处理方式-表

每个链按对分组的处理方式将相应地规则归入表中,按处理优先级由高到低依次为:

  • raw
  • mangle:解析并修改报文;
  • nat:网络地址转换;
  • filter:过滤数据包;
  • security*(SELinux)。

每个表由默认策略(policy)和若干条规则(rule)组成,如果没有任何规则与数据包匹配,则按默认策略处理数据包。

根据数据包的转递场景,某些链中不完全包含四类表,具体情况如下:

PREROUTINGFORWARDPOSTROUTINGINPUTOUTPUT
raw
mangle
nat
filter
security

iptables概念

匹配条件

根据指定的匹配条件来尝试匹配每个流经此处的包,并由指定的动作进行处理。

动作(target)
  • ACCEPT:允许数据包通过。

  • DROP:直接丢弃数据包,不给任何回应信息。

  • REJECT:拒绝数据包通过,必要时会给数据发送端一个响应的信息,客户端刚请求就会收到拒绝的信息。

  • SNAT:源地址转换,解决内网用户用同一个公网地址访问外部服务的问题。

  • MASQUERADE:是SNAT的一种特殊形式,适用于动态的、临时会变的IP地址上。

  • DNAT:目标地址转换,外部请求或响应进入内网时执行。

  • REDIRECT:在本机做端口映射。

  • LOG:在/var/log/messages文件中记录日志信息,然后将数据包传递给下一条规则,也就是说除了记录以外不对数据包做任何其他操作,仍然让下一条规则去匹配。

匹配顺序

一个表格中的规则,如果一条规则被匹配上,则后续规则不再匹配;反之,继续匹配剩下的规则。

在没有顺序要求的情况下(匹配范围有包含关系的情况),匹配频率高的规则应该放在前面,以减少规则检查次数。

自定义链

将符合同一规则的所有规则合并写入自定义链中,由默认链根据规则引用自定义链进行处理。自定义链还可以进一步引用其他自定义链。

使用自定义链等效于将原来的一级规则匹配变换成了多级匹配。同时,将具有公共部分的规则合并到自定义链中有助于简化规则管理。

NAT

当网络内部主机的报文经过路由器时,路由器会维护一张NAT表,表中记录了报文来自于哪个内部主机的哪个进程(内部主机IP+端口)。

SNAT

SNAT:当报文经过路由器时,路由器会将报文的内部主机源IP替换为路由器的IP地址,把源端口也映射为某个端口,NAT表会把这种对应关系记录下来。外部主机收到报文时,源IP与源端口显示的都是路由的IP与端口。当外部网络中的主机进行回应时,外部主机将响应报文发送给路由器,路由器根据NAT表中的映射,将响应报文中的目标IP与目标端口再改为内部主机的IP与端口号,然后再将响应报文发送给内部网络中的主机。整个过程中,外部主机都不知道内部主机的IP地址,内部主机还能与外部主机通讯,于是起到了隐藏网络内主机IP的作用。还能够让局域网内的主机共享公网IP,让使用私网IP的主机能够访问互联网。

DNAT:会话开始时使用DNAT,如公网中的客户端通过路由器向内网中的服务器发起请求,路由器将“目标IP+端口”映射到“内网IP+端口”。

LVS

Linux Virtual Server

LVS由两部分组成,ipvsipvsadm

  • ipvsadm:LVS管理工具,管理员通过ipvsadm定义或管理集群规则。

  • ipvs:LVS核心实现,根据定义好的集群规则进行工作。ipvs作为netfilter的模块存在。

ipvs

LVS-NAT

LVS主机作为内网中服务器集群的代理,通过NAT将来自客户端的请求转发给服务器集群。LVS通过调度模块ipvs来调节转发给集群各服务器的负载。

LVS-NAT

VIP:公网IP,客户端实际访问的IP;

RIP:集群中服务器的内网IP;

DIP:LVS主机链接集群的接口分配的内网IP。

输入输出

TCP Socket

Connect and Accept

The connect and accept function the three-hand-shake procedure.

accept will peak one connection request from the backlog queue, which has finished three-hand-shake procedure.

三次握手的过程是由内核完成,不是connect/connect完成.

https://www.cnblogs.com/pengyusong/p/6434788.html

调用*listen*系统调用时,socket状态会变为LISTEN,此时需要为这个socket指定一个backlog。backlog通常被用来指定队列能容下的链接的个数。

在TCP建立连接的三路握手过程中,连接需要先经历SYN RECEIVED状态才能到达最终的ESTABLISHED状态,处于ESTABLISHED状态的连接才能被*accept*系统调用返回给应用。正因为如此,TCP/IP协议栈通常有两种实现backlog queue的策略:

  1. 使用一个队列,其大小由listen系统调用的backlog参数决定。当服务器收到SYN数据包后,会发送SYN/ACK数据包给客户端并将该连接入队列;当服务器收到客户端的ACK确认数据包后,连接状态变为ESTABLISHED,该连接可以被应用程序使用。这意味着这个队列会包含SYN RECEIVEDESTABLISHED两种状态的连接,只是只有处于ESTABLISHED状态的连接才会返回给用户程序中的accept系统调用。
  2. 使用两个队列,一个SYN 队列(或者说是未完成连接队列)和一个accept 队列(或者说是连接完成队列)。处于SYN RECEIVED状态的连接会被添加到SYN队列,然后,当这一连接状态变为ESTABLISHED后,其被移至accept队列。accept系统调用只会从accept队列中消耗连接。在这种策略中,listen系统调用的backlog参数决定的是accept 队列的大小。

https://harveyqing.gitbooks.io/python-read-and-write/content/python_advance/how_to_use_linux_epoll.html

任务调度模式

Simplified Matrix of Basic Linux I/O Models

IO密集型和CPU密集型任务
同步和异步

根据用户程序与内核的交互方式,进程的运行方式可分为同步和异步:

  • 同步:用户程序向内核发起操作请求,并从内核空间中获取数据;
  • 异步:用户程序向内核发起操作请求,内核完成请求后将数据写入用户空间(通过信号处理函数或回调函数写入用户空间提前分配的内存)。
阻塞和非阻塞

阻塞:用户程序等待内核操作结束;以输入为例,read系列方法将尝试读取指定字节数据、或读取直到指定字符(例如换行\n或文件结束EOF)。默认模式下,如果输入流还未满足返回条件,则方法将被阻塞。

非阻塞:用户不等待内核操作结束,而是立即返回内核操作的状态。

同步阻塞模式

The application blocks until the system call is complete. The calling application consumes no CPU and simply awaits the response, so it is efficient from a processing perspective.

the read/write task is multiplexed with other work in the kernel.

同步非阻塞模式

可读取的数据量不确定,可能会频繁从内核读取少量数据,增加上下文切换开销。

low data throughput due to frequent system call with context switch.

异步阻塞通知模式

IO多路复用non-blocking I/O with asynchronous blocking notifications

==不存在“异步阻塞IO”的模式==:如果用户程序阻塞等待内核操作完成,则“异步阻塞IO”与同步阻塞IO对于计算任务调度是相同效果,区别仅在于读取数据的方式。

在同步IO模式下,若用户程序阻塞于一个IO操作,而此时其它文件已经准备好操作,则进程无法转去操作其他文件,导致进程的运行效率降低(CPU的利用率)。因此,应该将所有进行IO操作的文件进行统一管理,通过查询文件读写状态获取可操作的文件,并执行相应的操作。只要存在可执行的IO操作,则进程不会出现空闲状态,从而提高进程运行效率。

Linux提供了selectpollepoll接口监听文件描述符状态变化信息。select需要用户程序对发生状态变化的文件查询其发生的事件,而pollepoll则由内核来设置文件描述符发生的事件。

监听接口是异步阻塞模式(依赖于内核事件通知),直到存在文件描述符准备就绪。在该模式下,任何内核操作都应该立即返回结果而非发生阻塞(非阻塞IO模式),否则令监听机制失效(阻塞在内核操作处,而非阻塞在监听接口处)。

select call can provide notification for many descriptors. A convenient model for asynchronous notification, not advised for high-performance I/O.

异步非阻塞模式

多线程模型
线程池模型
事件驱动模型

参考文献

  1. Boost application performance using asynchronous I/O, Learn when and how to use the POSIX AIO API. https://developer.ibm.com/technologies/linux/articles/l-async/

套接字

对于套接字而言,accept()函数会一直阻塞,直到有来自客户端的连接;recv()函数会一直阻塞,直到接收到了客户端的数据(或者没有更多的可接收数据);send()函数也会一直阻塞,直到发送给客户端的数据已经准备好了。

多线程(进程)阻塞模型:程序主线程包含了监听socket,这个监听socket接受来自众多客户端的连接;每当一个客户端连接进来时,将新创建的socket交给另外一个线程,由这个线程与客户端进行通信。因为每个线程只会与一个客户端通信,因此一个线程的阻塞并不会影响其它工作线程。使用多线程的阻塞socket写的代码逻辑上很简单直接,但这种模型有一些缺点,比如多线程的数据同步和单CPU多线程的低效。

异步套接字:对异步socket的操作会立即返回成功或失败,程序可以通过返回来决定后续操作。因为异步socket是非阻塞的,所以不需要用多线程,所有任务可能都在一个线程中完成。

异步多线程模型:主线程负责网络,其它线程处理阻塞任务;

安全

数字证书

PEM (originally “Privacy Enhanced Mail”) is the most common format for X.509 certificates, CSRs, and cryptographic keys. A PEM file is a text file containing one or more items in Base64 ASCII encoding, each with plain-text headers and footers (e.g. -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----). A single PEM file could contain an end-entity certificate, a private key, or multiple certificates forming a complete chain of trust. Most certificate files downloaded from SSL.com will be in PEM format.

PEM files are usually seen with the extensions .crt, .pem, .cer, and .key (for private keys), but you may also see them with different extensions. For example, the SSL.com CA bundle file available from the download table in a certificate order has the extension .ca-bundle.

Binary文件

All ELF binaries need to have a few things in place in order for them to be interpreted by the Linux kernel properly. As with Windows EXEs, there’s a structure to the header that defines the overall layout of the binary.

readelf -a elf_file
# -h --file-header       Display the ELF file header
# -l --program-headers   Display the program headers
# -S --section-headers   Display the sections' header
# -s --syms              Display the symbol table
# --dyn-syms             Display the dynamic symbol table
工具命令说明
hexdumphexdump -C execfile查看文件的十六进制编码
objdump -d execfile
readelfreadelf -aW file查看ELF二进制文件结构
strip execfile

strip reads a binary file, and removes a lot of the extra debug and compiler info that isn’t needed.

动态链接库

查看可执行程序的动态链接库依赖信息

ldd -v /usr/bin/bash                 # -v 递归显示依赖关系(默认列出所有直接和简介依赖项)
ldd /usr/bin/bash | awk '{print $1}' # 提取输出信息中的依赖项信息