lxd與lxc掛卷過程對比分析

      網友投稿 825 2025-04-04

      LXD掛卷流程

      在lxd環境中執行如下命令時:

      lxc?config?device?add?test-centos?sdb?unix-block?path=/dev/sdb

      lxd的整個處理流程如下:

      步驟1:lxc client先向lxd daemon發送一個put請求,更新當前容器的config,調用鏈如下:

      lxc/config.go:?(c?*configCmd)?run(config?*lxd.Config,?args?[]string) lxc/config.go:?(c?*configCmd)?deviceAdd(config?*lxd.Config,?which?string,?args?[]string) ????client.go:?(c?*Client)?ContainerDeviceAdd(container,?devname,?devtype?string,?props?[]string) ????????client.go:?(c?*Client)?put(base?string,?args?interface{},?rtype?api.ResponseType)

      步驟2:lxd daemon在收到請求后,通過與原有config進行對比,得到addDevices,然后開始掛卷,調用鏈如下:

      lxd/container_put.go:?containerPut(d?*Daemon,?r?*http.Request) lxd/container_lxc.go:?(c?*containerLXC)?Update(args?containerArgs,?userRequested?bool) ????lxd/container_lxc.go:?(c?*containerLXC)?insertUnixDevice(name?string,?m?types.Device)

      步驟3:在insertUnixDevice函數中,lxd會先調用createUnixDevice函數在”/var/lib/lxd/devices//”目錄下創建一個與待掛載設備具有相同major和minor號的塊設備。

      步驟4:創建好塊設備后,insertUnixDevice函數會調用insertMount函數,完成掛載操作。具體步驟如下:

      4.1、通過調用mount這個linux系統調用,并將flag設置為MS_BIND,lxd會在/var/lib/lxd/shmounts/目錄下創建一個臨時文件,該文件為在步驟3中創建的塊設備的復制文件,具體關系參見linux對mount調用中關于MS_BIND這個flag的描述:http://man7.org/linux/man-pages/man2/mount.2.html。

      需要注意的是,/var/lib/lxd/shmounts/這個目錄具有一定的特殊性,它被掛載到了容器內部的/dev/.lxd-mounts目錄下(lxd/container_lxc.go: (c *containerLXC) initLXC()函數中的err = lxcSetConfigItem(cc, "lxc.mount.entry", fmt.Sprintf("%s dev/.lxd-mounts none bind,create=dir 0 0", shared.VarPath("shmounts", c.Name())))),因此lxd這里創建的臨時文件在容器中也能看到。

      4.2、調用forkmount函數,在容器內部進行mount。forkmount函數的實現在lxd/main_nsexec.go文件中,是通過C語言實現的。

      forkmount函數中,首先將user namespace與mount namespace切換到該容器的namespace中,然后根據目標路徑創建file或dir(根據4.1中創建的臨時文件的類型來定),最后通過mount調用以及MS_MOVE flag將步驟1中復制的塊設備文件move到目標路徑(當前從用戶參數中的path字段獲取,目前沒有弄清楚為何不從用戶參數的target字段獲取。)。

      至此,宿主機上的塊設備已經可以在容器中看到了。

      LXC掛卷流程

      不過上述操作是用戶執行掛載命令時lxd的內部流程,當容器重啟后,lxd會對這些掛載設備進行恢復,這里走的就是另一條流程了,具體流程如下:

      步驟1:lxc client向lxd daemon發送一個start請求,調用鏈如下:

      lxd/action.go:?(c?*actionCmd)?run(config?*lxd.Config,?args?[]string) client.go:?(c?*Client)?Action(name?string,?action?shared.ContainerAction,?timeout?int,?force?bool,?stateful?bool)

      步驟2:lxd daemon在收到請求后,根據數據庫中記錄生成配置文件,作為啟動容器的參數,調用鏈如下:

      lxd/container_state.go:?containerStatePut(d?*Daemon,?r?*http.Request) client.go:?(c?*Client)?Action(name?string,?action?shared.ContainerAction,?timeout?int,?force?bool,?stateful?bool) ????lxd/container_lxc.go:?(c?*containerLXC)?Start(stateful?bool) ????????lxd/container_lxc.go:?(c?*containerLXC)?startCommon() ????????????lxd/container_lxc.go:?(c?*containerLXC)?initLXC()

      在initLXC函數中,各個device會被轉化為配置文件中的lxc.mount.entry參數。

      lxd與lxc掛卷過程對比分析

      在最初執行的那個“lxc config device add”命令掛載的磁盤會轉化為如下的配置項:

      lxc.mount.entry?=?/var/lib/lxd/devices/test-centos/unix.dev-sdb?dev/sdb?none?bind,create=file

      其中,unix.dev-sdb就是在上面的步驟3中createUnixDevice函數中創建的塊設備,dev/sdb是用戶命令中的path字段,none和bind,create=file的含義后面會提到。

      步驟3:lxc在啟動容器時,對lxc.mount.entry參數的處理在文件conf.c中,調用鏈如下:

      conf.c:?lxc_setup(struct?lxc_handler?*handler) conf.c:?setup_mount_entries(const?struct?lxc_rootfs?*rootfs,?struct?lxc_list?*mount,?const?char?*lxc_name,?const?char?*lxc_path) ????conf.c:?make_anonymous_mount_file(struct?lxc_list?*mount) ????conf.c:?mount_file_entries(const?struct?lxc_rootfs?*rootfs,?FILE?*file,?const?char?*lxc_name,?const?char?*lxc_path) ????????lxcmntent.c:?getmntent_r?(FILE?*stream,?struct?mntent?*mp,?char?*buffer,?int?bufsiz) ????????conf.c:?mount_entry_on_relative_rootfs(struct?mntent?*mntent,?const?struct?lxc_rootfs?*rootfs,?const?char?*lxc_name,?const?char?*lxc_path) ????????????conf.c:?mount_entry_on_generic(struct?mntent?*mntent,?const?char*?path,?const?struct?lxc_rootfs?*rootfs,?const?char?*lxc_name,?const?char?*lxc_path) ????????????????conf.c:?mount_entry_create_dir_file(const?struct?mntent?*mntent,?const?char*?path,?const?struct?lxc_rootfs?*rootfs,?const?char?*lxc_name,?const?char?*lxc_path) ????????????????conf.c:?cull_mntent_opt(struct?mntent?*mntent) ????????????????conf.c:?parse_mntopts(const?char?*mntopts,?unsigned?long?*mntflags,?char?**mntdata) ????????????????conf.c:?mount_entry(const?char?*fsname,?const?char?*target,?const?char?*fstype,?unsigned?long?mountflags,?const?char?*data,?int?optional,?int?dev,?const?char?*rootfs) ????????????????????utils.c:?safe_mount(const?char?*src,?const?char?*dest,?const?char?*fstype,?unsigned?long?flags,?const?void?*data,?const?char?*rootfs) ????????????????????????utils.c:?open_without_symlink(const?char?*target,?const?char?*prefix_skip)

      其中需要注意的有:

      3.1、lxc_setup在調用setup_mount_entries提供的mount_list中存儲的就是lxc配置文件中所有lxc.mount.entry的值(在confile.c: config_mount(const char *key, const char *value, struct lxc_conf *lxc_conf){函數中構造)

      3.2、make_anonymous_mount_file函數將所有的mount_entries的內容寫到了一個內存文件中,供mount_file_entries函數使用。

      3.3、getmntent_r函數用于將mount_entries的內容解析為如下結構體:

      struct?mntent { ????char*?mnt_fsname; ????char*?mnt_dir; ????char*?mnt_type; ????char*?mnt_opts; ????int?mnt_freq; ????int?mnt_passno; };

      以步驟2中生成的配置項為例,該配置項會被解析為如下內容:

      struct?mntent?mp; mp.mnt_fsname=/var/lib/lxd/devices/test-centos/unix.dev-sdb; mp.?mnt_dir=?dev/sdb; mp.mnt_type=?none; mp.mnt_opts=?bind,create=file; mp.mnt_freq=0; mp.mnt_passno=0;

      3.4、mount_entry_on_relative_rootfs在調用mount_entry_on_generic時,會將rootfs與mnt_dir拼接起來,組成mnt_dir在宿主機上的絕對路徑,作為path參數傳給mount_entry_on_generic。

      3.5、mount_entry_create_dir_file在path對應的路徑上創建一個file或dir(根據mnt_opts中的create=file或create=dir來決定),這一步與上面的4.2步forkmount中的創建操作一致。

      3.6、cull_mntent_opt函數與parse_mntopts函數共同將mnt_opts轉化為mntflags。

      3.7、safe_mount函數中調用的open_without_symlink函數的作用僅僅是打開待掛載路徑,得到一個文件句柄(通過校驗保證待掛載路徑不是文件鏈接)。

      3.8、safe_mount函數最后調用mount調用,完成第一次掛載。需要注意的是,調用mount調用時,并沒有直接使用待掛載路徑作為dest參數,而是將/proc/self/fd/與3.7中得到的文件句柄拼接起來,作為mount調用的dest參數。這塊有點不明覺厲。

      3.9、mount_entry在調用safe_mount函數完成第一次掛載后,再次直接調用mount調用進行第二次掛載,兩次掛載的區別在于第一次去掉了MS_REMOUNT flag,第二次加上了MS_REMOUNT flag。這塊也有點不明覺厲。

      對比lxd的掛卷過程與lxc的掛卷過程,可以發現相比于lxd,lxc的掛卷過程少了一次namespace切換,也少了一次帶MS_MOVE flag的mount操作(lxd的掛卷過程是先將devices目錄下的unix.dev-sdb通過bind mount方式mount到shmounts目錄下,然后通過move mount方式mount到dev目錄下,lxc則直接將devices目錄下的unix.dev-sdb通過bind mount方式mount到dev目錄下)。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:為什么顯示的是兼容模式 ? ? ?昨天保存的文件沒有了
      下一篇:word如何在表格中添加斜線(word的表格怎么添加斜線)
      相關文章
      亚洲欧洲日韩综合| 亚洲午夜国产精品| 一本久久综合亚洲鲁鲁五月天| 亚洲国产精品一区二区三区在线观看| 亚洲精品免费视频| 亚洲va在线va天堂va四虎| 亚洲成av人影院| 午夜影视日本亚洲欧洲精品一区 | 亚洲熟女少妇一区二区| 国产aⅴ无码专区亚洲av麻豆| 久久精品夜色噜噜亚洲A∨| 亚洲一本大道无码av天堂| 伊人久久精品亚洲午夜| 伊人久久精品亚洲午夜| 久久精品国产亚洲沈樵| 国产精品亚洲а∨无码播放| 亚洲乱码中文字幕综合| 亚洲AV第一页国产精品| 亚洲一区免费观看| 亚洲国产综合在线| 中文字幕亚洲男人的天堂网络| 中文字幕亚洲综合久久综合| 亚洲一线产区二线产区区| 亚洲欧美日韩自偷自拍| 亚洲AV性色在线观看| 亚洲av手机在线观看| 亚洲男人第一无码aⅴ网站 | 国产精品亚洲精品日韩已方| 亚洲愉拍99热成人精品热久久| 久久精品国产亚洲麻豆| 久久亚洲精品成人av无码网站| 亚洲精品国产福利片| 亚洲最大的黄色网| 亚洲国产成人综合精品| 国产AV日韩A∨亚洲AV电影| 国产午夜亚洲不卡| 久久亚洲精品中文字幕无码| 亚洲黄色免费网址| 中文字幕在线日亚洲9| 男人的天堂亚洲一区二区三区 | 亚洲av日韩av激情亚洲|