L B T

记 录 过 去 的 经 验

Program 是一个静态实体,只是存储在操作系统中的文件(集合)

Process 是操作系统上的活动(Active)实体,是 Program 由操作系统加载到内存并运行之后的实体。

Process 运行过程中需要操作系统为其分配各种资源,如 CPU、Memory、Files、IO 等来完成其运行。

如果一个 Program 被操作系统运行(启动)了多次,那么其产生的多个 Process 属于分割(单独)的实体。

Linux 系统中调度的进程/线程,通常被称为任务(Task)

Linux 中常见的进程状态如下表:

状态标识 状态名称 状态说明 示例
R task_running 进程处于运行或就绪状态
S task_interruptible
sleeping
可中断的睡眠状态
D task_uninterruptible 不可中断的睡眠状态
1. 它是一种睡眠状态,意味着处于此状态的进程不会消耗 CPU
2. 睡眠的原因是等待某些资源(比如锁或者磁盘 IO),这也是非常多 D 状态的进程都处在处理 IO 操作的原因
3. 是它不能被中断,这个要区别于 硬件中断 的中断,是指不希望在其获取到资源或者超时前被终止。因此他不会被信号唤醒,也就不会响应 kill -9 这类信号。这也是它跟 S(可中断睡眠)状态的区别
T task_stopped
task_traced
Traced
暂停状态或跟踪状态
Z task_dead
exit_zombie
zombie
退出状态,进程成为僵尸进程
X task_dead
exit_dead
退出状态,进程即将被销毁
I idle 空闲状态
+ 表示关联了前台操作(Foreground Operation),比如前台运行的进程

进程调度

Linux 中任务(Task)调度算法默认使用 CFS(Completely Fair Scheduler,内核版本 >= 2.6.23).

CFS 调度算法没有使用固定的进程(此处的进程可能是进程或者线程,Linux 中统称为 Task)优先级(Priority),而是根据进程的 nice value 为进程分配一定比例的(Proportional) CPU 计算时间。 [1]

nice 的取值从 -20 - +19,值越小,优先级越高,默认值为 0 。优先级高的进程会被分配到更高比例的 CPU 处理时间。

在 Linux 内核中,优先级分为两种:静态优先级和动态优先级。CFS 使用的是动态优先级,它是根据 nice 值计算出来的。

CFS Scheduler 不会直接设定 Priorities,而是为每个 Task 维护变量 vruntime(virtual runtime)(可以检查系统 /proc/<PID>/sched),这个值记录了每个 task 使用了多少 CPU 时间。

CFS 通过将进程的虚拟运行时间(vruntime)与其他进程的虚拟运行时间进行比较来决定调度优先级。权重越大,vruntime 增长越慢,意味着进程的优先级越高,能够更频繁地被调度。

假入一个任务有默认的 Priority(nice=0),那么他的 vruntime 和实际使用的 CPU 时间相同。例如一个 nice=0 的进程使用了 CPU 200ms,那么他的 vruntime=200ms低优先级(low priority,nice>0 的任务在 CPU 上运行了 200ms,那么他的 vruntime>200ms,相反的,一个 高优先级(high priority,nice<0 的任务在 CPU 上运行了 200ms,那么他的 vruntime<200ms基于此,CFS Scheduler 在选择下一个要执行的任务时,会选择 vruntime 最小的任务来运行。如果有高优先级的任务就绪(可执行),它会抢占(preemptive)正在执行的低优先级的任务。

假如 Linux 中有 2 个优先级一样的任务(nice=0),一个进程为 I/O-bound,另一个为 CPU-bound。通常情况下,在一个 CPU 周期内,I/O-bound 的任务会使用很少的 CPU 时间就会因等待 IO 而中断在 CPU 上的执行并进入 CPU 的等待调度队列(schedule queue),而 CPU-bound 的任务会用尽分配给它的整个 CPU 周期。执行一段时间之后, I/O-bound 的任务的 vruntime 的值会显著的小于 CPU-bound 的任务的 vruntime,导致 I/O-bound 的任务拥有比 CPU-bound 的任务更高的优先级,当 I/O-bound 的任务就绪(IO 返回数据)时,它会立即抢占 CPU 开始执行。

nice 只能针对单一的进程调整优先级,无法同时将优先级配置关联到相关进程,如 子进程或者同一个服务中的其他进程

修改 nice 的值

在 Linux 中,nice 值用于调整进程的优先级,数值范围从 -20最高优先级 )到 19最低优先级 )。较低的 nice 值表示进程有更高的优先级,会更频繁地获得 CPU 时间,而较高的 nice 值表示进程有较低的优先级。

你可以使用 nicerenice 命令来修改进程的 nice

普通用户(root 之外的用户)启动应用程序时默认只能配置初始 nice0-19

普通用户(root 之外的用户)修改正在运行的应用程序的 nice 值,只能改大,不能改小。比如进程启动时,其 nice10,拥有权限的普通用户只能将其 nice 值改为大于 10 的值。

nice 命令

nice 命令用于在启动进程时指定 nice 。如果不指定,默认 nice 值为 0

nice -n 10 command

renice 命令

renice 命令可以修改已经在运行的进程的 nice 值。需要提供进程 ID( PID )来指定要修改的进程。

renice <nice_value> -p <PID>

如果想要一次修改多个进程的 nice 值,可以传递多个 PID:

sudo renice 10 -p 1234 2345 3456

Linux 进程资源控制 cgroups

cgroups 可以将一个任务(task)标识(绑定)到一个特殊的 控制组(Control Group ,此任务启动的子进程可以继承父进程的 控制组(Control Group

控制组(Control Group 可以限制的资源类型如下:

Type Description
blkio Storage
限制到存储设备(如 Hard Disk、USB Drivers等)的请求
cpu CPU
限制 CPU 调度
cpuacct Process Accounting
上报 CPU 使用状态,可以用于统计客户端使用的处理量并进行收费
cpuset CPU Assignment
在多处理器的系统上,将 Task 分配到特定的处理器以及关联的内存
devices Device Access
限制 控制组(Control Group 中的 Task 对目标设备类型的使用
freezer Suspend/Resume
暂停/恢复 控制组(Control Group 中的 Task
memory Memory Usage
限制并报告 控制组(Control Group 中的 Task 使用的内存
net_cls Network Bandwidth
制并报告 控制组(Control Group 中的 Task 使用的网络流量,主要通过对网络流量打上 cgroups 相关标签实现
net_prio Network Traffic
控制 控制组(Control Group 中的网络流量的优先级
ns Namespaces
cgroups 分配到不同的 Namespaces,如此同一个 cgroups 中的进程只能看到本 Namespace 中的进程

Linux 进程管理

top 命令

使用 top 命令可以查看系统负载、CPU 和 内存使用情况。也可以查看单个进程的具体信息。

top 命令常用选项

选项 说明 示例
-H Threads Mode,线程模式。默认情况 top 展示进程的简要信息,使用此选项显示进程中的线程状态。
对应交互式命令 H
  • 显示单个进程的(线程)详细信息
    # top -H -p 1423
    top - 09:44:42 up 54 days, 23:53, 2 users, load average: 8.82, 6.84, 7.21
    Threads: 15 total, 0 running, 15 sleeping, 0 stopped, 0 zombie
    %Cpu(s): 40.9 us, 10.8 sy, 0.0 ni, 48.1 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0 st
    KiB Mem : 15790488 total, 466056 free, 7761544 used, 7562888 buff/cache
    KiB Swap: 0 total, 0 free, 0 used. 3895716 avail Mem

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    1423 root 20 0 1477368 778788 4260 S 39.5 4.9 11999:41 [watchdog:1:6]
    2572 root 20 0 1477368 778788 4260 S 37.9 4.9 11363:48 [watchdog:1:6]
    1436 root 20 0 1477368 778788 4260 S 34.2 4.9 11286:08 [watchdog:1:6]
    1435 root 20 0 1477368 778788 4260 S 33.9 4.9 12059:53 [watchdog:1:6]
    1434 root 20 0 1477368 778788 4260 S 33.2 4.9 10249:00 [watchdog:1:6]
    1437 root 20 0 1477368 778788 4260 S 30.6 4.9 11717:47 [watchdog:1:6]
    1431 root 20 0 1477368 778788 4260 S 28.9 4.9 11222:06 [watchdog:1:6]
    21378 root 20 0 1477368 778788 4260 S 27.6 4.9 12143:35 [watchdog:1:6]
    1433 root 20 0 1477368 778788 4260 S 17.6 4.9 8738:21 [watchdog:1:6]
    1428 root 20 0 1477368 778788 4260 S 8.0 4.9 7650:56 [watchdog:1:6]
    1429 root 20 0 1477368 778788 4260 S 0.0 4.9 0:00.04 [watchdog:1:6]
    1430 root 20 0 1477368 778788 4260 S 0.0 4.9 0:00.05 [watchdog:1:6]
    1432 root 20 0 1477368 778788 4260 S 0.0 4.9 0:00.03 [watchdog:1:6]
    1438 root 20 0 1477368 778788 4260 S 0.0 4.9 12260:30 [watchdog:1:6]
    1529 root 20 0 1477368 778788 4260 S 0.0 4.9 11068:39 [watchdog:1:6]

进程命令名和进程可执行文件名

在系统中遇到以下进程:

# ps -elf | grep 18686
5 S root 18686 1239 0 80 0 - 46620 pipe_w 15:50 ? 00:00:00 /usr/sbin/CROND -n
0 R root 18694 18686 7 80 0 - 610547 - 15:50 ? 00:00:02 /usr/local/php73/bin/php /home/www/admin/artisan PullData
0 S root 18754 18686 0 80 0 - 22453 pipe_w 15:50 ? 00:00:00 /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root

其中 PID 为 18686 的进程名为 /usr/sbin/CROND,其启动了另外两个子进程。但是在系统中检查,并不存在路径 /usr/sbin/CROND

# ls -l /usr/sbin/CROND
ls: cannot access /usr/sbin/CROND: No such file or directory

出现此种现象,主要是因为 在启动时,进程的命令名是根据路径传递给 execve() 函数的参数决定的,而不是直接与系统中的文件进行匹配

在 Linux 系统中,ps 命令显示的进程信息是从 /proc 文件系统中获取的,而 /proc 文件系统包含有关正在运行的进程的信息,包括每个进程的命令名。因此,即使实际上系统中不存在 /usr/sbin/CROND 文件,但如果进程的命令名是 /usr/sbin/CROND,那么 ps 命令仍然会显示进程的命令名为 /usr/sbin/CROND

进程的命令名可以查看 /proc/<PID>/cmdline 文件,本示例中显示如下:

# cat /proc/18686/cmdline 
/usr/sbin/CROND-n

对应的系统上的可执行文件的名称可以查看 /proc/<PID>/stat/proc/<PID>/comm/proc/<PID>/status 等文件

# cat /proc/900/comm 
crond

# cat /proc/900/status
Name: crond
Umask: 0022
State: S (sleeping)
Tgid: 900
Ngid: 0
Pid: 900
PPid: 1239
TracerPid: 0

# cat /proc/900/stat
900 (crond) S 1239 1239 1239 0 -1 4202816 1627 0 0 0 0 0 0 0 20 0 1 0 139129633 190955520 1478 18446744073709551615 94685936058368 94685936118156 140733000396032 140733000262488 140427856103840 0 0 4096 16387 18446744071797306256 0 0 17 3 0 0 0 0 0 94685938219080 94685938221648 94685948321792 140733000400770 140733000400789 140733000400789 140733000400872 0

在本示例中,实际执行的命令为 crond

参考链接|Bibliography

Operating System Concepts v10 Online
Linux进程状态说明

脚注


  1. 1.Operating System Concepts v10 Online 5.7.1 Example: Linux Scheduling

在 Linux 中,命令提供的 man 手册是一份非常详细的命令使用说明手册,要了解命令的使用方法及其工作原理,熟练参考 man 手册是及其必要的。

man 手册中不仅包含了命令的使用方法,还包括了命令相关的 (配置)文件说明System Calls 等相关信息,下表中列出了 man 手册不同编号对应的功能

Section Number Section Name Description
1 User Commands 用户可以在 shell 中运行的指令说明
man 命令不指定 Section Number 时默认为 1
2 System Calls 应用程序中的功能函数调用的内核函数
3 C Library Functions 应用程序针对特定的函数库提供的接口
4 Devices and Special Files 程序使用的硬件或软件
5 File Formats and Conventions 涉及的文件类型(如配置文件)及约定
6 Games
7 Miscellaneous 其他杂项,如 协议文件系统字符集
8 System Administration Tools and Daemons 需要 root 权限或其他管理员权限运行的命令

要查看命令 man 的某个部分,可以使用以下命令查看 passwd 命令的 File Formats and Conventions

man 5 passwd

环境信息

  • Centos 7

为源码编译安装的软件安装 man 手册

使用源码编译安装的软件默认是没有 man 手册的,使用 man 命令会报以下错误

$ man fswatch
No manual entry for fswatch

要为源码编译安装的软件安装 man 手册,可以参考以下步骤,此处示例软件为 fswatch,软件编译安装到了 /usr/local/fswatch-1.17.1/

  1. 一般情况下,源码中会附带软件的使用文档,编译安装后,可能位于以下路径,fswatch 编译安装后的 man 手册位于 /usr/local/fswatch-1.17.1/share/man/man7/fswatch.7

    ls /usr/local/fswatch-1.17.1/doc
    ls /usr/local/fswatch-1.17.1/share/doc
    ls /usr/local/fswatch-1.17.1/share/man/

  2. man 命令使用的文档默认来源于 /usr/share/man/

    $ ls /usr/share/man/
    cs de fr hu it ko man1 man1x man2x man3p man4 man5 man6 man7 man8 man9 mann pl pt_BR ro sk tr zh_CN
    da es hr id ja man0p man1p man2 man3 man3x man4x man5x man6x man7x man8x man9x nl pt pt_PT ru sv zh zh_TW

    要为编译安装软件的安装 man 帮助文档,首先将 fswatch 的帮助文档复制到 man 页面的目录

    cp /usr/local/fswatch-1.17.1/share/man/man7/fswatch.7 /usr/share/man/man7/

  3. 更新 man 索引

    $ mandb
    1 man subdirectory contained newer manual pages.
    47 manual pages were added.

    安装成功后,可以正常使用 man fswatch 查看帮助文档。

bash 快捷键

bash 常用的快捷键总结

删除类快捷键

快捷键 功能 示例
Ctrl + D 删除光标右侧的一个字符,等于 Delete
Ctrl + H 删除光标左侧的一个字符,等于 Backspace
Ctrl + K 删除从光标位置到行尾的所有字符。
Ctrl + U 删除从光标位置到行首的所有字符。
Ctrl + W 删除光标左侧的一个单词(以空格为分隔)。
Alt + D 删除光标右侧的一个单词。(空格,下划线,点分割)
Ctrl + C 删除整行

定位类快捷键

快捷键 功能 示例
Ctrl + F 向前挪动一个字符 ,等于 右箭头 ->
Ctrl + B 向后挪动一个字符 ,等于 左箭头 <-
Alt + F 向前挪动一个 word
Alt + B 向后挪动一个 word 。
Ctrl + A 跳到行首。
Ctrl + E 跳到行尾。

Recall 类快捷键

bash 中执行过的命令都保存在了历史记录中,可以通过 history 命令查看。为了快速重新执行或者修改之前的命令并执行,bash 提供了以下快捷键

快捷键 功能 示例
Ctrl + R 搜索 history最后一个 匹配的命令
Alt + P 搜索 history最常使用 的命令
!100 重新执行 history 中的第 100 号的命令,无确认,会立即执行
!! 重新执行 history 中的 最后一个 命令,无确认,会立即执行

bash 环境变量

shell 中的环境变量大体可以分为以下几种:

  • Local Variables : 当前 Shell 中的 所有的本地变量(Local Variables ,要查看所有的 本地变量(Local Variables),可以使用以下命令:

    • set
    • declare
      $ set | more
      BASH=/bin/bash
      BASHOPTS=checkwinsize:cmdhist:complete_fullquote:expand_aliases:extglob:extquote:force_fignore:globasciiranges:histappend:interactive_comments:progcomp:promptvars:sour
      cepath
      BASH_ALIASES=()
      BASH_ARGC=([0]="0")
      BASH_ARGV=()
      ...
  • Environment Variables : 本地变量(Local Variables)的一部分子集(subset)被稱為 环境变量(Environment Variables环境变量(Environment Variables 会被导入到任何从当前 Shell 启动的新的 Shell,要查看 环境变量(Environment Variables ,使用以下命令

    • env
    • printenv
      $ env
      SHELL=/bin/bash
      SESSION_MANAGER=local/U-3TSDMAL9IVFAQ:@/tmp/.ICE-unix/3396,unix/U-3TSDMAL9IVFAQ:/tmp/.ICE-unix/3396
      QT_ACCESSIBILITY=1
      COLORTERM=truecolor
      XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg
      SSH_AGENT_LAUNCHER=gnome-keyring
      XDG_MENU_PREFIX=gnome-
      GNOME_DESKTOP_SESSION_ID=this-is-deprecated
      GTK_IM_MODULE=fcitx
      LANGUAGE=en
      LC_ADDRESS=en_US.UTF-8
      GNOME_SHELL_SESSION_MODE=ubuntu
      LC_NAME=en_US.UTF-8
      SSH_AUTH_SOCK=/run/user/408001114/keyring/ssh
      XMODIFIERS=@im=fcitx
      DESKTOP_SESSION=ubuntu
      LC_MONETARY=en_US.UTF-8
      GTK_MODULES=gail:atk-bridge
      DBUS_STARTER_BUS_TYPE=session
      ...
      查看单个变量
      $ echo $PATH 
      /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin

bash 常用环境变量

环境变量 功能 示例
PATH 可执行文件查找路径变量。
查找顺序从左向右,找到即停止
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
PS1
PS[234]
PS1 环境变量定义了 bash 中的命令后提示符包含的内容
定义中支持的元字符可以查看 man bash 手册
PS1="[\u@\h \W]\\$

bash 命令查找优先级

bash 中命令执行优先顺序如下:

  • Aliases : 由 alias 命令配置的命令别名。
  • Shell 预留的关键字 : 主要是 Shell 编程中会用到的关键字,如 dowhilecaseelse 等。
  • Built-in Command : shell 内嵌的命令,如 cdecho
  • Filesystem Command : 环境变量 PATH 中的命令。

要查找命令所在位置,可以使用 typewhich 命令

$ type bash
bash is /usr/bin/bash

$ which bash
/usr/bin/bash

$ type -a ls
ls is aliased to `ls --color=auto'
ls is /usr/bin/ls
ls is /bin/ls

如果要查找命令所在的所有位置,可以使用 type -a ls,如上所示,这会显示 ls 命令所在的所有位置

bash 环境变量配置优先级

Linux 系统中的多个配置文件中可以配置环境变量,其中有些会针对所有的用户和 shell 生效,有些只会针对特定的用户生效。

以下表格中列出了常用的环境变量的配置文件:

File Description
/etc/profile 此配置中的环境变量对所有用户生效,在用户登陆时为其配置环境变量 ,它一般会加载(包含)/etc/profile.d 中的配置
/etc/bashrc 此配置中的环境变量针对所有的 shell 生效,每次打开一个 bash 时会执行 ,一般会设置包括 登陆提示alias
~/.bash_profile 此配置中的环境变量对 当前登陆 用户生效,仅在用户登陆时为其配置环境变量 ,一般会加载 ~/.bashrc 文件
~/.bashrc 此配置中的环境变量针对 当前登陆 的用户 bash 生效,并在用户打开每个新的 bash 时执行
~/.bash_logout 此配置中的环境变量对 当前登陆 用户生效,仅在 当前登陆用户登出(logout) 时执行

Operating System Concepts v10(操作系统导论第10版)中涉及的相关专业词汇。

memory

Main Memory

Main Memory 通常是 CPU 可以直接定位和访问的唯一的大存储设备。如果 CPU 要处理磁盘上的数据,数据必须首先被传输到 Main Memory,指令要能被 CPU 执行,也必须首先载入内存中。

Program 要能被运行,首先必须载入到内存中,并提供内存绝对地址给 CPU 以供加载指令和数据。

Logical Memory Address and Virtual Memory Address

通常情况下,CPU 生成的内存地址被称为 Logical Memory Address,也称为 Virtual Memory Address

Memory-address 注册器(memory-address register)加载的地址通常称为 Physical Memory Address

程序运行过程中,CPU 操作的是 Virtual Memory Address,需要由硬件设备 MMU(Memory Management Unit)负责将 Virtual Memory Address space 映射到对应的 Physical Memory Address space。程序或者是 CPU 运行过程中,不会直接操作(访问/access)物理内存地址空间。

DMA

Direct Memory Access (DMA) 在 CPU 需要从存储读写大量数据到内存时,如果数据通过 CPU 中转,成本太高,为了解决这个问题,DMA 被采用,在传输大块数据时,首先在 CPU 中为数据设置好必要的 buffer、指针、计数器等资源,设备控制器(DMA Controller)直接(在磁盘和内存中)传输数据块而无须 CPU 参与实际的数据传输,只需要在数据块传输完成时,向 CPU 产生中断以指示设备控制器(DMA Controller)数据传输已完成。在 DMA 过程中,CPU 依旧可以做其他工作。 [1]

Dynamic Linking and Shared Libraries

DLLs(Dynamic Linked Libraries) 是程序运行期间需要链接的系统库文件(system libraries)。[6]

部分操作系统不支持 DLLs 机制,只能使用 静态链接static linking),在这种情况下,应用程序运行过程中需要使用的系统库文件必须和应用程序一起编译进应用程序镜像(program image)中。假如多个应用程序使用了同样的库文件,那么这些库文件会同时存在于这些应用程序的程序镜像文件中,这些应用程序运行过程中也会在内存中加载多份同样的库文件。这可能会产生以下缺点:

  • 应用程序镜像较大,占用更多磁盘空间
  • 应用程序运行时,因为要加载多个同样的库文件,会占用更多的内存
  • 库文件升级不灵活,要升级某个库文件,要重新编译所有使用此库文件的应用程序。

DLLs(Dynamic Linked Libraries) 是在运行过程中,动态加载库文件。这个特性在系统库文件(如 standard C language library)中很常用。程序编译过程中及运行过程中动态加载系统库文件,无需将系统库文件打包进应用程序镜像中,应用程序运行过程中动态加载系统库文件,如果要加载的目标系统库文件已经存在于内存中,则无需重复加载,直接共享使用即可。DLLs 有以下优势:

  • 应用程序镜像无需包含常用的库文件,减少了磁盘使用量
  • 应用程序运行时,可以在多个进程中共享库文件,无需重复加载(只存在一个库实例即可),减少了内存使用率并提升了内存效率
  • 共享库文件升级后,所有加载此库文件的应用程序都可以直接使用更新后的新版本。应用程序和库文件中都包含了相应的版本信息,会防止应用程序使用错误的库版本
  • 内存中可以加载多个版本的共享库文件,应用程序会使用自己的库文件版本信息加载正确的库文件

Memory Allocation

Fragmentation

假设在一个全新的未运行任何应用程序的操作系统中,初始情况下内存中存在一整块连续的可用地址范围(one large block of available memory),或者叫一个 Hole

假设如下图所示情况,系统中运行了 processes 5, 8, and 2 并且占用了所有的内存地址范围。之后 process 8 退出,其所使用的内存释放,形成了一个 Hole,随后 process 9 开始运行并申请了一段内存,随后 process 5 退出并释放内存,导致内存中出现了 2 个地址非连续(noncontigous)的 Hole随着系统中进程的运行和退出,内存中可能会存在很多大小不一,地址范围不连续的 Hole,这些 Hole 既是 外部内存碎片(External Memory Fragmentation

外部内存碎片(External Memory Fragmentation 存在时,可能出现系统有足够多的空闲内存(非连续的地址空间,内存碎片)但是这些内存都是内存碎片。

在现代的内存管理中,内存的分配一般是以 block 为单位进行分配,block 是一个固定大小的内存。在这种情况下,假如 block 大小为 512 bytes,应用程序申请了 600 bytes 内存,那么需要分配 2 个 block 给应用程序,实际分配了 1024 bytes 。申请的内存(600 bytes)和实际分配的内存(2 blocks = 1024 bytes)之间的差值被称为 内部内存碎片(Internal Memory Fragmentation)

Paging

在当前的操作系统中,解决 外部内存碎片(External Memory Fragmentation 的问题,通用解决方法是采用 内存页(Memory Paging) 技术: 应用程序申请的 Logical 内存/Physical 内存地址非连续(横跨多个 Hole)

Paging 解决了 外部内存碎片(External Memory Fragmentation 的问题,没有解决 内部内存碎片(Internal Memory Fragmentation)

Virtual Memory

Virtual Memory 提供了 允许程序运行过程中,无需将整个程序内容全部加载到内存中的技术 [7]

主要有以下优势:

  • 程序大小可以大于物理内存空间。开发者不必再关注物理内存是否足够。
  • Virtual Memory 将 main memory 抽象成一个统一的大存储阵列,基于此,程序开发者可以不用关注物理内存的差异和限制。
  • 允许进程共享文件和库,并实现了共享内存(shared memory)。

Page Fault

Virtual Memory 提供了 允许程序运行过程中,无需将整个程序内容全部加载到内存中的技术,实现此技术主要使用了 Demand Paging 技术,使用 demand-paged virtual memory,可以实现程序运行过程中,仅仅在 运行过程中必须的 pages 才会被加载到内存中,那些没有用到的 pages 永远不会加载到物理内存中Demand Pageing 也是 Virtual Memory 的一个主要优势。

Demand Paging 技术的实现依赖于硬件的支持和 Page Fault 技术。

根据 Demand Paging 方案的原则,程序运行过程中用不到的 Pages 不应该被加载到内存中。因此,当一个程序刚刚启动时,它之后运行过程中所需的 Pages 理论上都不在内存中,而是存在于次级存储(secondary storage,如磁盘或者 swap)中,如果 程序运行过程中需要内存中不存在的 Pages,这时就要有机制确保实现将程序运行所需的 Pages 加载到内存中来,并且只是加载程序运行所需的 Pages,而不会加载用不到的 Pages,实现此机制的技术叫 Page Fault

Page Fault 需要硬件支持,主要是内存组件中的硬件设备(Paging Hardware,负责 page table 内存地址转换),当程序运行过程中需要不存在于内存中的 Pages 时,会向 OS 产生一个 Page Fault 的 trap,此 trap 是一个 OS Failure(指示所需 Pages 不在内存中),硬件会注意到此 OS Failure 并进行相应处理。

操作系统处理 Page Fault 的简要工作流程如下:

  1. 检查并确保此内存请求合法后,如果所需的 pages 不在内存中,开始将其加载入内存(page in)
  2. 在物理内存中找到空闲的 frame(free frame)
  3. 向次级存储(secondary storage,如磁盘或者 swap)发送读取所需 Pages 到新分配的 frame 的请求
  4. 当存储(Pages)读取到内存完毕后,修改程序所属的 internal table 和 page table 以指示所需的 Pages 已经在内存中
  5. 重新启动因为缺少所需 Pages 而被中断(interrupted)的指令(instruction)

Major Page Fault

Major Page Fault 发生于所需要的 Page 不在内存中,这是系统需要在物理内存中找到可用的 Frames,然后将数据从 次级存储(secondary storage,如磁盘或者 swap) 读取到内存中并更新对应的 internal tablepage table 以指示所需的 Pages 已经在内存中

Minor Page Fault

Minor Page Fault 发生在以下 2 中情况:

  1. 应用程序需要引用共享库,此共享库已经在内存中,但是应用程序的 page table 中没有到此共享库所在内存页的 mapping。此种情况下,只需要更新应用程序的 page table,向其中添加到共享内存库所在内存页的引用。
  2. Virtual Memory 中的 Page 被应用程序释放(reclaimed),对应物理内存 Frame 被标记为 free-frame,但是上面的数据未被 zeroed out,此时 Page 被分配给其他应用程序。

Linux 系统中,使用 ps 命令的 -eo min_flt,maj_flt,cmd 参数可以查看进程相关的 major 和 minor page fault。

Page Relpacement

现代的操作系统内存,一般都允许 超分配(over-allocating),即允许操作系统分配比实际的物理内存更多的内存给应用程序,当一个应用程序运行过程中,因为 Page Fault 要 Page In 时,可能会出现物理内存不足(no free frames on the free-frame list)的情况,此时操作系统会尽最大可能的满足应用程序对内存的申请。以前的操作系统大都采用 Swap 来实现,即将当前未在 CPU 上运行的应用程序使用的内存 swap out 到 Swap 中,并将空余出来的内存分配给要运行的程序使用。Swap 技术已被当前大多数操作系统摈弃,因为其在内存和 Swap 中拷贝数据成本太高,取而代之的技术是 Page Repalcement

使用 Page Relpacement 技术,在物理内存不足时,使用 page-replacement 算法找到物理内存中的 Frames(victim frames),将其上的内容写入(如果其上内容未改变,不用执行写入操作,如果内容改变,执行写操作)到次级存储(secondary storage),如果之后的运行过程中,应用程序需要这些数据,它会产生 Page Fault,这些数据会被重新加载回内存中。

Memory Compression

Memory Compression 是在物理内存不够时,使用的一种可替代 Page Repacement 的技术。在系统内存不足时,系统不执行 Page Replacement,而是先对选择出的 victim frames 执行内存压缩,将多个 frames 压缩成一个 frame 进而降低内存使用率,而无需进行 Page Swapping out 操作。

移动设备操作系统,包括 Android 和 IOS 都不支持 SwapPage Repalcement,都采用了 Memory Compression 技术作为 Memory Management strategy 的一部分

在性能方面,Memory Compression 拥有比 Page Repalcement 更快的速度。

Kernel Memory

内核(kernel)使用的内存和用户模式(user mode space)中的应用程序使用的内存分配机制不同。主要基于以下原因: [8]

  • Kernel 中的数据结构大小差异很大,很多都比 Page Size 要小,内存使用不当会导致非常多的 internal fragmentation,导致内存严重浪费。而且很多操作系统并未将内核代码放置到 Paging Memory 中。
  • user-mode 中的应用程序分配的内存在物理内存地址上没必要是连续地址空间,然而内核中包含的很多硬件设备都需要直接通过物理内存进行交互,无法使用 Virtual Memory Interface,因此需要内存中连续的地址空间(contiguous pages)分配

CPU

SMP

Symmetric Multiprocessing (SMP),对称处理系统,在系统中存在多个处理器,每个处理器中包含一个或多个 CPU,每个 CPU 有自己的 L1 Cache (CPU 独享)和 Register,每个处理器有 L2 Cache(处理器中的多个 CPU 共享 L2 cache),同一个处理器中的 CPU 使用处理器内部通信系统通信,跨处理器的 CPU 之间通过系统总线(Bus)通信。 [2]

NUMA

系统中的处理器不宜太多,多处理器可以提高系统的任务处理能力,但是当 CPU 过高时,对系统总线的争抢会成为系统瓶颈。

为了避免太多 Processors 争抢系统总线造成的性能下降,一个可选的 方法是为每个 CPU(或 CPU 集)提供专用的本地内存,CPU 通过一个更小更快的本地 bus 连接专用的本地内存。所有的 CPU 通过一个 内部通信系统 进行连接,所有 CPU 共享同一个物理地址空间,这种架构被称为 Non-uniform Memory Access (NUMA)

Program and Process

Program 是一个静态实体,只是存储在操作系统中的文件(集合)

Process 是操作系统上的活动(Active)实体,是 Program 由操作系统加载运行之后的实体。

Process 运行过程中需要操作系统为其分配各种资源,如 CPU、Memory、Files、IO 等来完成其运行。

如果一个 Program 被操作系统运行(启动)了多次,那么其产生的多个 Process 属于分割(单独)的实体。

Process Control Block

PCB (Process Control Block) 代表了 OS 中进程(Process)的各种信息,包括

  • Process State : 进程当前的状态
  • Program counter : 程序计数器指针。指向了当前进程中下一个要执行的指令的地址。
  • CPU registers : 根据计算机架构的不同,可能包括计数器、栈指针(stack pointers)、通用指针(general-purpose registers)等。
  • CPU-scheduling information : 包括进程优先级(process priority)信息、调度队列指针(pointers to scheduling queues)以及其他调度参数等。
  • Memory-management information : 进程使用的内存信息
  • Accounting information : CPU 数量和 CPU 使用时间,CPU 使用限制,进程数量等信息
  • I/O status information : 分配给进程的 IO 设备,打开的文件列表等。

Scheduling Queues

  • ready queue : 进程开始后,会被放入 ready queue,等待 CPU 被分配给它。ready queue 是一个 链接队列,其首部包含一个指向队列中第一个 PCB 的指针,每个 PCB 都包含一个指针,指向 ready queue 中的下一个 PCB。
  • wait queue : 当 ready queue 中的进程被分配了 CPU 后,其执行一段时间后会中止或者是等待特殊事件,如 IO,在起等待的过程中会被放入 wait queue

操作系统负载(system load) 衡量的就是在 Scheduling Queues 中的进程(线程)的数量。即有多少任务等待 CPU 空闲以运行。

Context Switch

CPU 会频繁的调度(因为 CPU Schedule 或 Interrupts)不同的进程来执行,当调度发生,OS 需要保存当前进程的上下文信息(Context)以备当前进程再次开始执行时恢复 Context,Context 信息保存在 PCB

切换 CPU core 要保存当前进程的 Context (到 PCB)并恢复另一个进程的 Context(从 PCB),这个过程被称为 Context Switch

在现代化的操作系统上,OS 调度的更多的是 kernel-level threads,而不是 processes (进程)

Processor Affinity

Processes 在指定的 CPU 上运行的过程中,一般会加载数据到 CPU 的 cache 中,下一次调度到同一个 CPU 运行时,仍然可以使用 Cache 中的数据(称为 warm cache)。 [5]

假如后续 CPU 调度时,此进程被调度到了另一个 CPU,那么原来 CPU Cache 中的数据就要失效,新的 CPU 必须从新加载数据到 Cache 中。因为让 CPU Cache 中的数据失效和加载 Memroy 中的数据到 Cache 中是一个成本较高的操作,现代的 SMP 操作系统都会避免这类缓存迁移操作,会尽量将进程调度到同一个 CPU 以使用 warm cache 数据,这就是 Processor Affinity,一个进程对其正在运行的 CPU 拥有亲和性。

常见的 CPU 亲和性(Processor Affinity)有两类:

  • soft affinity : 尽量保证一个进程被调度到同一个 CPU,但是不保证一定,尽最大可能
  • hard affinity : 允许配置进程只运行在选定的 CPU 集(subset of processors)

大多数系统同时实现了以上两种类型。

Disk

可以将 NVS (Nonvolatile Storage (NVS))根据硬盘组成划分成 2 大类

  • 机械硬盘(Mechanical): HDDs,Optical Disks,Holographic storage,Magnetic Tape。
  • Electrical(NVM): Flash Memory,FRAM,NRAM,SSD

HDD


HDD (Hard Disk Driver)的主要组成如上图。主要包括以下主要组件: [9]

  • Platter
    Platter 的两面由磁性材料(magnetic material)覆盖组成。

  • Read-Write Head
    Platter 的两面分别有个 Read-Write Head (读写磁头) 负责在磁碟(Platter)上写入/读取数据。磁头和 Platters 之间一般由一层非常薄的气体作为保护层,如氦气(helium),磁头和 Platter 表面接触会造成 Platter 表面的磁性材料损坏,这被称为 Head Crash,一般无法修复,磁盘上面数据会丢失。

  • Disk Arm
    Disk Arm 连接了 Read-Write Head (读写磁头) 和 Arm AssemblyRead-Write Head (读写磁头) 通过 Disk Arm 的移动来寻址到不同的存储单元。

  • Tracks
    Platter 的面被(逻辑/Logically)分割成圆弧形(两个不同半径的圆分割组成的部分)

  • Sectors
    Tracks 被逻辑(Logically) 分割成 Sectors (扇形),Sectors 是 HDD 磁盘上最小的传输单元,一般有固定大小的存储空间(2010 年前一般是 512 bytes,之后大多升级到了 4KB)。每个 Track 上面包含多个 Sectors。

  • Cylinder
    每个 Platter 上面,固定位置的 Disk Arm / Read-Write Head 垂直组成的存储单元组成一个 Cylinder 。每个磁盘上面会包含很多个同心的(Concentric) Cylinder

HDD 磁盘中包含一个高速旋转的 马达(disk driver motor),HDD 磁盘的传输速度(Transfer Rate)和马达的旋转速度相关,通常使用 RPM(Rotations Per Minute) 来衡量 HDD 磁盘的转速,通常在 5400/7200/15000 RPM。有些 HDD 磁盘在电量不足或者使用频率低时会降低 RPM 来节能和提高磁盘使用寿命。

Transfer Rate 是衡量数据在 HDD 磁盘和操作系统之间传输速度的指标。这个性能指标主要受到 寻址时间(Positioning Time) 或者叫 Random-Access Time 的影响,这个时间主要由 2 部分组成,一般在 几毫秒(several milliseconds)

  1. Seek Time : Disk Arm 移动到目标 Cylinder 的时间
  2. Rotational Latency : 目标 Sector 移动到 Disk Read Wirte Head 的延迟。

NVM

Nonvolatile Memory Devices (NVM) 使用电特性(Electrical)存储,而不是机械(mechanical)特性,相比 HDD,其拥有更高的可靠性和性能(没有 Seek Time 和 Rotational Latency),更加的省电,劣势是他们价格相对 HDD 更贵并且存储空间相对较小,但是随着时间推移,这些劣势也会逐渐消弭。

NVM 存储的 写入性能 通常和其存储可用大小相关,因为 NVM 设备内部通常会保留部分空间用于 Garbage CollectionOver-Provisioning(通常为 20%),如果可用空间小,会导致 写性能 下降。

NVM 设备的寿命和其 Erase 的次数正相关

Error Detection and Correction

Error Detection 是计算机系统中最基础的功能之一,普遍用于 Memory、Network、Storage 系统中。Error Detection 的常见功能包括:

  • 检测存储中存储的数据是否发生了(错误/非自发/无意的)改变,比如某个数据位由 0 变为了 1
  • 网络数据在传输过程中发生了改变
  • 存储设备上的数据读写不一致。
    通过检测这些错误,可以防止在使用这些错误数据之前对其进行修正或者是发送通知给使用者。

Parity Bit

Memory 长使用 Parity Bit (奇偶校验位) 算法进行 Error Detection,Memory 系统中的每个 Byte 都有一个对应的 Parity Bit,此 Parity Bit 记录了对应的 byte 中 1 出现的次数是 偶数(even,Parity Bit = 0 还是 奇数(odd, Parity Bit = 1)

根据 Parity Bit 算法的计算结果,会出现以下情况:

  • Memory 中的一个 byte 中的一个 bit 位发生了改变,那么此 byte 计算出的 Parity Bit 值和保存的 Parity Bit 值不一样,说明数据不可靠
  • Memory 中的一个 byte 中的任何 bit 都正常,但是保存的 Parity Bit 值发生了变化,那么此 byte 计算出的 Parity Bit 值和保存的 Parity Bit 值不一样,说明数据不可靠
  • Memory 中的一个 byte 计算出的 Parity Bit 值和保存的 Parity Bit 值一致,说明数据可靠

Parity Bit 无法检测 2 个 bit 位的错误,因为 2 个 bit 位的数据错误,其正确计算出来 Parity Bit 的值不变。

Parity Bit 容易实现并且计算起来非常的快,但是因为 Parity Bit 的存在,会为每个 byte 增加一个 bit 的大小

CRCs

CRCs (Cyclic Redundancy Check) 是网络传输中常用的 Error Detection 算法。可以检测到多个 bit 位的错误。

ECC

ECC (Error Correction Code) 算法不仅可以检测到数据错误,并且可以尝试恢复数据。

EEPROM

EEPROM(Electrically Erasable Programmable Read-Only Memory) 是主板上的一个 NVM Flash Memory Firmware。

服务器上电运行后启动的第一个程序是 bootstrap loader program (操作系统启动引导程序,如 Linux 中的 grub2,其启动之后会加载操作系统)。RAM 是易失性存储,不能将 bootstrap program 放置到 RAM 中,当下主机会使用 EEPROM 放置 bootstrap programbootstrap program 可以被修改,但是不能频繁修改,它属于慢速存储,其中包含了不经常会使用的静态程序和数据。比如 iPhone 使用了 EEPROM 存储了设备 serial numbershardware information

RAID

RAID (Redundant Arrays of Independent Disks)

RAID 0: Striping Volume

RAID 0,Striping Volume,条带卷,在多个存储设备上面,以 Block 为基本单位组成条带(Strip),数据分散写入到每个存储设备的对应的条带上,读取时从多个设备上的对应条带中读取。

  • 不提供任何冗余(Redundancy),任意存储设备损坏,会导致整个存储系统上的数据损坏
  • 有极高的读写速度,在多个设备上并发的读写
  • 存储设备利用率 100%

RAID 1: Mirrored Volume

由 2 或多块物理存储设备(physical storage device)组成一个逻辑存储设备(logical disk),每次写入数据时,同时写入到 2 个或多个物理存储设备。

  • 提供冗余(Redundancy),数据同时写入 2 个或多个物理存储设备上,只有在所有数据盘都损坏的情况下数据才会损坏
  • 有极高的读取速度,可以从每个镜像盘读取数据
  • 数据写入速度降低,因为要将数据写入多个数据盘,可能会导致写入速度下降
  • 存储设备利用率 50%

RAID 4: Block-Interleaved Parity

RAID 4 使用 RAID 0 的 Strip(条带)作为基础,至少需要 3 块数据盘。数据存储以 Block 为单位。

假设由 3 块数据盘组成 RAID 4,数据写入过程如下:

  1. 写入数据的第一个 block 写入到第 1 块数据盘中,第二个 block 写入到第 2 块数据盘中,对写入第一块盘和第二块盘上的这 2 个 block 做 Error Correcting Code 计算,计算结果存储在第 3 块数据盘中。
  2. 第三个 block 继续写入到第 1 块数据盘中,第四个 block 写入到第 2 块数据盘中,对此 2 个 block 做 Error Correcting Code 计算,计算结果存储在第 3 块数据盘中
  3. 依次类推,直到数据写入完成。
  • 其中一块盘存储了 Error Correction Code
  • 假如其中一个数据盘或其上的一个 Block 损坏,那么这块数据可以根据其他盘上的数据和 Error Correction Code 做计算进行恢复
  • 有极高的读写速度,在多个设备上并发的读写。
  • 存储设备利用率 (N-1)/N

RAID 5: Block-Interleaved Distributed Parity

和 RAID 4 唯一的不同是: RAID 4 使用一个单独的盘存储了 Error Correction Code(Parity Bit),RAID 5 将 Error Correction Code(Parity Bit)分布存储在所有数据盘上,通过将 Parity 分布到所有的数据盘上,避免了专用的 Parity Block 的盘压力太大。

RAID 6: P + Q redundancy scheme

RAID 6 为每 4 个 Block 保留了 2 个冗余(Redundant)的 Block,使用了更复杂的算法来进行 Error Detect 和 Error Correcting,可以支持 2 块数据盘损坏。

RAID 10


RAID 10 的结构:

  • 首先将硬盘分成多个镜像组(RAID 1),每个镜像组包含两个或多个硬盘。
  • 然后将这些镜像组组合成一个条带组(RAID 0)。

优缺点:

  • 高数据冗余:每个数据块都有一个镜像副本,因此可以抵抗单个硬盘故障。

  • 高读写性能:通过条带化技术,可以提高读写速度。

  • 成本较高:需要至少四个硬盘,而且有效存储容量只有硬盘总数的一半。

RAID 01

RAID 01 的结构:

  • 首先将硬盘分成多个条带组(RAID 0),每个条带组包含两个或多个硬盘。
  • 然后将这些条带组组合成一个镜像组(RAID 1)。

优缺点:

  • 高读写性能:通过条带化技术,可以提高读写速度。

  • 数据冗余:整个条带组被镜像,因此可以抵抗硬盘故障。

  • 成本较高:需要至少四个硬盘,而且有效存储容量只有硬盘总数的一半。

  • 数据恢复复杂:如果一个硬盘发生故障,整个条带组都需要被重建,恢复时间较长。

RAID 10 和 RAID 01 在单一硬盘故障时的恢复方式基本一致,都是从对应的镜像组恢复数据到故障硬盘。 在涉及到多个硬盘的故障的情况下,RAID 10 相比 RAID 01 拥有更多的优势:

  • RAID 10 提供了更高的冗余性和恢复灵活性,因为它允许多个独立的镜像组。
  • RAID 10 可以承受每个镜像组中的一个硬盘故障而不影响整体系统。
  • RAID 01 在任一条带组中失效的硬盘超过一个时,会导致整个 RAID 组不可用。

File System

File Systems 为访问(storedlocatedretrieve)存储系统(Storage)上存储的文件提供了高效和便利的途径(方式)。文件系统的设计主要涉及到以下问题: [10]

  • File System 对用户(User)应该如何呈现。包括如何定义文件以及文件属性,允许哪些操作,目录结构是怎样的(如何组织)
  • 使用怎样的算法来创建文件或者目录结构,以及如何将逻辑上的文件(logical files system)和物理存储(硬件)上的内容进行对应

File System 通常由多个功能分层(Levels or layers)构成,其中 下层为上层提供服务,上层利用下层提供的服务来构建文件系统的功能(特性)

在如上图所示的分层的文件系统架构中,主要包括以下层:

  • I/O control : 主要由存储设备驱动器(Device Drivers)和中断控制器(interrupt handlers)组成。主要功能是在存储设备和主内存之间传输数据。它接受更高层的指令(如 retrieve block 123),并将此指令转换为能被存储设备驱动器识别的针对存储硬件设备的指令。
  • basic file system : 在 Linux 系统中被称为 “block I/O subsystem。这一层的主要功能是将上层的通用的读写 block 的指令发布给正确的存储设备驱动器(device driver)来读写存储设备上的 block。它发送给存储设备驱动器(device driver)的是 逻辑块地址(Logical block address),它还负责 IO request scheduling 以及 buffers 和 caches。
  • file-organization module : 主要存储了文件和他们对应的 logical blocks 的信息。还包括 free-space manager,用来跟踪未分配的 logical blocks,当 file-organization module 需要空闲块时提供空闲的块地址信息。
  • logical file system : 负责管理 metadata 信息。Metadata 信息存储了所有的文件系统结构信息(包括 目录结构文件名),只是不包括具体的数据。关键信息包括 FCB (file-controle blocks,即 inode),存储了文件的具体信息如 owershippermissionslocations of file contents(location of logical blocks)

system-wide open-file table And per-process open-file table

大多数操作系统再打开并使用文件之前,都需要(implicitly or explicitly)系统调用 open(),操作系统维护了一个 open-file table,维护着所有的打开文件的信息。当一个文件操作(如读,写,执行等)被请求前,系统调用 open(),文件会通过一个 index 被加入到 open-file table,当文件不在使用时,open-file table 中关于此文件的条目(index) 被移除。

系统中维护了 2 中类别的 open-file table:

  • per-process open-file table : 跟踪进程打开的文件列表,此表中的列表实际是指向 system-wide open-file table 中相关条目(entry)的指针(pointer)。
  • system-wide open-file table : 维护了系统上所有的打开的文件信息,保存了如 文件路径文件大小文件时间属性(access time 等)open count 等信息,当一个进程打开一个文件后,system-wide open-file table 中就会添加一个此文件相关的条目,当另一个进程打开同样的文件后,进程的 per-process open-file table 中只是会添加一条到 system-wide open-file table 中对应文件的引用(entry)。system-wide open-file table 中的每个条目还维护了一个 open count,记录了有多少进程打开了此文件,当某个进程关闭此文件后,其 open count 会减少,值为 0 时,表示没有任何进程打开了此文件,此条目可以从 system-wide open-file table 中移除。

system-wide open-file table 里面关于文件的信息,保存的主要是 FCB 的拷贝(Copy)

per-process open-file table 里面存储的主要是到 system-wide open-file table 里面的对应文件的指针(pointers)

Linux 系统中要查看 open-file table 相关信息,可以使用 lsoffuser/proc/<pid>/fd/ 等方法。

Security

Encryption

Symmetric Encryption

对称加密算法(Symmetric Encryption) 中,加密和解密使用同样的密钥(Key)

Glossary

GNU: GNU’s Not Unix,Richard Stallman 于 1984 年开始开发的自由(free)的类 Unix 操作系统。包括编译器、编辑器、库文件、游戏等应用,没有内核。

GNU/Linux: 1991 年,Linus Torvalds 发布了最初版类 Unix 操作系统内核,并使用了 GNU 提供的编译器和工具。

FSF: Free Software Foundation,自由软件基金会,由 Richard Stallman 于 1985 年成立,旨在鼓励和推动自由软件的使用和开发。自由不等于免费

GPL: GNU General Public License。

ELF: Executable and Linkable Format.

ABI : Application Binary Interface. ABI 定义了在不同架构以及不同的操作系统中,一个二进制代码(Binary Code)提供的接口的差异性。ABI 定义了二进制代码的底层细节,如地址宽度、向系统调用(system calls)提供参数的方法、运行时的堆栈(stack)的组织方式、系统库的二进制格式、数据类型等信息。ABI 是架构级别(architecture-level)的定义,如果一个二进制文件是根据特点的架构(如 ARMv8 processor)编译(链接)而成,那将他迁移到支持此 ABI 的另外的系统上,理论上它也可以正常运行。[3]

RTE : Run Time Environment.

LKMs : Loadable Kernel Modules.

Darwin : iOS/MacOS 底层的类 Unix 操作系统,主要基于 BSD UNIX 的 microkernel

IPC : Inter-Process Communication.

RPCs : Remote Procedure Calls.

JNI : Java Native Interface.

HAL : Hardware Abstraction Layer.

UEFI : Unified Extensible Firmware Interface.

initramfs : 在系统启动过程中(Bootstrap Program 运行过程,比如 GRUB),因为根文件系统(root file system)还未挂载,为了挂载根文件系统,必须加载必要的驱动程序和内核模块,boot loader 会创建一个临时 RAM 文件系统,即 initramfs。一旦内核开始运行,必要的驱动已经安装,内核会将根文件系统从 initramfs 切换到已经挂载的根文件系统。然后启动 systemd 进程。

CMT : Chip MultiThreading. 一个物理 CPU 核上实现了双线程或多线程的支持。在一个 CPU 线程因等待内存加载数据而空闲(Memory Stall)时,另一个 CPU 线程开始执行。每个 CPU 线程拥有自己独立的状态数据(architectural state)、指令指针(instruction pointer)、注册器(regester set)等,可以将其当作一个单独的逻辑 CPU 来运行单独的线程(进程/Job)。如 Intel 的超线程(hyper-threading)或者叫 SMT(Simultaneous Multi-Threading)

CFS : Completely Fail Scheduler。Linux kernel 2.6.23 版本开始默认的 CPU 调度算法。

MMU : Memory Management Unit.

HDD : Hard Disk Driver.

SSD : Solid State Disk.

ATA : Advanced Technology Attachment. 存储设备接口协议

SATA : Serial ATA. 存储设备接口协议

SAS : Serial Attached SCSI. 存储设备接口协议

USB : Universal Serial Bus. 存储设备接口协议

FC : Fibre Channel. 存储设备接口协议

NVMe : NVM express. NVM 存储设备专用的通信接口协议。NVM 设备通过 NVMe 接口协议直接连接在系统 PCI 总线上。 相比其他存储接口协议(如 SATA 、SAS 等)增加了吞吐量及减少了延迟。

HBA : Host Bus Adapter.

HAS : Host Attached Storage.

NAS : Network Attached Storage. 存储设备接口协议,使用基于 TCP/IP 的 RPC 实现,如 NFS

FC : Fibre Channel,光纤通道,存储设备接口协议

SANs : Storage Area Networks. 使用基于 TCP/IP 的 RPC 实现

RAIDs : Redundant Arrays of Independent Disks.

DES : Data Encryption Standard.

AES : Advanced Encryption Standard

IKE : Internet Key Exchange.

TLS : Transport Layer Security. SSL 的升级版

SSL : Secure Sockets Layer.

MAC : Mandatory Access Control. Linux 系统中,是 SELinux 的一部分。

DAC : Discretionary Access Control. UNIX-based 系统上使用的权限控制策略(chmodchownchgrp

PAM : Pluggable Authentication Modules. UNIX-based 系统上使用的用户鉴权模块

BSD : Berkeley Software Distribution. 第一个 Unix 发行版,由 California Berkeley 大学发行

FSF : Free Software Foundation, 自由软件基金会,由 理查德斯托曼 于 1984 年发起的开源运动,GNU 是其主要的一部分

参考链接|Bibliography

Operating System Concepts v10 Online

脚注


  1. 1.Operating System Concepts v10 Online 1.2.3 I/O Structure
  2. 2.Operating System Concepts v10 Online 1.3.2 Multiprocessor Systems
  3. 3.Operating System Concepts v10 Online 2.6 Why Applications Are Operating-System Specific
  4. 4.Operating System Concepts v10 Online CHAPTER 5 CPU Scheduling
  5. 5.Operating System Concepts v10 Online 5.5.4 Processor Affinity
  6. 6.Operating System Concepts v10 Online 9.1.5 Dynamic Linking and Shared Libraries
  7. 7.Operating System Concepts v10 Online CHAPTER 10 Virtual Memory
  8. 8.Operating System Concepts v10 Online 10.8 Allocating Kernel Memory
  9. 9.Operating System Concepts v10 Online 11.1.1 Hard Disk Drives
  10. 10.Operating System Concepts v10 Online 14.1 File-System Structure

环境信息

  • centos7
  • python3
  • Django 4

ModuleNotFoundError: No module named ‘MySQLdb’

ModuleNotFoundError: No module named ‘MySQLdb’

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.

解决方法

pip3 install pymysql

编辑文件./python36/lib/python3.6/site-packages/django/db/backends/mysql/__init__.py, 输入以下内容

import pymysql 
pymysql.install_as_MySQLdb()
阅读全文 »

环境信息

  • centos 7
  • Python 3.10
  • Django 4.0
  • uwsgi 2.0.20
  • nginx 1.20.1
  • venv

    示例中虚拟环境位于 /opt/vb/, Django工程目录位于 /opt/vb/vb/ , 工程名称为vb

uwsgi 配置文件 (uwsgi.ini) 配置示例

uwsgi.ini
[uwsgi]
socket = 127.0.0.1:8081
chdir = /opt/vb/vb
wsgi-file = ./vb/wsgi.py
master = true //主进程
vhost = true //多站模式
no-site = true //多站模式时不设置入口模块和文件
workers = 2 //子进程数
reload-mercy = 10
vacuum = true //退出、重启时清理文件
max-requests = 1000
limit-as = 512
buffer-size = 30000
pidfile = uwsgi-8081.pid
daemonize = uwsgi-8081.log
pythonpath = /opt/vb/env/lib/python3.10/site-packages/
阅读全文 »

Nginx 服务配置

全局通用配置

nginx.conf
user nginx nginx;    

# 建议设置为 cpu 核心数或者 cpu 核心数的 2 倍,进程会包含一个 `master process`,多个 `worker process`
# master process 负责绑定端口、调度进程等,不负责业务的处理
# worker process 是业务进程,负责业务的处理
worker_processes auto;

# 一个 worker 进程可以打开的最大的 fd 个数,受 Linux 内核限制
# 理论值应该是系统最多打开文件数(ulimit -n)与 nginx 进程数相除
# 可通过 ulimit 设置或修改系统文件:`/etc/securit/limits.conf`
worker_rlimit_nofile 1024;

# cpu 亲和性设置
worker_cpu_affinity 0001 0010 0100 1000;

# 工作进程调度优先级,-20 到 19 之间的值,值越小越优先调用。
# 如果系统同时运行多个任务,你可能需要提高 nginx 的工作进程的优先级
worker_priority 0;

# ssl 硬件加速服务器,需要硬件支持
# ssl_engine ssl_engine device;

# nginx 是否以守护进程运行,是否让 nignx 运行于后台;调试时可为 off,使得所有信息直接输出在控制台
daemon on | off;

# events 模块中包含 nginx 中所有处理连接的设置。
events {
# 每个 worker 进程允许的最多连接数,
# nginx 服务最大连接数:worker_processes * worker_connections (受 worker_rlimit_nofile 限制)
worker_connections 1024;
use epoll;

# 是否允许一次性地响应多个用户请求
multi_accept on;

# 是否打开 nginx 的 accept 锁;此锁能够让多个 worker 进行轮流地、序列化地与新的客户端建立连接;
# 而通常当一个 worker 进程的负载达到其上限的 7/8,master 就尽可能不将请求调度至 worker.
accept_mutex on | off;
}

# HTTP 模块控制着 nginx http 处理的所有核心特性
http {
include mime.types;
default_type application/octet-stream;

# 是否在错误页面中显示和响应头字段中发出 nginx 版本号。
# 安全考虑建议关闭
server_tokens on | off | string;

# 是否启用 sendfile 内核复制模式功能。作为静态服务器可以提供最大的 IO 访问速度。
sendfile on | off;

# 尽快发送数据,否则会在数据包达到一定大小后再发送数据。这样会减少网络通信次数,降低阻塞概率,但也会影响响应及时性。
# 比较适合于文件下载这类的大数据通信场景。
tcp_nodelay on|off;

# 单位s,适当降低此值可以提高响应连接数量
keepalive_timeout 65;

# 一次长连接上允许的最大请求数
keepalive_requests 100;

# 禁止指定浏览器使用 keepalive
keepalive_disable msie6|none;

# 读取 http 请求首部的超时时长。如果客户端在此时间内未传输整个头,则会向客户端返回 408(请求超时)错误
client_header_timeout 1;

# 读取 http 请求包体的超时时间。
client_body_timeout 2;

# 发送响应的超时时长。超时后连接将关闭。
send_timeout 5;

# http 请求包体的最大值,常用于限定客户端所能够请求的最大包体,根据请求首部中的 Content-Length 来检查,以避免无用的传输。
client_max_body_size 1m;

# 限制客户端每秒传输的字节数,默认为0,表示没有限制。单位 Byte/s
limit_rate 0;

# nginx 向客户端发送响应报文时,如果大小超过了此处指定的值,则后续的发送过程开始限速,单位 Byte
limit_rate_after 0;

# 是否忽略不合法的 http 首部,默认为 on,off 意味着请求首部中出现不合规的首部将拒绝响应。
ignore_invalid_headers on|off;

# 用户访问的文件不存在时,是否将其记录到错误日志中。
log_not_found on|off;

# nginx 使用的 dns 地址,及缓存解析结果的时间
resolver 8.8.8.8 [valid=time] [ipv6=on|off];

# dns 解析超时时间
resolver_timeout 2;

# 是否打开文件缓存功能,max:用于缓存条目的最大值,
# inactive:某缓存条目在指定时长内没有被访问过时,将自动被删除,即缓存有效期,通常默认为 60s。
open_file_cache off;
open_file_cache max=N [inactive=time];

# 是否缓存文件找不到或没有权限访问等相关信息。
open_file_cache_errors on | off;

# 多长时间检查一次缓存中的条目是否超出非活动时长。
# 建议值:小于等于 open_file_cache inactive
open_file_cache_valid 60;

# 在 open_file_cache inactive指 定的时长内被访问超过此处指定的次数时,才不会被删除(删除低命中率的缓存)。
open_file_cache_min_uses 2;

# 开启内容压缩,可以有效降低客户端的访问流量和网络带宽
gzip on | off;

# 内容超过最少长度后才开启压缩,太短的内容压缩效果不佳,且会浪费系统资源。
# 压缩长度会作为 http 响应头 Content-Length 字段返回给客户端。 建议值:64
gzip_min_length length;

# 压缩级别,默认值为 1。范围为1~9级,压缩级别越高压缩率越高,但对系统性能要求越高。建议值:4
gzip_comp_level 1~9;

# 压缩内容类型,默认为 text/html;。只压缩 html 文本,一般我们都会压缩 js、css、json 之类的,可以把这些常见的文本数据都配上。
如:text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_types mime-type …;

# 自动显示目录
autoindex on;

# off : 以人类易读的方式显示文件大小,on:以 bytes 显示文件大小
autoindex_exact_size off;

# 定义限制区域,imit_req_zone 只能放在 http {} 内,使用此限制可以在 http {} (对服务器内所有的网站生效)、server {} (对具体的一个网站生效)或 location {} (对具体的一个网址生效)
# 此区域名称为 test,可根据需求自定义; 10m 表示此区域存储 key($binary_remote_addr)使用的大小
# 存储大小,一般 1m 空间大约能保存 1.6 万条 IP 地址,空间满了新数据会覆盖旧数据
# rate=1r/m ,限制访问请求频率为每分钟 1 次,可根据需要自行设置,1r/s 是 1 秒 1 次,时间单位只能选择 s (秒)或 m (分),最低频率限制是每分钟 1 次访问请求
# rate=10r/m,1分钟最多访问 10 次,同时不能超过 1r/6s,即 6s 内最多访问 1 次。超过 1r/6s 返回 503
limit_req_zone $binary_remote_addr zone=test:10m rate=1r/m;

# 定义日志格式
log_format main '{ time: $time_iso8601|'
'http_host:$http_host|'
'cdn_ip:$remote_addr|'
'request:$request|'
'request_method:$request_method|'
'http_user_agent:$http_user_agent|'
'size:$body_bytes_sent|'
'responsetime:$request_time|'
'upstreamtime:$upstream_response_time|'
'upstreamhost:$upstream_addr|'
'upstreamstatus:$upstream_status|'
'url:$http_host$uri|'
'http_x_forwarded_for:$clientRealIp|'
'status:$status}';

# server 负责具体的 http 服务器实现
server {
listen 80 [default_server] [rcvbuf=SIZE] [sndbuf=SIZE] [ssl];

# 可使用通配符*或正则表达式(~开头),多个域名先精确匹配,再通配,再正则,'_'表示空主机头
server_name _ ;

access_log logs/access.log main;
error_log logs/access.err.log;

# 跨域配置
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers 'Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With';
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header Access-Control-Allow-Credentials: true;

location / {
# web 资源路径
root html;

# 定义默认页面,从左往右匹配
index index.html index.htm;

# 自左向右读取指定路径,找到即停止,如果都不存在,返回一个错误码
try_files $uri $uri.html $uri/index.html =404;

# 自左向右读取指定路径,找到即停止,如果都不存在,返回一个 uri
try_files $uri $uri.html $uri/index.html /404.html;
}

location /i/ {
# 路径别名,只能用于 location 中。
# 访问 http://a.com/i/a.html, 资源路径为:/data/www/html/a.html
# 若是root指令,访问 http://a.com/i/a.html,资源路径为:/data/www/html/i/a.html
alias /data/www/html/;
}

# 对于某个请求发生错误,如果匹配到错误码,重定向到新的 url
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

# 对于某个请求发生错误,如果匹配到错误码,重定向到新的 url,同时可以更改返回码
error_page 404 =200 /404.html;
}

# 包含其他配置文件
include vhosts/*.conf;
}


阅读全文 »

nftables 是一个 netfilter 项目,旨在替换现有的 {ip,ip6,arp,eb}tables 框架,为 {ip,ip6}tables 提供一个新的包过滤框架、一个新的用户空间实用程序(nft)和一个兼容层(iptables-nft)。它使用现有的钩子、链接跟踪系统、用户空间排队组件和 netfilter 日志子系统。

在 Linux 内核版本高于 3.13 时可用

它由三个主要组件组成:

  • 内核实现: 内核提供了一个 netlink 配置接口以及运行时规则集评估
  • libnl netlink : libnl 包含了与内核通信的基本函数
  • nftables : 用户空间前端。nftables 的用户空间实用程序 nft 评估大多数规则集并传递到内核。规则存储在链中,链存储在表中。

iptables 不同点

  • nftables 在用户空间中运行,iptables 中的每个模块都运行在内核(空间)中
  • 表和链是完全可配置的。在 nftables 中,表是没有特定语义的链的容器。iptables 附带了具有预定义数量的基链的表,即使您只需要其中之一,所有链都已注册,未使用的基础链也会损害性能。
  • 可以在一个规则中指定多个操作。在 iptables 中,您只能指定一个目标。这是一个长期存在的局限性,用户可以通过跳到自定义链来解决,但代价是使规则集结构稍微复杂一些。
  • 每个链和规则没有内置计数器。在 nftables 中,这些是可选的,因此您可以按需启用计数器。由于 iptables 内置了一个数据包计数器,所以即使这些内置的链是空的,也会带来性能损耗
  • 更好地支持动态规则集更新。在 nftables 中,如果添加新规则,则剩余的现有规则将保持不变,因为规则集以链表形式表示,这与整体式 blob 表示相反,后者在执行规则集更新时内部状态信息的维护很复杂。
  • 简化的双堆栈 IPv4/IPv6 管理,通过新的 inet 系列,可让您注册同时查看 IPv4 和 IPv6 流量的基链。 因此,您不再需要依赖脚本来复制规则集。

服务名称默认为 nftables,默认配置文件为 /etc/nftables.conf ,其中已经包含一个名为 inet filter 的简单 ipv4/ipv6 防火墙列表。

/etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
chain input {
type filter hook input priority 0;
}
chain forward {
type filter hook forward priority 0;
}
chain output {
type filter hook output priority 0;
}
}

nftables 服务的 systemd 配置文件如下:

/lib/systemd/system/nftables.service
[Unit]
Description=nftables
Documentation=man:nft(8) http://wiki.nftables.org
Wants=network-pre.target
Before=network-pre.target shutdown.target
Conflicts=shutdown.target
DefaultDependencies=no

[Service]
Type=oneshot
RemainAfterExit=yes
StandardInput=null
ProtectSystem=full
ProtectHome=true
ExecStart=/usr/sbin/nft -f /etc/nftables.conf
ExecReload=/usr/sbin/nft -f /etc/nftables.conf
ExecStop=/usr/sbin/nft flush ruleset

[Install]
WantedBy=sysinit.target

nftables 使用的内核模块如下,加载这些模块,服务才能正常运行

# lsmod | grep nf
nf_log_syslog 20480 1
nft_chain_nat 16384 17
nf_nat 49152 3 xt_nat,nft_chain_nat,xt_MASQUERADE
nf_conntrack_netlink 49152 0
nft_counter 16384 142
nf_reject_ipv4 16384 1 ipt_REJECT
nf_conntrack 172032 6 xt_conntrack,nf_nat,xt_state,xt_nat,nf_conntrack_netlink,xt_MASQUERADE
nf_defrag_ipv6 24576 1 nf_conntrack
nf_defrag_ipv4 16384 1 nf_conntrack
nft_compat 20480 143
nf_tables 249856 570 nft_compat,nft_counter,nft_chain_nat
nfnetlink 20480 5 nft_compat,nf_conntrack_netlink,nf_tables,ip_set
阅读全文 »

Outline 官网

Outline 是由附属于 Google 的 Jigsaw 开发的开源的 VPN 软件。它的设计目标是为了实现 VPN 的简单部署和管理以及安全。Outline 提供了强加密、用户管理工具、并支持多平台,包括 Windows, macOS, Linux, iOS, 和 Android。

Outline 主要由 2 部分组成:

  • Outline Manager : 用来部署 VPN 服务器,以及管理用户、限速等
  • Outline Client : 连接 VPN 的客户端,支持多平台

本文示例基本环境信息

  • Ubuntu 22.04.4 LTS (Jammy Jellyfish)
  • Outline Manager Version 1.15.2

Outline 环境 部署

Outline Manager 部署

Outline Manager 部署非常的简单,只需要下载可执行文件,添加可执行权限并启动即可

$ wget https://s3.amazonaws.com/outline-releases/manager/linux/stable/Outline-Manager.AppImage

$ chmod +x Outline-Manager.AppImage

$ ./Outline-Manager.AppImage
Outline Manager is starting
libva error: vaGetDriverNameByIndex() failed with unknown libva error, driver_name = (null)
[42740:0925/174650.529715:ERROR:viz_main_impl.cc(186)] Exiting GPU process due to errors during initialization
Launching web app from outline://web_app/index.html?version=1.15.2&sentryDsn=https%3A%2F%2F9df8c810bf1b482d979da996e3e63c40%40o74047.ingest.sentry.io%2F215496
libva error: vaGetDriverNameByIndex() failed with unknown libva error, driver_name = (null)
[42774:0925/174651.006194:ERROR:viz_main_impl.cc(186)] Exiting GPU process due to errors during initialization
libva error: vaGetDriverNameByIndex() failed with unknown libva error, driver_name = (null)
[42800:0925/174651.310808:ERROR:gpu_memory_buffer_support_x11.cc(44)] dri3 extension not supported.
Checking for update
Generated new staging user ID: c5db7469-3a5b-5365-a374-7e29a6e0c71a
Update for version 1.15.2 is not available (latest version: 1.15.2, downgrade is disallowed).

  • 为安全起见,Outline Manager 不支持以 root 用户执行,请以普通用户身份执行

  • Outline Manager 依赖于 fuse,执行命令 sudo apt install fuse 安装

  • Outline VPN Server 依赖于 Docker 和 curl,请提前安装

Outline Manager 运行后会启动 UI

Outline VPN Server 部署

Outline 环境中,VPN Server 负责具体的 VPN 节点实现。要部署 VPN Server,选择合适的服务器环境,比如使用自己的本地服务器则选择 Set up Outline anywhere,然后根据提示在具体的 VPN Server 上部署程序即可

  1. 根据提示,执行以下命令,部署 VPN Server 环境程序

    # sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh)"
    > Verifying that Docker is installed .......... OK
    > Verifying that Docker daemon is running ..... OK
    > Setting PUBLIC_HOSTNAME to external IP ...... OK
    > Creating persistent state dir ............... OK
    > Generating secret key ....................... OK
    > Generating TLS certificate .................. OK
    > Generating SHA-256 certificate fingerprint .. OK
    > Writing config .............................. OK
    > Starting Shadowbox .......................... OK
    > Starting Watchtower ......................... OK
    > Removing watchtower container ............... OK
    > Restarting watchtower ....................... OK
    > Waiting for Outline server to be healthy .... OK
    > Creating first user ......................... OK
    > Adding API URL to config .................... OK
    > Checking host firewall ...................... OK

    CONGRATULATIONS! Your Outline server is up and running.

    To manage your Outline server, please copy the following line (including curly
    brackets) into Step 2 of the Outline Manager interface:

    {"apiUrl":"https://66.26.90.25:50472/Q6XjXdbbVbetfAV0TK2cyw","certSha256":"67695819036A0FA4CE3C9E4AFAA0466D3C4BE4D9B04DBF7D8BA820FB379C0E4C"}

    If you have connection problems, it may be that your router or cloud provider
    blocks inbound connections, even though your machine seems to allow them.

    Make sure to open the following ports on your firewall, router or cloud provider:
    - Management port 50472, for TCP
    - Access key port 13279, for TCP and UDP

    根据提示 Management port 50472, for TCPAccess key port 13279, for TCP and UDP,防火墙放通对应的端口

    默认情况下,Management portAccess key port 使用随机端口,要使用自定义的固定端口,使用以下命令配置 VPN Server 环境

    bash install_server.sh --api-port 65530 --keys-port 65531

    如果在同一台主机上重复执行 install_server.sh,请删除持久化数据目录,默认为 /opt/outline/ ,否则可能出现重复部署后某些配置依然是旧的。

  2. 下载客户端程序,COPY ACCESS KEY 到客户端测试连接。

阅读全文 »

Certbot 是 Let’s Encrypt SSL 官方推荐的 ACME 协议客户端,它是一个 Python 程序,且包含模块化插件支持。Let’s Encrypt 的根证书浏览器支持广泛,且支持泛域名。但单个证书的有效期为 90 天,以防止滥用。

安装 Certbot

官方安装步骤参考

以下步骤演示在 Python3 环境中安装 Certbot 及其相关依赖

  1. 安装 certbot
    pip install certbot
  2. 申请证书时,要使用 DNS 方式验证域名所有权并且 DNS 使用 Cloudflare 的情况下,可以安装 certbot-dns-cloudflare 插件实现自动验证,参考以下命令安装 certbot-dns-cloudflare,此模块需要 cloudflare 模块的支持
    pip install cloudflare
    pip install certbot-dns-cloudflare
    安装完成后检查相关模块和版本。其中 cloudflare 版本需要最低为 2.3.1 [1]
    # pip list
    certbot 2.10.0
    certbot-dns-cloudflare 2.10.0
    cloudflare 2.19.2
    以上模块安装完成后,即可使用 certbot 申请域名证书,并支持 Cloudflare DNS 的自动验证。

基于 Cloudflare DNS 的自动验证申请域名证书

参考步骤安装 certbot 及 Cloudflare DNS 插件后 即可使用 certbot 自动请求 Cloudflare DNS 创建申请证书时需要的 DNS 记录自动完成域名归属权的验证过程。

certbot 支持的 Cloudflare 相关的参数如下

参数 说明 示例
--dns-cloudflare 使用 Cloudflare 的 DNS 插件自动验证域名归属权
--dns-cloudflare-credentials 请求 Cloudflare 的授权配置文件
--dns-cloudflare-propagation-seconds 请求 Cloudflare DNS 添加相关 DNS 记录后,让 ACME 服务等待多少秒再验证 DNS 记录。主要用来防止 DNS 记录添加后,缓存 DNS 服务器未来得及更新最新记录。
默认为 10

Cloudflare Credentials 说明

假设有 Cloudflare 账号的 Global API Key,则 Credentials 配置文件内容参考如下

cloudflare.ini
# Cloudflare API credentials used by Certbot
dns_cloudflare_email = cloudflare@example.com
dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234

申请证书的具体命令如下,如果是第一次申请,需要根据提示填写自己的邮箱信息并同意许可协议,邮箱用于接受之后系统发送的错误或者域名证书过期等信息

certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
--dns-cloudflare-propagation-seconds 60 \
-d example.com \
-d www.example.com

如果是非交互式环境,可以使用参数 --email your-email@example.com--agree-tos 自动绑定邮箱并同意许可

阅读全文 »

Shadowsocks 官网简介

基于 Python3 环境的 Shadowsocks 部署

执行以下命令之一安装 Shadowsocks (Python3 版本)

pip3 install git+https://github.com/shadowsocks/shadowsocks.git@master

pip3 install shadowsocks

配置 Shadowsocks

创建一个配置文件,比如放在 /etc/shadowsocks/config.json 目录下。配置文件的内容如下:

{
"server": "0.0.0.0",
"server_port": 8388,
"local_address": "127.0.0.1",
"local_port": 1080,
"password": "your_password",
"timeout": 300,
"method": "aes-256-cfb",
"fast_open": false
}

配置选项说明

  • server : 服务器监听的 IP 地址,0.0.0.0 表示监听所有接口。
  • server_port : 服务器监听的端口,客户端连接到这个端口。
  • password : 用于加密流量的密码,请设置一个强密码。
  • method : 加密方法。其他加密算法请参考官网
  • timeout : 超时时间(秒)。
  • fast_open : 如果启用 TCP Fast Open,请将其设置为 true,但需要内核支持。

启动 Shadowsocks 服务

使用以下命令启动 Shadowsocks 服务,该命令会以后台进程的方式启动 Shadowsocks:

ssserver -c /etc/shadowsocks/config.json -d start

常用选项

选项 说明 示例
-h, --help 打印帮助信息
-d [start / stop / restart ] 以 Daemon 方式(后台)运行
--pid-file Daemon 模式启动时的 PID 文件路径
--log-file Daemon 模式启动时日志文件路径
--user 运行服务的用户
-v, -vv verbose 模式
-q, -qq quite 模式
-c 配置文件路径 ssserver -c /etc/shadowsocks/config.json -d start
-s 指定服务端监听地址,默认为 0.0.0.0 。等同于配置文件中的 server
-p 指定服务端监听端口,默认为 8388 。等同于配置文件中的 server_port
-k 指定密码 。等同于配置文件中的 password
-m 加密方法,默认为 aes-256-cfb 。 等同于配置文件中的 method
-t 超时时间(单位为 ),默认 300s 。 等同于配置文件中的 timeout
--fast-open 启用 TCP_FASTOPEN,需要 Linux 3.7+
--workers workers 数量,Linux/Unix 可用
--forbidden-ip , 分割的 IP 列表,在此列表中的 IP 禁止连接,即黑名单
--manager-address 服务端 UDP 管理地址

为 Shadowsocks 配置 systemd services 文件

参考以下内容,为 Shadowsocks 配置 systemd services 文件

/etc/systemd/system/shadowsocks.service
[Unit]
Description=Shadowsocks Proxy Server
After=network.target

[Service]
ExecStart=/usr/local/bin/ssserver -c /etc/shadowsocks/config.json
Restart=on-failure

[Install]
WantedBy=multi-user.target

启用并启动 Shadowsocks 服务

sudo systemctl enable shadowsocks
sudo systemctl start shadowsocks

客户端连接

在客户端(如 Windows、macOS、iOS 或 Android)上,使用 Shadowsocks 客户端进行连接。

客户端配置

  • 服务器地址 : 填写你服务器的公网 IP。
  • 服务器端口 :填写 config.json 中的 server_port,如 8388
  • 密码 : 填写配置文件中的 password
  • 加密方法 : 选择 method 中配置的加密方法,如 aes-256-cfb

如果需要调试或查看运行日志,可以通过以下命令查看 Shadowsocks 的日志:

journalctl -u shadowsocks

ss 命令是一个查看 Linux 系统 socket 统计信息的工具,类似于 netstat,但是能显示更多的 TCP 和状态信息。

常用选项 ,可查看 man ss

选项 说明 示例
-h, --help 输出选项的简要说明
-V, --version 打印版本信息
-H, --no-header 不打印首行(Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
-n, --numeric 不对服务名进行解析,直接输出端口(数值类型)
-r, --resolve 尝试解析 IP 和 端口 为对应的名称,默认行为
-a, --all 列出正在监听的端口以及已经建立连接的端口
-l, --listening 仅列出正在监听的端口,默认不列出。
-m, --memory 显示 socket内存 使用情况
-p, --processes 显示使用此 socket 的进程
-s, --summary 显示简略的统计信息
-4, --ipv4 只显示 IPv4 相关的 socket
-6, --ipv6 只显示 IPv6 相关的 socket
-t, --tcp 只显示 TCP 相关 socket
-u, --udp 只显示 UDP 相关 socket

环境信息

  • Centos 7

automake

编译安装软件报错

error: require Automake 1.14, but have 1.13.4

Automake 版本不匹配,需要安装 Automake 1.14

$ rpm -qa | grep automake
automake-1.13.4-3.el7.noarch

以下步骤安装 automake-1.14.1

wget http://ftp.gnu.org/gnu/automake/automake-1.14.1.tar.gz
tar -xf automake-1.14.1.tar.gz
cd automake-1.14.1
./bootstrap.sh

以上步骤执行完成后,会生成 configure 可执行文件

./configure
make
make install

安装完成后,执行以下命令验证版本

$ automake --version
automake (GNU automake) 1.14.1
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Tom Tromey <tromey@redhat.com>
and Alexandre Duret-Lutz <adl@gnu.org>.

makeinfo

编译安装软件报错

makeinfo: command not found

makeinfo 命令不存在,执行以下命令安装

yum install texinfo

gcc

no acceptable C compiler found in $PATH

缺少 gcc 编译器,安装即可

yum install -y gcc

A compiler with support for C++11 language features is required

编译安装软件时报错

configure: error: *** A compiler with support for C++11 language features is required.

错误原因为 gcc 版本太低。查看当前 gcc 版本

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)

安装 gcc-8.3.0

以下步骤演示安装 gcc-8.3.0 [1]

  1. 下载安装包,官方下载地址
    wget ftp://ftp.irisa.fr/pub/mirrors/gcc.gnu.org/gcc/releases/gcc-8.3.0/gcc-8.3.0.tar.gz
    tar -xf gcc-8.3.0.tar.gz
    cd gcc-8.3.0
  2. 编译安装。编译依赖 GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+,需要先按照顺序安装这 3 个依赖。依赖安装参考: 安装 GMP安装 MPFR安装 MPC
    $ ./configure --prefix=/usr/local/gcc-8.3.0 --disable-multilib
    $ make
    $ make install
  3. 安装完成后,需要更新系统标准库 查看当前系统使用的 gcc 库文件,可以看到版本为 libstdc++.so.6.0.19
    $ ls /usr/lib64/libstdc++.so.6
    libstdc++.so.6 libstdc++.so.6.0.19

    $ ls /usr/lib64/libstdc++.so.6 -l
    lrwxrwxrwx 1 root root 19 May 30 08:05 /usr/lib64/libstdc++.so.6 -> libstdc++.so.6.0.19
    执行以下操作,更新 libstdc++.so.6 到最新安装的版本
    $ rm -rf /usr/lib64/libstdc++.so.6

    $ ln -s /usr/local/gcc-8.3.0/lib64/libstdc++.so.6.0.25 /usr/lib64/libstdc++.so.6

    $ ls /usr/lib64/libstdc++.so.6 -l
    lrwxrwxrwx 1 root root 46 Jun 9 09:31 /usr/lib64/libstdc++.so.6 -> /usr/local/gcc-8.3.0/lib64/libstdc++.so.6.0.25

安装 GMP

安装包下载地址

wget ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2
tar -jxvf gmp-6.1.0.tar.bz2
cd gmp-6.1.0
./configure
make && make install

安装 MPFR

安装包下载地址

wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.4.tar.bz2
tar -jxvf mpfr-3.1.4.tar.bz2
cd mpfr-3.1.4
./configure
make && make install

安装 MPC

安装包下载地址

wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz
tar -zxvf mpc-1.0.3.tar.gz
cd mpc-1.0.3
./configure
make && make install
阅读全文 »

环境信息

  • Ubuntu 24.04.1 LTS (Noble Numbat)
  • nftables v1.0.9 (Old Doc Yak #3)

配置错误

Statement after terminal statement has no effect

有以下配置:

ip daddr 127.0.0.11 jump DOCKER_OUTPUT counter;

meta l4proto tcp ip daddr 127.0.0.11 tcp dport 53 dnat to 127.0.0.11:45143 counter;

加载配置时报错: Error: Statement after terminal statement has no effect

错误原因nftables 规则中在 终止语句(terminal statement(如 jumpdnat)后添加的语句(如 counter)没有作用,因为 jumpdnat 语句已经处理了数据包,不会再执行后续的语句。

nftables 中,终止语句(terminal statement 是那些一旦执行后就结束了该数据包的处理,例如 jumpacceptdropdnatsnat 等。因为这些语句会决定数据包的最终去向,所以在这些语句后面再添加如 counter 这样的语句是无效的。

解决方法 : 要正确配置计数器,你需要 counter 放在终止语句 之前,这样在执行 jumpdnat 之前,数据包会先经过计数器。

ip daddr 127.0.0.11 counter jump DOCKER_OUTPUT;

meta l4proto tcp ip daddr 127.0.0.11 tcp dport 53 counter dnat to 127.0.0.11:45143;

syntax error

一次性配置多个端口

假如要在一个规则中同时放通 HTTP 和 HTTPS (80443 端口),以下是错误语句

# nft insert inet filter input handle 11 tcp dport 80,443 counter accept comment \"for nginx\"
Error: syntax error, unexpected inet, expecting rule
insert inet filter input handle 11 tcp dport 80,443 counter accept comment for nginx
^^^^

nftables 中,多个端口 在指定时不能直接用逗号分隔。对于多个端口,应该使用集合({})的语法来指定。以下为正确语法

# nft insert rule inet filter input handle 11 tcp dport { 80,443 } counter accept comment \"for nginx\"

注意: 使用 集合{})语法时要注意其中的空格,{ 80,443 } 是正确格式,如果写成 {80,443} 则是错误格式

cmd 中添加注释报错

使用以下语句添加规则报错:

# nft insert rule inet filter input handle 11 tcp dport { 80,443 } counter accept comment "for nginx" 
Error: syntax error, unexpected string, expecting end of file or newline or semicolon
insert rule inet filter input handle 11 tcp dport { 80,443 } counter accept comment for nginx
^^^^^

正确格式如下:

# nft insert rule inet filter input handle 11 tcp dport { 80,443 } counter accept comment \"for nginx\"

注意: 在 cmd 中交互式操作时,注释中使用的 双引号("" 要使用 转义(\

阅读全文 »

环境信息

  • Centos 7.9.2009
  • docker-ce-19.03.15

Docker 网络模式

Bridge 模式

bridge 模式是 docker 的默认网络模式,不使用 --network 参数,就是 bridge 模式。

当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,默认主机上启动的 Docker 容器会连接到这个虚拟网桥上。

容器启动时,docker 会从 docker0 网桥的子网中分配一个 IP 地址给容器中的网卡。大体流程为在主机上创建一个 `veth pair`,Docker 将 veth pair 的一端放在容器中,命名为 eth0 并配置 IP,网关,路由等信息,将 veth pair 的另一端加入 docker0 网桥。

通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。

Host 模式

如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机一样在 Root Network Namespace,容器中看到的网络方面的信息和宿主机一样,容器使用的网络资源在整个 Root Network Namespace 不能出现冲突。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口,主机名也是使用宿主机的。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

host 模式下的容器可以看到宿主机上的所有网卡信息,可以直接使用宿主机 IP 或主机名与外界通信,无需额外的 NAT,也无需通过 Linux bridge 进行转发或者数据包的封装,可以访问主机上的其他任一容器

使用如下命令参数启动 host 网络模式的容器

docker run --network host --name test1 -p 80:80 -d -it centos:centos7.9.2009

host 模式的容器,没有自己的 network namespace,在 root network namespace 中。进入测试容器 test1,查看网卡、 IP 信息及端口、主机名信息,会看到和宿主机一样的信息。

$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:e7:c0:27 brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:f2:1b:dc:ea brd ff:ff:ff:ff:ff:ff

$ ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:e7:c0:27 brd ff:ff:ff:ff:ff:ff
inet 192.168.142.10/24 brd 192.168.142.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fee7:c027/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:f2:1b:dc:ea brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever

$ [root@test1 /]# netstat -anutp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:81 0.0.0.0:* LISTEN 124/nginx: master p
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 36 192.168.142.10:22 192.168.142.1:61396 ESTABLISHED -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::81 :::* LISTEN 124/nginx: master p
tcp6 0 0 :::22 :::* LISTEN -

host 模式的缺点

  • 容器没有自己的 network namespace ,网络和宿主机或其他使用 host 模式的容器未隔离,容易出现资源冲突,比如同一个宿主机上,使用 host 模式的容器中启动的端口不能相同。

None 模式

使用 none 模式,Docker 容器拥有自己的 Network Namespace,但是,系统并不为 Docker 容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡(lo 回环网卡除外)、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。

参考以下命令创建 none 模式的容器

docker run --network none --name test-none -p 82:80 -d -it centos7:my

容器创建后,进入容器中,查看网卡和 IP 等信息,容器中默认只存在 lo 网卡,不存在其他网卡

$ ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever

$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

以下操作演示手动为容器配置网络

  1. 创建 veth pair

    ip link add veth0 type veth peer name veth0_p
  2. veth pair 的一端 veth0 放入 docker 默认的网桥 docker0,另一端 veth0_p 放入容器中

    首先使用命令 docker inspect test-none | grep "Pid" 找到容器对应的 PID,此处为 84040,根据此 PID 将 veth 的一端放入容器的 network namespace 中

    ip link set dev veth0 master docker0

    ip link set dev veth0 up

    ip link set veth0_p netns 84040

    在宿主机上面检查 veth0,确定其已经加入网桥 docker0,并且 veth0_p 已不在 root network namespace

    $ ip link
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:e7:c0:27 brd ff:ff:ff:ff:ff:ff
    3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:f2:1b:dc:ea brd ff:ff:ff:ff:ff:ff
    12: veth0@if11: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue master docker0 state LOWERLAYERDOWN mode DEFAULT group default qlen 1000
    link/ether 16:7f:98:d8:9d:dc brd ff:ff:ff:ff:ff:ff link-netnsid 0

    重新进入容器,检查网卡信息,可以看到容器中已经有了网卡 veth0_p,状态为 DOWN

    $ ip -d link
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    11: veth0_p@if12: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether be:f1:94:9f:b8:c9 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
    veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
  3. 为容器中的网卡配置 IP 及网关等信息

    为了能在宿主机对容器的 network namespace 进行操作,首先需要将容器的 network namespace 暴露出来,之后可以在宿主机通过 network namespace 名称(此处为 84040,可以自定义)操作 network namespaceLinux network namespace 参考

    $ ln -s /proc/84040/ns/net /var/run/netns/84040
    $ ip netns ls
    84040 (id: 0)

    通过 network namespace 名称(此处为 84040)配置容器中网卡的 IP 地址信息

    ip netns exec 84040 ip link set dev veth0_p name eth0
    ip netns exec 84040 ip link set dev eth0 up

    ip netns exec 84040 ip add add 172.17.0.10/16 dev eth0

    ip netns exec 84040 ip route add default via 172.17.0.1

    进入容器检查网络信息

    $ ip add
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    valid_lft forever preferred_lft forever
    15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 7e:36:b3:20:a1:8c brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.10/16 scope global eth0
    valid_lft forever preferred_lft forever

    $ ip route show
    default via 172.17.0.1 dev eth0
    172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.10

    进入容器测试网络连接

    $ ping 8.8.8.8
    PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
    64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=37.4 ms
    64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=37.0 ms
    ^C
    --- 8.8.8.8 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 1000ms
    rtt min/avg/max/mdev = 37.047/37.234/37.422/0.269 ms
阅读全文 »

环境信息

  • Centos 7.9.2009
  • docker-ce-19.03.15
  • docker-20.10.9

Docker 安装

yum 安装 docker

安装 yum 源,docker官方 centos 安装文档

yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

安装 docker

yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

yum 离线安装 docker

参考链接下载rpm安装包

wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-19.03.15-3.el7.x86_64.rpm
wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-cli-19.03.15-3.el7.x86_64.rpm
wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.4.13-3.1.el7.x86_64.rpm
wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-compose-plugin-2.3.3-3.el7.x86_64.rpm

安装 docker

yum localinstall -y containerd.io-1.4.13-3.1.el7.x86_64.rpm \
docker-ce-cli-19.03.15-3.el7.x86_64.rpm \
docker-ce-19.03.15-3.el7.x86_64.rpm \
docker-compose-plugin-2.3.3-3.el7.x86_64.rpm

以上 2 条命令可以使用以下 1 条命令完成

yum localinstall -y https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-19.03.15-3.el7.x86_64.rpm \
https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-cli-19.03.15-3.el7.x86_64.rpm \
https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.4.13-3.1.el7.x86_64.rpm \
https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-compose-plugin-2.3.3-3.el7.x86_64.rpm

启动docker

systemctl enable docker --now
阅读全文 »

iptables 的底层实现是 netfilternetfilter 的架构是在整个网络流程(TCP/IP 协议栈)的若干位置放置一些钩子,并在每个钩子上挂载一些处理函数进行处理。

IP 层的 5 个钩子点的位置,对应就是 iptables 的 5 条内置链,分别是

  • PREROUTING
  • FORWARD
  • INPUT
  • OUTPUT
  • POSTROUTING


当网卡收到一个网络报文送达协议栈时,最先经过的 netfilter 钩子是 PREROUTING,此处常见的钩子函数是 目的地址转换 (DNAT)。无论 PREROUTING 是否存在钩子处理网络数据包,下一步内核都会通过 查询本地路由表 决定这个数据包的流向

  • 如果是发送给本地进程,则进入 INPUT 链传给本地进程
  • 如果是发送给其他机器(或者其他 network namespace),则经过 netfilterFORWARD 钩子传送出去,相当于将本地机器当作路由器

所有马上要发送到网络协议栈之外的数据包,都会经过 POSTROUTING 钩子,这里常见的处理函数是 源地址转换(SNAT) 或者 源地址伪装(Masquerade, 简称 Masq)

除了 5 条内置的链,iptables 还有 5 张表,这 5 张表主要是用来给 iptables 中的规则(rule)分类,系统中所有的 iptables 规则都被划分到不同的表集合中。5 张表分别为

  • raw - iptables 是有状态的,即 iptables 对数据包有连接追踪 (connection trackong) 机制,而 raw 可以用来去除这种追踪机制
  • mangle - 用于修改数据包的 IP 头信息
  • nat - 用于修改数据包的源或者目的地址
  • filter - 用于控制到达某条链上面的数据包是继续放行、直接丢弃(drop)、或拒绝(reject)
  • security - 用于在数据包上面应用 SELinux

表是有优先级的,5 张表的优先级从高到低是: rawmanglenatfiltersecurityiptables 不支持自定义表。不是每个链上都能挂表,iptables 表与链的对应关系如下图

- PREROUTING FORWARD INPUT OUTPUT POSTROUTING
raw Y N N Y N
mangle Y Y Y Y Y
nat (SNAT) N N Y N Y
nat (DNAT) Y N N Y N
filter N Y Y Y N
security N Y Y Y N

iptables 表和链的工作流程图如下

iptables 命令

常用选项说明

选项 说明 示例
-F ,--flush 清除所有规则,默认规则除外
-P ,--policy 设置默认规则
-t ,--table 指定要操作的表,默认为 filter iptables -t nat -P INPUT ACCEPT
--list ,-L [chain [rulenum]] 列出(指定的链或所有链)的规则 iptables -t nat -L -v -n --line-numbers
--verbose ,-v verbose mode
--numeric ,-n 不解析协议和端口号,以数字的形式显示
--line-numbers 显示规则的行号,可以根据行号对具体的规则进行操作
--jump ,-j 匹配的规则的处理 target iptables -A INPUT -j LOG
--append ,-A chain 像指定的链中追加规则 -A INPUT -i lo -j ACCEPT
--insert ,-I chain [rulenum] 向指定的链中指定的位置插入规则 iptables -I INPUT 10 -p tcp --dport 80 -j ACCEPT
--delete ,-D chain rulenum 删除指定链中的指定位置的规则 iptables -D INPUT 10
--replace ,-R chain rulenum 更新指定链中的指定位置的规则
-S, --list-rules [chain] 按照类似 iptables-save 的输出打印规则
阅读全文 »

环境信息

  • Centos 7

logrotate 程序是一个日志文件管理工具。用于分割日志文件,压缩转存、删除旧的日志文件,并创建新的日志文件

logrotate 是基于 crond 来运行的,其脚本是 /etc/cron.daily/logrotate,日志轮转是系统自动完成的。
实际运行时,logrotate 会调用配置文件 /etc/logrotate.conf
/etc/cron.daily/logrotate 文件内容如下:

/etc/cron.daily/logrotate
#!/bin/sh

/usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
/usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0

可以执行以下命令手动执行日志切割:

logrotate -f /etc/logrotate.conf

以下命令可以检测配置文件是否正确:

logrotate -d /etc/logrotate.conf
阅读全文 »

systemd 是一种用于 Linux 操作系统的系统和服务管理器。它被广泛应用于许多现代 Linux 发行版中,如 CentOS、Fedora、Ubuntu 等。systemd 旨在替代传统的 SysVLSB init 系统,并提供更强大、灵活的系统启动和服务管理功能。

systemd 的关键概念和组件

  • Unit 文件

    • systemd 使用单元(Unit)文件来描述系统资源。常见的单元类型包括:
      • Service Unit (*.service) : 用于定义和管理服务。
      • Target Unit (*.target) : 用于分组和同步一组单元的启动,如 multi-user.target
      • Timer Unit (*.timer) : 用于定时任务,相当于 cron 的替代品。
      • Socket Unit (*.socket) :用于管理网络或 IPC 套接字。
      • Mount Unit (*.mount) : 用于定义挂载点。
      • Path Unit (*.path) : 监控文件或者目录的变化。当监控的文件或目录发生变化时,可以触发相应的 service 单元。
    • 单元(Unit)文件通常存储在以下目录
      • `/etc/systemd/system/ :系统管理员定义的单位文件,优先级较高。
      • /lib/systemd/system/ :发行版提供的单位文件,优先级较低。
      • /run/systemd/system/ :运行时生成的单位文件,临时的。
  • Target 文件

    • systemd 使用 target 取代传统的运行级别(runlevel)。常见的目标包括:
      • multi-user.target : 相当于传统的运行级别 3,支持多用户、无图形界面。
      • graphical.target : 相当于传统的运行级别 5,支持多用户和图形界面。
      • rescue.target : 相当于传统的单用户模式,提供基本的系统恢复环境。
  • 日志管理

    • systemd 使用 `journald` 来管理系统日志。你可以使用 journalctl 命令查看日志
  • 服务间的依赖关系

    • systemd 处理服务间的依赖关系。你可以通过 After=Before=Requires= 等指令在单元文件中定义这些依赖。

systemd 的优势:

  • 并行启动systemd 可以并行启动服务,减少启动时间。
  • 依赖管理 : 能够自动处理服务间的依赖关系,保证系统按需启动服务。
  • 日志记录systemd 的日志管理功能强大,提供了统一的接口查看和分析日志。
  • 定时任务管理 : 通过 timer 单元文件可以灵活地配置定时任务,作为 cron 的替代方案。
阅读全文 »