POSIX Shell 与常用工具集

共 42 题
📑 题目列表 42 题
#
★★★

1. POSIX find 的 -name、-type、-perm、-mtime、-exec 的标准操作

POSIX find 的 -name、-type、-perm、-mtime、-exec 标准操作如何理解?

  • find 的常用谓词
  • -name/-type/-perm/-mtime/-exec
  • 用法

find 按条件查找文件:-name(按文件名,支持通配符)、-type(按类型,f 文件/d 目录/l 符号链接)、-perm(按权限,如 -perm -u+w)、-mtime(按修改时间,如 -mtime +7 表示 7 天前修改)、-exec(对找到的每个文件执行命令,cmd {} ; 或 cmd {} + 批量)。可组合 predicate(-o 或、-a 且、! 非)。工程上:find 用于定位/批量处理文件,-exec 配合文件名处理;注意 -name 用通配符需引号,-exec 的 {} 与结束符。POSIX find 是标准工具,跨平台行为一致。

find 用谓词组合筛选文件。-name 按名、-type 按类型、-perm 按权限、-mtime 按时间、-exec 执行命令,是文件查找与批量操作的标准工具。

#
★★

2. find 的 -exec cmd {} + 批量传参、-exec cmd {} ; 逐个执行,两者效率有何差异?配合 -print0 与 xargs -0 如何安全处理含空格/换行的文件名?

find 的 -exec cmd {} + 与 -exec cmd {} ; 的效率差异如何?配合 -print0 与 xargs -0 如何安全处理含空格/换行的文件名?

  • -exec {} + vs ;
  • 批量 vs 逐个
  • -print0/xargs -0

find -exec cmd {} ; 对每个文件单独执行一次命令(启动次数多,慢);-exec cmd {} + 把找到的文件作为参数批量传给命令(一次启动,快,但需命令接受多参数)。配合 -print0:find 用 -print0 以 NUL 分隔输出(而非换行),xargs -0 按 NUL 分隔读取,安全处理含空格/换行的文件名(默认换行分隔会被空格/换行破坏)。工程上:批量处理用 -exec {} + 或 -print0 | xargs -0(高效且安全),避免逐文件执行与文件名含特殊字符的问题。

-exec {} + 批量减少命令启动次数(高效),-print0/xargs -0 用 NUL 分隔安全处理任意文件名(含空格/换行)。两者结合高效安全。

#
★★

3. set -e、set -u 与 -o pipefail 的可移植性如何,pipefail 为何不是 POSIX 标准?

set -e、set -u 与 -o pipefail 的可移植性如何?pipefail 为何不是 POSIX 标准?

  • set -e/-u
  • pipefail
  • POSIX 可移植性

set -e(出错即退出,errexit)、set -u(未定义变量报错,nounset)是 POSIX 支持的(bash 也支持,可移植)。-o pipefail(管道中任一命令失败则整个管道失败)不是 POSIX 标准,是 bash/korn 的扩展(POSIX 中管道退出码只取最后一个命令)。因此 pipefail 在 bash 可用但不可移植到严格 POSIX sh。工程上:脚本用 set -euo pipefail 增强健壮性(bash 环境),但若需严格 POSIX 可移植,pipefail 不可用(需手动处理管道失败);注意 set -e 在条件/管道中的行为需谨慎。

set -e/-u 是 POSIX 的,pipefail 是 bash 扩展非 POSIX。健壮脚本用 set -euo pipefail(bash),严格 POSIX 需注意 pipefail 不可移植。

#
★★

4. POSIX make 的推断规则(如 .c.o)如何自动选择编译命令,$@、$<、$^ 三个自动变量分别指代什么?

POSIX make 的推断规则(如 .c.o)如何自动选择编译命令?$@、$<、$^ 三个自动变量分别指代什么?

  • make 推断规则
  • $@/$</$^
  • 自动变量

make 的推断规则(implicit rule)根据目标与先决条件的后缀推断编译命令,如 .c.o 规则:从 .c 源文件生成 .o 目标,自动用 cc 编译(cc -c $< -o $@)。自动变量:$@ ——当前目标名;$< ——第一个先决条件;$^ ——所有先决条件(POSIX 支持 $? 等)。推断规则让 make 无需显式规则即可编译常见源文件(.c→.o→可执行)。工程上:make 用推断规则 + 自动变量自动编译,$@ 目标、$< 首依赖、$^ 全部依赖,用于自定义规则。注意 $^ 是 GNU make 扩展(POSIX 为 $? 等)。

推断规则按后缀自动选编译命令,$@ 目标、$< 首依赖、$^ 全部依赖。make 借此自动编译,减少显式规则。

#
★★

5. at 与 batch 都运行一次性任务,batch 的负载阈值(load average)语义如何决定实际执行时机?

at 与 batch 都运行一次性任务,batch 的负载阈值(load average)语义如何决定实际执行时机?

  • at/batch
  • batch 负载阈值
  • 执行时机

at 在指定时间执行一次性任务(时间明确);batch 也在"负载低时"执行一次性任务——它不指定具体时间,而是等系统负载降到阈值(load average,由 atd 的启动参数设定,如默认 1.5)以下才执行。batch 的负载阈值语义:当系统 load average 降至设定值以下时,batch 任务才被执行,用于在系统空闲时运行低优先级批处理(避免高峰加重负载)。工程上:定时任务用 at,负载低时运行用 batch(避免高峰),batch 执行时机由负载阈值决定。

at 定时执行,batch 等负载降到阈值才执行。batch 用 load average 阈值延迟到低负载期运行,适合避免高峰批处理。

#
★★

6. PTHREAD_MUTEX_NORMAL、ERRORCHECK、RECURSIVE 三种互斥锁在死锁、重复解锁、错误检测上的行为差异是什么?

PTHREAD_MUTEX_NORMAL、ERRORCHECK、RECURSIVE 三种互斥锁在死锁、重复解锁、错误检测上的行为差异是什么?

  • mutex 类型
  • 死锁/重复解锁/错误检测
  • 行为差异

三种 mutex 行为差异:NORMAL(普通)——同一线程重复加锁会死锁(未定义),重复解锁未定义/可能崩溃,错误检测弱;RECURSIVE(递归)——同一线程可重复加锁(计数),需等量解锁,重复解锁(非持有者)返回错误,可用于可重入;ERRORCHECK(错误检查)——重复加锁(非递归)返回 EDEADLK,解锁非持有者/未锁定返回 EPERM,错误检测强(利于调试)。差异:NORMAL 无错误检测(死锁/未定义),RECURSIVE 允许重入(计数),ERRORCHECK 检测非法操作返回错误。工程上:调试用 ERRORCHECK,可重入用 RECURSIVE,一般用 NORMAL(性能)。

NORMAL 死锁/未定义,RECURSIVE 允许重入(需等量解锁),ERRORCHECK 检测非法操作返回错误码。按场景选型。

#
★★

7. 标准信号不排队而实时信号(SIGRTMIN+)排队的差异,在可靠事件通知场景应如何选型?

标准信号不排队而实时信号(SIGRTMIN+)排队的差异,在可靠事件通知场景应如何选型?

  • 标准 vs 实时信号排队
  • 可靠事件通知
  • 选型

标准信号不排队(同信号多次投递合并为一个,可能丢事件),实时信号(SIGRTMIN+)排队(多次投递按序全部投递,不丢,可靠)。可靠事件通知场景(事件不能丢、需知道每次发生)应选实时信号:实时信号排队保证每次投递都被接收,可携带数据(sigqueue),且有优先级。工程上:需要可靠事件通知(如文件变化、事件计数)用实时信号(SIGRTMIN+),配合 sigwait/signalfd 处理;标准信号只用于常规控制(不要求可靠递送)。选型核心:事件是否需可靠、有序、不丢。

实时信号排队可靠(不丢、有序、可携带数据),标准信号合并(可能丢)。可靠事件通知用实时信号。

#
★★

8. 多线程进程中信号如何被投递到特定线程,pthread_sigmask 与 pthread_kill 如何控制这一行为?

多线程进程中信号如何被投递到特定线程?pthread_sigmask 与 pthread_kill 如何控制?

  • 信号投递到线程
  • pthread_sigmask/pthread_kill
  • 控制

多线程中信号投递:进程级信号(kill 发)由任一未阻塞该信号的线程处理(未指定线程);线程级信号(pthread_kill)投递到指定线程。控制:pthread_sigmask 设置线程的阻塞掩码(阻塞某信号,该线程不接收),从而控制进程级信号由哪些线程处理(未阻塞的线程);pthread_kill 精确投递到指定线程。工程上:让信号由特定线程处理——其他线程用 pthread_sigmask 阻塞该信号,指定线程不阻塞并用 sigwait 接收;或直接用 pthread_kill 投递到目标线程。

进程级信号由任意未阻塞线程处理,pthread_sigmask 控制阻塞(决定哪些线程接收),pthread_kill 精确投递到线程。

#
★★

9. POSIX sh 的 15 个特殊内建命令(break、:、continue、.、eval、exec、exit、export、readonly、return、set、shift、times、trap、unset)与普通内建命令(如 cd、pwd、read)在命令查找与变量赋值持久性上有何区别?

POSIX sh 的 15 个特殊内建命令与普通内建命令在命令查找与变量赋值持久性上有何区别?

  • 特殊内建命令
  • 命令查找
  • 变量赋值

POSIX sh 的 15 个特殊内建命令(break、:、continue、.、eval、exec、exit、export、readonly、return、set、shift、times、trap、unset)与普通内建命令(cd、pwd、read)的区别:命令查找——特殊内建命令在命令查找时不经过 PATH 搜索(即使有同名外部命令也优先用内建,且不激活函数),普通内建命令在 PATH 搜索前检查(若被函数/外部覆盖,行为有别);变量赋值——特殊内建前置的变量赋值在命令执行后持久(如 x=1 export x 中 x 的赋值在命令后仍有效),普通内建前置的赋值仅对命令本身有效(不持久)。特殊内建的错误会终止 shell(非交互)。工程上:理解特殊内建保证命令查找与赋值语义正确。

特殊内建命令"优先查找(不查 PATH)+ 前置赋值持久 + 错误终止 shell",普通内建"在 PATH 后 + 赋值不持久"。这是 shell 语义的关键差异。

#
★★

10. POSIX awk 的记录(record)与字段(field)处理模型

POSIX awk 的记录(record)与字段(field)处理模型如何理解?

  • awk 记录/字段
  • RS/FS
  • 处理模型

awk 的处理模型基于"记录(record)与字段(field)":awk 把输入按记录分隔符(RS,默认换行)拆成记录(行),每行按字段分隔符(FS,默认空白)拆成字段($1 第 1 字段、$NF 最后字段),逐条记录执行模式-动作(pattern-action)。内建变量:NR(当前记录号)、NF(字段数)、FS(输入分隔符)、OFS(输出分隔符)、RS(记录分隔符)。处理模型:awk 逐行读取、按 FS 分字段、按 pattern 匹配、执行 action(printf 输出)。工程上:awk 用于文本字段提取/统计,按记录-字段模型处理。

awk 按 RS 分记录、FS 分字段,逐记录执行 pattern-action。$n 字段、NR 记录号、NF 字段数,是字段处理的核心模型。

#
★★

11. POSIX grep 的 BRE(基本正则)与 ERE(扩展正则)的差异

POSIX grep 的 BRE(基本正则)与 ERE(扩展正则)的差异如何理解?

  • BRE vs ERE
  • 正则差异
  • 使用

grep 默认用 BRE(基本正则),-E 用 ERE(扩展正则)。差异:BRE 中 +?|() 等元字符需转义才有特殊含义(如 \+\(\)),未转义按字面处理;ERE 中这些元字符直接是特殊含义(+?|()),无需转义。即:BRE 保守(元字符少、需转义),ERE 开放(元字符全、简洁)。工程上:用 ERE(-E)写更简洁的正则(+、?、|、() 直接使用),BRE 需转义;量词相同(*、{m,n})。注意字符类与锚点(^、$)两者相同。

BRE 的 +?|() 需转义,ERE 直接使用(-E)。ERE 更简洁,是常用选择,BRE 是默认兼容。

#
★★

12. POSIX sed 的 s 命令与 -e、-i 标志的工程语义

POSIX sed 的 s 命令与 -e、-i 标志的工程语义如何理解?

  • sed s 命令
  • -e/-i 标志
  • 用法

sed 的 s 命令(s/old/new/flags)做替换(替换匹配的文本),flags 如 g(全局替换)、p(打印)、数字。标志:-e——追加一个编辑脚本(可多个 -e 组合命令),-i——原地编辑(in-place,直接修改文件而非输出到 stdout)。工程上:s 命令用于文本替换,-i 用于原地修改文件(配合备份),-e 用于组合多个编辑命令。注意 -i 不可移植(非 POSIX 标准,GNU/BSD 语义有差异),POSIX 用 -e 组合。

s 命令做替换、-e 组合命令、-i 原地编辑。工程上 -i 便于脚本直接改文件,但可移植性差,跨平台需注意。

#
★★

13. POSIX xargs 的输入分块(-n)与空输入边界

POSIX xargs 的输入分块(-n)与空输入边界如何理解?

  • xargs -n 分块
  • 空输入边界
  • 参数批量

xargs 从标准输入读取参数,按块批量执行命令。分块:-n N 每次最多取 N 个参数执行一次命令(参数过多时自动分成多块);不带 -n 时尽量一次传所有参数(受 ARG_MAX 限制,xargs 自动分块)。空输入边界:若输入为空,xargs 默认执行一次命令(无参数),用 -r(/--no-run-if-empty)可禁止空输入执行命令。工程上:-n 控制单次命令的参数数量,-r 避免空输入误执行,配合 find -print0 / xargs -0 处理文件名。

-n 控制每次命令的参数个数(分块),-r 禁止空输入执行。配合 -0 处理文件名,避免参数过多与空输入问题。

#
★★

14. POSIX 工具集,awk、sed、grep、find、xargs 的标准化规范(POSIX.1-2017 §12)

POSIX 工具集:awk、sed、grep、find、xargs 的标准化规范(POSIX.1-2017 §12)如何理解?

  • POSIX 工具集
  • §12 标准规范
  • 工具行为

POSIX.1-2017 第十二章(Shell and Utilities)定义标准工具集(Utilities)的行为规范,包括 awk、sed、grep、find、xargs 等常用命令。标准规定:命令的选项、参数、退出码、输入/输出处理、环境变量影响,保证跨平台(不同 Unix 实现)行为一致。含义:这些工具是 POSIX 必需的(mandatory),实现需符合规范;GNU 扩展(如 grep -P、sed -i)超出 POSIX 标准,可移植性差。工程上:用 POSIX 标准选项可移植,涉及 GNU 扩展时需注意跨平台兼容。

§12 定义标准工具集行为,保证跨平台一致性。awk/sed/grep/find/xargs 是必需工具,GNU 扩展超标准。

#
★★

15. POSIX sh 参数扩展 ${var:-default}、${var:=default}、${var:?error} 的语义差异是什么?

POSIX sh 参数扩展 ${var:-default}、${var:=default}、${var:?error} 的语义差异是什么?

  • 参数扩展
  • :- / := / :? 语义
  • 默认值处理

POSIX sh 参数扩展(parameter expansion)用于取默认值:(1) ${var:-default}——若 var 未设置或为空,用 default 的值(不修改 var);(2) ${var:=default}——若 var 未设置或为空,把 default 赋给 var 再用(修改 var);(3) ${var:?error}——若 var 未设置或为空,输出 error 并退出(非交互 shell 退出,用于强制校验)。差异核心::- 只取不赋值、:= 赋值再用、:? 未设置则报错退出。注意带冒号(:-)表示"未设置或为空都触发",不带冒号(${var-default})只对"未设置"触发。工程上::- 用于安全取默认值,:= 用于初始化,:? 用于必填参数校验。

:- 取默认不赋值、:= 赋值再用、:? 未设置报错退出。冒号表示"为空也触发"。用于参数默认值与校验。

#
★★

16. awk 的内建变量 NR(当前记录号)、NF(字段数)、FS(输入分隔符)、OFS(输出分隔符)各起什么作用?BEGIN/END 块为何只在首尾各执行一次?

awk 的内建变量 NR、NF、FS、OFS 各起什么作用?BEGIN/END 块为何只在首尾各执行一次?

  • awk 内建变量
  • NR/NF/FS/OFS
  • BEGIN/END 块

awk 内建变量:NR(当前记录号,即处理到第几条记录)、NF(当前记录的字段数)、FS(输入字段分隔符,默认空白)、OFS(输出字段分隔符,默认空格)。BEGIN/END 块:BEGIN 在读取任何输入前执行一次(用于初始化变量、设置 FS/OFS),END 在处理完所有输入后执行一次(用于汇总输出)。原因:awk 的处理模型是"先执行 BEGIN(初始化)→ 逐记录执行 pattern-action → 最后执行 END(收尾)",故 BEGIN/END 各只执行一次。工程上:FS/OFS 在 BEGIN 设置,NR/NF 用于统计与字段操作,END 输出汇总。

NR 记录号、NF 字段数、FS 输入分隔符、OFS 输出分隔符。BEGIN 初始化、END 汇总,各执行一次。

#
★★

17. POSIX locale 与 i18n 在 shell 工具中的处理

POSIX locale 与 i18n(国际化)在 shell 工具中的处理如何理解?

  • locale 概念
  • i18n 国际化
  • 字符分类/排序

locale(区域设置)定义语言、字符集、排序(collation)、数字/货币格式等环境,由环境变量(LC_ALL、LC_CTYPE、LC_COLLATE、LANG 等)控制。i18n(国际化)让工具输出与处理适配不同区域。对 shell 工具的影响:字符分类(isalpha、字符类 like [[:alpha:]])、排序(sort 的 collation 顺序)、大小写转换、字节 vs 字符处理。工程上:工具默认按用户 locale 行为,但为稳定可设 LC_ALL=C(POSIX locale,字节序、ASCII 排序)保证跨环境一致;涉及多字节(UTF-8)时注意字符边界。POSIX locale 是最小集,保证字节与 ASCII 行为可预测。

locale 控制语言/字符集/排序,LC_ALL=C 提供最小可预测行为。i18n 让工具适配区域,但需注意字符 vs 字节。

#
★★

18. POSIX signal 默认动作(default action)的 5 类

POSIX signal 默认动作(default action)的 5 类是什么?

  • 信号默认动作
  • 5 类动作
  • 信号处理

POSIX 信号在未设置处理器时的默认动作(default action)分 5 类:(1) 终止(Term)——终止进程(如 SIGTERM、SIGINT);(2) 终止并转储核心(Core)——终止进程并生成 core dump(如 SIGSEGV、SIGABRT、SIGBUS);(3) 停止(Stop)——暂停进程,可继续(如 SIGSTOP、SIGTSTP);(4) 继续(Continue)——恢复被停止的进程(如 SIGCONT);(5) 忽略(Ignore)——丢弃信号无影响(如 SIGCHLD、SIGURG)。工程上:理解默认动作便于选信号(如用 SIGTERM 优雅退出、SIGSEGV 崩溃转储、SIGSTOP 暂停、SIGCONT 恢复)。

5 类默认动作:Term 终止、Core 终止转储、Stop 停止、Continue 继续、Ignore 忽略。按用途选信号。

#
★★

19. POSIX 必需工具 vs 可选工具的 SUS 标记

POSIX 必需工具 vs 可选工具的 SUS 标记如何理解?

  • 必需工具
  • 可选工具
  • SUS 标记

POSIX/SUS(Single UNIX Specification)将工具分为必需(mandatory)与可选(optional)。必需工具——所有 POSIX 实现都必须提供(如 ls、cat、sh、awk、grep);可选工具——实现可选择是否提供(如 cron、at、make、pax 等),若是可选工具,标准也规定其行为。SUS 标记(如 XSI、UPE、UP)表示工具或功能属于哪个扩展层:XSI(X/Open System Interface)扩展包含更多工具(如 make、crontab),UPE/UP 表示 POSIX 可选。工程上:判断工具可移植性看它是否必需(mandatory),可选工具需确认目标系统提供。

必需工具都必须提供,可选工具可实现可省略。SUS 标记(XSI 等)标注扩展层级,判断可移植性。

#
★★

20. POSIX 标准规定 vs GNU 扩展的兼容性处理

POSIX 标准规定 vs GNU 扩展的兼容性处理如何理解?

  • POSIX 标准
  • GNU 扩展
  • 兼容性

POSIX 标准规定是最小可移植行为,GNU 扩展(如 grep -P、sed -i、find -printf、ls --color)在 POSIX 基础上增加功能,但不可移植。兼容性处理:追求可移植时只用 POSIX 选项(严格 POSIX 子集);用 GNU 扩展时需确认目标环境(Linux 一般有 GNU coreutils,BSD/macOS 不同)。POSIXLY_CORRECT 环境变量、--posix 选项让 GNU 工具更严格遵循 POSIX。工程上:跨平台脚本用 POSIX 兼容写法,Linux 专用可用 GNU 扩展;用测试(command -v、版本探测)保证功能可用。

POSIX 是最小可移植,GNU 扩展增强但不可移植。可移植用 POSIX 子集,专用用 GNU 扩展。

#
★★

21. POSIX.1-2017 与 SUSv4(Single UNIX Specification v4)的对应关系

POSIX.1-2017 与 SUSv4(Single UNIX Specification v4)的对应关系如何理解?

  • POSIX.1-2017
  • SUSv4
  • 标准关系

POSIX.1-2017 与 SUSv4(Single UNIX Specification version 4)实际上是同一套标准:SUSv4 由 Austin Group 联合制定,其系统接口与 POSIX.1-2017 一致(两者等同)。SUS 在 POSIX 基础上增加 XSI 扩展(X/Open System Interface),包含更多 UNIX 传统接口(如某些系统调用、STREAMS、cron 等)。关系:POSIX 是最小可移植核心,SUS 是超集(含 XSI 扩展与 UNIX 标志要求)。实现通过 SUS 认证(如 macOS、某些 BSD)才可称为 UNIX 系统。工程上:查询标准时 POSIX.1-2017 与 SUSv4 对应,XSI 扩展是区分"POSIX 兼容"与"完整 UNIX"的关键。

POSIX.1-2017 与 SUSv4 等同,SUS 增加 XSI 扩展。POSIX 是最小核心,SUS 是含扩展的 UNIX 超集。

#
★★

22. pax/tar 的 ustar 格式在路径长度(name 100/prefix 155 字节)与文件大小(8GB)上有何限制,pax 扩展头如何突破?

pax/tar 的 ustar 格式在路径长度与文件大小上有何限制,pax 扩展头如何突破?

  • ustar 格式
  • 路径长度限制
  • pax 扩展头

ustar(Unix Standard tar)格式的字段限制:文件名 name 字段 100 字节、prefix 字段 155 字节(长路径拆成 name+prefix 组合),文件大小字段以 8 进制存 12 字节(最大约 8GB)。这些限制导致 ustar 无法表示超长路径与大文件。pax 扩展头突破:pax(POSIX archiver)用"扩展头记录"(extended header)以 key=value 形式记录超长路径、大文件大小、长链接名等,不占用固定字段,突破 100/155 字节与 8GB 限制。工程上:归档长路径/大文件用 pax 格式或 GNU tar 的 --format=pax;传统 ustar 仅适合常规路径与 <8GB 文件。

ustar name 100 字节、prefix 155 字节、文件大小上限约 8GB。pax 扩展头用 key=value 突破这些限制。

#
★★

23. POSIX 1003.1-2017 的章节(chapter)结构与查询路径

POSIX 1003.1-2017 的章节结构如何?如何查询条目?

  • 标准章节结构
  • 查询路径
  • 标准组织

POSIX.1-2017 的主章节分类:Base Definitions(基础定义,含通用术语、字符集、环境变量)、System Interfaces(系统接口,含头文件、函数、系统调用)、Shell and Utilities(Shell 与工具)、Rationale(设计理由)。每部分按"章节 + 条目"组织,如函数条目(如 write、open)在 System Interfaces 章节,工具条目(如 awk、grep)在 Shell and Utilities 章节。查询路径:根据条目类型定位——函数/系统调用查 System Interfaces,工具查 Shell and Utilities,头文件查 Base Definitions(头文件条款)。工程上:查询 POSIX 用在线标准(opengroup.org)按章节检索,或查 POSIX 手册页。

标准分四大部分:Base Definitions、System Interfaces、Shell and Utilities、Rationale。函数查 System Interfaces、工具查 Shell and Utilities。

#
★★

24. POSIX thread-safe 函数与 MT-safe 等级

POSIX thread-safe 函数与 MT-safe 等级如何理解?

  • thread-safe
  • MT-safe 等级
  • 线程安全

POSIX 函数分为 thread-safe(线程安全)与非 thread-safe。thread-safe:可在多线程中同时调用而无需外部同步,不产生数据竞争(如 read、write、pthread 自身)。MT-safe 等级:标准用"MT-Safe"标记(如 AS-Safe 异步信号安全、AC-Safe 异步取消安全、MC-Safe 信号安全)标注函数在不同并发上下文下的安全性。非 thread-safe 函数(如 strtok、rand、getenv、某些 errno 相关)因使用静态/全局状态,需用锁或线程安全变体(如 strtok_r、rand_r)。工程上:多线程程序用 *_r 变体或加锁保护非安全函数,避免数据竞争。

thread-safe 函数可多线程并发调用,MT-Safe 标注不同上下文安全性。非安全函数用 *_r 变体或加锁。

#
★★

25. POSIX 头文件(unistd.h、sys/types.h、pthread.h)的包含规则

POSIX 头文件(unistd.h、sys/types.h、pthread.h)的包含规则如何理解?

  • 头文件包含
  • 功能宏
  • 声明可见性

POSIX 头文件通过功能测试宏(feature test macros)控制声明可见性:编译时需定义 _POSIX_C_SOURCE(或 _XOPEN_SOURCE)才能看到 POSIX 函数/常量声明,否则可能只看到默认(ANSI C)声明。unistd.h——POSIX 系统接口(fork、exec、read、write、getpid 等);sys/types.h——基础类型(pid_t、size_t、off_t 等);pthread.h——线程接口(pthread_create、mutex 等)。包含规则:按需包含对应头文件,定义适当的功能宏(如 -D_POSIX_C_SOURCE=200809L)保证声明可见;pthread 程序需链接 -pthread。工程上:正确设置功能宏与包含头文件,避免隐式声明与链接错误。

功能宏(_POSIX_C_SOURCE)控制 POSIX 声明可见性。unistd.h 系统接口、sys/types.h 类型、pthread.h 线程,链接需 -pthread。

#
★★

26. POSIX 选项(POSIXLY_CORRECT)的 GNU 工具兼容

POSIX 选项(POSIXLY_CORRECT)的 GNU 工具兼容如何理解?

  • POSIXLY_CORRECT
  • GNU 工具兼容
  • 选项语义

POSIXLY_CORRECT 环境变量让 GNU 工具(coreutils 等)更严格遵循 POSIX 行为:启用后,GNU 工具对选项解析、默认行为更接近 POSIX 标准(如某些模糊选项、默认输出格式按 POSIX)。反之,GNU 工具默认可能有 GNU 扩展行为(如 ls 默认带颜色、grep 支持 -P 等)。工程上:设置 POSIXLY_CORRECT 可获得更可移植的行为,但会失去部分 GNU 增强;兼容脚本用 POSIXLY_CORRECT 保证行为一致,或用 --posix 选项。注意 POSIXLY_CORRECT 不是 POSIX 标准本身,是 GNU 提供的兼容开关。

POSIXLY_CORRECT 让 GNU 工具更严格遵循 POSIX。兼容用 POSIXLY_CORRECT,增强用 GNU 默认。

#
★★

27. crontab 的五个时间字段(分时日月周)如何组合匹配,SHELL、PATH、MAILTO 环境变量如何影响任务执行?

crontab 的五个时间字段如何组合匹配?SHELL、PATH、MAILTO 环境变量如何影响任务执行?

  • crontab 时间字段
  • 匹配规则
  • 环境变量

crontab 的五个时间字段是"分 时 日 月 周":分(0-59)、时(0-23)、日(1-31)、月(1-12)、周(0-7,0/7 均为周日)。匹配规则:当所有字段匹配当前时间时执行(日与周是"或"关系——任一字段匹配即执行,除非两者都指定数值)。环境变量:SHELL 指定执行命令的 shell(默认 /bin/sh)、PATH 指定命令查找路径(crontab 默认 PATH 很有限,需显式设置)、MAILTO 指定任务输出邮件发送对象(未设置则不发送)。工程上:crontab 需显式设置 PATH 和 SHELL,用绝对路径或设置 PATH,避免命令找不到;MAILTO 控制输出告警。

五字段为分时日月周,日与周是"或"匹配。SHELL/PATH 需显式设置避免命令找不到,MAILTO 控输出。

#
★★

28. 为什么条件变量必须配合 mutex 保护谓词变量,单独使用条件变量会产生竞态?

为什么条件变量必须配合 mutex 保护谓词变量?单独使用条件变量会产生竞态?

  • 条件变量
  • mutex 协作
  • 竞态

条件变量(pthread_cond_wait)必须配合 mutex 使用,因为:等待者需先检查谓词(predicate)再决定等待,检查与等待之间必须原子(否则产生竞态)。单独使用条件变量的问题:若等待者先检查谓词发现"不满"再调用 wait,但若在检查与 wait 之间生产者已满足谓词并 signal,则等待者错过 signal 而永久阻塞(丢失唤醒 lost wakeup)。正确流程:pthread_mutex_lock 后检查谓词,不满足则 pthread_cond_wait(wait 原子地释放锁并等待,唤醒后重新抢锁),再检查谓词、解锁。mutex 保证谓词检查与 wait 的原子性,防止"检查后 signal 前"的竞态。

条件变量必须配合 mutex 保护谓词,wait 原子地"释放锁+等待+唤醒后抢锁"。否则谓词检查与 wait 之间漏掉 signal 导致丢失唤醒。

#
★★

29. 异步信号安全(async-signal-safe)函数列表为何如此有限,malloc、printf 为什么不能安全地在信号处理器中调用?

异步信号安全函数列表为何如此有限?malloc、printf 为何不能安全地在信号处理器中调用?

  • async-signal-safe
  • 信号处理器
  • malloc/printf 不可用

异步信号安全(async-signal-safe)函数指可在信号处理器中安全调用的函数(如 write、_exit、sigaction、read)。列表有限是因为信号处理器可在任意指令处打断主程序,若主程序正在执行某个函数(如 malloc 获取堆锁、printf 获取 stdio 锁)时被信号打断,处理器内再调用同一函数会"重复加非递归锁"导致死锁或数据损坏。malloc 持有 arena 锁、printf 持有 stdio 缓冲锁,在信号处理器中再调用会死锁;且 malloc 的内部分配状态可能处于不一致中间态。工程上:信号处理器只调用 async-signal-safe 函数(如 write、sigaction),或多用"信号只置标志 + 主循环处理"(self-pipe / signalfd)。

async-signal-safe 函数有限,因为信号可打断持锁中的主程序,malloc/printf 持锁重入会死锁。处理器只置标志让主循环处理。

#
★★

30. 被信号打断的慢速系统调用返回 EINTR 时,SA_RESTART 自动重启与手动重试各有什么陷阱?

被信号打断的慢速系统调用返回 EINTR 时,SA_RESTART 自动重启与手动重试各有什么陷阱?

  • EINTR
  • SA_RESTART
  • 手动重试

慢速系统调用(read、write、wait 等)被信号打断时提前返回 EINTR。SA_RESTART 标志让内核自动重启这类调用(如 Linux 对 read/write 自动重启),陷阱:并非所有调用都自动重启(如 select、poll、epoll_wait、sem_wait 不重启),且重启可能掩盖"已读部分数据"的语义(如 read 已读部分后中断,重启会丢弃已读数据)。手动重试陷阱:需自行判断是否重试(处理 EINTR 时重新调用),但可能丢失部分数据(read 返回 EINTR 前已读的字节),且无限重试可能被高频信号饿死。工程上:用安全阻塞(sigwait 集中处理信号)或者用带 SA_RESTART 的处理器并明确哪些调用会重启,手动重试时先检查是否已读部分数据。

SA_RESTART 自动重启部分调用(非所有),手动重试需处理 EINTR 但注意已读数据与新信号。两者都有语义陷阱。

#
★★

31. sed 的 pattern space 与 hold space 如何配合(h/H/g/G/x)实现多行处理?地址范围(如 1,5、/re1/,/re2/)如何限定命令作用行?

sed 的 pattern space 与 hold space 如何配合实现多行处理?地址范围如何限定命令作用行?

  • pattern/hold space
  • h/H/g/G/x 命令
  • 地址范围

sed 有两个缓冲区:pattern space(当前处理行)与 hold space(保留缓冲区)。命令配合:h/H(把 pattern 复制/追加到 hold)、g/G(把 hold 复制/追加到 pattern)、x(交换 pattern 与 hold)。多行处理:用 hold space 暂存多行,配合命令实现跨行拼接/处理(如把多行合并、在行间插入)。地址范围:单地址(如 1 第 1 行、$ 最后一行、/re/ 匹配行)与范围(如 1,5 第 1-5 行、/re1/,/re2/ 从匹配 re1 到匹配 re2 的行)限定命令作用范围。工程上:hold space 用于多行状态,地址范围限定处理行集。

pattern space 存当前行、hold space 暂存,h/H/g/G/x 在两者间传输实现多行。地址范围限定命令作用行。

#
★★

32. grep 的 -E(ERE)、-F(固定字符串)、-P(PCRE)分别用什么匹配引擎?找到匹配返回 0、未找到返回 1、出错返回 2 的退出码约定有何用途?

grep 的 -E、-F、-P 分别用什么匹配引擎?退出码 0/1/2 约定有何用途?

  • grep 匹配引擎
  • -E/-F/-P
  • 退出码

grep 的匹配引擎:默认 BRE(基本正则)、-E 用 ERE(扩展正则)、-F 用固定字符串(fgrep,不做正则,字面匹配)、-P 用 PCRE(Perl 兼容正则,GNU 扩展)。退出码约定:找到匹配返回 0、未找到返回 1、出错返回 2(如文件不存在、非法选项)。用途:在脚本中可用退出码判断是否有匹配,作为条件(if grep -q pattern file)或流程控制(如终止条件)。工程上:grep -q 抑制输出只取退出码,-F 对纯字符串查找更快更安全(避免正则转义),-P 提供高级正则但非 POSIX。

-E 用 ERE、-F 字面匹配、-P 用 PCRE。退出码 0/1/2 分别表示匹配/不匹配/出错,用于脚本条件。

#
★★

33. 命令替换的反引号与 $() 在嵌套引用与可读性上有何差异,为何推荐后者?

命令替换的反引号与 $() 在嵌套引用与可读性上有何差异,为何推荐后者?

  • 命令替换
  • 反引号 vs $()
  • 嵌套引用

命令替换(command substitution)有两种写法:反引号(cmd)与 $()($(cmd))。差异:反引号内部的转义规则复杂(嵌套反引号需转义 `...`,反斜杠处理混乱),$() 内可自然嵌套($(cmd $(inner))),且 $() 内引号处理更直观、可读性更好。推荐 $():因为嵌套与引号处理更清晰、转义规则简化、可读性高,且 POSIX 支持。反引号主要用于旧脚本兼容。注意:$() 内反斜杠、引号、$ 的处理更符合直觉。工程上:新脚本用 $(),避免反引号的转义陷阱。

$() 嵌套与引号处理更清晰、可读性好,反引号转义复杂。推荐 $(),反引号是旧式兼容。

#
★★

34. xargs 的 -n(每次参数数)、-P(并发数)、-I(替换占位符)如何控制批量与并发?并发执行时输出交错与退出码如何处理?

xargs 的 -n、-P、-I 如何控制批量与并发?并发时输出交错与退出码如何处理?

  • xargs 参数
  • -n/-P/-I
  • 并发与退出码

xargs 参数:-n N 每次传递 N 个参数(批量)、-P N 并发执行(同时运行 N 个进程)、-I place 用替换占位符(每次取一个输入项替换占位符执行)。并发执行时:多个子进程可同时运行,输出可能交错(顺序不确定),需注意日志/输出合并;退出码——xargs 默认返回 0/123/124/125 等:总体成功 0,任一命令失败返回 123,命令无法执行 126/127,自己被杀 124/125。工程上:-P 提升并发吞吐但输出交错,需用 -I 或输出重定向;检查退出码判断整体成败。

-n 批量、-P 并发、-I 替换占位符。并发输出交错需处理,退出码 123 表示有命令失败。

#
★★

35. 为什么脚本中处理文件名要用 NUL 分隔(-print0/xargs -0)而非默认换行分隔?

为什么脚本中处理文件名要用 NUL 分隔(-print0/xargs -0)而非默认换行分隔?

  • NUL 分隔
  • -print0/-0
  • 文件名安全

Unix 文件名可以包含换行符(换行是合法文件名字符,仅 NUL 和 / 不能出现在文件名中)。若用默认换行分隔(find ... | xargs),含换行的文件名会被拆成多行,导致参数错误、命令执行错误甚至安全风险(文件被当成多个参数)。用 NUL 分隔(find -print0 | xargs -0):NUL 不可能出现在文件名中,是唯一安全的分隔符,能完整保留文件名(含空格、换行、特殊字符)。工程上:处理任意文件名一律用 -print0/xargs -0(或 find -exec {} +),避免文件名边界问题。

NUL 不能出现在文件名中,是唯一安全分隔符。含换行/空格的文件名用 -print0/xargs -0 才安全。

#
★★

36. here document 与 here string 的区别是什么,bash 的数组、[[ ]]、<<< 为何不可移植?

here document 与 here string 的区别是什么?bash 的数组、[[ ]]、<<< 为何不可移植?

  • here document
  • here string
  • bash 扩展不可移植

here document(<<EOF ... EOF)是 POSIX 标准:把一段文本作为命令的标准输入,支持变量展开与命令替换(<<- 去前导 tab)。here string(<<< "text")是 bash 扩展:把单个字符串作为命令输入,非 POSIX。bash 的数组、[[ ]]、<<< 等是 bash 扩展,POSIX sh 不支持(POSIX sh 无数组、[ ] 单括号、无 here string),故不可移植。工程上:可移植脚本用 here document(POSIX),避免依赖 bash 扩展;确需 bash 特性时声明 #!/bin/bash 并接受非 POSIX 移植性。

here document 是 POSIX 标准,here string(<<<)、数组、[[ ]] 是 bash 扩展不可移植。可移植用 here document。

#
★★

37. 什么是虚假唤醒(spurious wakeup)?为什么条件变量等待必须写成 while 循环而非 if 判断?

什么是虚假唤醒(spurious wakeup)?为什么条件变量等待必须写成 while 循环而非 if 判断?

  • 虚假唤醒
  • while 循环
  • 条件变量

虚假唤醒(spurious wakeup)指条件变量在谓词仍未满足时被唤醒(信号丢失、多生产者竞态、实现允许),导致等待者醒来后发现条件不满足。因此条件变量等待必须写成 while 循环而非 if 判断:while (!predicate) pthread_cond_wait(...),醒来后重新检查谓词,不满足则继续等待。原因:if 只检查一次,若被虚假唤醒(或唤醒后谓词仍未满足)会继续执行后续逻辑,导致错误(如消费者对空队列取值)。while 循环保证"唤醒后重新验证谓词",直到真正满足才退出。这是 POSIX 与广泛实现的推荐写法。

虚假唤醒使谓词可能未满足即被唤醒,while 循环唤醒后重新检查,if 只查一次会误执行。故必须 while。

#
★★

38. pthread_cond_signal 与 pthread_cond_broadcast 的区别是什么?何时必须用 broadcast 避免饥饿?

pthread_cond_signal 与 pthread_cond_broadcast 的区别是什么?何时必须用 broadcast 避免饥饿?

  • signal vs broadcast
  • 唤醒策略
  • 饥饿

pthread_cond_signal 唤醒一个等待线程(具体哪个不确定),pthread_cond_broadcast 唤醒所有等待线程。必须用 broadcast 的场景:当多个等待线程等待不同谓词或资源(如生产者-消费者中有空位与满两种等待),若只 signal 一个,可能唤醒"错误"的线程(其谓词不满足又回去等待),导致本应被唤醒的线程饥饿;broadcast 唤醒所有线程,各自重新检查谓词,已满足的继续执行。工程上:多个谓词/多个等待者竞争时用 broadcast;单谓词单消费者可用 signal 提高效率。

signal 唤醒一个、broadcast 唤醒全部。多谓词/多竞争场景用 broadcast 避免唤醒错误线程致饥饿。

#

39. pthread_once 如何保证初始化例程在多线程下只执行一次?

pthread_once 如何保证初始化例程在多线程下只执行一次?

  • pthread_once
  • 单次初始化
  • 线程安全

pthread_once(pthread_once_t once_control)保证初始化例程在多线程下只执行一次:多个线程同时调用 pthread_once 时,只有一个线程执行 init 例程,其余线程阻塞直到 init 完成(或返回)。实现:pthread_once_t 内部有状态(未初始化/初始化中/已初始化),配合内存屏障/锁保证"只执行一次 + 完成后可见"。用途:替代需要加锁的惰性初始化(如全局单例、一次性注册),比"标志 + 锁"更简洁安全。工程上:用 pthread_once 做线程安全的单次初始化,避免竞态与重复执行。

pthread_once 用内部状态保证 init 只执行一次,其余线程等待完成。比"标志+锁"更简洁安全的单次初始化。

#

40. sigaction 相比传统 signal() 在语义可移植性与标志控制上解决了哪些历史问题,SA_RESTART 改变了什么?

sigaction 相比传统 signal() 在语义可移植性与标志控制上解决了哪些历史问题?SA_RESTART 改变了什么?

  • sigaction
  • signal() 历史问题
  • SA_RESTART

传统 signal() 的历史问题:语义在 Unix 实现间不统一(System V 中处理后信号处置重置为默认,BSD 中不重置),且无法控制细节(如重启、阻塞、是否带 siginfo)。sigaction 解决了这些问题:用 sigaction 结构体显式指定处理器(sigaction 不被重置)、sa_flags 标志控制行为(SA_RESTART、SA_NOCLDWAIT、SA_RESETHAND、SA_SIGINFO 等)、sa_mask 指定处理期间阻塞的信号,语义可移植。SA_RESTART:让被信号打断的慢速系统调用自动重启(而非返回 EINTR),改变"中断后需手动重试"的行为。工程上:用 sigaction 替代 signal(),按需设置 SA_RESTART 等标志。

sigaction 解决 signal() 语义不统一、无法控制细节的问题,用标志控制行为。SA_RESTART 让慢系统调用自动重启。

#

41. SA_SIGINFO 标志使处理器获得 siginfo_t 与 ucontext,能额外取得哪些故障地址与发送者信息?

SA_SIGINFO 标志使处理器获得 siginfo_t 与 ucontext,能额外取得哪些故障地址与发送者信息?

  • SA_SIGINFO
  • siginfo_t
  • ucontext

SA_SIGINFO 标志让信号处理器接收额外参数(siginfo_t *info 与 ucontext_t *context),而非仅一个信号号。siginfo_t 提供丰富信息:si_signo(信号号)、si_code(来源代码,如 SI_USER/SI_KERNEL/SEGV_MAPERR)、针对故障信号(SIGSEGV/SIGBUS)的 si_addr(故障地址)、si_pid 与 si_uid(发送者进程与用户)、si_errno、si_value(sigqueue 附带数据)。ucontext 提供被中断时的寄存器上下文(用于栈回溯、诊断)。工程上:SA_SIGINFO + siginfo_t 用于区分故障来源(如 si_addr 记录非法地址、si_code 区分用户/内核触发),配合 ucontext 做崩溃诊断。

SA_SIGINFO 提供 siginfo_t(si_addr、si_code、si_pid 等)与 ucontext(寄存器上下文),用于区分故障来源与诊断。

#

42. sigaltstack 配置的备用栈在 SIGSEGV/SIGBUS 处理中为何不可或缺,未配置会导致什么后果?

sigaltstack 配置的备用栈在 SIGSEGV/SIGBUS 处理中为何不可或缺?未配置会导致什么后果?

  • sigaltstack
  • 备用信号栈
  • SIGSEGV/SIGBUS

sigaltstack 配置备用信号栈(signal stack),用于在栈溢出/栈耗尽时执行信号处理器。不可或缺:当 SIGSEGV/SIGBUS 因栈溢出(stack overflow)触发时,主栈已耗尽,处理器若在主栈上运行会再次溢出崩溃(无法执行处理器)。sigaltstack 提供独立备用栈,处理器在备用栈上运行,可安全处理(如记录栈回溯、优雅退出)。未配置后果:栈溢出触发的 SIGSEGV 处理器无法运行(无栈可用),进程直接崩溃/二次 fault,无法诊断或恢复。工程上:对栈溢出敏感的进程用 sigaltstack + SA_ONSTACK 提供备用栈,可靠处理崩溃。

sigaltstack 提供备用栈,栈溢出时处理器可在其上运行。未配置则处理器无栈可用,崩溃无法诊断。