fork 后写时复制页表在内存共享与 exec 之间的状态演变
这道题在面试里出现的频率很高,但真正能讲清楚的人不多。多数人背下来的答案是「fork 之后子进程和父进程共享物理页,页表项被标成只读,任何一方写入就触发缺页,内核复制一份新的物理页」。这段答案本身没错,可它只覆盖了从 fork 返回、到某一方第一次写入之间的那一小段。面试官一旦追问「exec 的时候这些页怎么办」「如果子进程还没写就 exec,父进程的页会不
这道题在面试里出现的频率很高,但真正能讲清楚的人不多。多数人背下来的答案是「fork 之后子进程和父进程共享物理页,页表项被标成只读,任何一方写入就触发缺页,内核复制一份新的物理页」。这段答案本身没错,可它只覆盖了从 fork 返回、到某一方第一次写入之间的那一小段。面试官一旦追问「exec 的时候这些页怎么办」「如果子进程还没写就 exec,父进程的页会不
多租户 SaaS 里最常见的线上事故不是全站被打挂,而是某一个租户刷爆了共享的 API 网关配额,把其他租户一起拖下水。用 limit_req 做全局限流很容易,limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s; 一行就完事;但要做到「按租户分桶、每桶独立计数」,难点在于取 key 的方式:
把二维数组按某个字段重建成「键 = 字段值」的哈希表,是 PHP 业务代码里出现频率极高的操作。自 PHP 5.5 引入 array_column 之后,很多人会写成 array_column($rows, null, 'sku') 一行完成索引重建,然后配合 array_key_exists 判断某个键是否存在,或者用 isset 做快速命中检查。问题在于
面试里问 sync.Pool,多数人能背出「临时对象复用、减轻 GC 压力」,但真正能区分候选人的是细节:GC 到底怎么清空 Pool?清空之后马上 Put 进去的对象,会不会在下一轮 GC 又被清掉?为什么必须调用 runtime.GC() 两次才能观察到一个对象从 Pool 中彻底消失?Put 里的对象什么时候会被「私吞」而不是进入共享队列? 本文从 G
批量处理文件清单是运维脚本的日常:备份目录列表、待清理日志清单、批量重命名映射表。很多人第一次踩坑都长一个样——清单里明明有 a b/c.log 这种带空格的路径,脚本跑完发现只处理了 a,后半截 b/c.log 被当成了另一个变量或者直接丢掉。更糟的是循环体里一执行 ssh、ffmpeg、read 之类会吃掉标准输入的命令,循环莫名其妙只跑了第一行就结束了
排行榜是 Redis 最经典的用法之一:用 ZSET 存成员与分数,ZREVRANGE 取前 N 名,ZINCRBY 累加积分。但只要业务从「累计积分榜」滑向「热度榜 / 互动分榜」,问题立刻出现——成员分数需要被反复更新,而更新又要遵循「只能涨不能跌」或「窗口内有效」的规则。纯 ZSET 方案在这里会暴露三个具体缺陷:分数更新的并发覆盖、过期语义缺失导致僵
在 Rust 里解析配置、处理网络响应、读取嵌套 JSON 时,最常见的写法是 match 里套 match,层层缩进之后,真正的业务逻辑被推到屏幕右侧。Rust 1.65 稳定了 let-else,它把「匹配成功就绑定变量、失败就立刻离开当前控制流」这段样板代码压缩成一行。但很多人把它当成语法糖直接套到嵌套 Result 上,随后撞上两类硬伤:一是 els
面试里只要提到 TypeScript 条件类型,面试官十有八九会追问一句:「为什么 T extends any ? T[] : never 传入联合类型后返回的是联合的数组,而不是数组的联合?」紧接着还会补一刀:「那如果传 never 呢?」这两个问题看着小,实际上分别打在了 distributive conditional types(分配式条件类型) 和
滚动发布或批量重启之后,很多团队会用一个 sleep 任务加一次 curl 探测来「确认服务起来了」。这种写法在测试环境勉强能用,到了生产环境就会暴露三个问题:探测太早,服务还在初始化,任务直接失败;探测太晚,流水线白白空转几十秒;服务确实挂了却没有明确的重试策略,失败点分散在多个 task 里,很难定位到底是谁把服务拖垮的。Ansible 的 uri 模块