diff --git a/README.md b/README.md index 489c096..0474b4c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # 笔记 +自 2026-07-01 起,大部分内存来自 AI,需要甄别。 + - [ ] 慢慢整理中,一些没必要的,可以用AI替代的笔记可以删除了。这里面除了 misc 文件夹,其他文件夹就不要新增内容了 diff --git a/misc/README.md b/misc/README.md index 67241e6..fafc38a 100644 --- a/misc/README.md +++ b/misc/README.md @@ -900,17 +900,6 @@ node:internal/modules/esm/load:209 Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:' ``` -## 复习:ESM 中获取 `__dirname` - -```js -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -console.log(__dirname) -``` ## 在 word 中使用 vba 为指定命令分配快捷键 diff --git "a/misc/linux\345\221\275\344\273\244.md" "b/misc/linux\345\221\275\344\273\244.md" new file mode 100644 index 0000000..2993879 --- /dev/null +++ "b/misc/linux\345\221\275\344\273\244.md" @@ -0,0 +1,1137 @@ + +CPU 有两种权限分级:内核态和用户态。 +linux 内核,是纯底层管理程序,无内置命令、无脚本解释器,只管理硬件资源、提供系统调用接口; +那些命令、shell、启动管理器等程序,都属于用户态,主动通过系统调用向内核申请能力; +开机流程:内核初始化硬件后,立刻移交控制权给用户态初始化程序,由它负责执行脚本、启动服务、提供人机交互。 + +`uname -r` 命令用于查看 linux 内核。 + - 只要输出的内核版本带 `-`,则说明使用的是分发版linux(比如Ubuntu等)。此时需要查看对应分发版linux的手册。 + - 如果是核心linux,输出内容只会有版本号。核心linux版本可以查看对应 [kernel release] + - 发行版都有 /etc/os-release 文件? + +## 命令常识 + +- [POSIX.1-2017]:POSIX 规范,跨 Unix(Linux/macOS/BSD/AIX)通用标准,所有合规程序必须遵守,写脚本、跨平台工具必须优先遵循; +- GNU 扩展规范:Linux 上 coreutils / util-linux / procps 等工具独有,macOS/BSD 不一定支持,仅 Linux 日常使用、本地脚本可用。 + +常见 POSIX 规范 +- 短选项 `-` 后面只跟单个英文字母,比如 `-v` +- 无参数的短选项支持连续拼接,比如 -a -b -c = -abc +- 单独写 `--`,代表后面所有内容不再解析为命令参数,全部视为文件 / 操作字符串 +- 选项在前,参数在后。 +- 单引号的内容代表字符串,但不能包含单引号 +- 双引号的内容保留 `$` 和 `\` 的特殊功能 + +常见 GNU 扩展规范 +- 短选项配套对应长选项,可读性强,比如 `--version` +- 传参可以使用 `--key=value` 或 `--key value` + +POSIX 要求预装的命令有? +- shell 解释器(必带) +- GNU coreutils(核心基础工具集,重中之重) +- util-linux(磁盘、挂载、系统基础工具) + - mount umount blkid lsblk dmesg hwclock fdisk losetup free fallocate + - sysctl +- procps(进程 / 内存查看) + - ps top vmstat pidof +- iproute2(网络基础) + - ip ss +- gawk / GNU grep(文本检索) + - grep awk sed +主流发行版 linux 默认自带的命令有? +- systemd 整套工具套件(freedesktop 独立项目) + - systemctl、journalctl、hostnamectl、timedatectl、systemd-analyze +- 网络工具集 ipconfig netstat route curl wget ssh +- 压缩工具 tar gzip bzip2 xz zip unzip +- 文档 / 文本工具 less more diff patch +- 用户管理工具 useradd usermod passwd su sudo +- 系统调试工具 strace lsof tcpdump + +## 未整理 + + +```sh +# 和终端相关的 linux 命令 +who +w +whoami +tty +chvt +last +# 等等 + + + +man ss +# 为何没有显示软件包 + +grep PREEMPT /boot/config-$(uname -r) +# 很多阿里云 / 腾讯云轻量虚拟机,默认内核抢占模型:voluntary(自愿抢占) +# 低负载场景下,内核长时间不触发任务抢占,休眠进程唤醒延迟显著。 +# 核查抢占模式: +# CONFIG_PREEMPT_VOLUNTARY:自愿抢占(容易出现唤醒延迟,云主机通病) +# CONFIG_PREEMPT:完全抢占,交互响应更好(桌面 / 交互式服务器推荐) + +cat /proc/softirqs +# 观察软中断分布 + +ps -eLo pid,stat,wchan | sort -k4 +# 观察进程等待通道,看大量进程卡在何处 + +strace -p 882 -tt -s 256 +# 持续附加跟踪主 sshd PID:882 + +ss -ntp | grep :22 +# 进程中有两个,代表父子进程 +pstree -p | grep sshd +# 可以通过该命令查看,确定确实是父子进程 + +lshw +# 属于 $Rev$ 是啥? + + +dpkg -S $(which ionice) +# ubuntu/Debian 中查看 ionice 命令归属哪个软件 +dpkg -S $(which df) +# 查看不到,此时可以使用 man 命令,在末尾的左下角也会显示对应的软件及其版本。 +# 但 man 下方的软件包可能不一定准?或者说不同版本存在差异? +# 比如 man hostname 中看到所属软件包是 net-tools,但 hostname 在 GNU coreutils 中也有。 +# 而 net-tools 似乎已经过时,现在已经换成了 iproute2,通过 man ip 可以看到确实是 iproute2 + + +prlimit +# 配置进程的资源限制 + +lsof | wc -l +# 查看文件打开数量 + +vmstat 1 +# 查看 vm 状态,每1s刷新 +# 好像没对齐 + +getconf PAGE_SIZE +# 查看内存页单位,通常是 4096,也就是 4KiB。 +# tips:存储的计量单位通常都是 Byte,网络场景则喜欢 bit。 + +watch -n 0.1 -d free -h +# 使用 watch 界面,每0.1 秒刷新,查看 free -h 的情况。-d 表示高亮变化区域 +# 退出使用 ctrl+c +# 其实 free 本身有 -s 实时刷新参数,不过是频繁输出,不好看 + +fg +# 恢复最近一个暂停任务 +bg +# 后台恢复最近暂停任务 +# ctrl+z 是挂起,ctrl+c是终止 +jobs -l +# 查看终端后台作业(仅当前 shell + +pgrep -u k2 +# 查看 k2 的所有进程 PID +kill $(pgrep -u k2) +# 杀死 k2 用户所有进程 + +who -b +uptime +uptime -p +last reboot +cat /proc/uptime +# 查看开机时间 + +getpcaps [PID] +getpcaps $$ +# 属于 libcap2-bin / libcap 工具包,专门用来查看正在运行进程的 Linux Capability(能力集)。 +# =ep 表示全部能力集合 + + +LANG=en_US.utf8 +export LC_ALL=en_US.utf8 + +date +date +%Y/%m/%d +date +%H:%M +cal [month] [year] + +bc +scale=小数点位数 +quit + +[command] --help +man [command] # 空格翻页 + +who +netstat -a +ps -aux + +sync # 将数据同步写入硬盘中 +shutdown # 退出 +poweroff # 关机 +halt # 关机 +reboot # 重启 +systemctl [command] # 上面几个关机/重启命令实际都是调用该指令 + +ulimit -m +# 限制进程最大常驻物理内存 RSS(KB) +# 例:ulimit -m 1048576 单个进程最多 1G 物理内存,超出 malloc 失败; +ulimit -v +# 限制单个进程虚拟内存 VIRT,和 overcommit 互补。 + +lsof -p 6778 +# 查看进程打开日志文件 + +systemctl status user-1000.slice +``` + + +## 虚拟内存相关命令 +```SH +free -h +# 查看内存情况,看看是否有分配虚拟内存 +swapon [--show | -s] +# 无输出说明无虚拟内存 +cat /proc/sys/vm/swappiness +# 查看 swappiness(内核使用 Swap 倾向) +grep SwapTotal /proc/meminfo +# 查看虚拟内存详情 +cat /proc/meminfo | grep Swap +# 查看内核内存统计(包含 Swap) +cat /etc/fstab | grep swap +# 查看开机自动挂载 Swap(永久生效配置) + +ulimit -v +# +cat /etc/security/limits.conf +# + +swapoff -a +# 临时关闭所有虚拟内存 +swapon -a +# 临时开启 + + +fallocate -l 1G /swapfile +# 在根目录生成一个占用 1GB 磁盘空间的空白文件,作为交换文件载体。 +# fallocate:预分配磁盘空间工具,比dd速度极快,直接向磁盘申请整块空间,不会逐字节写 0; +# -l 1G:-l = length,指定文件大小为 1GiB;支持单位 K/M/G/T; +# /swapfile:文件存放路径,放在/根目录,文件名固定 swapfile。 +chmod 600 /swapfile +# 限制 swap 文件权限(似乎没有用,当内存爆满时并没法让 root 单独使用 +mkswap /swapfile +# 格式化空白文件,写入 swap 专属文件系统元数据,把普通文件转换成Linux 交换分区格式,让内核识别这是虚拟内存文件。 +swapon /swapfile +# 临时挂载启用 swap(重启失效),让系统立刻开始使用这个 1G 交换文件作为磁盘虚拟内存。 +# 如果需要持久化,需要额外写入/etc/fstab,具体命令是 echo '/swapfile none swap sw 0 0' >> /etc/fstab + +swapoff /swapfile +# 先卸载关闭 swap(必须第一步,否则删文件会内核报错),查看 free -h 会显示 swap total 为 0 +rm -f /swapfile +# 卸载磁盘 swap 文件 +# 如果有在开机配置文件,还需要修改配置文件 + + + +sysctl vm.swappiness=80 +# 临时修改(重启),积极性改为80 + +vm.overcommit_memory = 0 +# 触发OOM前预留内存,防止整机卡死。内存超额策略,防止无限申请内存 + +grep -A5 watermark /proc/zoneinfo +# 查看内存水位线,确认WMARK_MIN=128MB + + + +# 保护1号systemd、sshd、日志服务,优先驻留物理内存,-1000表示永不OOM杀死,尽量留在物理内存 +echo -1000 > /proc/1/oom_score_adj +# 可将上面命令写入开机脚本 /etc/rc.local,然后赋予执行权限:chmod +x /etc/rc.local + + + +mkdir -p /etc/systemd/user/k.slice.d +vim /etc/systemd/user/k.slice.d/memory-limit.conf +# 写入: +[Slice] +MemoryMax=1200M +MemorySwapMax=800M +# 用于限制用户 k 的内存占用 +``` + +在这里过程中学到了一些命令,记录一下: +```sh +top -d 0.1 -o %MEM +# 按内存占用率从高到低排序,实时刷新,刷新频率为 0.1s + +ls -l /proc/6778/exe +# 查看进程启动命令 +cat /proc/6778/cmdline +# 查看启动参数,找到业务程序 + +kill -9 6778 + + +ps -ef +# 查看进程,其中 PPID 就是父PID +ps -ef | grep 6778 +# 查看与 PID 有关的进程 +ps -o pid,ppid,cmd -p 6778 +# 查看 1481 PID 是由谁调用的。 +cat /proc/6678/status | grep PPid +# 从 proc 中查看状态,也能找到父PID +pstree -ap +# 可以输出树形结构,更加直观 + +ps aux | grep -E "code|vscode-server|extensionHostConnection" | grep -v grep | wc -l +# 统计所有code / code-server / node 属于VSCode的进程 +``` + + +想要彩色提示,需要三个条件:/bin/bash, .bashrc, .profile +bashrc 中应该配置了彩色配置,profile 中应该指明了登录时默认加载 bashrc。 +bash 用于支持配置命令(sh 通常只支持 POSIX 等命令,用于兼容 UNIX) +新创建的用户,自动创建家目录时,会复制默认模板目录 /etc/skel,所有可以在这个目录下创建默认。需要注意 /etc/skel/ 下的文件可读权限,不然新用户不一定复制的了 + +TTY = Teletype / Teletypewriter 电传打字机。早年计算机没有显示器、鼠标,人机交互靠一台叫「电传打字机」的硬件设备:一边打字输入指令,一边打印输出结果,设备缩写就是 TTY。Linux 沿用这个概念,把所有能输入输出文本的终端交互接口统一叫 tty。当使用 VNC 远程连接时,使用 w 命令可以看到来源时 tty + + +## [GNU coreutils] + +GNU Core Utilities(通常简称为 coreutils)是 GNU 操作系统项目中的一个核心软件包,它包含了在类 Unix 操作系统中最基础、最常用的文件和 Shell 操作工具。目前有100多个命令。 + +下面这些常见的命令都是属于这个软件包的: +- ls +- cat +- cp +- mv +- rm +- rmdir +- pwd +- echo +- touch +- date +- df 查看磁盘空间使用情况 +- du 查看文件/目录大小 +- dd 转换和复制文件 +- tail +- head +- sort +- uniq +- wc +- whoami +- hostname +- chmod +- chown +- kill +- id +- who +- test +- dir +- users +- groups +- tee 从标准输入读取并写入标准输出和文件 +- tty 输出当前标准输入所绑定的终端设备路径 +- stat 查看文件 / 文件系统的状态信息(包括 inode 信息) +- uname 用来查看特定系统信息 +- tr +- cut +- basename +- dirname +- sync + + +```sh +ls -ahlsv +# -a 显示 . 开头的文件 +# -h 让文件大小更易读(需搭配 -l 或 -s 参数) +# -l 文件单行格式化显示 +# -s 显示文件占用磁盘大小,默认以 block 为单位 +# -v 表示按数字进行排序 + + +stat -f /run +# 显示 + + + + +df -h +# 查看服务器磁盘分区的整体使用情况,包含总容量、已用、剩余、挂载点、使用率 +du -h --max-depth=1 +# 当前目录下各文件夹大小 + +``` + +## iproute2 + +现代 Linux 统一使用 iproute2 软件包,替代老旧 net-tools 软件包。iproute2 主要以 ip 为核心工具,附带多个配套工具。 + +- ip 搭配 addr、link、route、neigh、rule、tunnel 等参数来查改网络相关配置 +- ss 替换 netstat,查看端口连接。【AI】man手册看不到软件包 +- + +不属于 iproute2,但也和网络相关? +- dhclient 已经停止维护,用 nmcli + + +```sh +ip addr +# 查看 IP 地址 +ip link +# 查看网卡硬件、链路状态 +ip route +# 查看系统路由表 +ip neigh +# 查看 ARP 表 + + +ip link set [网卡名称] up +# 启动网卡 + +``` +## NetworkManager + +iproute2 是 Linux 内核配套底层网络工具集,由 Linux 内核社区维护,是内核网络栈的直接交互工具。 +而 NetworkManager 是 Freedesktop 开源的上层网络连接管理服务,是一套完整的网络管理解决方案,底层依赖 iproute2,更方便使用。 + +- nmcli +- nmtui + +```sh +nmcli connect show +nmcli connect up [网卡] + +``` + + +## [util-linux] + +[util-linux] 是 Linux 内核组织维护的一套 Linux 专用工具集。含有很多命令,这些命令的官方手册,就是 man 手册。 + +AI 分类: +- 磁盘 / 分区管理:cfdisk、sfdisk、wipefs、swapon/mkswap、fstrim、blkdiscard、partx +- 硬件 / 系统信息:lscpu、lsirq、lsmem、hwclock、rtcwake、rfkill、zramctl +- 挂载与文件系统:mount、umount、findmnt、fsck 系列、fallocate、fsfreeze +- 用户 / 登录管理:su、runuser、vipw、chsh、last、lastlog2、login、newgrp +- 进程 / 资源调度:kill、ionice、chrt、taskset、prlimit、unshare、nsenter +- 日志与系统输出:dmesg、logger、hexdump、wall、write +- 通用运维工具:cal、more、script、uuidgen、whereis、column、flock + +- 磁盘 / 分区管理 + - lsblk 列出块设备 + - blkid 查看块设备属性 + - fdisk 管理磁盘分区表 + +- 进程 / 资源调度 + - ionice 读写进程的 IO 调度类和优先级 + +```sh +lsblk -f +# +``` + +### ionice + +- ionice 用于配置进程I/O调度类和优先级,IO调度类有三种:Idle(3)、Best-effort(2)、Realtime(1)、none(0) + - Idle:最低优先级,仅系统无其他 IO 时才允许该进程读写磁盘;普通用户可用,无优先级层级。 + - Best-effort:系统进程默认类别;搭配优先级层级 0~7,数字越小 IO 优先级越高 + - Realtime:最高磁盘优先级,会抢占所有其他 IO,极易把系统 IO 打满;仅 root 用户能使用 + - none: + +注意:主流调度器 mq-deadline/kyber(blk-mq 多队列)不支持 class 分级调度,就算设置了 ionice -c 也没用。 +可以先查看一下调度器是什么类型。 + +可以尝试使用 cgroup v2 中的 io.weight 或者 systemd 中的 IOWeight 进行替代。 + +```sh +cat /sys/block/vda/queue/scheduler +# ??? + +ionice +# 配置 io 作业的优先级 + +watch -d -n 0.1 iostat +# 看磁盘 IO 负载 +``` + +## [systemd] + +systemd 是一套用于 Linux 系统的基本构建模块。它提供了一个系统和服务管理器,该管理器以进程 ID 1(PID 1)的身份运行,并启动系统的其余部分。它替代数十个老式独立工具,虽然是软件,但涉及的级别和内核差不多,不理解内核是很难懂 systemd 的。推荐阅读 [《Rethinking PID 1》] 来了解 systemd 的起源。 + +systemd 具备强大的并行处理能力,采用套接字(socket)和 D-Bus 激活机制启动服务,支持守护进程按需启动,通过 Linux 控制组(cgroups)追踪进程,管理挂载点和自动挂载点,并实现了基于事务依赖关系的复杂服务控制逻辑。 + +其他功能包括 +- 内置专属日志守护进程(`journald`) +- 基础系统配置管理工具(管控主机名、系统时间、区域、语言等) +- 进程与虚拟化资源监控管理(维护已登录用户列表、正在运行的容器和虚拟机等) +- 用户与运行时资源管理(系统账户信息、运行时目录、运行时参数等) +- 网络相关守护进程能力(简易网络配置管理、网络时间同步、日志转发、域名解析) + + + +```sh +systemd --version + +systemd-cgtop --depth 1 -p -d 0.1 +# 按照 cgroup 查看各组性能情况 Task、CPU、Memory、IO +# --depth 1 表示只查看一个层级,即 user.slice,system.slice。为 0 表示只查看根,默认 3 +# -p 是 --order=path 的简写,按照 path 排序,这样不容易上下行切换 +# -d 0.1 刷新频率 0.1s + +systemd-cgls -u user.slice +# 打印完整 cgroup 层级树, +# -u user.slice 只看用户切片 + +systemd-run +# 操作 transient unit + +systemctl +# 操作 cgroup 下的 slice / service / scope + +systemctl get-default +# 查看系统默认开机启动目标(默认运行级别),查看是桌面版还是最小精简版 +``` + +### systemd cgroup ^[1]^ ^[2]^ + +systemd 只封装了四种 cgroup controllers:cpu、io、memory、pids。 + +systemd 以三种不同的 unit 类型暴露了底层内核 cgroups 功能。 +- .service 此单元类型用于封装systemd自身启动的进程的单元,服务单元通常基于磁盘上的单元文件实例化 +- .scope 和 service 单元类似,但区别在于此类单元封装的进程由某个不相关的管理进程派生。与 service 不同,scope 只能通过编程方式声明和启动,即始终是临时的。 +- .slice 切片单元,该单元类型不直接包含任何进程,它始终作为 cgroup 树中的内部节点( service 和 scope 始终是 cgroup 树中的叶子节点)。它的命名直接对应 cgroup 树路径。通过命令 `ls /sys/fs/cgroup/ | grep slice` 查看 + +[systemd 默认会创建四个切片单元]: +- -.slice 是根切片,即所有其他切片的父级,直接映射到 cgroup v2 的顶级目录 +- system.slice 系统服务的默认存放位置(systemd --system) +- user.slice 放置用户会话(所有用户,包括 root),每个用户在下面会有自己的切片 +- machines.slice 虚拟机/容器的预设存放位置 + + +通过 `systemd-cgls` 可以查看 cgroup 树,结构层级类似这样: +```text +-.slice # 根切片 +├─ system.slice # 系统后台服务总切片(所有系统服务放这里) +│ └─ yyy.service # 单个系统服务,每个service独占一个cgroup子目录 +├─ user.slice # 所有用户会话总切片 +│ ├─ user-1000.slice # UID=1000用户专属切片 +│ │ ├─ session-1.scope # 用户登录会话(图形/终端) +│ │ └─ app.service # 用户级后台服务 +└─ machine.slice # 容器/虚拟机总切片 + └─ xx.scope + +/sys/fs/cgroup/cgroup.subtree_control +这个文件可以查看 cgroup 开启的控制器??? + + +【AI】systemd 的 unit 配置是否只针对子 cgroup?对于根 cgroup 的配置项,systemd 没有提供接口,而是需要自己直接编辑文件?直接操作 cgroup?是吗? + +``` + +#### systemctl + +##### 查 +```sh +systemctl cat user.slice +# 查看 user.slice 所有配置(会显示 drop-in 来源路径) +systemctl show user.slice +# 查看合并后最终生效的配置信息 +# 具体是在查看 /sys/fs/cgroup/user.slice/ 文件下的相关文件。 + +systemctl show user-0.slice -p MemoryCurrent +cat /sys/fs/cgroup/user.slice/user-0.slice/memory.current +# 查看 root 用户当前内存占用 + +systemctl show system.slice -p IOWeight +cat /sys/fs/cgroup/system.slice/io.weight +# show 和 cat 还是有区别的,区别在于 show 是 systemd 提供的格式,有可能实现 not set 这样的值,而这种值通过 cat 查看,会有一个具体值,也就是默认值 + +systemctl status user.slice +# 查看状态 + +systemctl list-units --type=service,slice,scope +# 列出所有启用了的 slice、scope、service +systemctl list-units --type=service | grep network +# 查看有关 network 的服务。 +``` + +##### 改 +```sh + +systemctl set-property --runtime 子组和配置 +# 修改配置,写入 /run 目录,重启失效 +# 默认是持久化到 /etc 中,想要临时生效,可以添加 --runtime 参数,它将写入临时内存配置目录 /run,重启失效,方便测试 +# 通过 systemctl cat 命令查看 drop-in 来源可以得到配置文件具体位置,包括 /run 和 /etc 的。 + +systemctl revert xxx.slice +# 重置配置,也就是删除所有 drop-in 文件 + +systemctl edit system.slice +# 和 set-property 区别? + +nano xxx文件???? +systemctl daemon-reload +# 可以手动修改配置(修改单元文件或 drop-in),这种方式修改后,需要重启守护进程 daemon 才会生效。 +# 目的是让 systemd 读取新配置 +# set-property 不需要重启,因为它利用 DBus 运行时即时生效 +# systemctl edit 也不需要,因为它在保存配置后会自动调用。 + + + + +systemctl set-property --runtime user-0.slice MemoryMin= +# 想要恢复默认值,可以直接留空 +systemctl revert user-0.slice +# 或者直接使用 revert,它会删除该 slice 所有 drop-in 文件 + +``` + +#### [systemd slice] + +systemd 切片专属命名规则是短横线`-`表达层级父子关系,比如创建了 root-users.slice.d 文件后,会自动派生出 root.slice 父节点,该节点没有独立 .slice 实体文件。 + + +- `user.slice` 是父切片,限制所有用户进程总和,不存在所有优先级问题。 +- `user-.slice` 是 [slice 模板],代表每位用户的默认设置。 + - 比如,/etc/systemd/system/user-.slice.d/ 目录下的 *.conf 文件中的配置会被所有 user-PID.slice 所引用。 + - 在 /etc/systemd/system/ 下的配置属于手动配置,通过 set-property 修改的配置是 DBus 运行时持久化接口,它固定输出到 /etc/systemd/system.control,而且后者是新版本 systemd 才有的特性。有关配置文件的优先级,可以查看 [systemd.unit 文档] + +需要注意,user-.slice 能作为模板,并不是 systemd 专门指定了一个名叫 user- 的模板,而是由 systemd unit 特性所决定的,其特定对分隔符 `-` 定义了一种配置优先级。 + +登录时用 PAM + systemd 模板 user@.service 区分 UID,只对 UID≥1000 加载限制 +单独为 root 创建 slice,开机时自动将 root 从 user.slice 中移除。 +这两种方式都是AI给的,但似乎都是魔改,不适合。但如果确实需要将 root 从 user.slice 中抽离出来,应该怎么实现呢? + +system.slice,管理什么?管理 sshd 等后台进程?shell bash top kill 算吗、那是否可以给他们设置最小内存,这样也能实现我的需求?目的就是奔溃时允许让 root 登进去调用命令操作。 + +```sh +systemctl set-property --runtime user.slice MemoryMax=700M MemorySwapMax=0 +# 配置所有用户最大内存占用,包括root用户 +systemctl set-property --runtime user-0.slice MemoryMin=200M +# 然后再单独为 root 用户配置最小内存占用 +systemctl show user.slice | grep Memory +# 查看配置情况 + + +# 创建临时目录 +mkdir -p /run/systemd/system/root-users.slice.d +# 写入配置文件,注意,root-users.slice 这种命令,会让 systemd 自动派生父节点 root.slice +cat > /run/systemd/system/root-users.slice.d/override.conf <<'EOF' +[Slice] +MemoryMax=max +CPUWeight=10000 +TasksMax=infinity +EOF +# 重载systemd让配置生效 +systemctl daemon-reload +systemd-run --slice=root-users.slice --shell +# systemd-run:临时创建一个临时 systemd 服务单元 +# --slice=root-users.slice:指定归属 cgroup 切片 +# --shell:直接启动交互式 bash shell + +cat /proc/self/cgroup +# 可以查看当前 shell 属于哪个切片。 +systemctl cat root-users.slice +# 查看 slice 资源配置 +systemctl list-units --type=scope +# 查看正在运行的临时 scope +``` +#### IO 封装 + +IOAccounting=:开启 IO 统计 +IOWeight=:IO 调度权重。(需要 % util 持续≈100% 才生效?) +IODeviceWeight=:单块磁盘 IO 权重 +IOReadBandwidthMax=:读带宽上限 +IOWriteBandwidthMax=:写带宽上限 +IOReadIOPSMax=:读 IOPS 上限(限制 4k)为何普通用户没有这个选项,但又确实存在? +IOWriteIOPSMax=:写 IOPS 上限 +IODeviceLatencyTargetSec=:设备 IO 延迟目标 + +2. IO 压力监控(PSI) +IOPressureThresholdSec= +IOPressureWatch= + +3. 进程 IO 调度优先级(linux SCHED_IDLE 等) +IOSchedulingClass= +IOSchedulingPriority= + +#### 开机脚本 + +老式 sysvinit 时代开机执行的脚本文件是 /etc/rc.local, +如果想用老式开机脚本,需要让 systemd 开启兼容,具体是查看 rc-local.service 是否已经启用。 + +有三种方式查看 +```sh +systemctl is-enabled rc-local +# enabled:已设置开机自启(你执行过 systemctl enable) +# disabled:未设置开机自启 +# static:默认原生状态,服务文件缺少 [Install] 安装段,无法直接 enable/disable,必须手动启用才能开机运行 +# masked:被屏蔽,完全无法启动 + +systemctl status rc-local +# 直接查看状态 + +systemctl list-unit-files --type=service | grep rc-local +# 从所有 unit 中找到 rc-local 查看状态 +``` + +## shadow-utils + +shadow-utils 是 Linux 系统中用于管理用户账户、用户组以及 Shadow 密码文件的实用工具集 + +常用命令: +- useradd:创建新的用户账户或更新默认的新用户信息。 +- userdel:删除用户账户及其相关文件。 +- usermod:修改现有用户账户的属性。 +- passwd:修改用户密码 +- +- groupadd:创建新的用户组。 +- groupdel:删除指定的用户组。 +- groupmod:修改用户组的定义或属性。 +- groupmems:管理用户主组的成员。 +- gpasswd:管理 /etc/group 和 /etc/gshadow 文件。 +- +- lastlog:打印所有用户的最后登录时间。 +- chage:更改用户密码的过期信息。 +- newgrp:登录到一个新的用户组。 +- vipw / vigr:安全地编辑密码、组、Shadow 密码或 Shadow 组文件(编辑时会自动加锁,防止冲突)。 +- +- pwconv:将系统密码转换为 Shadow 密码格式。 +- pwunconv:将 Shadow 密码转换回标准的 UNIX 密码格式。 +- pwck:验证密码文件和 Shadow 文件的完整性。 +- grpck:验证用户组配置文件的完整性。 + +```sh +useradd -m -s /bin/bash k +# 创建用户, +# -m:自动创建家目录,默认是在 /home/[user] +# 如果忘记创建家目录,可以手动创建文件夹,注意权限也要分配给对应用户,不然用户无法登录 +# chown -R k:k /home/k 将目录以及目录下的所有文件所有者改为 k +# -s /bin/bash:指定用户 shell,默认为 /bin/sh +# sh 代表 POSIX 兼容模式,用于兼容所有 unix 平台,而 bash 更加强大,支持命令补全、别名、彩色提示等等。 +# 如果忘记修改 shell 了,可以通过 usermod -s /bin/bash k 修改 + +passwd k +# 设置密码,通常不推荐设置密码,而且服务器一般默认禁止密码登录,具体可以查看一下 +# cat /etc/ssh/sshd_config | grep PasswordAuthentication +# 的设置,默认为 no +# 所以通常建议使用公私钥进行登录。 +# 在 window 上运行命令 ssh-keygen -t rsa,然后生成一份公私钥,将私钥保存在 C:\Users\k\.ssh 中 +# 然后将公钥内容复制到服务器 /home/[user]/.ssh/authorized_keys 文件中。 +# 这样就可以通过秘钥登录了 + +su k2 +# 切换为 k2 身份,但环境不变。 +su - k2 +# 切换 k2 登录,但环境也变更为 k2 的 +sudo su - k2 +# 如果忘记 k2 密码,但知道自身密码以及具有管理员权限,可以免密码登录 k2 +su - +# 后面不带用户名,代表切换 root 登录,需要输入 root 密码 +# su 不一定成功,比如 k2 没有设置密码时代表密码不可用,此时 su k2 是无法切换过去的。 +# 或者说系统设置了 pam_wheel 权限 + + +# 切换为 root,需要 root 密码 +sudo -i +# 切换为 root 权限 + +passwd -S k2 +# 查看用户 k2 的密码情况 +# P 代表已设置可用密码 +# L 代表密码锁定(无有效登录密码) + + +chgrp # 改变文件所属群组 +chown # 改变文件拥有者 +chmod # 改变文件权限 + +useradd # 创建用户 passwd k,密码 Qwe112233@ +adduser # 相当于 useradd -m,自动创建家目录 +passwd # 设置用户密码 +change # 修改密码属性 +usermod # 修改用户信息 +userdel # 删除用户 +id # 查看 UID、GID +chfn # change finger +chsh # change shell +groupadd # +groupmod +groupdel +gpasswd # 群组管理员功能 + + +su # 切换到 root 身份,需要获取 root 密码 +sudo # 以 root 权限运行命令,需要用户本身密码,同时用户须在 /etc/sudoers 中 +visudo # 不要直接编辑 /etc/sudoers 文件,而是借助此命令, +usermod -aG sudo k # 默认创建了一个 sudo 组,可以将新用户添加到此组中 +# -a 是追加 append,-G 是添加附属组。 +# 主组:一个用户只能有一个主组,创建用户时自动分配,默认组名和用户名相同。 +# 附属组,一个用户可以归属多个附属组,附属组才是常规上理解的“组” +gpasswd -d k sudo # 将用户 k 从 sudo 组中移除 + +cat /etc/ssh/sshd_config | grep PubkeyAuthentication +# 查看是否允许 ssh 秘钥登录,默认是允许的 +cat /etc/ssh/sshd_config | grep PasswordAuthentication +# 查看默认是否允许密码登录,默认是不允许的 + + +whoami +who +who -uH +id +w +tty +users +# 上面这些都只能看到顶层用户(SSH、VNC),诸如 vscode 这种远程连接的是看不到的 +# 此时可以使用进程搜索哪些用户在线 +ps -eo user | sort -u + + +pkill -u k +# 关闭用户 k 的所有进程(正常关闭) +pkill -9 -u k +# 强制关闭 + +``` + +## 其他常用单软件 + +- hdparm +- locale +- curl +- wget + +### 软件管理 + +- Debian 系 + - 软件包后缀 `.rpm`,使用 yum(centos7)或者 dnf(centos8+,yum升级),底层调用 rpm +- RHEL 系 + - 软件包后缀 `.deb`,使用 apt 管理,底层调用 dpkg + +### curl + +### wget + +```sh +wget [参数] 下载地址 + +``` + +### [xx]stat + +iostat +pidstat +mpstat +tapestat +cifsiostat + +vmstat: procps-ng? + + +### man / whatis + +man 命令,全称 manual 手册,是 linux 系统中的离线手册。 +手册分为9个章节: +1. 可执行程序或者 shell 命令 +2. 内核提供的系统调用函数 +3. 库调用(程序库提供的函数) +4. 特殊的文件(通常位于 /dev 下) +5. 文件格式和约定(比如 /etc/passwd) +6. Games +7. Miscellaneous(其他一些零碎的,比如一些宏包之类的) +8. 只能用 root 执行的命令 +9. 内核程序(非标准) + +通过 whatis 可以查看对应命令在 man 中的各章节简介(one-line) + +```sh +whatis hostname +# 查看 hostname 在各个章节中的简介 + +man 5 hostname +# 查看 hostname 第5章节内容。默认打开第一章节 +``` + +### grep + +grep 命令用于打印与 PATTERNS(匹配内容) 所匹配的行。早期一些旧命令(egrep, fgrep, rgrep)已经废弃,可以使用参数实现对应功能。 + +```sh +命令 | grep [参数] "匹配内容" +# 管道用法(最常用) + +grep [参数] "匹配内容" 文件名 +# 其他用法 + +grep -E +# 等同 egrep,表示扩展 grep,支持扩展正则(EREs) +grep -F +# 等同 fgrep,表示将匹配内容识别为纯字符串 +grep -r +# 等同 rgrep,表示递归 grep +``` + +以实际命令来学习 +```sh + +ps aux | grep -- -bash +# 注意,这条 grep 命令和 grep '-bash' 并不等价 +# 必须使用 -- ,因为单引号是 shell 的语法,grep 接收到的还是 -bash,依旧会将其识别为参数。 + + +cat /proc/meminfo | grep -w -e Buffers -e Cached -e SReclaimable +grep -wi -e Buffers -e Cached -e SReclaimable /proc/meminfo +# 从文件 /proc/meminfo 中读取出 Buffers + Cached + SReclaimable 的值 +# -w 表示精准匹配(默认模糊匹配) +# -i 表示忽略大小写(默认严格大小写) +# -e 表示增加匹配内容(解释的好像不太好,但懂那个意思) + +systemctl status user.slice | grep -m 1 "Memory:" +# -m 1 只取第一个匹配到的值 +``` + +### hostname + +主机名,有关文件有: +- `/proc/sys/kernel/hostname` 内核文件,系统重启时通过读取磁盘文件获取主机名 +- `/etc/hostname` 磁盘文件 + +```sh +hostname +uname -n +cat /proc/sys/kernel/hostname +cat /etc/hostname +# 查看主机名 + +hostname K +echo "K" > /proc/sys/kernel/hostname +# 临时修改主机名,主机重启失效 +# 注意,修改后,当前会话 bash 并不会刷新,因为 bash 一般使用 /h 获取主机名,登录时获取到该值就会放在缓存中 +# 想要刷新当前会话的主机名,可以执行 exec bash 刷新 bash。 +# 还有,linux 中一切皆文件,指的是内核对外提供的统一访问接口。 +# 对于进程自己的缓存(堆栈内存)并不存在具体的文件,比如 bash 启动时 /h 获取主机名后,就会始终存放在进程自己申请的内存中了。这点要清楚。 + +echo "K" > /etc/hostname +# 修改磁盘主机名,重启生效。 +hostnamectl set-hostname K +# 修改内核和磁盘主机名。 +``` + +## dmesg 和 journalctl + +dmesg 读取内核环形缓冲区(kernel ring buffer),需要管理员权限运行 +journalctl 是 systemd 日志客户端。systemd 服务提前抓取、存为二进制日志库,journalctl 负责读取这个二进制日志库,普通用户可以读取自己的相关信息,但无法读取其他用户的。 + +```sh +sudo dmesg -H +# 查看全部内核日志 +# -H 表示分页视图,方便查看 +sudo dmesg -xT -l err,warn +# -T 表示修改日期显示方式为时分秒而不是开机时间 +# -x 表示显示日志类型 +# -l 指定日志级别,过滤无用的 info 日志。故障级别是 crit,alert,emerg +sudo dmesg -wT +# -w 持续监听内核缓冲区,实时打印 +sudo dmesg -c +# -c 打印日志然后清空。-C 是直接清空 + + +journalctl -k --since "2026-07-08 14:00" --until "2026-07-09 05:00" > oom_history.log +# ? +``` + +## /proc + +linux 把 CPU、内存、IO、进程状态全部存在 `/proc` 文件夹中,`ps`、`top`、`free` 等命令都是属于软件工具,本质上就是读取 `/proc/stat`、`/proc/meminfo`、`/proc/[pid]/status`、`/proc/[pid]/stat` 等文件的内容,然后整理输出。 + +### ps + +`ps`:process status 进程状态。 + +`pstree` 可以以树形输出进程关系,其中 `*[{}]` 表示线程 + +```sh +pstree -u +# 在括号中显示进程所属用户,没有括号代表 root +pstree k +# 只查看用户 k 的 +pstree -p +# 显示 PID +pstree -ap +# 显示 PID 和进程命令(不一定显示全) + +ps -ft pts/1 +# ?? + +ps -eo user | sort -u +# -e:every,显示系统全部进程 +# -o user:自定义输出字段,只查看 user 列 +# sort -u 排除并去重(--unique) + +ps -u k -o rss | awk 'NR>1{sum+=$1}END{print sum/1024,"MB"}' +# 查看用户 k 所有进程占用物理内存总和。 + +ps -ef | grep [pid] +# 查看 PID 的进程相关信息 + +ps -o pid,ppid,cmd -p [pid] +# 查看指定 pid 有谁调用 + + +``` + +### free + +free 属于 procps-ng(procps)工具集,本质上读取的是 /proc/meminfo 数据,用于查看物理内存和交换内存情况,有以下值,默认单位是 KiB,原因是 /proc/meminfo 中使用 KiB 作为单位: +- total:总内存,读取 MemTotal / SwapTotal 值 + - 具体地说,MemTotal = 物理总内存 - 硬件预留内存 - 内核占用内存 + - 想要查看物理总内存,可以直接查看 bios 数据 `dmidecode -t memory` +- free:裸空闲内存,读取 MemFree / SwapFree 值 + - 属于狭义上的空闲内存,不考虑可回收内存等信息 +- shared:共享内存,读取 Shmem 值,主要是 tmpfs 占用内存 +- available:可用内存,读取 MemAvailable 值(内核≥3.14) +- buffers:内核块设备缓冲,读取 Buffers 值 +- cache:文件页缓存 + 可回收 slab 内存。通过计算 Cached + SReclaimable 得到 +- buff/cache:buffers + cache +- used:已使用内存,也就是“总的”减去“可用的”,具体是通过计算 MemTotal - MemFree - Buffers - Cached - SReclaimable / SwapTotal - SwapFree 值得到。 + - 注意,SwapCached 值指曾经被换出到 swap,之后又重新加载回内存,但 swap 并不会回收这块数据,目的是下次回收时,不需要再次写入 swap ,这能节省磁盘 IO 开销。所以计算 swap used 时不会减去 SwapCached + +```sh +free -h -w +# -h / --human,自动适配单位 +# -w / --wide,将 buffers 和 cache 拆分成两列单独展示 + +dmidecode -t memory +# 通过查看 bios 数据来查看物理总内存 + +systemctl status user.slice | grep -m 1 "Memory:" +# 查看 user.slice 的内存占用情况 +``` + +### top + +`top` 和 `ps` 一样,只不过是实时系统资源监控工具。 +- 按键 `e` 切换内存单位 KiB / MiB / GiB +- 按键 `d` 设置刷新间隔(单位秒) + +- `htop` +- `pidstat` + +```sh +top -e g -E g +# -e 指定进程列表 VIRT/RES/SHR 单位 +# -E 指定顶部内存行 Mem/Swap 单位 +# 可选值:k(KiB) / m(MiB) / g(GiB) / t(TiB) / p(PiB) / e(EiB) + +top -d 0.1 +# 指定刷新间隔0.1s(默认3s) +top -u k +# 只监控指定用户 k 的进程 +top -p 3839 +# 只监控指定PID进程 + +top -b -n 1 +# 等同 ps 快照。 +# -b 表示批处理,-n 1 表示只输出一次就退出,适合脚本采集数据 +``` + +```sh +top - 16:09:01 up 55 min, 5 users, load average: 1.71, 14.66, 10.37 +Tasks: 144 total, 1 running, 143 sleeping, 0 stopped, 0 zombie +%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st +MiB Mem : 1690.6 total, 1165.8 free, 312.1 used, 212.6 buff/cache +MiB Swap: 0.0 total, 0.0 free, 0.0 used. 1226.3 avail Mem + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 4430 k 20 0 1747900 65656 12196 S 0.0 3.8 0:13.73 MainThread + 4479 k 20 0 1555300 36732 9980 S 0.0 2.1 0:04.64 MainThread +``` + +第1行:系统运行状态 +``` +top - 15:43:33 up 30 min, 2 users, load average: 0.25, 0.17, 0.18 +``` +- `15:43:33`:当前系统时间 +- `up 30 min`:服务器已开机运行30分钟 +- `2 users`:当前登录系统的用户总数(SSH/本地终端都算) +- `load average: 0.25, 0.17, 0.18`:1分钟、5分钟、15分钟平均负载 + - 单核机器:负载接近1代表满载;多核看逻辑CPU总数,4核CPU负载4才是满载 + +第2行:任务进程统计 +``` +Tasks: 138 total, 1 running, 137 sleeping, 0 stopped, 0 zombie +``` +- `138 total`:系统当前全部进程总数 +- `1 running`:正在占用CPU运行的进程 +- `137 sleeping`:休眠进程(等待IO/定时事件,绝大多数服务都处于此状态) +- `0 stopped`:被暂停的进程(`Ctrl+Z`后台暂停那种) +- `0 zombie`:僵尸进程(子进程退出父进程未回收,0代表无异常) + +第3行:CPU 全局占用(单位百分比) +``` +%Cpu(s): 4.8 us, 4.8 sy, 0.0 ni, 90.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st +``` +注意一下,这顶部的百分比,是整机的百分比,下方表格的 %CPU列则不是整机的百分比,而是单核上限100,双核则上限200 +字段含义: +- `us` user:用户态CPU占用(程序自身代码,业务进程消耗) +- `sy` system:内核态CPU占用(系统内核、驱动调用开销) +- `ni` nice:调整过优先级的用户进程CPU占比 +- `id` idle:空闲CPU(这里90.5%,CPU非常闲) +- `wa` iowait:CPU 空闲但在等待磁盘 IO 的时间占比(数值高代表磁盘瓶颈) +- `hi` hardware irq:硬件中断消耗CPU +- `si` software irq:软中断消耗CPU(网络收发高时会上涨) +- `st` steal:被虚拟化宿主机抢占走的CPU(物理机永远0,云虚拟机才会有数值) + +第4行:物理内存 Mem(单位 MiB) +``` +MiB Mem : 1690.6 total, 214.3 free, 344.3 used, 1132.1 buff/cache +``` +- `total`:总物理内存 ≈ 1.7G +- `free`:完全空闲、未分配的裸内存(数值很小是Linux正常机制,会缓存文件) +- `used`:进程程序实际占用内存 +- `buff/cache`:缓冲区+页缓存(磁盘文件、目录元数据缓存,可回收给进程使用) + +第5行:交换分区 Swap + 可用内存 +``` +MiB Swap: 0.0 total, 0.0 free, 0.0 used. 1170.2 avail Mem +``` +- `total`:交换分区总大小(0 = 未创建 swap) +- `free`:交换分区空闲空间 +- `used`:已经把内存数据写到磁盘 swap 的容量 +- `avail Mem`:系统可分配给新程序的总内存,也就是 /proc/meminfo 中的 MemAvailable,数值等于 free 空白内存 + 可回收的buff/cache缓存 + 可回收slab内核内存。它写在交换分区这一行只是排版美观,并不是指虚拟内存 + +第6行表头 +```sh + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND +4430 k 20 0 1747900 65656 12196 S 0.0 3.8 0:13.73 MainThread +``` +- PID:Process ID,进程唯一ID号 +- USER:运行用户,启动进程的系统用户名 +- PR:Priority,内核调度优先级,默认20,数值越小优先级越高 +- NI:Nice值,用户可调优先级,范围 -20~19;负数更高权限,0默认 +- VIRT:Virtual Memory,虚拟内存总量(VMSIZE):堆+栈+共享库+交换占用,单位KiB。这是程序申请的内存,并不是实际占用内存 +- RES:Resident Memory,常驻物理内存(RSS):真正占用的物理RAM,不含交换 +- SHR:Shared Memory,共享内存大小(共享库、共享页面) +- S:Status,进程状态, + - S,sleeping 休眠 + - R,运行 + - Z,僵尸 + - T,暂停 + - D,不可中断休眠,调用 kill -9 也无法终止。 +- %CPU:CPU占用率,单CPU核心瞬时占用,多核进程可超过100% +- %MEM:内存占比,该进程占用整机物理内存百分比 +- TIME+:CPU累计时间,进程启动后总共占用CPU的时长(时分秒毫秒) +- COMMAND:启动命令,进程程序名/完整启动命令 + + +[kernel release]: https://www.kernel.org/releases.html +[systemd]: https://systemd.io/ +[1]: https://systemd.io/CONTROL_GROUP_INTERFACE/ +[2]: https://systemd.io/CGROUP_DELEGATION/ +[util-linux]: https://github.com/util-linux/util-linux/tree/master +[GNU coreutils]: https://github.com/coreutils/coreutils +[《Rethinking PID 1》]: https://0pointer.de/blog/projects/systemd.html +[POSIX.1-2017]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html +[slice 模板]: https://www.freedesktop.org/software/systemd/man/latest/user@.service.html#Controlling%20resources%20for%20logged-in%20users +[systemd slice]: https://www.freedesktop.org/software/systemd/man/latest/systemd.slice.html# +[systemd 默认会创建四个切片单元]: https://www.freedesktop.org/software/systemd/man/latest/systemd.slice.html# +[systemd.unit 文档]: https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html# \ No newline at end of file diff --git "a/misc/linux\345\237\272\347\241\200.md" "b/misc/linux\345\237\272\347\241\200.md" new file mode 100644 index 0000000..35380c6 --- /dev/null +++ "b/misc/linux\345\237\272\347\241\200.md" @@ -0,0 +1,108 @@ + +学习 Linux,并不是学习命令,而是学习Linux的相关概念,这需要学习一些内容: +- 计算机的组成,了解各种设备的作用 +- Linux发展历史 +- 文件系统 + +## draft + +systemd 是什么,和 `systemctl` 和 `sysctl` 命令关系 +snap 软件包又是什么 + + +## 软件管理 + + +- Debian 系 + - 软件包后缀 `.rpm`,使用 yum(centos7)或者 dnf(centos8+,yum升级),底层调用 rpm +- RHEL 系 + - 软件包后缀 `.deb`,使用 apt 管理,底层调用 dpkg + + +```sh +yum repolist all +# 查看现有全部仓库 + +``` + +## 区域语言 + +Linux 的 locale 定义一套本地规则:字符编码、大小写、排序规则、日期格式、数字格式等。 +LC_ALL:全局环境变量,一旦赋值,会覆盖所有 LC_* 细分语言变量。 +设置 LC_ALL 的值 C 表示兼容 POSIX 规范,所有排序是按照 ASCII / 字节原始编码排序的。 +如果设置为 en_US,则代表按照本地化字符排序规则。 +C 和 en_US 在字母方面排序是一样的,都是 AaBbCc…… 这样排序,但 `.` 时排序则完全不同。 +比如 a2 a10 .test 三个文件,在在 POSIX 规范下 .test 在最前,但在 en_US 下则在最后。 +注意,对于数字 2 和 10,想要让 2 在 10 前面,需要的不是设置 locale,而是使用 ls 的 -v 参数。 + +注意区分“区域语言”和所谓“字符集”的区别。中文显示乱码或无法输入中文,这是属于字符集的锅,和区域语言没关系。 + +## 网络 + +/sys/class/net 是系统内核网卡目录,里面每个文件夹对应一张网卡。其中 lo 文件夹就是本地环路 +/etc/netplan/ 是? +/etc/network/ 是? + + + +## 文件系统 + +### 磁盘分区 +- 分区方式 + - MBR(MS-DOS 分区表) + - 以前,第一个磁区通常 512B,其中 446B 拿来做开机管理,64B 用来做分区表,最后2B做开机签名,标记这是可启动磁盘 + - MBR 中的分区表固定划分为四个分区槽,分区槽只能是主分区Primary(可以格式化)或扩展分区Extended(不可以格式化,仅作为容器,一块磁盘最多存在一个) + - 扩展分区内部可以划分多个逻辑分区Logical + - 单块磁盘最大识别 2TiB / 2.2TB(MBR 分区记录中存储扇区数量只用 32 位无符号整数,标准扇区大小规定为 512 字节) + - GPT(GUID 分区表) + - 没有主分区、扩展分区、逻辑分区的概念,所有分区都是独立主分区 + - 扇区大小,也就是物理扇区,值硬件真实最小读写单元。 + - 以前扇区大小提桑时 512字节,现在的扇区大小通常时 4096字节(4Ki)。但为了兼容,4K 物理硬盘采用 512e的方式,把 1 个真实 4K 硬件块虚拟拆分为 8 个 512 字节逻辑块(LBA);代价是零散小写入会产生额外写开销,需要开启 4K 对齐才能减小开销。 + - 4K 对齐无法消除 “单块内部修改产生的单次 RMW”,但能杜绝 “跨块导致的多次叠加 RMW”,大幅降低整体写入开销; + - GPT 采用 LBA 逻辑区块地址来记录分区信息,比如一块 4TB 硬盘,如果使用 512e 的方式,则它可以有 4TB / 512B 个 LBA。GPT 分区将前面 LBA0 拿来做保护型MBR,LBA1 做GPT表头记录,LB2~33 来作为分区记录,默认情况下一个LBA可以做4笔分区记录,所以可以有4*32=128笔分区记录, + - GPT 采用64位来记录LBA编号,所以最大寻址范围 / LBA 编号范围是 LBA0 到 LBA2^64-1,如果采用 512e,则可以标记 $2^{64} \times 512\mathrm{B} = 8\mathrm{ZiB}$ 的硬盘 + +### 权限 + +每一个文件都具有 User、Group、Others 三种身份的权限。 +每一个用户拥有一个用户UID和群组GID。 +- /etc/passwd 中存储了用户信息,每行格式为 账号名称:密码:UID:GID:用户信息说明:用户目录:shell 。 + - UID 为 0 表示管理员。约定上 1~999时系统账号,通常都是不可登录的 + - 现在密码都是显示x,实际密码放在 /etc/shadow 文件中 + - shell 为 /sbin/nologin 时表示不可登录 +- 同理 /etc/group 存储了群组信息,每行格式为 组名:群组密码:GID:组员 + - 密码同样是另存在 /etc/gshadow 中 + + + +### 常见文件夹介绍 + +Linux中一切皆文件 + +- /dev 几乎所有硬件都在此目录下,比如硬盘、U盘、鼠标、键盘、虚拟设备等 + - /dev/console 在 tty 中执行 cat /dev/console 后,似乎无法退出,只能通过 ctrl+alt+F2 这种方式切换登录然后 kill,但在 pts 中则可以 ctrl+c 退出。 +- /proc 内核虚拟文件系统,是内核驻留在内存的动态虚拟控制面板。可以查看系统实时资源、临时调整内核内存 / 网络等运行策略,关机后全部清空,不属于磁盘持久文件。使用 `ll /proc` 可以注意到 total 的值和实际的值相差甚远,大部分文件的大小都是 0,这是因为 ll 统计的是磁盘占用块的数量,而那些大小为 0 的实际上是在内存中的。 + - /proc/uptime 一行两个数字,第一个数字是开机后的总允许秒数,精确到毫秒。 + - /proc/pressure/ 是 PSI,用来统计进程因资源不足而停滞等待的耗时占比 + - /proc/[pid] 代表一个进程,存放该进程专属信息 + - /proc/self/ 代表? + - /proc/sys/ 内核运行参数调节区。可以再细分多个模块,比如 + - /proc/sys/vm/:Virtual Memory 虚拟内存全套开关 + - /proc/sys/net/:网络内核参数(TCP 连接、端口、缓冲区) + - /proc/sys/fs/:文件系统参数(最大打开文件数等) + - /proc/meminfo、/proc/cpuinfo、/proc/uptime:可用于读取整机全局硬件 / 资源状态,无修改能力。 +- /etc + - /etc/os-release +- /run 运行时数据,挂载在内存,重启失效。所谓临时配置就是在这里 + - /run/systemd/system.control/user-0.slice.d/ +- /var + - /var/run/utmp +- /sys + - /sys/block + - /sys/block/vda/queue/nr_requests ??? + - /sys/fs/cgroup/ + +#### /etc/ssh/sshd_config + +ClientAliveInterval,为 0 表示不进行心跳检测,不会主动踢掉 ssh 连接。 +ClientAliveCountMax,默认为 3,表示连续3次没有收到回应时断开。 \ No newline at end of file diff --git "a/misc/linux\350\275\257\344\273\266.md" "b/misc/linux\350\275\257\344\273\266.md" new file mode 100644 index 0000000..ff19580 --- /dev/null +++ "b/misc/linux\350\275\257\344\273\266.md" @@ -0,0 +1,88 @@ +### fio 磁盘测速软件 + +```sh +fio -name=seq-r -rw=read -bs=1M -size=100M -direct=1 -numjobs=1 -filename=/tmp/fio_test +fio -name=rand-r-4k -rw=randread -bs=4k -size=100M -direct=1 -numjobs=1 -filename=/tmp/fio_test +``` + +```sh +#!/bin/bash +TEST_FILE=/tmp/fio_test +SIZE=1G + +echo "===== 顺序写 1M =====" +fio -name=seq-w -rw=write -bs=1M -size=$SIZE -direct=1 -numjobs=1 -filename=$TEST_FILE +echo -e "\n===== 顺序读 1M =====" +fio -name=seq-r -rw=read -bs=1M -size=$SIZE -direct=1 -numjobs=1 -filename=$TEST_FILE +echo -e "\n===== 4K随机写 =====" +fio -name=rand-w-4k -rw=randwrite -bs=4k -size=$SIZE -direct=1 -numjobs=1 -filename=$TEST_FILE +echo -e "\n===== 4K随机读 =====" +fio -name=rand-r-4k -rw=randread -bs=4k -size=$SIZE -direct=1 -numjobs=1 -filename=$TEST_FILE + +rm -f $TEST_FILE +echo "测试完成,临时文件已清理" +``` + +### stress-ng 手搓内存占用 + +压测工具 +```sh +stress-ng --vm 1 --vm-bytes 400M --timeout 30 +# --vm 1 创建 1 个独立内存压测线程/进程? +# --vm-bytes 1400M 控制每个 vm 进程?占用内存大小 +# --timeout 30 运行 30s 后自动结束,默认需要 ctrl+c 才结束,这个时间是CPU运行时间,遇到卡顿会延长 + +stress-ng --vm 1 --vm-bytes 100M --vm-lock --timeout 30 +stress-ng --vm 1 --vm-bytes 100M --vm-populate --timeout 30 +# --vm-lock 调用 mlock 锁定内存,禁止内核回收、swap 迁出页面? +# --vm-populate,mmap 阶段一次性预分配所有物理页面? +# 效果就是在 top 中对应压测进程不会抖动。不过实测这两个在前面 10s 还是会抖动,后面才稳定。 +# 但修改占用 500M 时,--vm-lock 参数加上后,直接不分配内存了。--vm-populate 参数加上后,则是30s还是在抖动,加了等于没加。这个时候我就怀疑,这两个参数大概率根本就没法防抖动,所以不加这两个参数,纯测试了 100M,发现100M在10s后也不抖动了! +``` + +感觉这个软件,还不如 AI 手搓的 C 程序: +```sh +vim alloc_mem.c +# 创建文件, 写入下面的代码 +gcc alloc_mem.c -o alloc_mem +# 编译生成可执行程序 +./alloc_mem 1500 +# 一次性申请占用 1500M内存,按下 ctrl+c 取消占用 +``` + +具体源码: +```c +#include +#include +#include +#include +#include // mmap / munmap 内存映射系统调用、映射宏定义 + +// 参数:./alloc_mem 占用大小(MB) +int main(int argc, char **argv) +{ + // 入参校验 + if (argc != 2) { + printf("Usage: %s MB\n", argv[0]); + return 1; + } + long mb = atol(argv[1]); + long bytes = mb * 1024 * 1024; + + // MAP_ANONYMOUS 匿名内存,MAP_POPULATE 一次性预分配物理页 + char *p = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0); + if (p == MAP_FAILED) { + perror("mmap failed"); + return 2; + } + // memset逐字节写入内存,强制 CPU 访问每一页虚拟内存,确保全部物理页绑定,避免缺页延迟分配 + memset(p, 0x55, bytes); + // 执行到 printf 这一行时,指定大小的物理内存已经 100% 全部占用完毕,没有惰性延迟分配。 + printf("Success alloc %ld MB memory, hold 60s\n", mb); + sleep(60); + + // mmap 配套释放函数,归还虚拟内存与物理内存给操作系统; + munmap(p, bytes); + return 0; +} +``` diff --git "a/misc/linux\351\253\230\347\272\247.md" "b/misc/linux\351\253\230\347\272\247.md" new file mode 100644 index 0000000..8846b94 --- /dev/null +++ "b/misc/linux\351\253\230\347\272\247.md" @@ -0,0 +1,456 @@ +# OOM、oom Killer、NUMA、D-Bus 通信标准 + +专门写篇文章介绍一下 + +## misc + +【AI】linux 中万物皆文件,指的是内核所有可读写、可使用文件描述符访问的对象,统一使用文件接口(open/read/write/ioctl/close)操作。 +皆文件的这个文件具体分为磁盘设备文件(普通文件)和内核虚拟对象(无磁盘实体,只有文件描述符 fd,比如匿名管道、ptmx、socket、eventfd、signalfd等)。 + +【AI】“磁盘硬件 IO 一旦下发,内核无法中途强行暂停 / 取消”底层原理: +Linux 块设备层 → 磁盘硬件(SSD/HDD)存在明确边界: +1. 内核下发 IO 请求到硬件队列(blk-mq 硬件提交阶段) + 当内核把bio请求提交给 SSD 硬件队列(通过 NVMe/SCSI 指令发送给磁盘控制器); +2. 磁盘硬件独立执行 IO,CPU / 操作系统失去控制权 + SSD 固件拿到 IO 指令后,自主处理读写;现代 NVMe SSD 拥有独立处理器、缓存。 + 👉 CPU 内核没有任何机制,可以发送指令让磁盘「立刻放弃正在执行的 IO」。 + 磁盘硬件规范(NVMe、SATA、SCSI)只支持: + 取消还在软件队列、尚未下发硬件的 IO; + 不支持中断一块已经正在执行的物理 IO 操作。 + +# linux 高级 + +linux 高级,只记录 linux 内核相关的。 + +内核,主要负责: +- 硬件抽象:驱动管理 CPU、内存、磁盘、网卡等硬件; +- 进程管理:进程创建、调度、信号、cgroup、namespace 隔离; +- 内存管理:虚拟内存、页表、缓存、swap、内存回收、OOM; +- 文件系统:ext4/xfs/tmpfs/proc/sysfs 虚拟文件; +- 网络协议栈:TCP/IP、socket; +- 权限安全:用户 / 组、文件权限、capability; +- 系统调用:对外暴露统一接口,给用户态程序使用; +- 中断、时钟、锁、内核模块等底层基础设施。 + +## 设备 /dev + +参考 [linux dev] + +## 内核、进程 + +内核状态: +- D状态:不可中断的睡眠状态,使用 kill -9 也无法杀死。因为强制 kill 也只是给进程发送一个 SIGKILL 信号。当进程处于D状态时,无法处理任何信号,包括 SIGKILL,所以进程无法终止。 + - D 状态只代表:进程在等待 IO 结果,分两种场景: + - 场景 A:IO 正在 SSD 硬件上执行(硬件队列中); + - 场景 B:IO 还堵在内核软件队列排队,还没轮到下发给磁盘硬件; + +注意,STAT 字段代表的是内核状态+修饰标记,修饰标记有: +- `+` 进程属于前台进程组 +- `s` session leader(会话首进程) +- `l` 多线程进程 +- `<` 高优先级 +- `N` 低优先级 + + + +### [cgroup v2] + +cgroup 是 control group 的缩写,文档术语规定了首字母永远不大写,默认挂载到 `/sys/fs/cgroup/` 目录中。 + +cgroup 是 Linux 内核进程分层资源管控机制,主要由两部分组成:core 和 controllers。cgroup core 负责进行层级化,cgroup controllers 主要负责资源管控。资源管控从上至下传递,子 cgroup 无法突破父级资源限制。 + +资源管控,主要管控的是 CPU、内存、IO、进程线程数量、网卡等等。 + +[cgroup v1 和 v2 版本对比]: +- v1 中采用多个独立层级,cpu、memory、io、pid等各自都是独立树形目录。v2 则采用统一层级,之前的独立层级全部挂到 `0::` 下。格式为 `0::$PATH`。可以通过 `/proc/$PID/cgroup` 文件查看进程归属 cgroup。 +- v1 中父子层级约束混乱,v2 中解决了这个问题(资源管控自上而下传递) + - 子 cgroup 无法突破父节点的资源限制 + - 子 cgroup 只能启用父节点允许的控制器 + - 父节点若开启域控制器,自身不能存在运行进程,彻底隔离父内部进程与子组资源竞争;(但根 cgroup 豁免该限制,承载系统匿名资源;) +- v1 各控制器接口不统一、语义割裂,v2 全局标准化接口规范,所有控制器复用同一套语义。 +- v1 线程 / 进程模型混乱,v2 区分 domain/threaded 双模型。在 domain 模式下,一个进程的所有线程都必须属于同一个 cgroup + + +可以直接读写配置文件进行操作 cgroup,也可以借助 systemd 软件管理 + +```sh +cat /sys/fs/cgroup/xxx子组/控制文件 +# 读取配置 +echo xxx > /sys/fs/cgroup/xxx子组/控制文件 +# 同步(阻塞)修改配置 +``` + +【AI】cgroup v2 定义四类标准文件格式:单行值、空格多值、flat-keyed、nested-keyed + +#### [cgroup 内存管理] + +cgroup 内存管理中, +- 父子层级有额度竞争:父额度是子组总额度上限,子组总额度超额时,按实际占用比例瓜分父级保护额度; +- 额度的单位是字节,并且会自动向上对齐 PAGE_SIZE(通常是4096B) + +四种保护档位 +- memory.min:硬保护,当实际内存占用低于该值时,无论系统压力多大,都不回收该 cgroup 的内存。无可回收内存时直接 OOM; +- memory.low:相比 min 更宽松,系统压力过大时会回收该 cgroup 的内存; +- memory.max:硬上限,当 cgroup 占用内存触及硬上限,并且没有可以回收的内存时,触发 OOM。 +- memory.high:相比 max 更宽松,可以超出 high 值而不是直接触发 oom,但超限后 cgroup 内进程会被节流限流,同时承受高强度内存回收压力; +- +- 配置时最好是软硬一起搭配,比如同时配置 max 和 high,这样当内存达到 high 时,就会启动内存回收,而不是达到 max 才开始疯狂回收。一个不太准确但很形象的比喻就是,把水桶大小看作 max,前期加水可以疯狂加(疯狂申请内存),但快满了的时候(达到high),加水速度就应该慢下来,这样才更容易控制水不会溢出。 +- +- 注意,max 和 high 有两种编辑方式:O_WRONLY(阻塞式/同步)和 O_NONBLOCK(非阻塞式/异步)。 +- 使用命令行的编辑都是同步,同步修改后会进行同步回收、oom等操作。 +- 通过编程语言可以传递 O_NONBLOCK 进行异步修改,异步修改会跳过同步回收、OOM 触发逻辑,延迟到下一次内存分配时处理。这个“延迟到下一次内存分配时再处理”意味着风险,如果业务侧持续疯狂申请、刷写内存,不触发新内存分配,那么内存占用将长时间高于 high/max。 + +#### [cgroup IO管理] + +IO 控制器用于管控 IO 资源分配,同时支持两种调度模型: +- 基于权重(weight) 分配 IO 资源,仅在使用 cfq-iosched 调度器时生效 +- 基于带宽或 IOPS 上限的绝对限制分配方式。 +注意:blk-mq(多队列块设备)两种模型都不支持 + +常规 IO 配置项: +- io.stat 只读、嵌套键值格式文件 + - 统计当前 cgroup 及其子树所有 IO 设备的读写指标 + - rbytes:已读取字节总数 + - wbytes:已写入字节总数 + - rios:读 IO 请求次数 + - wios:写 IO 请求次数 + - dbytes:Discard 操作丢弃的字节(TRIM / 丢弃字节) + - dios:Discard 操作请求次数(TRIM / 丢弃 IO 次数) +- io.weight + - 取值范围 1 到 10000 ,默认 100 + - **只在兄弟节点之间生效**。 + - 取值代表的不是绝对值,而是竞争时的分配比例,设备不饱和时不生效。 + - 分配的是设备耗时,而不是单纯的流量,毕竟随机和顺序IO差距很大。 +- io.max + - 用来限制一个 cgroup 对指定块设备能使用的最大 IO,具体支持四个限制: + - rbps 每秒最大读字节数,限流读流量带宽。 + - wbps 每秒最大写字节数,限流写流量带宽。 + - riops 每秒最大读 IO 次数,限制读 IOPS。 + - wiops 每秒最大写 IO 次数,限制写 IOPS。 +- io.pressure + - 只读,用于查看 IO 阻塞情况。详情请查看 [PSI(Pressure Stall Information)] + +##### IO cost model + +IO cost model 是基于控制器 CONFIG_BLK_CGROUP_IOCOST 的。已经实现了 io.weight 的配置。有关 IO 的配置管理,也是一个很深的话题,IO 开销模型是最近才出的,详细可以从这篇博客[Improving performance with SCHED_EXT and IOCost] 和论文 [IOCost: Block Input–Output Control for Containers in Datacenters] 去扩展了解。 + +【AI】CONFIG_BLK_CGROUP_IOCOST 是内核编译选项(.config 配置项),控制是否编译、启用 blk-iocost IO 成本控制器。。有三个值: +- y:编译进内核,永久启用; +- m:编译为可加载内核模块; +- n:完全不编译,无 iocost 相关功能。 +CONFIG_BLK_CGROUP_IOCOST 依赖前置开关 CONFIG_BLK_CGROUP,只有开启基础 blk cgroup 后,CONFIG_BLK_CGROUP_IOCOST 才能生效。 +绝大多数发行版会把内核配置文件放在 `/boot/config-$(uname -r)`,所以可以通过 `cat /boot/config-$(uname -r) | grep CONFIG_BLK_CGROUP_IOCOST` 查看内核是否启用了 iocost + +- io.cost.qos 该文件用来配置 IO cost model 的 QoS(服务质量) + - 仅在根 cgroup 下存在(/sys/fs/cgroup/io.cost.qos) + - 可配置参数有: + - enable:控制器开关,默认关闭,设置为 1 开启; + - ctrl:管控模式,有 auto 和 user 两种; + - rpct:读延迟百分位,取值 [0, 100] + - wpct:写延迟百分位,取值 [0, 100] + - rlat:读延迟阈值 + - wlat:写延迟阈值 + - min:全局总 IO 吞吐缩放下限百分比,取值 [1, 10000] + - max:全局总 IO 吞吐缩放上限百分比,取值 [1, 10000] + - 【AI】min 和 max 的基准值取决于 io.cost.model 测算的基准值,此值是内核评估的稳态标准负载,不是满速上限。 + - rpct、wpct 默认为零,表示不配置,此时内核通过硬件队列深度、设备繁忙度、IO 完成耗时等内部指标,来判断磁盘是否饱和,然后动态调整总 IO 吞吐速率(范围在 min 和 max 参数之间) + - 配置 rpct/wpct, rlat/wlat 后,内核则根据配置的值(延迟阈值)来判断磁盘是否饱和。 + - 举例:enable=1 ctrl=user rpct=95 rlat=75000 wpct=95 wlat=150000 min=50 max=150 + - enable=1 表示开启控制器, + - ctrl=user 表示管控模式为手动,因为后面手动指定了值,所以即使这里设置为 auto,最终的文件也会变成 ctrl=user。 + - rpct=95 rlat=75000 wpct=95 wlat=150000 表示,内核会对一段时间内磁盘所有 IO 请求完成的耗时进行排序,如果 95% 的读延迟大于 75ms 或者 95% 的写延迟大于 150ms 时,则判定这块磁盘依据饱和,发生拥塞,此时内核会自动压低 IO 下发速率,最低不低于基准值的 50%。反之,如果非饱和,则内核允许拉高 IO 下发速率,最高不超过基准值的 150%。 + - 所以,设置的延迟阈值越低,QoS 表现越好,但代价是整体总带宽会下降(延迟优先,牺牲吞吐);min 和 max 区间越窄,IO行为越贴合预设的资源开销模型,流量波动越可控。但盲目设置 min/max,会让磁盘整体吞吐能力大幅浪费,IO 资源管控质量变差——要么限制过松完全无管控,要么限制过紧造成不必要限速; + - min/max 非常适合管控负载波动剧烈、短时行为反差极大的硬件设备,典型例子是 SSD。固态硬盘这类负载忽高忽低、会短暂满载后长时间阻塞的设备,通过 min 设置最低资源保障、max 设置瞬时资源上限,可有效平稳管控其 IO 行为。 + - 平时使用,直接用 auto 模式就可以了,其他的值由内核算法自行得出。 + +这里要注意一下,我在官方文档引用原文然后让 AI 解释时,AI 完全解释错了,而且在我没有实践的情况下,完全无法知道 AI 说的对不对,知道实践后,才发现 AI 是错的。 +> [原文中的描述]是这样的: When “ctrl” is “auto”, the parameters are controlled by the kernel and may change automatically. Setting “ctrl” to “user” or setting any of the percentile and latency parameters puts it into “user” mode and disables the automatic changes. The automatic mode can be restored by setting “ctrl” to “auto”. +> +> AI 给的解释是 io.cost.qos/io.cost.model 中 ctrl 参数是共享的,手动和自动所修改的都是 io.cost.model 中的 [r|w]bps, [r|w]seqiops, [r|w]randiops 。但我实践后(执行 `echo "252:0 enable=1 ctrl=auto rpct=95.00 rlat=75000 wpct=95.00 wlat=150000 min=50.00 max=150.0" > /sys/fs/cgroup/io.cost.qos`),发现并不是这样,执行该命令后 qos 的 ctrl 会自动变成 user,于是问 AI 其为何两个文件的 ctrl 会不同时,AI 给的解释是缓存问题,让我重新读取一下…… +> +> 其实最开始的时候,看到原文说“or setting any of the percentile and latency parameters”时,我就想过这个 ctrl 可能是控制 qos 的参数的,毕竟原文对参数的解释,也用到了 percentile 和 latency 两个单词,但因为我自认为英语比不上 AI,所以最终还是选择相信 AI 的,毕竟原文是使用 percentile 和 latency 两个单词来描述,而不是直接说 rpct/rlat/wpct/wlat 参数。而且 +> +> 因为在官方文档中使用 AI 解读太过方便,太过顺利,让我下意识地认为只要给了依据,AI的答案就不会错,结果这次打脸了,所以有必要记录一下。 + +- io.cost.model 该文件用来配置 IO cost model 的 cost model + - 仅在根 cgroup 下存在(/sys/fs/cgroup/io.cost.model) + - 可配置参数有: + - ctrl:auto 或 user + - model:使用的 cost 模型,目前是 linear 线性模型,后续可能加入其他模板,比如 bpf 自定义模型。目前线性模型定义了下面 6 个参数,当 ctrl=auto 时,下面的值由内核自动生成: + - [r|w]bps:限制顺序 IO 最大吞吐带宽 + - [r|w]seqiops:限制 4KB 块大小的顺序读写,每秒最大 IO 请求数 + - [r|w]randiops:限制 4KB 块大小的随机读写,每秒最大 IO 请求数 + +##### IO latency + +[io.latency] 有两种限流方式(共存): +- 队列深度节流。【AI】队列深度节流的本质,是限制该 cgroup同时持有的 outstanding IO 最大数量。outstanding IO 指的是已经发给磁盘、还没处理完、等待返回结果的 IO 请求。 +- 人工延迟诱导。【AI】swap 交换 IO、文件元数据 IO(目录更新、inode、日志元刷盘、内存换页),这类 IO 不能用队列深度节流 —— 如果限制这类IO,会导致内存回收、文件系统元同步阻塞,反过来拖慢高优先级业务,引发更大故障。所以内核策略不限制这类 IO 的队列深度;但会记账延迟成本,用进程级人工延时惩罚该 cgroup。 + +【AI】人工延迟诱导需要好好理解一下:swap 和元数据 IO 不能直接拦截限流,否则会拖累高优先级业务;所以内核允许这类 IO 正常跑,但把拥堵成本记在发起 IO 的 cgroup 头上,通过给该 cgroup 所有进程强制休眠延时的方式间接惩罚、压低该组整体负载。 +完整流程: +1. 磁盘拥堵,高优先级 cgroup 延迟超标,进入节流状态; +2. 普通用户 cgroup 产生大量 swap / 元数据 IO,这些 IO 正常跑,占磁盘带宽; +3. 每一笔这类 IO,都会给这个 cgroup 累计一笔延迟惩罚额度(单位微秒,存在 io.stat 的delay字段); +4. 只要这个 cgroup 里的进程执行任何系统调用、读写、计算时,内核会主动挂起进程,强制休眠对应累计的微秒,再放行进程; +举个直观例子: +普通用户大量 swap,累计 delay=500000us(0.5 秒)。用户进程执行read()读文件,内核先让进程休眠 500ms,再执行真正的 IO 读写。 +如果大量 swap 风暴,累计惩罚延迟会无限膨胀,进程每次调用都休眠十几秒,系统直接卡死。所以内核限制:单次进程休眠最多 1 秒,不会出现单次休眠数秒的极端情况。 +具体可以从 io.stat 查看到 use_delay 和 delay 增加。 + +开启 IO latency 后,有两个相关配置 +- io.latency +- io.stat 开启后,再 io.stat 中会多出三个字段: + - depth 当前 cgroup 的队列深度 + - avg_lat + - win + +--- + + +io-interface-files:基础限流、权重、监控(最核心) +writeback:缓冲写脏页 IO 专项管控(内存 + IO 联动) +io-latency + how-io-latency-throttling-works + io-latency-interface-files:完整一套 IO 延迟保障 QoS(概述 + 原理 + 配置文件) +io-priority:全局批量调整进程 IO 抢占优先级 + +- $CGROUP/io.stat + - rbytes:累计读取字节,全盘扫描会暴涨; + - rios:累计读 IO 次数,大量小文件扫描该值飙升; + - depth/avg_lat(开启 io.latency 后出现):磁盘队列深度、平均 IO 延迟,D 进程越多 avg_lat 越高; +- 开启 io.latency,io.stat 会新增 + - avg_lat:IO 平均完成延迟,全盘扫描会从几 ms 飙升至几十 / 上百 ms; + - depth:磁盘排队 IO 深度,队列越长进程越容易 D 阻塞。 + - $CGROUP/memory.stat 中 file 字段 = 文件页缓存,扫描文件越多该值越大。 +- $CGROUP/io.pressure + - some:部分进程等待 IO 的时长; + - full:组内所有进程都卡在 IO 等待(大量 D 进程时该数值持续上涨);只要 full 数值持续走高,就说明全盘扫描的读 IO 把磁盘打满,进程无法获取 IO 资源进入 D 状态。 + +- io.latency 给核心业务 cgroup 设置延迟目标,磁盘拥塞时内核自动节流扫描任务 cgroup,优先保障核心业务 IO 延迟。逻辑:当扫描任务把磁盘打满、业务 IO 延迟超过 75ms,内核主动限制扫描 cgroup 的并发 IO 数量,降低扫描抢占,业务进程不会进入 D 状态。 +- io.prio.class 把全盘扫描的 cgroup 所有进程 IO 优先级改为最低 IDLE,只有磁盘完全空闲时才允许扫描读 IO,完全不抢占业务 IO。 + +```sh +cat /sys/block/vda/queue/scheduler +# 查看指定硬盘当前支持的调度器,我的默认输出 [mq-deadline] none +``` + +### `/proc/[pid]` + + +- oom_adj 已废弃(2.6 版本以前使用,区间-16 ~ 15,值 -17 代表豁免) +- oom_score 只读, +- oom_score_adj 人工调节偏移量 +oom 取值区间固定:-1000 ~ 1000,默认值全部为 0,表示完全按内核默认规则打分。 +指定为 -1000 表示豁免,但也仅屏蔽系统全局 OOM Killer,如果是 cgroup 内存限制触发的容器内部 OOM,该参数不生效。 +可以直接使用 `echo -1000` 或者 `choom` 命令人工调节偏移量。 + +- sched +- wchan + +### /proc/zoneinfo 和 [/proc/meminfo] + +### /proc/pressure/ + +/proc/pressure/ 是 [PSI(Pressure Stall Information)] 虚拟目录,用来统计进程因资源不足而停滞等待的耗时占比,简单地说就是用来查看过去一段时间,进程因为缺 CPU / 内存 / IO 白白等待了百分之多少的时间。 + +该目录里面有三个文件 cpu、memory,io。每个文件的格式是一样的,有下面字段: +- some:至少有 1 个任务因该资源阻塞的时间占比(轻度压力) +- full:所有非空闲任务同时阻塞,CPU 空转、系统剧烈颠簸的严重压力 + - 系统级 CPU 的 full 指标无实际意义(不存在 CPU 空等 CPU 的情况),所以内核统一置 0 做兼容 +- avg10/avg60/avg300:近 10/60/300 秒平均阻塞占比,最大值100 +- total:系统启动至今累计阻塞总微秒数 + +开启 cgroup v2 后,每个 cgroup 目录(/sys/fs/cgroup)下会存在 cpu.pressure / memory.pressure / io.pressure 文件。 + +三个压力文件还支持写入配置触发器阈值,写入和读取之间互不影响。 + + +### [/proc/sys/vm] + +`/proc/sys/vm` 是 Virtual Memory 虚拟内存,该文件夹下的文件都是存在内核中的,不是磁盘中的具体文件,这些文件是用来调控 Linux 虚拟内存子系统行为的,比如内存分配策略、缓存回收、脏页刷盘、OOM 查杀、Swap 倾向、内存预留、缓存清理等。 + +具体手册可以查看官网 [/proc/sys/vm] + +相关配置命令有: +```sh +cat /proc/sys/vm/[config] +sysctl vm.[config] +# 查看当前值 + +echo [value] > /proc/sys/vm/[config] +sysctl vm.[config]=[value] +# 修改值,立刻生效,不过重启失效 + +# 想要永久生效,就将上面的修改命令写入到开机脚本中 /etc/sysctl.conf +echo "vm.[config]=[value]" >> /etc/sysctl.conf +sysctl -p # 啥作用? +sysctl -w # 啥作用? +``` + +#### oom 相关参数 + +- panic_on_oom + - OOM(out-of-memory,内存耗尽) 时行为控制,可选杀死进程或整机 panic(奔溃重启)。 + - 0 表示触发 OOM killer,挑选进程杀掉,尽量保系统运行(默认值) + - 1 表示仅整机内存耗尽才 panic;若仅单个 NUMA 节点内存不足,只杀该节点进程,不 panic。 + - 2 表示任意区域发生 OOM(含 cgroup 内 OOM、单节点 OOM),整机强制 panic,配合 kdump 可抓崩溃快照排查内存泄漏。 + - 内存耗尽后,会根据 `/proc/sys/kernel/panic` 的值判断多少秒后重启内核。默认为 0 表示永不重启 +- oom_kill_allocating_task + - 设置 oom kill 的选择被杀进程的策略 + - 0 表示遍历所有进程,选分数最高的杀,耗时久。(默认) + - 1 表示直接杀死触发 oom 的任务,响应更快。 +- oom_dump_tasks + - OOM 触发时是否打印全系统进程内存信息(pid、rss、vm 大小、oom_score_adj 等),用于定位内存大户。 + - 可以通过 dmesg / journalctl 命令查看,或者查看日志文件,日志通常保存在 /var/log/messages 或 /var/log/kern.log + - 注意日志中的进程打分表,也就是 Tasks state (memory values in pages),他的 rss 列单位是内存页,一般服务器的内存页单位都是 4096(即4KiB),具体可以通过 `getconf PAGE_SIZE` 命令查看。 + - 1 表示打印(默认) + - 0 表示只打印极简日志,减少开销 + + +- overcommit_memory + - 控制系统内存超额分配策略(控制的是进程申请的虚拟地址空间总大小,而不是进程实际占用的空间大小) + - 间接决定 oom 触发概率 + - 0 表示 guess 模式,允许超额,但拒绝明显不合理超大内存申请(默认) + - 1 表示允许超额,无任何限制。 + - 2 表示 never 模式,禁止超额分配,可申请上限取决于 overcommit_ratio 和 overcommit_kbytes + - 不要随便设置成 never 模式,很可能导致所有 ssh 被杀死,并且无法登录进去,VNC也无法登录,因为无法分配到内存,最终只能重启(毕竟修改测试时应该是使用临时生效吧,如果修改成开机生效,那恐怕就得加内存或者重装了)。 + - 重启后正常设置为 never 模式后,很多软件的运行都会有问题,因为这些软件系统性申请冗余内存,但因为 never 模式禁止他们申请太多内存,就导致很多软件无法正常使用。 +- overcommit_ratio + - 当 overcommit_memory 为 2 时生效。 + - 控制物理内存计入上限的比例,默认 50。 + - 最大允许申请虚拟地址空间 = Swap 总大小 + (物理内存总量 × overcommit_ratio百分比) +- overcommit_kbytes + - 当 overcommit_memory 为 2 时生效。 + - 直接指定可使用的物理内存额度 + - 最大允许申请虚拟地址空间 = Swap 总大小 + overcommit_kbytes(KiB) +> [!WARNING] +> overcommit_ratio 和 overcommit_kbytes 是互斥的,两者只能生效一个。修改其中一个,另外一个会自动变为 0。 +> 不要随便测试,防止改错设置导致断开所有连接(包括VNC),最终只能强制重启。 + +- admin_reserve_kbytes + - 为具备 cap_sys_admin 的用户预留的最小内存。 + - 按照官方文档的说法,需要预留的最小内存 = 登录服务 (sshd/login) + shell (bash) + 运维工具 (top/ps/kill)。 + - 当 overcommit_memory 为 0(guess模式)时,按程序实际驻留内存 RSS 求和,x86_64 架构约 8MB。 + - 当 overcommit_memory 为 2(never模式) 时,取所有工具最大虚拟内存 VSZ + 全部 RSS 之和,x86_64 架构建议 128MB。 + - 该值不受 overcommit_memory 影响,一直生效 + - TODO:实际测试下来,物理内存1.7G,无交换,设置 admin_reserve_kbytes 为1G,guess模式,然后用普通用户压测,依旧可以占满内存让所有用户都无法操作,只不过使用 stress-ng 压测会触发 oom,但使用 vscode 则不会 oom + - 原因是该值只限制一次性的虚拟内存申请(无法测试成功),并不是强制保留 1G 物理内存不给普通用户。 + - 使用c代码一次性申请 1380MiB,会消耗10多秒才申请到内存,并且占用10多秒后就会被kill。 + - 但申请 1352MiB 空间,就能够申请成功,并且持续占用,此时会发现 root 用户明显卡顿,新建 ssh 登录也会发现无法登录,这不就是 vscode 的场景!我现在是弄出了我想要的可复现的场景出来!有点区别的就是我精准的控制了内存占用大小,不会被 kill,但同时又会导致所有用户卡顿,无法ssh登录,而且我可以随时 ctrl+c 终止这种状态(当然也会卡顿10多秒,但好过等好几个小时)! + - 测试发现,不管我设不设置该值,普通用户都能申请 1352MiB 空间,并导致所有用户卡顿。 + - 如果我改成 never 模式呢?崩,所有用户直接被强制退出,无法登录,VNC也一样!大概率是因为我前面操作了一些东西,导致很多进程占用了申请的内存,所以其他进程就无法继续申请内存了,连root也无法申请。重启一下再改为 never 就没问题了,但也只是 root 用户没问题,普通用户登进去只敲了个 `w` 就被闪退了 + - 总的来说,这个参数没什么用,具体可以看看 [博客VmAdminReserveNotEnough],不过这篇文章说 已登录的 root 可以使用该保留的内存,我实测下来发现普通用户占满内存后,已登录的root依旧卡顿,没法使用保留的内存。 +- user_reserve_kbytes + - 当 overcommit_memory 为 2 时生效。 + - 给用户预留的内存。默认128MiB + - TODO:因为前面的 admin_reserve_kbytes 没测试明白,这个配置我就不解读了。当我彻底弄懂了上面的后,这里的也很容易理解了。 +- lowmem_reserve_ratio + - 保护 DMA/DMA32 低区内存,和整机用户预留无关。 + + +- min_free_kbytes + - 设置内核强制预留的最小空闲物理内存(单位:KiB) + - 这是用来隔离「内核」和「上层应用」的,和用户无关系, + - 低于该值时会触发进程同步回收、卡死、OOM。 + - /proc/meminfo 中的 MemAvailable 计算时,会先用 MemFree 减去 min_free_kbytes 值。不过修改此值对用户的感知只是可用内存变少了,毕竟保留的内存并不会给用户使用,所以内存占满时,依旧会无法敲命令,无法ssh登录。 + - 验证方法:该值默认是 44MiB,用自建函数占用临界值内存,执行 `free` 命令会发现 free 值比 available 大 44。修改此值为100MiB,再次用自建函数占用临界值内存,会发现临界值内存变小了,并且查看到的 free 值比 available 大 100。两个值都可以找到临界值内存,也就是占用此内存后,所有用户卡顿,并且占用内存的进程不会被 oom killer + +- zone_reclaim_mode + - NUMA 节点本地内存耗尽时优先回收本地缓存,避免跨节点分配、延迟本地 OOM 触发。 + - NUMA 专用,本地 node 内存不够时,优先回收本地缓存,尽量不跨 node 拿远端内存,降低延迟。 + + +- swappiness + - 控制 swap 交换积极性,0 表示尽可能不交换,100表示尽可能交换。 + - 默认是 0 +- page-cluster + - 一次交换读写连续页面数量,优化 Swap IO 性能 + - 取值为对数:0=1 页、1=2 页、2=4 页、默认 3=8 页 +- vfs_cache_pressure + - swappiness 控制「页缓存 vs 匿名页(swap)」回收权重; + - vfs_cache_pressure 控制「目录项 / 索引节点缓存(dentry/inode)vs 页缓存」回收权重。 +- vfs_cache_pressure_denom + - 默认 100 + +- 控制 kswapd 回收激进程度,水位调大提前回收内存,减少 OOM 概率。 + - watermark_scale_factor + - watermark_boost_factor +- 脏页回写(脏页回收减少文件缓存占用,间接改变 swap 触发时机) + - dirty_background_bytes + - dirty_background_ratio + - dirty_bytes + - dirty_expire_centisecs + - dirty_ratio + - dirty_writeback_centisecs +- 辅助碎片 / 压缩参数(间接改变内存可用量,影响 swap 频率) + - compaction_proactiveness /compact_memory/defrag_mode 内存压缩整理连续页,提升大页分配成功率,减少内存碎片化导致的虚假内存不足,降低 swap 触发概率。 + - extfrag_threshold 碎片阈值,达到阈值触发压缩 / 直接回收,回收会包含匿名页,间接增加 swap。 + + + +#### 其他未整理 +```sh +admin_reserve_kbytes +compact_memory +compact_unevictable_allowed +compaction_proactiveness +dirty_background_bytes +dirty_background_ratio +dirty_bytes +dirty_expire_centisecs +dirty_ratio +dirty_writeback_centisecs +dirtytime_expire_seconds +drop_caches +extfrag_threshold +hugetlb_shm_group +legacy_va_layout +lowmem_reserve_ratio +max_map_count +memory_failure_early_kill +memory_failure_recovery +min_free_kbytes +min_slab_ratio +min_unmapped_ratio +mmap_min_addr +mmap_rnd_bits +mmap_rnd_compat_bits +nr_hugepages +nr_hugepages_mempolicy +nr_overcommit_hugepages +numa_stat +numa_zonelist_order +oom_dump_tasks +oom_kill_allocating_task +overcommit_kbytes +overcommit_memory +overcommit_ratio +page_lock_unfairness +page-cluster +panic_on_oom +percpu_pagelist_high_fraction +stat_interval +stat_refresh +swappiness +unprivileged_userfaultfd +user_reserve_kbytes +vfs_cache_pressure +watermark_boost_factor +watermark_scale_factor +zone_reclaim_mode +laptop_mode +``` + + +[cgroup v2]: https://docs.kernel.org/admin-guide/cgroup-v2.html +[cgroup 内存管理]: https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files +[cgroup IO管理]: https://docs.kernel.org/admin-guide/cgroup-v2.html#io +[/proc/meminfo]: https://www.kernel.org/doc/Documentation/filesystems/proc.rst +[/proc/sys/vm]: https://docs.kernel.org/admin-guide/sysctl/vm.html +[博客VmAdminReserveNotEnough]: https://utcc.utoronto.ca/~cks/space/blog/linux/VmAdminReserveNotEnough +[cgroup v1 和 v2 版本对比]: https://docs.kernel.org/admin-guide/cgroup-v2.html#issues-with-v1-and-rationales-for-v2 +[PSI(Pressure Stall Information)]: https://docs.kernel.org/accounting/psi.html +[io.latency]: https://docs.kernel.org/admin-guide/cgroup-v2.html#how-io-latency-throttling-works +[Improving performance with SCHED_EXT and IOCost]: https://lwn.net/Articles/966618/ +[IOCost: Block Input–Output Control for Containers in Datacenters]: https://www.cs.cmu.edu/~dskarlat/publications/top_iocost.pdf +[原文中的描述]: https://docs.kernel.org/admin-guide/cgroup-v2.html +[linux dev]: https://www.kernel.org/doc/html/latest/admin-guide/devices.html \ No newline at end of file diff --git a/misc/shell.md b/misc/shell.md index 48a4331..f9e0c1d 100644 --- a/misc/shell.md +++ b/misc/shell.md @@ -1,3 +1,5 @@ +(Get-NetTCPConnection | Where-Object State -eq Established).Count + ### 查找命令路径 ```sh diff --git "a/misc/\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" "b/misc/\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" index 2117540..e334d07 100644 --- "a/misc/\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" +++ "b/misc/\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" @@ -18,6 +18,9 @@ - Cb+Cr 合称 Chroma 色度(色彩),只负责颜色,不承载任何画面轮廓细节。 ```sh +magick *.png -quality 80 -sampling-factor 4:2:0 pic_%03d.jpg +# 将 png 批量变更为 jpg + magick 1.jpg -sampling-factor 4:2:0 2.jpg # 压缩图片,只压缩色彩,文档扫描通常将 4:4:4 压缩到 4:2:0 magick 1.jpg -quality 90 -sampling-factor 4:2:0 2.jpg diff --git "a/misc/\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/misc/\345\270\270\350\247\201\351\227\256\351\242\230.md" index 5f46643..94fbef8 100644 --- "a/misc/\345\270\270\350\247\201\351\227\256\351\242\230.md" +++ "b/misc/\345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -1,3 +1,16 @@ +### WT 通过 ssh 远程服务器处于 nano 编辑模式时,因长时间无操作断连,导致窗口无滚动条 + +执行下面命令 + +```pwsh +Write-Host "`e[?1049l" +``` + +```sh +tput rmcup +printf '\e[?1049l' +``` + ### cmd/pwsh 代理 ```sh diff --git "a/misc/\345\277\253\346\215\267\351\224\256.md" "b/misc/\345\277\253\346\215\267\351\224\256.md" new file mode 100644 index 0000000..c4d2082 --- /dev/null +++ "b/misc/\345\277\253\346\215\267\351\224\256.md" @@ -0,0 +1,44 @@ +## window terminal + +alt+shift++ 是水平分割 +alt+shift+- 是垂直分割 +alt+shift+方向键 是调整窗格大小 +alt+方向键 切换窗格 + +## bash 快捷键 + +Bash 的快捷键,其风格源自 Emacs 编辑程序,具体是通过 [GNU Readline Library] 实现的,bash 等其他 shell 基本都是调用这个库实现快捷键。 + +- ctrl + u 清空光标到行首 +- ctrl + k 清空光标到行尾 +- ctrl + w 删除光标前的 unix word(具体地讲,是 whitespace-separated unix word,即以空格或制表符作为边界) +- alt + d 删除光标后的 word(具体地讲,是 Readline word,即以空格制表符和`\/-_.:`作为边界。) +- alt + backspace 删除光标前的 word +- ctrl + l 翻页 +- +- ctrl + a 等同 home(ahead) +- ctrl + e 等同 end +- ctrl + b 等同 ← ArrowLeft(backward) +- ctrl + f 等同 → ArrowRight(forward) +- ctrl + p 等同 ↑ ArrowUp(previous) +- ctrl + n 等同 ↓ ArrowDown(next) +- +- ctrl + r 搜索历史命令 +- ctrl + g 退出搜索 +- +- ctrl + c 终止当前命令 +- ctrl + z 挂起当前命令 +- ctrl + d 产生一个 EOF + +在 WT 中使用 pwsh 通过 plink 串口连接虚拟机linux(本质上是 风格绑定) + +## win + +- ctrl+win+数字:不触发预览,直接切换,速度更快。 +- win+数字:会触发预览,所以当快速按下win+数字时,界面会卡在预览界面,需要再次点击win才可以。只有速度慢点,才是正常期待的效果。 + - 可以在注册表 regedit 中进入 `计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced` 新建DWORD32值 LastActiveClick,值设置成1,用来关闭预览,也就是实现 ctrl+win+数字 的效果 + + + + +[GNU Readline Library]: https://tiswww.cwru.edu/php/chet/readline/rluserman.html \ No newline at end of file