Skip to main content

OS

OS

  • [Doc] TTY
  • [Doc] OS (操作系统)
  • [Doc] 命令行参数
  • [Basic] 负载
  • [Point] CheckList
  • [Basic] 指标

TTY

"tty" 原意是指 "teletype" 即打字机, "pty" 则是 "pseudo-teletype" 即伪打字机. 在 Unix 中, /dev/tty* 是指任何表现的像打字机的设备, 例如终端 (terminal).

你可以通过 w 命令查看当前登录的用户情况, 你会发现每登录了一个窗口就会有一个新的 tty.

$ w
 11:49:43 up 482 days, 19:38,  3 users,  load average: 0.03, 0.08, 0.07
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
dev      pts/0    10.0.128.252     10:44    1:01m  0.09s  0.07s -bash
dev      pts/2    10.0.128.252     11:08    2:07   0.17s  0.14s top
root     pts/3    10.0.240.2       11:43    7.00s  0.04s  0.00s w

使用 ps 命令查看进程信息中也有 tty 的信息:

$ ps -x
  PID TTY      STAT   TIME COMMAND
 5530 ?        S      0:00 sshd: dev@pts/3
 5531 pts/3    Ss+    0:00 -bash
11296 ?        S      0:00 sshd: dev@pts/4
11297 pts/4    Ss     0:00 -bash
13318 pts/4    R+     0:00 ps -x
23733 ?        Ssl    2:53 PM2 v1.1.2: God Daemon

其中为 ? 的是没有依赖 TTY 的进程, 即守护进程.

在 Node.js 中你可以通过 stdio 的 isTTY 来判断当前进程是否处于 TTY (如终端) 的环境.

$ node -p -e "Boolean(process.stdout.isTTY)"
true
$ node -p -e "Boolean(process.stdout.isTTY)" | cat
false

OS

通过 OS 模块可以获取到当前系统一些基础信息的辅助函数.

属性 描述
os.EOL 根据当前系统, 返回当前系统的 End Of Line
os.arch() 返回当前系统的 CPU 架构, 如 'x86''x64'
os.constants 返回系统常量
os.cpus() 返回 CPU 每个核的信息
os.endianness() 返回 CPU 字节序, 如果是大端字节序返回 BE, 小端字节序则 LE
os.freemem() 返回系统空闲内存的大小, 单位是字节
os.homedir() 返回当前用户的根目录
os.hostname() 返回当前系统的主机名
os.loadavg() 返回负载信息
os.networkInterfaces() 返回网卡信息 (类似 ifconfig)
os.platform() 返回编译时指定的平台信息, 如 win32, linux, 同 process.platform()
os.release() 返回操作系统的分发版本号
os.tmpdir() 返回系统默认的临时文件夹
os.totalmem() 返回总内存大小(同内存条大小)
os.type() 根据 [uname](https://en.wikipedia.org/wiki/Uname#Examples) 返回系统的名称
os.uptime() 返回系统的运行时间,单位是秒
os.userInfo([options]) 返回当前用户信息

不同操作系统的换行符 (EOL) 有什么区别?

end of line (EOL) 同 newline, line ending, 以及 line break.

通常由 line feed (LF, \n) 和 carriage return (CR, \r) 组成. 常见的情况:

符号 系统
LF 在 Unix 或 Unix 相容系统 (GNU/Linux, AIX, Xenix, Mac OS X, ...)、BeOS、Amiga、RISC OS
CR+LF MS-DOS、微软视窗操作系统 (Microsoft Windows)、大部分非 Unix 的系统
CR Apple II 家族, Mac OS 至版本9

如果不了解 EOL 跨系统的兼容情况, 那么在处理文件的行分割/行统计等情况时可能会被坑.

OS 常量

  • 信号常量 (Signal Constants), 如 SIGHUP, SIGKILL 等.
  • POSIX 错误常量 (POSIX Error Constants), 如 EACCES, EADDRINUSE 等.
  • Windows 错误常量 (Windows Specific Error Constants), 如 WSAEACCES, WSAEBADF 等.
  • libuv 常量 (libuv Constants), 仅 UV_UDP_REUSEADDR.

Path

Node.js 内置的 path 是用于处理路径问题的模块. 不过众所周知, 路径在不同操作系统下又不可调和的差异.

Windows vs. POSIX

POSIX Windows
path.posix.sep '/' path.win32.sep '\\'
path.posix.normalize('/foo/bar//baz/asdf/quux/..') '/foo/bar/baz/asdf' path.win32.normalize('C:\temp\\foo\bar\..\') 'C:\\temp\\foo\\'
path.posix.basename('/tmp/myfile.html') 'myfile.html' path.win32.basename('C:\temp\myfile.html') 'myfile.html'
path.posix.join('/asdf', '/test.html') '/asdf/test.html' path.win32.join('/asdf', '/test.html') '\\asdf\\test.html'
path.posix.relative('/root/a', '/root/b') '../b' path.win32.relative('C:\a', 'c:\b') '..\\b'
path.posix.isAbsolute('/baz/..') true path.win32.isAbsolute('C:\foo\..') true
path.posix.delimiter ':' path.win32.delimiter ','
process.env.PATH '/usr/bin:/bin' process.env.PATH C:\Windows\system32;C:\Program Files\node\'
PATH.split(path.posix.delimiter) ['/usr/bin', '/bin'] PATH.split(path.win32.delimiter) ['C:\\Windows\\system32', 'C:\\Program Files\\node\\']

看了上表之后, 你应该了解到当你处于某个平台之下的时候, 所使用的 path 模块的方法其实就是对应的平台的方法, 例如笔者这里用的是 mac, 所以:

const path = require('path');
console.log(path.basename === path.posix.basename); // true

如果你处于其中某一个平台, 但是要处理另外一个平台的路径, 需要注意这个跨平台的问题.

path 对象

on POSIX:

path.parse('/home/user/dir/file.txt')
// Returns:
// {
//    root : "/",
//    dir : "/home/user/dir",
//    base : "file.txt",
//    ext : ".txt",
//    name : "file"
// }
┌─────────────────────┬────────────┐
│          dir        │    base    │
├──────┬              ├──────┬─────┤
│ root │              │ name │ ext │
"  /    home/user/dir / file  .txt "
└──────┴──────────────┴──────┴─────┘

on Windows:

path.parse('C:\\path\\dir\\file.txt')
// Returns:
// {
//    root : "C:\\",
//    dir : "C:\\path\\dir",
//    base : "file.txt",
//    ext : ".txt",
//    name : "file"
// }
┌─────────────────────┬────────────┐
│          dir        │    base    │
├──────┬              ├──────┬─────┤
│ root │              │ name │ ext │
" C:\      path\dir   \ file  .txt "
└──────┴──────────────┴──────┴─────┘

path.extname(path)

case return
path.extname('index.html') '.html'
path.extname('index.coffee.md') '.md'
path.extname('index.') '.'
path.extname('index') ''
path.extname('.index') ''

命令行参数

命令行参数 (Command Line Options), 即对 CLI 使用上的一些文档. 关于 CLI 主要有 4 种使用方式:

  • node [options] [v8 options] [script.js | -e "script"] [arguments]
  • node debug [script.js | -e "script" | :] …
  • node --v8-options
  • 无参数直接启动 REPL 环境

Options

参数 简介
-v, --version 查看当前 node 版本
-h, --help 查看帮助文档
-e, --eval "script" 将参数字符串当做代码执行
-p, --print "script" 打印 -e 的返回值
-c, --check 检查语法并不执行
-i, --interactive 即使 stdin 不是终端也打开 REPL 模式
-r, --require module 在启动前预先 require 指定模块
--no-deprecation 关闭废弃模块警告
--trace-deprecation 打印废弃模块的堆栈跟踪信息
--throw-deprecation 执行废弃模块时抛出错误
--no-warnings 无视报警(包括废弃警告)
--trace-warnings 打印警告的 stack (包括废弃模块)
--trace-sync-io 只要检测到异步 I/O 出于 Event loop 的开头就打印 stack trace
--zero-fill-buffers 自动初始化(zero-fill) BufferSlowBuffer
--preserve-symlinks 在解析和缓存模块时指示模块加载程序保存符号链接
--track-heap-objects 为堆快照跟踪堆对象的分配情况
--prof-process 使用 v8 选项 --prof 生成 Profilling 报告
--v8-options 显示 v8 命令行选项
--tls-cipher-list=list 指明替代的默认 TLS 加密器列表
--enable-fips 在启动时开启 FIPS-compliant crypto
--force-fips 在启动时强制实施 FIPS-compliant
--openssl-config=file 启动时加载 OpenSSL 配置文件
--icu-data-dir=file 指定ICU数据加载路径

环境变量

环境变量 简介
NODE_DEBUG=module[,…] 指定要打印调试信息的核心模块列表
NODE_PATH=path[:…] 指定搜索目录模块路径的前缀列表
NODE_DISABLE_COLORS=1 关闭 REPL 的颜色显示
NODE_ICU_DATA=file ICU (Intl object) 数据路径
NODE_REPL_HISTORY=file 持久化存储REPL历史文件的路径
NODE_TTY_UNSAFE_ASYNC=1 设置为1时, 将同步操作 stdio (如 console.log 变成同步)
NODE_EXTRA_CA_CERTS=file 指定 CA (如 VeriSign) 的额外证书路径

负载

负载是衡量服务器运行状态的一个重要概念. 通过负载情况, 我们可以知道服务器目前状态是清闲, 良好, 繁忙还是即将 crash.

通常我们要查看的负载是 CPU 负载, 详细一点的情况你可以通过阅读这篇博客: Understanding Linux CPU Load 来了解.

命令行上可以通过 uptime, top 命令, Node.js 中可以通过 os.loadavg() 来获取当前系统的负载情况:

load average: 0.09, 0.05, 0.01

其中分别是最近 1 分钟, 5 分钟, 15 分钟内系统 CPU 的平均负载. 当 CPU 的一个核工作饱和的时候负载为 1, 有几核 CPU 那么饱和负载就是几.

在 Node.js 中单个进程的 CPU 负载查看可以使用 pidusage 模块.

除了 CPU 负载, 对于服务端 (偏维护) 还需要了解网络负载, 磁盘负载等.

CheckList

有一个醉汉半夜在路灯下徘徊,路过的人奇怪地问他:“你在路灯下找什么?”醉汉回答:“我在找我的KEY”,路人更奇怪了:“找钥匙为什么在路灯下?”,醉汉说:“因为这里最亮!”。

很多服务端的同学在说到检查服务器状态时只知道使用 top 命令, 其实情况就和上面的笑话一样, 因为对于他们而言 top 是最亮的那盏路灯.

对于服务端程序员而言, 完整的服务器 checklist 首推 《性能之巅》 第二章中讲述的 USE 方法.

The USE Method provides a strategy for performing a complete check of system health, identifying common bottlenecks and errors. For each system resource, metrics for utilization, saturation and errors are identified and checked. Any issues discovered are then investigated using further strategies.

This is an example USE-based metric list for Linux operating systems (eg, Ubuntu, CentOS, Fedora). This is primarily intended for system administrators of the physical systems, who are using command line tools. Some of these metrics can be found in remote monitoring tools.

Physical Resources

componenttypemetric
CPUutilizationsystem-wide: vmstat 1, "us" + "sy" + "st"; sar -u, sum fields except "%idle" and "%iowait"; dstat -c, sum fields except "idl" and "wai"; per-cpu: mpstat -P ALL 1, sum fields except "%idle" and "%iowait"; sar -P ALL, same as mpstat; per-process: top, "%CPU"; htop, "CPU%"; ps -o pcpu; pidstat 1, "%CPU"; per-kernel-thread: top/htop ("K" to toggle), where VIRT == 0 (heuristic). [1]
CPUsaturationsystem-wide: vmstat 1, "r" > CPU count [2]; sar -q, "runq-sz" > CPU count; dstat -p, "run" > CPU count; per-process: /proc/PID/schedstat 2nd field (sched_info.run_delay); perf sched latency (shows "Average" and "Maximum" delay per-schedule); dynamic tracing, eg, SystemTap schedtimes.stp "queued(us)" [3]
CPUerrorsperf (LPE) if processor specific error events (CPC) are available; eg, AMD64's "04Ah Single-bit ECC Errors Recorded by Scrubber" [4]
Memory capacityutilizationsystem-wide: free -m, "Mem:" (main memory), "Swap:" (virtual memory); vmstat 1, "free" (main memory), "swap" (virtual memory); sar -r, "%memused"; dstat -m, "free"; slabtop -s c for kmem slab usage; per-process: top/htop, "RES" (resident main memory), "VIRT" (virtual memory), "Mem" for system-wide summary
Memory capacitysaturationsystem-wide: vmstat 1, "si"/"so" (swapping); sar -B, "pgscank" + "pgscand" (scanning); sar -W; per-process: 10th field (min_flt) from /proc/PID/stat for minor-fault rate, or dynamic tracing [5]; OOM killer: dmesg | grep killed
Memory capacityerrorsdmesg for physical failures; dynamic tracing, eg, SystemTap uprobes for failed malloc()s
Network Interfacesutilizationsar -n DEV 1, "rxKB/s"/max "txKB/s"/max; ip -s link, RX/TX tput / max bandwidth; /proc/net/dev, "bytes" RX/TX tput/max; nicstat "%Util" [6]
Network Interfacessaturationifconfig, "overruns", "dropped"; netstat -s, "segments retransmited"; sar -n EDEV, *drop and *fifo metrics; /proc/net/dev, RX/TX "drop"; nicstat "Sat" [6]; dynamic tracing for other TCP/IP stack queueing [7]
Network Interfaceserrorsifconfig, "errors", "dropped"; netstat -i, "RX-ERR"/"TX-ERR"; ip -s link, "errors"; sar -n EDEV, "rxerr/s" "txerr/s"; /proc/net/dev, "errs", "drop"; extra counters may be under /sys/class/net/...; dynamic tracing of driver function returns 76]
Storage device I/Outilizationsystem-wide: iostat -xz 1, "%util"; sar -d, "%util"; per-process: iotop; pidstat -d; /proc/PID/sched "se.statistics.iowait_sum"
Storage device I/Osaturationiostat -xnz 1, "avgqu-sz" > 1, or high "await"; sar -d same; LPE block probes for queue length/latency; dynamic/static tracing of I/O subsystem (incl. LPE block probes)
Storage device I/Oerrors/sys/devices/.../ioerr_cnt; smartctl; dynamic/static tracing of I/O subsystem response codes [8]
Storage capacityutilizationswap: swapon -s; free; /proc/meminfo "SwapFree"/"SwapTotal"; file systems: "df -h"
Storage capacitysaturationnot sure this one makes sense - once it's full, ENOSPC
Storage capacityerrorsstrace for ENOSPC; dynamic tracing for ENOSPC; /var/log/messages errs, depending on FS
Storage controllerutilizationiostat -xz 1, sum devices and compare to known IOPS/tput limits per-card
Storage controllersaturationsee storage device saturation, ...
Storage controllererrorssee storage device errors, ...
Network controllerutilizationinfer from ip -s link (or /proc/net/dev) and known controller max tput for its interfaces
Network controllersaturationsee network interface saturation, ...
Network controllererrorssee network interface errors, ...
CPU interconnectutilizationLPE (CPC) for CPU interconnect ports, tput / max
CPU interconnectsaturationLPE (CPC) for stall cycles
CPU interconnecterrorsLPE (CPC) for whatever is available
Memory interconnectutilizationLPE (CPC) for memory busses, tput / max; or CPI greater than, say, 5; CPC may also have local vs remote counters
Memory interconnectsaturationLPE (CPC) for stall cycles
Memory interconnecterrorsLPE (CPC) for whatever is available
I/O interconnectutilizationLPE (CPC) for tput / max if available; inference via known tput from iostat/ip/...
I/O interconnectsaturationLPE (CPC) for stall cycles
I/O interconnecterrorsLPE (CPC) for whatever is available

Software Resources

componenttypemetric
Kernel mutexutilizationWith CONFIG_LOCK_STATS=y, /proc/lock_stat "holdtime-totat" / "acquisitions" (also see "holdtime-min", "holdtime-max") [8]; dynamic tracing of lock functions or instructions (maybe)
Kernel mutexsaturationWith CONFIG_LOCK_STATS=y, /proc/lock_stat "waittime-total" / "contentions" (also see "waittime-min", "waittime-max"); dynamic tracing of lock functions or instructions (maybe); spinning shows up with profiling (perf record -a -g -F 997 ..., oprofile, dynamic tracing)
Kernel mutexerrorsdynamic tracing (eg, recusive mutex enter); other errors can cause kernel lockup/panic, debug with kdump/crash
User mutexutilizationvalgrind --tool=drd --exclusive-threshold=... (held time); dynamic tracing of lock to unlock function time
User mutexsaturationvalgrind --tool=drd to infer contention from held time; dynamic tracing of synchronization functions for wait time; profiling (oprofile, PEL, ...) user stacks for spins
User mutexerrorsvalgrind --tool=drd various errors; dynamic tracing of pthread_mutex_lock() for EAGAIN, EINVAL, EPERM, EDEADLK, ENOMEM, EOWNERDEAD, ...
Task capacityutilizationtop/htop, "Tasks" (current); sysctl kernel.threads-max, /proc/sys/kernel/threads-max (max)
Task capacitysaturationthreads blocking on memory allocation; at this point the page scanner should be running (sar -B "pgscan*"), else examine using dynamic tracing
Task capacityerrors"can't fork()" errors; user-level threads: pthread_create() failures with EAGAIN, EINVAL, ...; kernel: dynamic tracing of kernel_thread() ENOMEM
File descriptorsutilizationsystem-wide: sar -v, "file-nr" vs /proc/sys/fs/file-max; dstat --fs, "files"; or just /proc/sys/fs/file-nr; per-process: ls /proc/PID/fd | wc -l vs ulimit -n
File descriptorssaturationdoes this make sense? I don't think there is any queueing or blocking, other than on memory allocation.
File descriptorserrorsstrace errno == EMFILE on syscalls returning fds (eg, open(), accept(), ...).

ulimit

ulimit 用于管理用户对系统资源的访问.

-a   显示目前全部限制情况
-c   设定 core 文件的最大值, 单位为区块
-d   <数据节区大小> 程序数据节区的最大值, 单位为KB
-f   <文件大小> shell 所能建立的最大文件, 单位为区块
-H   设定资源的硬性限制, 也就是管理员所设下的限制
-m   <内存大小> 指定可使用内存的上限, 单位为 KB
-n   <文件描述符数目> 指定同一时间最多可开启的 fd 数
-p   <缓冲区大小> 指定管道缓冲区的大小, 单位512字节
-s   <堆叠大小> 指定堆叠的上限, 单位为 KB
-S   设定资源的弹性限制
-t   指定CPU使用时间的上限, 单位为秒
-u   <进程数目> 用户最多可开启的进程数目
-v   <虚拟内存大小> 指定可使用的虚拟内存上限, 单位为 KB

例如:

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 127988
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 655360
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 4096
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

注意, open socket 等资源拿到的也是 fd, 所以 ulimit -n 比较小除了文件打不开, 还可能建立不了 socket 链接.