应用挂载名称空间

linux基本功系列之mount命令实战 阅读详情

讲解高级 Linux 挂载特性的实际应用

如果能够让用户自由地创建他们的文件系统,而不受系统管理员指定的结构的限制,那么会怎么样?用户可以导出自己的文件系统树的一部分,并将其他用户导出的文件系统树导入自己的树中。本文向 Linux® 系统管理员介绍用挂载传播实现这种设置的详细步骤。

有时候,Linux® 中的文件系统是一个相当简单的树。进程可以对本身执行 chroot(),使其文件系统树的根成为系统文件系统根的一个子目录。在树中的任何节点上,可以用来自新设备的树覆盖文件系统。

在 2000 年,Al Viro 为 Linux 引入了绑定挂载和文件系统名称空间:

  • 绑定挂载(bind mount)允许从任何其他位置访问任何文件或目录。
  • 文件系统名称空间(filesystem namespace)是与不同进程相关联的完全独立的文件系统树。

 

clone(2)
Linux 手册页指出:clone(2) 系统调用创建一个新的子进程,它让子进程可以与执行调用的进程共享它的执行上下文的某些部分,比如内存空间、文件描述符表和信号处理函数表。细节参见 参考资料。

在执行 clone(2) 时,进程请求它当前的文件系统树的拷贝(更多信息见 参考资料);在此之后,新进程就拥有与原进程的文件系统树相同的拷贝。在建立拷贝之后,在这两个树中的任何挂载操作都不会影响另一个拷贝。

尽管每个进程使用单独的文件系统名称空间在理论上非常有意义,但是在实践中,完全隔离它们会造成较大的限制性。进程克隆了系统的文件系统名称空间之后,已经运行的系统守护进程无法为这个用户自动挂载 CD-ROM,因为在原文件系统名称空间中执行的挂载无法影响用户的拷贝。

2006 年引入的挂载传播(mount propagation)解决了这个问题,挂载传播定义了挂载对象之间的关系。系统用这些关系决定任何挂载对象中的挂载事件如何传播到其他挂载对象:

  • 如果两个挂载对象具有共享关系,那么一个挂载对象中的挂载事件会传播到另一个挂载对象,反之亦然。
  • 如果两个挂载对象形成从属(slave)关系,那么一个挂载对象中的挂载事件会传播到另一个挂载对象,但是反过来不行;在这种关系中,从属对象是事件的接收者。

传播事件的挂载对象称为共享挂载(shared mount);接收挂载事件的挂载对象称为从属挂载(slave mount)。既不传播也不接收挂载事件的挂载对象称为私有挂载(private mount)。另一种特殊的挂载对象称为不可绑定的挂载(unbindable mount),它们与私有挂载相似,但是不允许执行绑定挂载。不可绑定的挂载对于快速增长挂载对象尤其有意义(后面会进一步讨论这个概念)。

在默认情况下,所有挂载都是私有的。可以用以下命令将挂载对象显式地标为共享挂载:

mount --make-shared <mount-object>

例如,如果 / 上的挂载必须是共享的,那么执行以下命令:

mount --make-shared /

从共享挂载克隆的挂载对象也是共享的挂载;它们相互传播挂载事件。

通过执行以下命令,可以显式地将一个共享挂载转换为从属挂载:

mount --make-slave <shared-mount-object>

从从属挂载克隆的挂载对象也是从属的挂载,它也从属于原来的从属挂载的主挂载对象。

通过执行以下命令,可以将挂载对象标为私有的:

mount --make-private <mount-object>

通过执行以下命令,可以将挂载对象标为不可绑定的:

mount --make-unbindable <mount-object>

最后,这些设置都可以递归地应用,这意味着它们将应用于目标挂载之下的所有挂载。

例如:

mount --make-rshared /

将 / 之下的所有挂载转换为共享挂载。

每个登录专用的名称空间

清单 1 给出一个 PAM(pluggable authentication module,可插入身份验证模块)的部分代码,它将除根用户之外的每个用户放在一个私有名称空间中。如果 /tmp/priv/USER 目录存在,那么这个目录将被绑定挂载在用户的私有名称空间中的 /tmp 上。


清单 1. 实现每个登录专用的名称空间的 PAM 代码片段
                
#define DIRNAMSZ 200
int handle_login(const char *user)
{
        int ret = 0;
        struct stat statbuf;
        char dirnam[DIRNAMSZ];

        if (strcmp(user, "root") == 0)
                return PAM_SUCCESS;

        ret = unshare(CLONE_NEWNS);
        if (ret) {
                mysyslog(LOG_ERR, "failed to unshare mounts for %s/n", user);
                return PAM_SESSION_ERR;
        }

        snprintf(dirnam, DIRNAMSZ, "/tmp/priv/%s", user);
        ret = stat(dirnam, &statbuf);
        if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
                ret = mount(dirnam, "/tmp", "none", MS_BIND, NULL);
                if (ret) {
                        mysyslog(LOG_ERR, "failed to mount tmp for %s/n", user);
                        return PAM_SESSION_ERR;
                }
        } else
                mysyslog(LOG_INFO, "No private /tmp for user %s/n", user);
        return PAM_SUCCESS;
}

int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
                const char **argv)
{
        const char *PAM_user = NULL;
        char *fnam;
        int ret;

        ret = pam_get_user(pamh, &PAM_user, NULL);
        if (ret != PAM_SUCCESS) {
                mysyslog(LOG_ERR, "PAM-NS: couldn't get user/n");
                return PAM_SESSION_ERR;
        }

        return handle_login(PAM_user);
}

要想使用这个 PAM 模块,可以从后面的 下载 一节下载完整的 pam_ns.c 文件和对应的 makefile。编译它并将生成的 pam_ns.so 文件复制到 /lib/security/ 中。然后在 /etc/pam.d/login 和 /etc/pam.d/sshd 中添加以下条目:

session   required   pam_ns.so

最后,为用户 USER 创建一个私有的 tmp 目录。

mkdir /tmp/priv
chmod 000 /tmp/priv
mkdir /tmp/priv/USER
chown -R USER /tmp/priv/USER

现在,以根用户身份在一个终端上登录,以 USER 用户身份在另一个终端上登录。作为 USER 执行以下命令:

touch /tmp/ab
ls /tmp

注意,USER 的 /tmp 只包含新创建的文件。

接下来,在根用户的终端上列出 /tmp 的内容清单;注意,这里有其他文件,但是没有 /tmp/ab。这些 /tmp 目录实际上是单独的目录。要想在根用户的终端上访问 USER 的 /tmp 目录,应该执行:

ls /tmp/priv/USER

这时会看到文件 ab。接下来,在根用户的终端上,在 /mnt 上挂载某些东西:

mount --bind /dev /mnt

 

mount(8) 和 unshare(2)
Linux 手册页指出:mount(8) 命令将某一设备上的文件系统附着到一个大文件树上。unshare(2) 系统调用允许进行调用的进程用原名称空间的拷贝替代所选资源的名称空间。参见 参考资料。

注意,在根用户的终端上,/dev 的内容出现在 /mnt 下面,但是在 USER 的终端上没有出现。这两个终端的挂载树是完全独立的。可以使用 mount(8) 命令获得与挂载传播相关的指令。在默认情况下,所有挂载都是私有的。所以,在作为 USER 登录之前,可以执行以下命令:

mount --make-rshared /

在此之后,挂载事件就会在后面的非共享名称空间之间传播。但是,在 USER 登录之后,将 /tmp/priv/USER 挂载到 /tmp 的事件不应该传播到父名称空间。为了解决这个问题,pam_ns.so 可以将它的文件系统标为从属对象,见清单 2。


清单 2. 将用户的名称空间标为从属对象的 PAM 模块
                
#define DIRNAMSZ 200
#ifndef MS_SLAVE
#define MS_SLAVE 1<<19
#endif
#ifndef MS_REC
#define MS_REC 0x4000
#endif
int handle_login(const char *user)
{
        int ret = 0;
        struct stat statbuf;
        char dirnam[DIRNAMSZ];

        if (strcmp(user, "root") == 0)
                return PAM_SUCCESS;

        ret = unshare(CLONE_NEWNS);
        if (ret) {
                mysyslog(LOG_ERR, "failed to unshare mounts for %s/n", user);
                return PAM_SESSION_ERR;
        }

        ret = mount("", "/", "dontcare", MS_REC|MS_SLAVE, ""));
        if (ret) {
                mysyslog(LOG_ERR, "failed to mark / rslave for %s/n", user);
                return PAM_SESSION_ERR;
        }

        snprintf(dirnam, DIRNAMSZ, "/tmp/priv/%s", user);
        ret = stat(dirnam, &statbuf);
        if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
                ret = mount(dirnam, "/tmp", "none", MS_BIND, NULL);
                if (ret) {
                        mysyslog(LOG_ERR, "failed to mount tmp for %s/n", user);
                        return PAM_SESSION_ERR;
                }
        } else
                mysyslog(LOG_INFO, "No private /tmp for user %s/n", user);
        return PAM_SUCCESS;
}


每个用户专用的根

LSPP
共同准则 Labeled Security Protection Profile 为 IT 产品指定了一组安全功能和保障需求。它支持两类访问控制机制,这些机制允许用户指定如何共享他们控制的资源(通过安全标志或 “标签” 来实施),还可以对用户之间的共享实施限制。LSPP 提供的保护级别适合于 “非敌对性的管理良好的用户社区,可以防止意外操作破坏系统安全”。参见 参考资料。

在 每个登录专用的名称空间 一节中,我们看到了一个通过挂载名称空间为用户提供私有名称空间的简单示例。通过使用挂载传播,这个解决方案非常适合为用户提供私有的 /tmp 目录;如果再添加一个由 pam_ns.c 解析的配置文件,就可以根据用户对其他目录进行重定向。LSPP 就是以这种方式提供多个实例化的主目录,它会根据登录过程的许可在 /home/USER 上挂载不同的目录。

但是,同一用户的每次登录都会获得一个私有的从属文件系统。所以这个用户在一个登录会话中执行的挂载不影响另一个登录会话。

非管理员用户可以以几种方式挂载文件系统。例如,用户可以使用 FUSE 挂载 sshfs(安全 shell)文件系统或 loopback 文件系统(见 参考资料)。如果不考虑本文后面讨论的用户之间的挂载共享问题,就不容易理解这些挂载如何只出现在用户的一个登录终端中,而不出现在其他终端中。但是,通过使用 “每个登录专用的名称空间” 一节中的方法,确实可以实现这种效果。

清单 3 给出 pam_chroot.so PAM 模块的部分代码。pam_ns.so 在登录时克隆挂载名称空间,但是 pam_chroot.so 模块需要在 /share/USER/root 下设置一个用户专用的文件系统,并使用 chroot() 将用户限制在他的私有文件系统中。


清单 3. 使用 chroot() 的 PAM 模块
                
int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
                const char **argv)
{
        const char *PAM_user = NULL;
        char fnam[400];
        int ret, err, count, i;
        struct mount_entries *entries;
        struct stat statbuf;

        ret = pam_get_user(pamh, &PAM_user, NULL);
        if (ret != PAM_SUCCESS) {
                mysyslog(LOG_ERR, "PAM-MOUNT: couldn't get user/n");
                return PAM_SESSION_ERR;
        }

        /* check whether /share/$pam_user/root exists.  If so, chroot to it */
        sprintf(fnam, "/share/%s/root", PAM_user);
        ret = stat(fnam, &statbuf);
        if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
                ret = chroot(fnam);
                if (ret) {
                        mysyslog(LOG_ERR, "PAM-MOUNT: unable to chroot to %s/n", fnam);
                        return PAM_SESSION_ERR;
                }
        }

        return PAM_SUCCESS;
}

在这个示例中,在系统启动时提前执行所有挂载。例如,在启动之后,执行:

mkdir -p /share/USER/root
mount --make-rshared /
mount --rbind / /share/USER/root
mount --make-rslave /share/USER/root
mount --bind /share/USER/root/tmp/priv/USER /share/USER/root/tmp

这里没有使用私有名称空间。相反,USER 的每次登录都通过 chroot() 限制在同一个目录(/share/USER/root)中。因此,在 USER 的所有登录中,会看到他的任何登录所执行的任何挂载。而 OTHERUSER 通过 chroot() 限制在 /share/OTHERUSER/root 中,因此不会看到 USER 的挂载活动。

这种方法的一个缺点是,可以回避普通的 chroot() 机制(虽然这需要某些特权)。例如,在具有某些特权(包括 CAP_SYS_CHROOT)的情况下,程序可以回避 chroot()(参见 参考资料),这会导致程序能够访问真正的文件系统根。根据使用用户专用文件系统树的实际意图的不同,这可能会成为一个问题。

 

pivot_root(2) 和 chroot(2)
Linux 手册页指出:pivot_root(2) 命令将当前进程的根文件系统转移到目录 put_old,并使 new_root 成为新的当前进程根文件系统。chroot(2) 命令将根目录改为路径中指定的目录;当前进程的所有子进程都继承这个根目录。只有具有特权的进程可以调用这个命令。参见 参考资料。

解决这个问题的方法是,在私有名称空间中使用 pivot_root(2)(而不是 chroot(2))将登录的根目录改为 /share/USER/root。chroot() 仅仅将进程的文件系统根指向一个指定的新目录,而 pivot_root() 会使指定的 new_root 目录(它必须是一个挂载)脱离它的挂载点,然后将它附着到进程的根目录。因为在挂载树中新的根目录没有父目录,所以无法像使用 chroot() 时那样欺骗系统,从而访问真正的文件系统根。我们将使用 pivot_root() 方式。

用户专用根目录的系统设置

您已经看到了用户专用私有挂载树的实现细节,包括在登录时必须执行的操作。在本节中,将看到在创建用户帐户和系统引导时使用的完整脚本。

清单 4 给出在创建用户时运行的脚本。


清单 4. 用于创建用户的脚本
                
create_user_tree {
        user = $1
        mkdir /user/$user
        mount --rbind / /user/$user
        mount --make-rslave /user/$user
        mount --make-rshared /user/$user

	#create a private mount. This is to facilitate pivot_root
	#to temporarily place the old root here before detaching the
	#entire old root tree. NOTE: pivot_root will not allow old root
	#to be placed under a shared mount.
	pushd /user/$user/
	mkdir -p __my_private_mnt__
	mount --bind __my_private_mnt__ __my_private_mnt__
	mount --make-private __my_private_mnt__
	popd
}

这个脚本假设已经运行了 init_per_user_namespace 脚本(后面会讨论这个脚本)。它在 /user/ 下面为用户帐户创建一个目录。然后将根目录递归地绑定挂载在 /user/$user/ 下面。这个递归复制的根文件系统树成为这个用户专用的文件系统,可以持久地保留这个用户执行的挂载活动(对多次登录有效,但是在重新启动之后就会失效)。

这个复制的树是根树的从属挂载,所以根树中的挂载活动会传播到这个拷贝,但是不会反向传播。这个树被标为共享的,所以后续的拷贝(也就是通过名称空间克隆建立的拷贝)是相互共享的挂载;任何拷贝中的挂载活动都会传播到所有其他拷贝。

最后,创建一个私有的挂载 __my_private_mnt__。这是为了帮助 pivot_root()(见清单 6)在删除树之前临时准备根挂载。目前不需要太关注它。了解了 pivot_root() 的语义之后,这个步骤的意义就会明确了。目前只需记住,如果准备的挂载是共享的,那么 pivot 挂载就不会成功。

清单 5 给出在系统引导时运行的脚本。


清单 5. 在引导时执行系统初始化的脚本
                
init_per_user_namespace {

	#start with a clean state by marking
	#all mounts as private.
	mount --make-rprivate /

        #create a unbindable mount called 'user'
        #and have all the users to bind the entire
        #system tree '/' under them.

        mkdir /user
        mount --bind /user /user
        mount --make-rshared /
        mount --make-unbindable /user
        foreach user in existing_user {
                create_user_tree $user
        }
}

它创建 /user 目录,用户专用的挂载树将放在这个目录中。然后,它将 /user 绑定挂载到本身。--rshared 等挂载传播指令只能针对挂载点指定。这个步骤确保在 /user 上存在挂载点。

接下来,将文件系统根标为 --rshared,这样的话以后的拷贝(包括通过绑定挂载或克隆挂载名称空间创建的拷贝)都与这个挂载相互共享,任何树中的挂载操作都会传播到所有共享挂载。

接下来,将 /user 上的挂载标为不可绑定的。对于每个用户,都会递归地复制整个挂载树,所以在 /user/$user_1 下面创建第一个用户的拷贝之后,在 /user/$user_2 下面创建的拷贝会包含 /user/$user_1 的递归拷贝(/user/$user_2/user/$user_1)。可以想像到,这会快速地消耗大量内存。将 /user 标为不可绑定的,就可以防止在递归地绑定挂载 / 时复制 /user。

最后,为每个用户执行一次 清单 4 中的脚本。如果 /user/$user 目录不存在,就创建这个目录,并按照前面的描述设置适当的挂载传播。

清单 6 给出在用户登录时执行的 PAM 模块的片段。


清单 6. 用户登录所用的 PAM 代码片段
                
#ifndef MNT_DETACH
#define MNT_DETACH		0x0000002
#endif
#ifndef MS_REC
#define MS_REC			0x4000
#endif
#ifndef MS_PRIVATE
#define MS_PRIVATE              1<<18   /* Private */
#endif

#define DIRNAMSZ 200
int handle_login(const char *user)
{
	int ret = 0;
	struct stat statbuf;
	char dirnam[DIRNAMSZ], oldroot[DIRNAMSZ];

	snprintf(dirnam, DIRNAMSZ, "/user/%s", user);
	ret = stat(dirnam, &statbuf);
	if (ret != 0 || !S_ISDIR(statbuf.st_mode))
		return PAM_SUCCESS;

	ret = unshare(CLONE_NEWNS);
	if (ret) {
		mysyslog(LOG_ERR, "failed to unshare mounts for %s, error %d/n",
			user, errno);
		return PAM_SESSION_ERR;
	}

	ret = chdir(dirnam);
	if (ret) {
		mysyslog(LOG_ERR, "failed to unshare mounts for %s, error %d/n",
			user, errno);
		return PAM_SESSION_ERR;
	}

	snprintf(oldroot, DIRNAMSZ, "%s/__my_private_mnt__", dirnam);
	ret = pivot_root(dirnam, oldroot);
	if (ret) {
		mysyslog(LOG_ERR, "failed to pivot_root for %s, error %d/n",
			user, errno);
		mysyslog(LOG_ERR, "pivot_root was (%s,%s)/n", dirnam, oldroot);
		return PAM_SESSION_ERR;
	}
	
	ret = mount("", "/__my_private_mnt__", "dontcare", MS_REC|MS_PRIVATE, "");
	if (ret) {
		mysyslog(LOG_ERR, "failed to mark /tmp private for %s, error %d/n",
			user, errno);
		return PAM_SESSION_ERR;
	}

	ret = umount2("/__my_private_mnt__", MNT_DETACH);
	if (ret) {
		mysyslog(LOG_ERR, "failed to umount old_root %s, error %d/n",
			user, ret);
		return PAM_SESSION_ERR;
	}

	return PAM_SUCCESS;
}

这个模块首先检查正在登录的用户的 /user/USER 树是否存在。如果这个树不存在,那么这个模块仅仅允许这个用户登录,而不执行任何其他操作。

如果 /user/USER 树存在,那么第一步是为这个登录进程下的任务克隆一个私有的名称空间。因此,这些进程会有自己的系统初始挂载树拷贝。但是,这些拷贝互不相连;复制的树中的每个挂载节点共享初始节点中对应的挂载节点。

接下来,登录进程使用 pivot_root() 将它的文件系统根改为 /user/$user。原来的根被挂载在新的 __my_private_mnt__ 下面。

下一步是将 __my_private_mnt__ 标为私有的,使后面的卸载操作不会传播到根挂载树的其他拷贝,包括原来的树。

最后,从 __my_private_mnt__ 卸载原来的根。

在用户创建脚本(见 清单 4 )中,将 __my_private_mnt__ 目录设置为私有的挂载,并指出这是为了辅助 pivot_root() 的操作。这么做实际上是由于 pivot_root() 有一个文档中没有记载的限制,这个限制与旧根和新根的挂载传播状态有关。要想让 pivot_root() 成功执行,以下挂载不能是共享对象:

  1. 旧根的目标位置
  2. 新根的当前父位置(在调用 pivot_root() 的时候)
  3. 新根的目标父位置

在 清单 4 中接近末尾的地方将 __my_private_mnt__ 标为私有的,就可以满足第一个条件。第二个条件已经满足了,因为新根的当前父位置是 /user,/user 上的挂载是一个不可绑定的挂载。第三个条件也已经满足了,因为新根的目标父位置就是当前根的父位置。这个挂载是一个不可见的 rootfs 挂载,它已经是私有的。

在本节中,讨论了如何实现每个用户专用的挂载树,让挂载事件在一个用户的所有登录会话之间共享,但是对其他用户隐藏。在下一节中,讨论如何允许用户相互共享挂载树。


可以选择性共享每个用户专用的挂载树

我们已经讨论了每个用户专用的挂载树;现在讨论如何允许用户选择一部分挂载树在用户之间共享。下面是一个用于系统引导的脚本。


清单 7. 用于系统引导的脚本
                
init_per_user_namespace {
	mkdir -p /user/slave_tree
	mkdir -p /user/share_tree

	#start with a clean state. Set all mounts to private. 
	mount --make-rprivate /

	mount --bind /user /user
	mount --bind /user/share_tree /user/share_tree
	mount --bind /user/slave_tree /user/slave_tree

	mount --make-rshared  /

	mount --make-unbindable /user

	for user in `cat /etc/user_list`; do
		sh /bin/create_user_tree $user
	done
}

首先创建一个 /user 挂载,它包含每个用户的根目录。然后,在 /user 下面创建另一个称为 /user/share_tree 的目录,其中包含每个用户可以与其他用户共享的挂载。还创建一个 /user/slave_tree 目录,其中的挂载由每个用户共享,但是不接受来自其他用户的任何修改。当然,为了包含非绑定挂载,我们将 /user 下面的挂载标为不可绑定的。最后,调用 create_user_tree 为每个用户创建挂载树。

清单 8 给出了创建挂载树并允许其他用户共享挂载涉及的步骤。


清单 8. 用于创建用户的脚本
                
create_user_tree {

        user = $1
	mkdir -p /user/$user

	#copy over the entire mount tree under /user/$user
	mount --rbind / /user/$user
	make --make-rslave /user/$user
	make --make-rshared /user/$user

	cd /user/$user/home/$user

	#export my shared exports
	mkdir -p my_shared_exports
	chown $user my_shared_exports
	mount --bind my_shared_exports my_shared_exports
	mount --make-private my_shared_exports
	mount --make-shared my_shared_exports
	mkdir -p /user/share_tree/$user
	mount --bind my_shared_exports /user/share_tree/$user

	#export my slave exports
	mkdir -p my_slave_exports
	chown $user my_slave_exports
	mount --bind my_slave_exports my_slave_exports
	mount --make-private my_slave_exports
	mount --make-shared my_slave_exports
	mkdir -p /user/slave_tree/$user
	mount --bind my_slave_exports /user/slave_tree/$user

	cd /user/$user

	#import everybody's shared exports
	mkdir -p others_shared_exports
	mount --rbind /user/share_tree others_shared_exports

	#import everybody's slave exports
	mkdir -p others_slave_exports
	mount --rbind /user/slave_tree others_slave_exports
	mount --make-rslave others_slave_exports

	#setup a private mount in the user's tree, This is to facilitate
	# pivot_mount executed later, during new user-logins.
	mkdir -p __my_private_mnt__
	mount --bind __my_private_mnt__ __my_private_mnt__
	mount --make-private __my_private_mnt__
}

首先,在 /user/$user 下复制整个挂载树。在用户的树中,创建一个共享的挂载 my_shared_exports,在 /user/share_tree/$user 下复制它,从而导出给所有用户。同样,在用户的树中创建 my_slave_exports,在 /user/slave_tree/$user 下复制它,从而导出给所有用户。这里的关键思想是,用户可以选择在 my_shared_tree 下挂载的内容,所有其他用户会自动地共享这些挂载。

接下来,复制 /user/share_tree 下的挂载树并将它挂载在正在登录的用户的 others_shared_exports 下,这样就导入了所有其他用户的共享挂载。同样,复制 /user/slave_tree 下的挂载树并将它挂载在 others_slave_exports 下,这样就导入了所有其他用户的从属挂载。当然,因为这些挂载由导出者以从属挂载的形式导出,所以我们将它们转换为从属挂载。

完成了实现共享所需的设置步骤之后,用户登录算法就与 清单 6 中一样了。在每次登录时,用户会获得一个完全相同的挂载树;同时,用户会在 /others_shared_export 和 /others_slave_exports 下分别看到其他所有用户导出的所有共享挂载和从属挂载。

如果用户希望向其他用户导出某些内容,那么只需将这些内容挂载在 my_shared_exports 下,所有用户就能够自动地看到它们。  

下载

描述名字大小下载方法
本文的挂载传播代码示例dw.mountscode.tgz3KBHTTP


参考资料

学习


获得产品和技术

  • 索取 SEK for Linux,共有两张 DVD,包含最新的 IBM Linux 试用软件,涉及 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。

  • 使用 IBM 试用软件 构建您的下一个 Linux 开发项目,这些软件可以从 developerWorks 直接下载。


讨论



作者简介

Serge Hallyn 是 IBM Linux Technology Center 安全团队的一名专职程序员。他从 College of William and Mary 获得了计算机科学博士学位。他实现了几个安全模型 (dte、bsdjail),并对其他的安全模型 (seclvl) 和 SELinux 贡献了自己的力量。他实现了 DTE 及相应的配置文件和分析工具,并在 Type Enforcement(SELinux 中的主要技术)方面具有丰富的经验。

避坑指南:STM32H7不同DMA控制器(MDMA/DMA2D/BDMA)到底该用哪个? 本文深入解析STM32H7系列芯片的DMA控制器选型策略,涵盖MDMA、DMA2D、BDMA等不同控制器的架构原理与性能对比。通过实际项目案例,提供高速TCM数据搬运、GUI加速、低功耗传输等场景的选型决策树,并分享性能优化与调试技巧,帮助工程师避开常见陷阱,提升系统效率。 阅读详情

相关推荐

论文精读——A Comprehensive Survey of Continual Learning: Theory, Method and Application

为了应对现实世界的动态性,智能系统需要在其整个生命周期中逐步获取、更新、积累并利用知识。这种能力被称为Continue Learning(持续学习)。从广义上讲,持续学习明显受到catastrophic forgetting(灾难性遗忘)的限制,即学习新任务通常会导致旧任务的性能急剧下降。Continue Learning的核心目的有二,其一为(稳定性与可塑性间的权衡),其二为(足够的任务内/任务间泛化能力)。

xsddys的博客 1241

每天一个Linux命令:mount

mount命令完全使用手册   转载请在文首保留原文出处:EMC中文支持论坛https://community.emc.com/go/chinese 介绍   工欲善其事,必先利其器。mount是UNIX/Linux系统管理员不可不会的命令。在Linux,UNIX和类似操作系统上,不同分区和可移动设备上的文件系统(例如CD, DVD, 或USB闪存驱动)可通过目录树的

我不会编程,但不是完全不会,我会一点点 5492

保姆级教程:在Ubuntu 22.04上用Conda搞定nuPlan数据集环境(Python 3.9 + PyTorch)

本文提供了一份详细的Ubuntu 22.04环境下使用Conda配置nuPlan自动驾驶数据集的保姆级教程,涵盖Python 3.9安装、Conda环境管理、PyTorch配置及数据集可视化验证等关键步骤,帮助开发者高效搭建开发环境。

weixin_29061997的博客 256

linux磁盘管理说明(8)文件系统的挂载(上)

 文件系统的挂载点是目录,这个目录就是挂载的文件系统的入口,文件系统只有被挂载上了之后才能访问,而移除外部存储设备时,请记得一定要卸载该设备。文件系统的挂载与卸载最常用的命令就是mount/umount,接下来我们介绍这两个命令。 mount 标准格式: mount -t 文件系统类型 设备文件名 目录  目录如果不是空目录,那么以前的内容在设备挂载期间不可访问 列出当前挂载的文件系统:  列出当前挂载的所有文件系统  指定列出文件系统的类型  修改文件系统的label  -l 参数在结果中显示label

fuluoyide312的博客 3624

制造业与工业知识图谱应用

在全球数字化转型背景下,制造业面临创新周期缩短、质量管控精细化等挑战。知识图谱技术通过结构化表示工业知识,为智能制造提供关键技术支撑。工业知识图谱具有专业性强、多源异构数据融合、实时性要求高等特点,其构建流程涵盖本体设计、知识抽取与整合等环节。研究显示,知识图谱与大模型结合可提升工业场景的智能化水平,在设备故障诊断、供应链优化等领域具有显著应用价值。该技术有望成为制造业数字化转型的核心驱动力。

分享一些感兴趣的技术方面 1444

opencv去除印章痕迹

今天看到一个很有意思的程序,就是去除票据上的红色印章,觉得很有意思,就把它抄过来记录一下,同时自己也开了一下脑洞。 原来的程序来自:https://www.cnblogs.com/skyfsm/p/7638301.html 可以看到图像效果很不错,下面是源码实现: #include &lt;iostream&gt; #include &lt;opencv2/imgproc/imgpro...

zeng_haoyu的博客 7961

磁盘挂在命令 mount

f, --fake 空运行;-l, --show-labels 也显示文件系统标签(列出当前已挂载的设备,文件系统名称和挂载点)-r, --read-only 以只读方式挂载文件系统(同 -o ro)-B, --bind 挂载其他位置的子树(同 -o bind)-O, --test-opts <列表> 限制文件系统集合(和 -a 选项一起使用)-w, --rw, --read-write 以读写方式挂载文件系统(默认)

Jennifering的博客 3103

Gradient Accumulation 梯度累加/梯度累积 (Pytorch)

我们在训练神经网络的时候,batch_size的大小会对最终的模型效果产生很大的影响。一定条件下,batch_size设置的越大,模型就会越稳定。batch_size的值通常设置在 8-32 之间,但是当我们做一些计算量需求大的任务(例如语义分割、GAN等)或者输入图片尺寸太大的时候,我们的batch size往往只能设置为2或者4,否则就会出现 “CUDA OUT OF MEMORY” 的不可抗力报错。 那么如何在有限的计算资源的条件下,训练时采用更大的batch size...

hxxjxw的博客 5824

mount你不知道的那些功能-挂载点共享

mount是 Linux 最常用的功能,用来将存储设备挂载到目录的挂载点上。注意:Linux 允许将文件系统挂载到已附加了文件系统的目录。这种操作通常很危险。

DBA大董的博客 2592

逆强化学习:从专家示范中学习策略

逆强化学习(Inverse Reinforcement Learning,简称IRL)是强化学习(Reinforcement Learning,简称RL)的一个子领域,它的目标是从专家的行为中学习策略。在传统的强化学习中,智能体通过与环境交互,根据奖励信号来学习一个策略。而在逆强化学习中,我们没有直接的奖励信号,而是通过观察专家的行为来推断出奖励函数,从而学习到一个与专家类似的策略。逆强化学习作为强化学习的一个重要子领域,在许多实际应用场景中都取得了显著的成功。

AI天才研究院 1453

RK3588芯片:ALSA音频通路下HDMI与耳机自动切换的技术探索

摘要:本文详细探讨了基于RK3588芯片的HDMI与耳机自动切换方案。文章首先剖析了ALSA音频架构的核心组件与工作原理,接着介绍了RK3588芯片的硬件特性及音频处理优势。重点阐述了自动切换的实现原理,包括硬件检测机制、ALSA驱动处理逻辑和软件控制流程,并提供了详细的配置步骤。同时针对常见问题如切换异常、音频质量下降等给出了解决方案。该方案通过合理配置设备树和ALSA文件,实现了音频输出的智能切换,提升了用户体验,展现了RK3588在音频处理领域的强大性能。未来随着AI音频技术的发展,该方案在智能家居、

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 267

安装 local-path-provisioner 基于 HostPath 动态制备PV

安装 local-path-provisioner 基于 HostPath 动态制备 PV

misakivv的博客 1470

【参数配置】OpenClaw 的破解之道(Doom Loop)

本文深入解析了OpenClaw中的Doom Loop(死循环)问题及其应对策略。主要内容包括: Agent Loop的核心概念:基于ReAct范式,OpenClaw通过"思考-行动-观察"循环执行任务,但可能陷入无进展的死循环。 Doom Loop现象:Agent持续执行但任务进度为零,常见于四种工具调用场景:相同调用重复执行、调用不存在工具、轮询工具卡死、操作交替振荡。 OpenClaw的防御机制: 第一道防线:重试端断路器,通过上下文压缩熔断(最多3次)和总重试次数熔断(32-160次)防止无限重试。

海兰 4042

NFS动态提供Kubernetes后端存储卷(NFS Client Provisioner)

nfs-client-provisioner 是一个 Kubernetes 的简易 NFS 的外部 provisioner,本文将介绍使用 nfs-client-provisioner 这个应用,利用 NFS Server 给 Kubernetes 作为持久存储的后端,并且动态提供 PV,nfs-client-provisioner 本身不提供 NFS,需要现有的 NFS 服务器提供存储,并且 NFS 服务器与 Kubernetes 的 Slave 节点都能网络连通。检查 pv 和 pvc。

运维工程师知识分享经验和最佳实践 1010

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

名称空间隔离化实践(初学用)

每个进程在某个名称空间内运行时,会看到自己的资源视图(如网络接口、文件系统等),而其他名称空间中的进程看到的是不同的资源视图。Linux内核团队发现,容器技术需要将一个进程组的资源(如进程ID、文件系统、网络接口等)隔离起来,使得这些进程组能够像在独立的虚拟机中一样运行,但同时又不需要为每个容器创建一个完整的虚拟机。每个名称空间内的进程只能访问该名称空间内的资源。:你依然看到的是容器的文件系统,但 Docker 会进一步封装一些管理功能,如容器内的文件系统层次、容器内的 PID 命名空间等。

Lyinj的博客 673

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

创建网络名称空间后的Linux幕后工作解析

Linux网络名称空间(Network Namespace)是一种强大的虚拟化技术🌐,允许用户隔离网络设备、IP地址、路由表等网络资源。这项技术在容器化和虚拟化领域发挥着关键作用,是构建现代云基础设施的基石之一⛅。当你创建一个新的网络名称空间时,Linux内核在幕后执行了一系列复杂且精巧的操作,本文将全面解析这些操作,揭示创建网络名称

展望、进击 570

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

k8s vpc 自动挂载到edu 名称空间

这里pv 和pvc,pod 在同一命名空间下,不能跨空间,nfs-cline和sc 在默认空间,这个可以不用管 [root@k8s-master1 ~]# cat wenruo_crm_v4.nfs.yaml apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: wenruo-crm name: wenruo-crm namespace: edu spec: ..

qq_36270681的博客 366

linux中隔离空间,linuxea:白话容器之名称空间与资源隔离(2)

namespace容器技术出现较早,最早FreeBSD的操作系统虚拟化,其目的就是为了让进程不受其他干扰。就算这个经常出现了某些故障,bug,以及异常行为也不会影响到容器边界之外的其他进程,破坏的边界也是此jail而已。这种隔离可使应用安全运行。这种技术被用到linux后,叫VServer,VServer也能实现一定的jail的效果,vserver实现的功能chroot,chroot将正在运行的进...

weixin_30563489的博客 555

Django 路由组织、名称空间与虚拟环境

自定义转换器需要提供regex和to_url()。前者定义 URL 中允许出现的文本,将匹配结果转为视图使用的值,to_url()则在反向解析时把 Python 值转为 URL 字符串。return f"int04d# order/views.py from django . http import HttpResponse def report(request , report_year) : return HttpResponse(f"报告年份: {

2501_91671229的博客 412

k8s的名称空间、标签、deployment、控制器、弹性伸缩

一、k8s中的名称空间 k8s中名称空间是用来隔离集群资源,而k8s中的资源也分为名称空间级资源以及集群级资源。 kubectl是k8s客户端,它跟k8s没有任何关系。 kubectl get [资源名称] 获取集群资源的命令 #获取名称空间 [root@k8s-m-01 ~]# kubectl get namespace NAME STATUS AGE default Active 5d16h kube-node-lease Active 5d

givenchy_yzl的博客 1284

存储卷基础,初始化容器,名称空间,Kuboard及静态Pod技术实战

摘要:本文介绍了Kubernetes中Pod容器数据持久化的两种方案:emptyDir和DownwardAPI。emptyDir用于Pod内多容器间临时数据共享,数据随Pod删除而丢失;DownwardAPI则将Pod元数据注入容器,支持环境变量和Volume挂载两种方式。文章详细演示了emptyDir的配置使用、多容器共享验证,以及DownwardAPI通过环境变量获取Pod信息的实现方法,包括metadata字段和资源限制的引用方式。这些方案为容器化应用提供了灵活的数据管理和元数据访问能力。

日常分享linux运维知识 2485

fdisk 分区/格式化/挂载

查看块设备命令 lsblk用于列出所有可用块设备的信息,而且还能显示他们之间的依赖关系,但是它不会列出RAM盘的信息。 [root@localhost bsjk]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 20G 0 disk ├─sda1 8:1 0 ...

峡谷胖猫的博客 1314

Moblin 内核 -- 应用程序沙箱

注意:本技术对于未来有着积极意义,但暂时还不是Mobilin发行版或镜像的一部分。此项目的主要目标是:1. 确保一个受损的应用程序不会给平台的其余部分造成损害。这意味着攻击者可能无法使用一个有漏洞的应用程序作为攻击其余设备的跳板。2. 对运行在平台上的其余应用程序隐藏与该应用程序相关的信息/数据。3. 限定只能访问该应用程序需要使用其特定功能的系统部分。沙箱应该提供文件系统

MeeGo社区博客 1108

nsenter 和 名称空间

然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在 IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。

lingshengxiyou的博客 400
上一篇: 通过 Java 编程处理 XML 服务定义
下一篇: 九行代码让windows崩溃
WAST
博客等级 码龄25年 85粉丝 160原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值