目录
目录README.md

基于CRIU技术实现dbus服务的冻结与恢复

提交的文件位于提交目录下,以下内容为项目功能说明书的拷贝

项目功能说明书

团队信息

学校:湖南大学

参赛队伍:风在山路吹过往的画面全都是我不队

队伍成员:刘昊鹏、林星彤、李平凡

指导教师:谢鲲

完成日期:2024年8月

项目结构

.
├── README.md
├── 提交        # !项目功能说明书等
│   └── ...
├── bin
│   ├── ... # dbus编译出的可执行文件,头文件与动态库
│   └── ...
├── docs
│   ├── cmd.md      # 记录一下运行指令
│   ├── record.md   # 个人日志
│   └── ref.md      # 参考资料
├── programs        # ! 一些dbus程序
│   ├── Makefile
│   ├── async_sample.c
│   ├── com.flork.dbus.conf   # 守护进程D-Bus daemon的配置文件
│   ├── mclient.c      # mcli
│   ├── method_call.c
│   ├── method_service.c
│   ├── mserver.c      # msrv
│   ├── signal_recv.c
│   ├── signal_send.c
│   └── sync_sample.c
├── records        # 会议记录
│   ├── 0331.md
│   ├── 0411.md
│   └── ...
├── scripts        # 运行脚本
│   ├── dump.sh
│   └── restore.sh
└── src            # criu 和 dbus 源码
    ├── criu
    └── dbus

技术理论

比赛过程中遇到的问题都尽量团队自己解决,因此每一步都走的十分艰难,期间搞坏了多次服务器。经过不断的摸索,探索出一套可能可行的方案,但目前未能完整实现。

在添加dump和restore dbus连接的socket选项后:

// dump dbus
int dump_dbus_socket(int fd, SkOptsEntry *skopts)
{
    int ret = 0;
    
    ret |= dump_opt(fd, SOL_SOCKET, SO_REUSEADDR, &skopts->reuseaddr);
    ret |= dump_opt(fd, SOL_SOCKET, SO_REUSEPORT, &skopts->reuseport);
    ret |= dump_opt(fd, SOL_SOCKET, SO_KEEPALIVE, &skopts->keepalive);

    return ret;
}

int restore_dbus_socket(int sk, SkOptsEntry *skopts)
{
    int ret = 0;

    if (skopts->has_reuseaddr)
        ret |= restore_opt(sk, SOL_SOCKET, SO_REUSEADDR, &skopts->reuseaddr);
    if (skopts->has_reuseport)
        ret |= restore_opt(sk, SOL_SOCKET, SO_REUSEPORT, &skopts->reuseport);
    if (skopts->has_keepalive)
        ret |= restore_opt(sk, SOL_SOCKET, SO_KEEPALIVE, &skopts->keepalive);

    return ret;
}

restore后的dbus服务仍然不具备有效的连接:

image-20240807171107269

此时dbus维护的connection已遭到破坏,为了完成dump dubs服务,仅仅dump socket连接远不能达到目标,所以也需要将dbus连接的一些必要信息进行存储。

DBus支持

DBusConnection

每个dbus服务一定要有一个DBusConnection连接,这个结构体中储存了该连接几乎所有的信息。经过对dbus源码和文档的阅读,未能发现 dbus 中心服务中储存了DBusConnection的信息。所以,应该是要从被dump的dbus进程本身获取DBusConnection。 dbus当前没有获取DBusConnection的接口,所以我们自己实现,方便起见采用了保存DBusConnection指针的地址的方式储存和获取DBusConnection。

// demo
void save_connection_to_file(DBusConnection *connection, pid_t pid) {
    char filename[256];
    snprintf(filename, sizeof(filename), "/tmp/%d_connection.txt", pid);

    FILE *file = fopen(filename, "w");
    if (file) {
        fprintf(file, "%p\n", connection);
        fclose(file);
    }
}

DBusConnection* dbus_bus_get_connection_from_file(pid_t pid) {
    char filename[256];
    snprintf(filename, sizeof(filename), "/tmp/%d_connection.txt", pid);

    FILE *file = fopen(filename, "r");
    if (file) {
        void *ptr = NULL;
        fscanf(file, "%p", &ptr);
        fclose(file);
        return (DBusConnection*)ptr;
    }
    return NULL;
}

criu支持

criu支持向被dump的进程本身注入代码,用于收集进程的一些信息。因此我们首先考虑在注入的代码里收集DBusConnection的信息。 criu/criu/pie/parasite.c

经过实验,注入的代码里仅能调用一些指定的系统调用,无法加载一些自定义的动态库。

image-20240725131635730

代码原创说明

programs中的代码为用于测试的dbus的程序,参考的是网络上的代码片段

src中为criu和dbus的源码,我们的软件在此基础上开发。

scripts中原创的脚本代码.

软件介绍

在 openkylin中编译dbus,需要将Kysec设置为Softmode,否则会编译失败

image-20240807224531977

编译dbus时指定前缀

./configure --prefix=$WORKSPACE/criu-dbus/bin/

使用自编译的dbus服务

export PKG_CONFIG_PATH=/home/lhp/criu-dbus/bin/lib/pkgconfig
export LD_LIBRARY_PATH=/home/lhp/criu-dbus/bin/lib:$LD_LIBRARY_PATH

软件测试

虽然没能完成题目的要求,但是基于这套框架进行了一个伪实现。

正常的method服务

image-20240725134309242

image-20240725134321029

dump

image-20240725134351802

dump脚本输出一个inode,用于restore,dump后method失效:

image-20240725134413565

restore

restore之后,右边重新可以调用dbus的method

image-20240725134528100

方法是在criu的restore代码中插入一段重新建立相同连接的代码:

image-20240725134215786

总结

从一开始信心满满选择这个具有挑战性的题目,到不断受挫,我们遇到了很多困难,最终才对这个题目和dbus、criu具有全面些的了解,但很遗憾最终无法完成全部的开发。为了弥补遗憾,最终针对一个特定的dbus程序的restore做了伪实现。

我们最终摸索出一套可能可行的方案,在注入的代码里收集dbus connection结构体的信息,但是注入的代码对可以调用的函数库有非常严格的限制,最终止步于此。

另外,可以从dbus的角度在daemon里保存没一个连接,则可以在connection的进程外获取其他进程的连接信息,这种方案会为每一个连接分配一块内存,造成资源浪费。

总之,在比赛的过程中,有很多的思考,学习了很多知识,比赛结束后希望能从别的队伍那里学习到更好的解决方案。

参考

Interface DBus
https://dbus.freedesktop.org/doc/api/html/dbus-connection_8h_source.html
https://dbus.freedesktop.org/doc/api/html/modules.html
Introduction to CRIU and Live Migration
DBus API的使用
https://man.archlinux.org/man/criu.8.en
https://criu.org/Main_Page
https://blog.csdn.net/f110300641/article/details/106823611
Criu入门
https://blog.csdn.net/weixin_38669561/article/details/98183545
https://blog.csdn.net/chao2016/article/details/82587503
https://blog.csdn.net/o0xgw0o/article/details/124497819
https://cloud.tencent.com/developer/article/1159496
https://qytz-notes.readthedocs.io/tech/dbus/dbus.html
https://blog.csdn.net/ZengYinning/article/details/128188252

Paper

Love R. Get on the D-BUS[J]. Linux Journal, 2005, 2005(130): 3.
Tošić A. Run-time application migration using checkpoint/restore in userspace[J]. arXiv preprint arXiv:2307.12113, 2023.

原readme

作品完成和提交方式:选择本赛题的参赛队伍需要首先复刻(Fork)本项目,然后在复刻的项目中添加参赛队员、合作完成作品开发即可,无需提交PR到赛题项目。如果作品为文档形式,也请将作品文档提交到项目代码库中。在作品完成过程中,围绕作品的相关讨论等可以以疑修(Issue)形式发布和讨论,也可使用里程碑对整个任务进行规划管理。

1. 赛题说明

基于CRIU技术(Checkpoint/Restore In Userspace),实现dbus服务的冻结、恢复功能。在冻结时,保存该dbus服务上下文位置关系,进程以CRIU固有形式或其他状态冻结于系统磁盘上;在恢复后,dbus服务可基于原流程继续执行。

2. 赛题要求

1)dbus服务类型为SYSTEM BUS,可自己新建或利用系统原有的dbus服务组件,dbus服务需具备method及signal。 2)在dbus服务被冻结、恢复后,其他dbus服务可接收到dbus-daemon发出的对应操作信号,表明该服务进入冻结状态,退出冻结状态。 3)在dbus服务被冻结时,对该服务的访问需有报错提示,当恢复后,访问可正常进行。 4)dbus服务在恢复后与冻结前的程序上下文位置保持一致。 5)支持在系统重启后的进程正常恢复功能。 6)额外功能:支持同架构下跨设备的冻结与恢复。 7)支持基于wayland图形化应用的冻结与恢复。 8)进程恢复耗时需远优于直接启动该进程耗时。

3. 赛题导师

zhangtingting@kylinos.cn

4. 参考资料

1)https://man.archlinux.org/man/criu.8.en 2)https://criu.org/Main_Page

关于
100.3 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

©Copyright 2023 CCF 开源发展委员会
Powered by Trustie& IntelliDE 京ICP备13000930号