专栏首页Rust学习专栏一个小BUG,引出对Linux启动机制Systemd的代码分析
原创

一个小BUG,引出对Linux启动机制Systemd的代码分析

最近我在生产上遇到一个非常有意思的问题,在Cent OS7以上的操作系统中,VG卷组一激活其默认对应的文件系统也一并挂载上了,而且这还不是红帽和CentOS的特有问题,如果fstab配置default参数的话,其它Linux发行版也有同样的问题。

一般来说用户只需要在启动的时候自动挂载文件系统,在日常使用中激活卷组后,用户很可能希望把这个卷组挂载到其它位置上,而非默认位置,Systemd和fstab这样的联动操作其实给用户日常的使用带来了不少的困扰。

这其实应该是systemd与fstab配合的一个bug,为了说清这个问题,我们先来看,fstab的使用方法,/etc/fstab 文件包含了如下字段,通过空格或 Tab 分隔:

<file system> <dir> <type> <options> <dump> <pass>

  • <file systems>:要挂载的分区或存储设备.
  • <dir>:文件系统的挂载位置。
  • <type>:要挂载设备或是分区的文件系统类型,
  • <options>:挂载时使用的参数,这个是我们重点要说的。

其中auto:在启动时时自动挂载。

defaults:使用文件系统的默认挂载参数,默认参数为:rw, suid, dev, exec, auto, nouser, async,也就是包含了auto这个参数

正如前文所说按照常理理解,auto应该是单指启动时的自动挂载。

初识systemd

在Cent os 7版本之前,红帽系的Linux一直采用init机制来进行系统初始化,现在还有很多经典书籍在介绍Linux启动时还是会详细说明0号init进程的由来,总体来说systemd之前的sysvinit和upstart没有太大区别,upstart只是一个支持USB启动的并行版sysvinit。

systemd的出现颇有后来者居上的气势,目前已经基本统一了linux初始化工具的江湖,它克服 sysvinit串行执行启动步骤的,大幅提高系统的启动速度。凭借着优异的表现目前upstart的拥趸Ubuntu也开始在最新版本中使用systemd了。

systemd提供了和 sysvinit 兼容的特性,原先版本系统中已经存在的服务和进程无需修改。这大幅降低了用户的升级成本,使得 systemd的升级替换相对比较平滑。而且systemd 提供了比 upstart 更优秀的并行启动能力,采用了 socket / D-Bus activation 等技术启动服务。并取得了更快的启动速度,我们明显可以感受到在相同配置的配置下,CentOS7比6启动速度要快。

但是因为systemd过于激进了,这其实也是造成前文那个BUG的直接原因。

systemd如何了解系统启动情况

systemd在进行启动任务编排并控制系统其它服务(service)时,需要详细了解系统当前的状态,我们看到systemd使用的技术基于inotify的钩子机制进行的。

比如在https://github.com/systemd/systemd/basic/fs-util.c下的inotify_add_watch_fd函数就是一个添加监视点的入口。

int inotify_add_watch_fd(int fd, int what, uint32_t mask) {

        int wd;



        /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */

        wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);

        if (wd < 0)

                return -errno;



        return wd;

}

而再翻开Linux下inotify的代码,你会发现添加监控点及通知操作的代码当中都是有加锁动作的,尤其是在新建监控点时甚至直接上了自旋锁。

https://github.com/torvalds/linux/blob/f8fbb47c6e86c0b75f8df864db702c3e3f757361/fs/notify/inotify/inotify_user.c

static int inotify_new_watch(struct fsnotify_group *group,

     struct inode *inode,

     u32 arg)

{

struct inotify_inode_mark *tmp_i_mark;

__u32 mask;

int ret;

struct idr *idr = &group->inotify_data.idr;

spinlock_t *idr_lock = &group->inotify_data.idr_lock;//直接上spinlock



mask = inotify_arg_to_mask(inode, arg);



tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL);

if (unlikely(!tmp_i_mark))

return -ENOMEM;



fsnotify_init_mark(&tmp_i_mark->fsn_mark, group);

tmp_i_mark->fsn_mark.mask = mask;

tmp_i_mark->wd = -1;



ret = inotify_add_to_idr(idr, idr_lock, tmp_i_mark);

if (ret)

goto out_err;



/* increment the number of watches the user has */

if (!inc_inotify_watches(group->inotify_data.ucounts)) {

inotify_remove_from_idr(group, tmp_i_mark);

ret = -ENOSPC;

goto out_err;

}



/* we are on the idr, now get on the inode */

ret = fsnotify_add_inode_mark_locked(&tmp_i_mark->fsn_mark, inode, 0);

if (ret) {

/* we failed to get on the inode, get off the idr */

inotify_remove_from_idr(group, tmp_i_mark);

goto out_err;

}





/* return the watch descriptor for this new mark */

ret = tmp_i_mark->wd;



out_err:

/* match the ref from fsnotify_init_mark() */

fsnotify_put_mark(&tmp_i_mark->fsn_mark);



return ret;

}

我们知道加锁区域内的代码一定要尽力的简洁,耗时操作是严格禁止的,只有这样才能提升内核的运转效率,尤其是自旋锁会将所有CPU全部阻塞住,更是要加小心,因此systemd在接收到inotify的通知时必须要以最快速度,运行完成全部代码。

VG一激活文件系统就挂载的问题到底能不能改

对于VG到底是在启动时被激活的,还是由用户手工激活的,systemd完全可以判断出来,但是这个判断是有代价的,由于inotify的api当中并没有提供一个标志是否为启动时调用的onboot标志,因此systemd只能自行判断系统是否处在启动态,而加这个判断无论是想取得系统的运行时间,还是要取得用户登陆状态,对于systemd这种优化到极致的软件来说,可能都是不能接受的。

为了验证上述是观点,我们可以在实测中尝试在通知处理函数中加入一些耗时操作来观察对于系统的影响,由于inotify可以改变原系统调用(syscall)行为的, 受试验实验环境所限,我手头机器的硬件水平不足以支持我搭建一个虚拟机平台任意测试的要求,因此模拟当中我们使用了另一个监测机制kprobe来进行,和inotify相比,kprobe属于旁路监测重量级较轻,我自行注册了一个监测文件变化的探针,然后在处理事件时加入延时操作,观察对于系统IO的影响。

结果测试发现在我所在的4核/8G的平台上,每秒钟文件打开、关闭文件操作的次数,由每秒867次的锋值下降到了72次,90%以上的下降。因此这个在systemd项目下开了近三年的ISSUE似乎没有好的解法,无论是sysvinit的0号init进程机制,还是在inotify的处理函数中加入系统运行状态的判断,都不是好的办法。如果各位读者有什么好的建议或者思路可以留言提供一下,咱们共同探讨!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux Systemd被爆远程漏洞 CVE-2017-9445 影响多种Linux系统

    在 Systemd (linux 操作系统流行的 init 系统和服务管理器) 中发现了一个关键漏洞, 这使得远程攻击者有可能触发缓冲区溢出, 从而通过 dns...

    Debian中国
  • 学会爱上 systemd

    systemd(是的,全小写,即使在句子开头也是小写),是初始化程序(init)和 SystemV 初始化脚本的现代替代者。此外,它还有更多功能。

    用户8639654
  • 【高危】漏洞预警 | Linux Systemd存在高危远程溢出漏洞(CVE-2017-9445)

    systemd是Linux下的init软件,主要用于减少系统引导时间和计算开销。 systemd 的systemd-resolved服务用于处理DNS请求并为本...

    安恒信息
  • Linux系统服务启动和停止的操作命令

    systemd 是在Linux上运行服务的新方式。 systemd 有一个被替代的 sysvinit 。 systemd 为Linux带来更快的启动时间,现在是...

    用户9042463
  • Linux Boot,Kernel 和 Service 介绍

    Linux 启动过程是初始化系统的过程。 它包括从第一次打开计算机电源到用户界面完全可操作时发生的所有事情。

    Jerry Wang
  • 在Proxmox VE上安装与配置Rocky Linux 8.3 RC1

    问题关键字:在Proxmox VE上安装与配置Rocky Linux 8.3 RC1

    欧巴云
  • K8S现存问题(一)

    即使是镜像采用 CentOS 母版,很多镜像制作者会给操作系统减肥。经过优化后,已经不是官方版本,在使用过程中你会遇到各种麻烦。例如调试的时候需要 curl,w...

    陈不成i
  • 在Proxmox VE上安装与配置Rocky Linux 8.3 RC1

    文章声明:此文基于木子实操撰写 生产环境:Rocky Linux release 8.3 问题关键字:在Proxmox VE上安装与配置Rocky Li...

    木子-Lee
  • 【内核模块auth_rpcgss】netns引用计数泄露导致容器弹性网卡残留

    ? 我们不久前定位了一个Linux内核bug,这个bug会影响所有在特权容器中启用了use-gss-proxy的Linux环境,表现为容器的网络命名空间(ne...

    腾讯技术工程官方号
  • 【深究】对常驻进程问题再剖析

    工作中的难点问题正是我们知识技术栈全谱查漏补缺的最佳机遇,有问题不可怕,all in、死磕就完事了,哈哈哈~

    架构精进之路
  • kubernetes 从懵圈到熟练:读懂此文,集群节点不下线!

    排查完全陌生的问题、不熟悉的系统组件,对许多工程师来说是无与伦比的工作乐趣,当然也是一大挑战。今天,阿里巴巴售后技术专家声东跟大家分享一例 Kubernetes...

    kubernetes中文社区
  • k8s|一次故障排查

    阿里云有自己的Kubernetes容器集群产品。随着Kubernetes集群出货量的剧增,线上用户零星的发现,集群会非常低概率地出现节点NotReady情况。据...

    heidsoft
  • CentOS7/RHEL7 systemd详解

    目录 1. 为什么是systemd (1) 关于Linux服务管理 (2) SysV init的优缺点 (3) UpStart的改进 (4) systemd的诞...

    力哥聊运维与云计算
  • linux基础命令介绍十三:启动流程

    固件(firmware)是指设备最底层的,让设备得以运行的程序代码。简单理解就是:固定在硬件上的软件。计算机中的许多设备都拥有固件(如硬盘、鼠标、光驱、U盘等)...

    用户5030870
  • [七夕特供]:流年不利啊,才处理了线程死亡案件,这次更猛,连着死了几个进程

    我们有一批专门定制的,供市场人员进行产品展示和推销的 pc(样子很花哨,有3个屏幕;配置也不错, i7 * 8 核,32g 内存);这是一台 pc,装的 win...

    曹工
  • Linux中父进程为何要苦苦地知道子进程的死亡原因?

    一个普遍的常识是,在Linux里面总是“白发人送黑发人”,子进程死亡,父进程透过wait()等待子进程死亡,并清理子进程僵尸,当然父进程也可以因此而获得子进程的...

    Linux阅码场
  • 使用 chkconfig 和 systemctl 命令启用或禁用 Linux 服务的办法

    对于 Linux 管理员来说这是一个重要(美妙)的话题,所以每个人都必须知道,并练习怎样才能更高效的使用它们。

    砸漏
  • 使用 chkconfig 和 systemctl 命令启用或禁用 Linux 服务的方法

    对于 Linux 管理员来说这是一个重要(美妙)的话题,所以每个人都必须知道,并练习怎样才能更高效的使用它们。

    砸漏
  • 破解Linux系统root用户密码

    linux系统的启动过程 在介绍破解Linux系统root密码之前先了解一下linux系统的启动过程: 1 开机自检(POST),初始化部分硬件 2 搜...

    小小科

扫码关注云+社区

领取腾讯云代金券