为什么要学习 Linux?

为什么要学习 Linux?

几年前入职了一家公司的运维部门,刚上班没几个礼拜,就碰到一起重大线上故障。有一天夜里,公司的服务器突然之间就莫名地一批接一批宕机。当时整个公司的物理机和虚拟机加起来有大几千台的规模。因为搞不清故障原因,就只能简单重启故障机器。然而没什么卵用,机器重启后隔一段时间又会宕掉了,当时组里的同事就这样通宵紧张忙活了一晚上——一直在重启服务器。

那天晚上,我好死不死八点前就睡着了。因为前刚搬到新城市,还不太适应,前天失眠了,所以那天晚上我就睡得特别早,而且睡得特别香。另外也可能因为刚入职没多久,除了招我进来的老板也没人惦记到我。第二天八点多,老板一条消息把我钉醒,大概意思是“大家都已经忙活一晚上了,你也该过来帮帮忙了”。话里就对我不太高兴的样子,我翻了群里的消息了解了上下文后,才知道昨天晚上出了这么大一出事。赶紧跟老板解释一下失眠的事,然后穿好衣服牙也没刷就赶到公司了。

到了公司,好家伙,运维组、研发组、安全组、QA 组再加上公司几位大领导都聚集到相邻的两间大会议室在解决这个事。作为刚入职的小透明,我抱着自己的笔记本找个偏僻的小角落坐下来。我仔细翻了群里的消息,看到里面有人提到过机器上出现了大量的 Salt 的僵尸进程。大家怀疑服务器宕机和这些 Salt 僵尸进程有关。Salt 是运维组用来管理服务器的工具,所以当时这口锅几乎就要板上钉钉地扣到我们运维组头上。

当时我看到僵尸进程这条线索时,顿时虎躯一震。两年前我自己磨写操作系统,当时被进程僵尸状态下谁来收集它的 return 值、什么时候释放僵尸进程的进程结构体这些问题卡了好长一段时间。后来翻了 Linux 内核代码才搞明白要怎么设计。所以我对这个问题印象就特别深刻——僵尸进程不退出你得看它父进程在干嘛呀!僵尸进程需要父进程调用`wait()`函数来给他们收尸——`wait()`调用收集到僵尸进程的 return 值后它才会彻底退出并释放系统进程号资源!如果僵尸进程的父进程死得比它早,那么僵尸进程会作为孤儿进程被`init`进程收养——`init`进程会变成他们的父进程。我弱弱让旁边的同时帮我申请到一台线上还活着的机器的权限,上去`ps`看了下了,这些僵尸进程的父进程居然都是`init`进程。按道理,`init`进程的一个主要任务就是给收尸,它咋不干活呢?看了下`init`进程在干啥,我天啊,`init`进程居然是`t`状态——它被暂停了,不知道是谁给`init`进程发送了`SIGSTOP`信号把它暂停了。如果是现在 2025 年的 Linux 系统上,应该没有那个发行版上的`init`(现在应该都是`systemd`进程了)进程会被`SIGSTOP`信号变成暂停下来,但是我们那会儿用的系统是古董级别的 CentOS 6,内核如果我没记错的话还是 2.6.32,在这个版本上`SIGSTOP`信号就是能把`init`进程给停下来。当时我们 Salt 配置的工作模式就是会产生僵尸进程,`init`进程一停下来没人给僵尸进程收尸了,而由于当时 Salt 数据采集比较频繁,系统中的僵尸进程快速地攒起来,直到进程 ID 耗光它就挂了。要解决这个问题也简单,给`init`进程发个`SIGCONT`把它唤醒起来干活就行:`sudo kill -s SIGCONT 1`。我在几台机器上试了这个条指令,果然一跑僵尸进程就都消失了。

把这个发现弱弱的告诉老板,老板难掩激动,赶紧让我再找几台机器验证给他看,发现果然有效,激动的直呼这是个爆炸性的发现(至少运维 Salt 的锅可以甩出去了),立马安排批量给服务器发送`sudo kill -s SIGCONT 1`指令,把服务器宕机这个燃眉之急的风险解除了。后续老板再接再厉拉着其他团队一起审查了最近时间段内所有可疑的变更,最后成功把锅扣到安全团队头上:安全团队一天前变更了安全扫码组件,这个变更导致安全组件在扫描服务器的时候会把`init`进程给停了。