[Share Experiences] 探讨 玲珑linglong 与 distrobox 区别
Tofloor
poster avatar
ThinkYoung
deepin
2023-06-15 04:23
Author

突然看到玲珑linglong有了离线bundle , 直接运行 ,感觉好神奇 , 就想看看玲珑到底底层怎么实现的!

后来发现玲珑 与 distrobox ,看似没啥关系,其实虽不同,但还有那么点异曲同工之妙!

1.玲珑的外文介绍 是 linglong is the container application toolkit of deepin. Standard oci runtime

从源码看,小白楼主认为,玲珑自己完整实现了runcpp(我起的名字哈,c++版的runc),直接用runcpp调用了oci容器

2.distrobox的英文介绍 是 Distrobox is a wrapper for podman or docker (whatever you prefer) ,

从源码看,小白楼主认为Distrobox只是 podman or docker的上层封装调用,而 podman or docker原理就是它最后的底层用runc调用了oci容器

因为我代码水平有限,我也有很多弄懂的地方,求大神指导!

1.oci容器也是有镜像文件的,distrobox 是podman pull拉取的,生成容器文件,而玲珑的容器文件在哪呢?

2.离线bundle直接运行,很像appimage,而appimage用了linuxdeploy封装了启动器实现,玲珑离线包uab是怎么实现的呢?貌似这个包是UOS包吧?看看UOS打包工具???

Reply Favorite View the author
All Replies
wlly-lzh
deepin
2023-06-15 04:41
#1

我也好奇ll-box是怎么和要运行的软件一起放在一个可执行文件里面的。

confused

集成了运行环境的打包方式??

算了,不了解,坐等大佬解答。

Reply View the author
ThinkYoung
deepin
2023-06-15 05:28
#2
wlly-lzh

我也好奇ll-box是怎么和要运行的软件一起放在一个可执行文件里面的。

confused

集成了运行环境的打包方式??

算了,不了解,坐等大佬解答。

果然秘密在ll-builder里面 涉及uab构建

🔗 uab 构建

从面向玲珑构建的项目中创建出一个能够运行的uab文件需要以下几个配置文件:

  1. linglong.yaml:玲珑项目的配置文件,决定了该包如何被编译和安装;
  2. loader:一个可执行文件,作为uab的入口。
  3. extra-files.txt:一个文本文件,其中写明该应用依赖base和runtime中的哪些文件, 这些文件会被拷贝到uab包中,应用运行时放置在相应位置。如果应用并不依赖runtime 以及base中的其他文件就可以运行起来,则可以不提供该文件。

如果只是将项目导出成uab用来向玲珑商店提交应用,并不需要其能够运行,并不需要提供 loader和extra-files.txt

loader的编写参考:

#!/bin/env sh
APPID=org.deepin.demo
./ll-box $APPID $PWD /opt/apps/$APPID/files/bin/demo

传递给ll-box的最后一个参数为需要在容器中运行的二进制。

Reply View the author
hanzn-zzx
deepin
2023-06-15 06:04
#3

玲珑的容器貌似就在包含包里面?

Reply View the author
SamLukeYes
deepin
2023-06-15 06:26
#4

distrobox 是一个发行版一个容器,而一般的容器化包管理器是一个应用一个容器。docker 和 podman 本来也是应用级容器,发行版的 oci 容器较为常见的用途是作为构建其他容器的基础,只不过 distrobox 提供了一种便捷的直接使用发行版容器的方式,并且支持在其中运行桌面应用。

Reply View the author
ThinkYoung
deepin
2023-06-15 07:30
#5
SamLukeYes

distrobox 是一个发行版一个容器,而一般的容器化包管理器是一个应用一个容器。docker 和 podman 本来也是应用级容器,发行版的 oci 容器较为常见的用途是作为构建其他容器的基础,只不过 distrobox 提供了一种便捷的直接使用发行版容器的方式,并且支持在其中运行桌面应用。

感谢大佬的答疑解惑!

突然感觉一下子有趣了起来!

Reply View the author
Rubbish
deepin
2023-06-15 09:19
#6

你可以strace看一下,我这边大概猜测原理是这样的

  1. bundle本身是elf格式所以可以运行
  2. 运行之后先找到自己的路径,然后从里面读或者解包出loader的elf(这里很神奇,我猜是挂载但没看到mount)
  3. 然后就是像正常运行容器那样运行loader了。估计bundle的这个elf本身就是runcpp,不过不带daemon啥?或者容器是靠解包出来的ll-box运行,这个我不清楚
execve("./com.163.music_1.2.1.1_x86_64.uab", ["./com.163.music_1.2.1.1_x86_64.u"...], 0x7ffe66e768a0 /* 58 vars */) = 0
brk(NULL)                               = 0x1b58000
brk(0x1b59240)                          = 0x1b59240
arch_prctl(ARCH_SET_FS, 0x1b58900)      = 0
uname({sysname="Linux", nodename="zyc-Laptop", ...}) = 0
set_tid_address(0x1b58bd0)              = 1087093
set_robust_list(0x1b58be0, 24)          = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x4f0ff0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x4f1080, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
readlink("/proc/self/exe", "/tmp/MicrosoftEdgeDownloads/859e"..., 4096) = 97
brk(0x1b7a240)                          = 0x1b7a240
brk(0x1b7b000)                          = 0x1b7b000
futex(0x639ecc, FUTEX_WAKE_PRIVATE, 2147483647) = 0
futex(0x639ed8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
prctl(PR_SET_PDEATHSIG, SIGKILL)        = 0
lstat("/proc", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
lstat("/proc/self", {st_mode=S_IFLNK|0777, st_size=0, ...}) = 0
readlink("/proc/self", "1087093", 4095) = 7
lstat("/proc/1087093", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
lstat("/proc/1087093/exe", {st_mode=S_IFLNK|0777, st_size=0, ...}) = 0
readlink("/proc/1087093/exe", "/tmp/MicrosoftEdgeDownloads/859e"..., 4095) = 97
lstat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=106496, ...}) = 0
lstat("/tmp/MicrosoftEdgeDownloads", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
lstat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
lstat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
stat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
openat(AT_FDCWD, "/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0\360X@\0\0\0\0\0"..., 4096) = 4096
lseek(3, 0, SEEK_SET)                   = 0
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0\360X@\0\0\0\0\0"..., 4096) = 4096
close(3)                                = 0
stat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
gettimeofday({tv_sec=1686762217, tv_usec=997983}, NULL) = 0
getpid()                                = 1087093
mkdir("/tmp/uab-cOZdva", 0700)          = 0
getpid()                                = 1087093
semget(0x109675, 1, IPC_CREAT|0666)     = 9
semctl(9, 0, SETVAL, NULL)              = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x1b58bd0) = 1087094
semop(9, [{0, -1, SEM_UNDO}], 1)        = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1087094, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
stat("/tmp/uab-cOZdva/loader", {st_mode=S_IFREG|0755, st_size=300, ...}) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x1b58bd0) = 1087098
wait4(-1, NULL, 0, NULL)                = 1087094
wait4(-1, NULL, 0, NULL)                = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
wait4(-1, NULL, 0, NULL)                = 1087098
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1087098, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, NULL, 0, NULL)                = -1 ECHILD (没有子进程)
umount2("/tmp/uab-cOZdva", 0)           = -1 EPERM (不允许的操作)
rt_sigaction(SIGINT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=NULL, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7ffe1ac4c0dc) = 1088255
wait4(1088255, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 1088255
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1088255, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0)                           = ?
+++ exited with 0 +++

这个就可以看出是mount上去的,而且没解包直接mount,看起来这个bundle里还自带了某种FUSE文件系统的库!

: mount | grep uab
/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab on /tmp/uab-rshHrb type fuse.com.163.music_1.2.1.1_x86_64.uab (ro,nosuid,nodev,relatime,user_id=1000,group_id=1000)
zyc@zyc-Laptop:/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c$ tree /tmp/uab-rshHrb -L 2
/tmp/uab-rshHrb
├── busybox
├── entries
│   ├── applications
│   ├── autostart
│   ├── icons
│   ├── plugins
│   └── services
├── files
│   ├── bin
│   ├── doc
│   ├── include
│   ├── lib
│   ├── netease-cloud-music
│   ├── netease-cloud-music.bash
│   ├── plugins
│   └── share
├── info.json
├── ll-box
└── loader

13 directories, 6 files
Reply View the author
ThinkYoung
deepin
2023-06-15 16:43
#7
Rubbish

你可以strace看一下,我这边大概猜测原理是这样的

  1. bundle本身是elf格式所以可以运行
  2. 运行之后先找到自己的路径,然后从里面读或者解包出loader的elf(这里很神奇,我猜是挂载但没看到mount)
  3. 然后就是像正常运行容器那样运行loader了。估计bundle的这个elf本身就是runcpp,不过不带daemon啥?或者容器是靠解包出来的ll-box运行,这个我不清楚
execve("./com.163.music_1.2.1.1_x86_64.uab", ["./com.163.music_1.2.1.1_x86_64.u"...], 0x7ffe66e768a0 /* 58 vars */) = 0
brk(NULL)                               = 0x1b58000
brk(0x1b59240)                          = 0x1b59240
arch_prctl(ARCH_SET_FS, 0x1b58900)      = 0
uname({sysname="Linux", nodename="zyc-Laptop", ...}) = 0
set_tid_address(0x1b58bd0)              = 1087093
set_robust_list(0x1b58be0, 24)          = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x4f0ff0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x4f1080, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
readlink("/proc/self/exe", "/tmp/MicrosoftEdgeDownloads/859e"..., 4096) = 97
brk(0x1b7a240)                          = 0x1b7a240
brk(0x1b7b000)                          = 0x1b7b000
futex(0x639ecc, FUTEX_WAKE_PRIVATE, 2147483647) = 0
futex(0x639ed8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
prctl(PR_SET_PDEATHSIG, SIGKILL)        = 0
lstat("/proc", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
lstat("/proc/self", {st_mode=S_IFLNK|0777, st_size=0, ...}) = 0
readlink("/proc/self", "1087093", 4095) = 7
lstat("/proc/1087093", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
lstat("/proc/1087093/exe", {st_mode=S_IFLNK|0777, st_size=0, ...}) = 0
readlink("/proc/1087093/exe", "/tmp/MicrosoftEdgeDownloads/859e"..., 4095) = 97
lstat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=106496, ...}) = 0
lstat("/tmp/MicrosoftEdgeDownloads", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
lstat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
lstat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
stat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
openat(AT_FDCWD, "/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0\360X@\0\0\0\0\0"..., 4096) = 4096
lseek(3, 0, SEEK_SET)                   = 0
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0\360X@\0\0\0\0\0"..., 4096) = 4096
close(3)                                = 0
stat("/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab", {st_mode=S_IFREG|0755, st_size=135758176, ...}) = 0
gettimeofday({tv_sec=1686762217, tv_usec=997983}, NULL) = 0
getpid()                                = 1087093
mkdir("/tmp/uab-cOZdva", 0700)          = 0
getpid()                                = 1087093
semget(0x109675, 1, IPC_CREAT|0666)     = 9
semctl(9, 0, SETVAL, NULL)              = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x1b58bd0) = 1087094
semop(9, [{0, -1, SEM_UNDO}], 1)        = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1087094, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
stat("/tmp/uab-cOZdva/loader", {st_mode=S_IFREG|0755, st_size=300, ...}) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x1b58bd0) = 1087098
wait4(-1, NULL, 0, NULL)                = 1087094
wait4(-1, NULL, 0, NULL)                = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
wait4(-1, NULL, 0, NULL)                = 1087098
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1087098, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, NULL, 0, NULL)                = -1 ECHILD (没有子进程)
umount2("/tmp/uab-cOZdva", 0)           = -1 EPERM (不允许的操作)
rt_sigaction(SIGINT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=NULL, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7ffe1ac4c0dc) = 1088255
wait4(1088255, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 1088255
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x4f05d0}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1088255, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0)                           = ?
+++ exited with 0 +++

这个就可以看出是mount上去的,而且没解包直接mount,看起来这个bundle里还自带了某种FUSE文件系统的库!

: mount | grep uab
/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c/com.163.music_1.2.1.1_x86_64.uab on /tmp/uab-rshHrb type fuse.com.163.music_1.2.1.1_x86_64.uab (ro,nosuid,nodev,relatime,user_id=1000,group_id=1000)
zyc@zyc-Laptop:/tmp/MicrosoftEdgeDownloads/859e6a23-5083-4324-81b5-183beacf3a0c$ tree /tmp/uab-rshHrb -L 2
/tmp/uab-rshHrb
├── busybox
├── entries
│   ├── applications
│   ├── autostart
│   ├── icons
│   ├── plugins
│   └── services
├── files
│   ├── bin
│   ├── doc
│   ├── include
│   ├── lib
│   ├── netease-cloud-music
│   ├── netease-cloud-music.bash
│   ├── plugins
│   └── share
├── info.json
├── ll-box
└── loader

13 directories, 6 files

like

太厉害了,分析的太好了,玲珑的确使用了fuse

可加载可执行的镜像,看起来bundle包本身是elf文件,所以才有loader加载器?

thinkyoung@NUC:~/下载$ readelf -h ./com.xunlei.download_1.0.0.2_x86_64.uab
ELF 头:
Magic:   7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
类别:                              ELF64
数据:                              2 补码,小端序 (little endian)
Version:                           1 (current)
OS/ABI:                            UNIX - GNU
ABI 版本:                          0
类型:                              EXEC (可执行文件)
系统架构:                          Advanced Micro Devices X86-64
版本:                              0x1
入口点地址:               0x4058f0
程序头起点:          64 (bytes into file)
Start of section headers:          2324960 (bytes into file)
标志:             0x0
Size of this header:               64 (bytes)
Size of program headers:           56 (bytes)
Number of program headers:         8
Size of section headers:           64 (bytes)
Number of section headers:         30
Section header string table index: 29

而loader本身就是个sh文件

loader的编写参考:

#!/bin/env sh
APPID=org.deepin.demo
./ll-box $APPID $PWD /opt/apps/$APPID/files/bin/demo

同时把ll-box封装进镜像,直接用ll-box运行容器

ps:

我刚看一个sh+tar/img合包成可执行bin的例子,这个原理很像win的捆木马原理,这个实现比较简单,也很好玩

之前我就想简单实现下苹果dmg那种包,可加载可执行磁盘镜像,双击直接挂载,直接就能使用。

Reply View the author