关于umount2函数的MNT_DETACH参数的探究

本文深入探讨了Linux中umount操作中的MNT_DETACH参数,通过实例演示了该参数如何影响挂载点的卸载过程。详细解释了在挂载点处于忙碌状态时,如何使用带MNT_DETACH参数的umount2函数成功卸载文件系统,以及在实际应用中可能出现的困惑和误解。

在manpages中有这么一段介绍:

    

 MNT_DETACH (since Linux 2.4.11)
              Perform a lazy unmount: make the mount point unavailable for new
              accesses, and actually perform the unmount when the mount  point
              ceases to be busy.






        其大意为如果函数执行带有此参数,不会立即执行umount操作,而会等挂载点退出忙碌状态时才会去卸载它。
不过此函数执行会阻止对该挂载点执行新的访问。之前就在访问此挂载点操作也不会强制其退出,而是会等待其自然退出。



       现在我有这样一个文件夹test,在test下有一个test_file文件.然后我在/mnt下创建test_dir,紧接着我会把./test挂载到/mnt/test_dir下。此时我切到/mnt/test_dir下可以看到test_file文件。之后我用open函数打开/mnt/test_dir/test_file成功获取文件描述符,这样我就可以保证挂载点/mnt/test_dir处于忙碌状态。此时用umount   /mnt/test_dir命令会报出忙碌的错误,是无法完成卸载的。而调用带MNT_DETACH的umount2函数就会执行成功,但是根据它的描述,它不会立即执行umount操作。但是此时你去执行  umount /mnt/test_dir命令会提示此目录不是挂载点的错误。






     其描述说挂载点不允许新的访问,但是实际的效果是此时挂载点已经不存在了,因为之前挂载过来的文件系统已经
被卸载掉了,此时/mnt/test_dir已经算不上真正意义上的挂载点了,它只是一个单纯的文件目录。所谓的不允许访问挂载点根本就没有任何意义了,因为挂载点根本就不存在了。而且此时挂载点消失的无影无踪,用
 umount /mnt/test_dir命令测试会报出不是挂载点的错误,我真的怀疑此时已经彻底将文件系统卸载掉了,只是保留了对于源文件目录即./test目录的链接指向罢了。因为我实在是无法去区分了。我觉得在errno.h中access错误码和文件
不存在错误还是不同的,此函数执行后,我去访问/mnt/test_dir目录报出access错误码的话我还是比较能接受的,但是事实并非这样。这个我就有点纠结了,最起码的来说,这个参数功能描述是错误的,往深处来说这个函数的实现是
存在问题的,没有严格按照其功能实现。






         搞了半天的东西是这个结局我实在是有点不太能接受,这个参数功能描述也太具有迷惑性了啊。写出来和大家分享,希望大家以后不要犯同样地错误了。如果有对这方面熟悉或者是对这个系统调用函数的具体实现了解的朋友看到,请求对我进行指导,求指教!!!求进步!!!



#include <iostream> #include <vector> #include <string> #include <sys/mount.h> #include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <unistd.h> #include <cstring> #include <cerrno> #include <fcntl.h> #include <ftw.h> // 常量路径定义 - 都是目录 const std::string A_PATH = "/data/baseinfo"; const std::string B_PATH = "/data/app_override"; const std::string C_PATH = "/data/info_backup"; const std::string OVERLAY_DIR = "/data/overlayfs"; const std::string WORK_DIR = OVERLAY_DIR + "/work"; const std::string MERGED_DIR = OVERLAY_DIR + "/merged"; // 错误处理函数 void handleError(const std::string &operation) { std::cerr << "Error [" << errno << "]: " << operation << " failed - " << strerror(errno) << std::endl; exit(EXIT_FAILURE); } // 递归创建目录(类似 mkdir -p) void createDirRecursive(const std::string &path) { std::string cmd = "mkdir -p " + path; if (system(cmd.c_str()) != 0) { handleError("Creating directory: " + path); } std::cout << "Created directory: " << path << std::endl; } // 递归删除目录内容(保留目录本身) int removeContentCallback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { if (typeflag == FTW_DP) { // 目录,在最后访问时删除 if (rmdir(fpath) == -1 && errno != ENOTEMPTY) { perror(("rmdir " + std::string(fpath)).c_str()); return -1; } } else if (typeflag == FTW_F || typeflag == FTW_SL) { // 文件或符号链接 if (unlink(fpath) == -1) { perror(("unlink " + std::string(fpath)).c_str()); return -1; } } return 0; } void removeDirectoryContents(const std::string &path) { if (nftw(path.c_str(), removeContentCallback, 64, FTW_DEPTH | FTW_PHYS) == -1) { handleError("Removing contents of " + path); } std::cout << "Cleared directory: " << path << std::endl; } // 初始化文件系统 void initializeFilesystem() { // 创建必要的目录结构 createDirRecursive(A_PATH); createDirRecursive(B_PATH); createDirRecursive(C_PATH); createDirRecursive(OVERLAY_DIR); createDirRecursive(WORK_DIR); createDirRecursive(MERGED_DIR); // 确保工作目录为空 removeDirectoryContents(WORK_DIR); } // 挂载 OverlayFS(目录版本) void mountOverlay() { // 组装挂载选项 (lowerdir=C, upperdir=B) std::string options = "lowerdir=" + C_PATH + ",upperdir=" + B_PATH + ",workdir=" + WORK_DIR; // 执行挂载 if (mount("overlay", MERGED_DIR.c_str(), "overlay", 0, options.c_str()) == -1) { handleError("Mounting OverlayFS"); } std::cout << "OverlayFS mounted at " << MERGED_DIR << "\n"; } // 绑定挂载目录 void bindMount() { // 确保目标目录存在 createDirRecursive(A_PATH); // 执行绑定挂载 if (mount(MERGED_DIR.c_str(), A_PATH.c_str(), nullptr, MS_BIND, nullptr) == -1) { handleError("Bind mounting to " + A_PATH); } std::cout << "Bind mounted " << MERGED_DIR << " to " << A_PATH << "\n"; } // 恢复B目录到初始状态 void restoreB() { std::cout << "\n[Restoring B to initial state]\n"; // 卸载绑定挂载点 if (umount2(A_PATH.c_str(), MNT_DETACH) == -1) { handleError("Unmounting " + A_PATH); } // 卸载OverlayFS if (umount2(MERGED_DIR.c_str(), MNT_DETACH) == -1) { handleError("Unmounting " + MERGED_DIR); } // 清空B目录(删除所有内容) removeDirectoryContents(B_PATH); // 重新挂载 mountOverlay(); bindMount(); std::cout << "Restore complete! B now mirrors C\n"; } // 清理函数 void cleanup() { umount2(A_PATH.c_str(), MNT_DETACH); umount2(MERGED_DIR.c_str(), MNT_DETACH); std::cout << "Cleanup complete\n"; } int main() { // 初始化文件系统 std::cout << "Initializing filesystem...\n"; initializeFilesystem(); // 挂载 OverlayFS std::cout << "Mounting OverlayFS...\n"; mountOverlay(); // 执行绑定挂载 std::cout << "Binding overlay to A...\n"; bindMount(); // 使用示例 std::cout << "\n=== Usage Demo ===\n"; std::cout << "1. Creating test files in A (modifies B)\n"; system(("touch " + A_PATH + "/test1.txt").c_str()); system(("mkdir " + A_PATH + "/subdir").c_str()); system(("echo 'modified' > " + A_PATH + "/subdir/file.txt").c_str()); std::cout << " Created files in A/B\n"; std::cout << "2. Restoring B to initial state (matching C)\n"; restoreB(); std::cout << "3. Cleanup\n"; cleanup(); return 0; } 在这段代码中,如果 std::cout << "2. Restoring B to initial state (matching C)\n"; restoreB(); 不执行,直接执行 std::cout << "3. Cleanup\n"; cleanup(); 对于 system(("touch " + A_PATH + "/test1.txt").c_str()); 这样的修改,是否持久化对B_PATH生效?
11-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值