2024年最全Linux 进程管理之命名空间_linux设置一个新的命名空间(1),阿里P8架构师

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

chroot 修改了进程的 root 目录的核心操作就是修改了 当前进程(struct task_struct) 的 current->fs->root = 当前文件名filename的struct path。
这样当前进程就认为 filename 是根目录,因为 fs->root 存的是 filename 目录的 path 结构。

chroot仅仅是在访问文件系统目录的时候,限定了用户的根目录。

二、uts_namespace

2.1 init_nsproxy

假如不指定ns,那么默认所有进程在创建的时候,都会指定一个默认的ns:init_nsproxy。
比如 init_pid_ns :
进程号命名空间用来隔离进程号,对应的结构体是pid_namespace。每个进程号命名空间独立分配进程号。进程号命名空间按层次组织成一棵树,初始进程号命名空间是树的根,对应全局变量init_pid_ns,所有进程默认属于初始进程号命名空间。

extern struct nsproxy init_nsproxy;

struct nsproxy init_nsproxy = {
	.count			= ATOMIC\_INIT(1),
	.uts_ns			= &init_uts_ns,
#if defined(CONFIG\_POSIX\_MQUEUE) || defined(CONFIG\_SYSVIPC)
	.ipc_ns			= &init_ipc_ns,
#endif
	.mnt_ns			= NULL,
	.pid_ns_for_children	= &init_pid_ns,
#ifdef CONFIG\_NET
	.net_ns			= &init_net,
#endif
#ifdef CONFIG\_CGROUPS
	.cgroup_ns		= &init_cgroup_ns,
#endif
};

init_nsproxy定义了初始的全局命名空间,其中维护了指向各个子系统初始化的命名空间对象的指针。

2.2 init_uts_ns

struct new\_utsname {
	char sysname[__NEW_UTS_LEN + 1];
	char nodename[__NEW_UTS_LEN + 1];
	char release[__NEW_UTS_LEN + 1];
	char version[__NEW_UTS_LEN + 1];
	char machine[__NEW_UTS_LEN + 1];
	char domainname[__NEW_UTS_LEN + 1];
};

这些字符串存储了系统名Linux,发行版,内核版本,机器等等,可以通过uname查看,也可以在/proc/sys/kernel/下查看:
在这里插入图片描述

struct uts\_namespace {
	struct kref kref;
	struct new\_utsname name;
	struct user\_namespace \*user_ns;
	struct ucounts \*ucounts;
	struct ns\_common ns;
};
extern struct uts\_namespace init_uts_ns;

struct uts\_namespace init_uts_ns = {
	.kref = {
		.refcount	= ATOMIC\_INIT(2),
	},
	.name = {
		.sysname	= UTS_SYSNAME,
		.nodename	= UTS_NODENAME,
		.release	= UTS_RELEASE,
		.version	= UTS_VERSION,
		.machine	= UTS_MACHINE,
		.domainname	= UTS_DOMAINNAME,
	},
	.user_ns = &init_user_ns,
	.ns.inum = PROC_UTS_INIT_INO,
#ifdef CONFIG\_UTS\_NS
	.ns.ops = &utsns_operations,
#endif
};
EXPORT\_SYMBOL\_GPL(init_uts_ns);

init_uts_ns是uts_ns的初始化配置,相关的预处理器常数在内核中各处定义,通过编译内核顶层Makefile动态生成。
系统名是固定的:

#define UTS\_SYSNAME "Linux"

三、创建namespace

3.1 fork

以fork为例:

SYSCALL\_DEFINE0(fork)
	-->\_do\_fork()
		-->copy\_process()
			-->copy\_namespaces()
				-->create\_new\_namespaces()

3.2 copy_namespaces

/\*
 \* called from clone. This now handles copy for nsproxy and all
 \* namespaces therein.
 \*/
int copy\_namespaces(unsigned long flags, struct task\_struct \*tsk)
{
	struct nsproxy \*old_ns = tsk->nsproxy;
	struct user\_namespace \*user_ns = task\_cred\_xxx(tsk, user_ns);
	struct nsproxy \*new_ns;

	if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
			      CLONE_NEWPID | CLONE_NEWNET |
			      CLONE_NEWCGROUP)))) {
		get\_nsproxy(old_ns);
		return 0;
	}

	if (!ns\_capable(user_ns, CAP_SYS_ADMIN))
		return -EPERM;

	/\*
 \* CLONE\_NEWIPC must detach from the undolist: after switching
 \* to a new ipc namespace, the semaphore arrays from the old
 \* namespace are unreachable. In clone parlance, CLONE\_SYSVSEM
 \* means share undolist with parent, so we must forbid using
 \* it along with CLONE\_NEWIPC.
 \*/
	if ((flags & (CLONE_NEWIPC | CLONE_SYSVSEM)) ==
		(CLONE_NEWIPC | CLONE_SYSVSEM)) 
		return -EINVAL;

	new_ns = create\_new\_namespaces(flags, tsk, user_ns, tsk->fs);
	if (IS\_ERR(new_ns))
		return  PTR\_ERR(new_ns);

	tsk->nsproxy = new_ns;
	return 0;
}

如果 clone 的参数里面没有 CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWCGROUP,就返回原来的 namespace,调用 get_nsproxy。
get_nsproxy函数只是将struct nsproxy成员的count加1。

static inline void get\_nsproxy(struct nsproxy \*ns)
{
	atomic\_inc(&ns->count);
}

相对应有个函数put_nsproxy将struct nsproxy成员的count减1,这个函数每次减1时会判断count是否等于0,等于0就释放掉调用free_nsproxy释放掉命名空间资源。

static inline void put\_nsproxy(struct nsproxy \*ns)
{
	if (atomic\_dec\_and\_test(&ns->count)) {
		free\_nsproxy(ns);
	}
}

get_nsproxy 和 put_nsproxy 操作类似于C++中的智能指针操作。

3.3 create_new_namespaces

如果 clone 的参数里面有 CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWCGROUP标志位,调用create_new_namespaces

/\*
 \* Create new nsproxy and all of its the associated namespaces.
 \* Return the newly created nsproxy. Do not attach this to the task,
 \* leave it to the caller to do proper locking and attach it to task.
 \*/
static struct nsproxy \*create\_new\_namespaces(unsigned long flags,
	struct task\_struct \*tsk, struct user\_namespace \*user_ns,
	struct fs\_struct \*new_fs)
{
	struct nsproxy \*new_nsp;
	int err;

	new_nsp = create\_nsproxy();
	if (!new_nsp)
		return ERR\_PTR(-ENOMEM);

	new_nsp->mnt_ns = copy\_mnt\_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs);
	if (IS\_ERR(new_nsp->mnt_ns)) {
		err = PTR\_ERR(new_nsp->mnt_ns);
		goto out_ns;
	}

	new_nsp->uts_ns = copy\_utsname(flags, user_ns, tsk->nsproxy->uts_ns);
	if (IS\_ERR(new_nsp->uts_ns)) {
		err = PTR\_ERR(new_nsp->uts_ns);
		goto out_uts;
	}

	new_nsp->ipc_ns = copy\_ipcs(flags, user_ns, tsk->nsproxy->ipc_ns);
	if (IS\_ERR(new_nsp->ipc_ns)) {
		err = PTR\_ERR(new_nsp->ipc_ns);
		goto out_ipc;
	}

	new_nsp->pid_ns_for_children =
		copy\_pid\_ns(flags, user_ns, tsk->nsproxy->pid_ns_for_children);
	if (IS\_ERR(new_nsp->pid_ns_for_children)) {
		err = PTR\_ERR(new_nsp->pid_ns_for_children);
		goto out_pid;
	}

	new_nsp->cgroup_ns = copy\_cgroup\_ns(flags, user_ns,
					    tsk->nsproxy->cgroup_ns);
	if (IS\_ERR(new_nsp->cgroup_ns)) {
		err = PTR\_ERR(new_nsp->cgroup_ns);
		goto out_cgroup;
	}

	new_nsp->net_ns = copy\_net\_ns(flags, user_ns, tsk->nsproxy->net_ns);
	if (IS\_ERR(new_nsp->net_ns)) {
		err = PTR\_ERR(new_nsp->net_ns);
		goto out_net;
	}

	return new_nsp;
	......
}

创建新的nsproxy及其所有关联的名称空间,返回新创建的nsproxy。

3.3.1 copy_utsname

比如copy_utsname:如果没有标志位CLONE_NEWUTS,则返回old_ns,如果设置了创建新的new_ns。

// linux-4.10.1/kernel/utsname.c
/\*
 \* Copy task tsk's utsname namespace, or clone it if flags
 \* specifies CLONE\_NEWUTS. In latter case, changes to the
 \* utsname of this process won't be seen by parent, and vice
 \* versa.
 \*/
struct uts\_namespace \*copy\_utsname(unsigned long flags,
	struct user\_namespace \*user_ns, struct uts\_namespace \*old_ns)
{
	struct uts\_namespace \*new_ns;

	BUG\_ON(!old_ns);
	get\_uts\_ns(old_ns);

	if (!(flags & CLONE_NEWUTS))
		return old_ns;

	new_ns = clone\_uts\_ns(user_ns, old_ns);

	put\_uts\_ns(old_ns);
	return new_ns;
}

3.3.2 copy_ipcs

比如copy_ipcs:

struct ipc\_namespace \*copy\_ipcs(unsigned long flags,
	struct user\_namespace \*user_ns, struct ipc\_namespace \*ns)
{
	if (!(flags & CLONE_NEWIPC))
		return get\_ipc\_ns(ns);
	return create\_ipc\_ns(user_ns, ns);
}

如果没有设置标志位CLONE_NEWIPC,调用get_ipc_ns将tsk->nsproxy->ipc_ns->count加1。

static inline struct ipc\_namespace \*get\_ipc\_ns(struct ipc\_namespace \*ns)
{
	if (ns)
		atomic\_inc(&ns->count);
	return ns;
}

如果有标志位CLONE_NEWIPC,调用 create_ipc_ns创建新的ipc_namespace命名空间。

3.3.3 copy_pid_ns

如果没有设置标志位 CLONE_NEWPID,则返回老的 pid namespace。

// linux-4.10.1/kernel/pid\_namespace.c
struct pid\_namespace \*copy\_pid\_ns(unsigned long flags,
	struct user\_namespace \*user_ns, struct pid\_namespace \*old_ns)
{
	if (!(flags & CLONE_NEWPID))
		return get\_pid\_ns(old_ns);
	if (task\_active\_pid\_ns(current) != old_ns)
		return ERR\_PTR(-EINVAL);
	return create\_pid\_namespace(user_ns, old_ns);
}

如果设置了,就调用 create_pid_namespace,创建新的 pid namespace。采用slab分配器分配struct pid_namespace对象。

// linux-4.10.1/kernel/pid\_namespace.c
static struct pid\_namespace \*create\_pid\_namespace(struct user\_namespace \*user_ns,
	struct pid\_namespace \*parent_pid_ns)
{
	......
	struct pid\_namespace \*ns = kmem\_cache\_zalloc(pid_ns_cachep, GFP_KERNEL);
	......
}

static inline void \*kmem\_cache\_zalloc(struct kmem\_cache \*k, gfp\_t flags)
{
	return kmem\_cache\_alloc(k, flags | __GFP_ZERO);
}

3.3.4 copy_cgroup_ns

如果没有设置标志位 CLONE_NEWCGROUP,则返回老的 struct cgroup_namespace,old_ns。

// linux-4.10.1/kernel/cgroup.c
struct cgroup\_namespace \*copy\_cgroup\_ns(unsigned long flags,
					struct user\_namespace \*user_ns,
					struct cgroup\_namespace \*old_ns)
{
	struct cgroup\_namespace \*new_ns;
	struct ucounts \*ucounts;
	struct css\_set \*cset;

	BUG\_ON(!old_ns);

	if (!(flags & CLONE_NEWCGROUP)) {
		get\_cgroup\_ns(old_ns);
		return old_ns;
	}

	/\* Allow only sysadmin to create cgroup namespace. \*/
	if (!ns\_capable(user_ns, CAP_SYS_ADMIN))
		return ERR\_PTR(-EPERM);

	ucounts = inc\_cgroup\_namespaces(user_ns);
	if (!ucounts)
		return ERR\_PTR(-ENOSPC);

	/\* It is not safe to take cgroup\_mutex here \*/
	spin\_lock\_irq(&css_set_lock);
	cset = task\_css\_set(current);
	get\_css\_set(cset);
	spin\_unlock\_irq(&css_set_lock);

	new_ns = alloc\_cgroup\_ns();
	if (IS\_ERR(new_ns)) {
		put\_css\_set(cset);
		dec\_cgroup\_namespaces(ucounts);
		return new_ns;
	}

	new_ns->user_ns = get\_user\_ns(user_ns);
	new_ns->ucounts = ucounts;
	new_ns->root_cset = cset;

	return new_ns;
}

如果设置标志位 CLONE_NEWCGROUP,则调用alloc_cgroup_ns创建新的struct cgroup_namespace,new_ns。

// linux-4.10.1/kernel/cgroup.c
static struct cgroup\_namespace \*alloc\_cgroup\_ns(void)
{
	struct cgroup\_namespace \*new_ns;
	int ret;

	new_ns = kzalloc(sizeof(struct cgroup\_namespace), GFP_KERNEL);
	if (!new_ns)
		return ERR\_PTR(-ENOMEM);
	ret = ns\_alloc\_inum(&new_ns->ns);
	if (ret) {
		kfree(new_ns);
		return ERR\_PTR(ret);
	}
	atomic\_set(&new_ns->count, 1);
	new_ns->ns.ops = &cgroupns_operations;
	return new_ns;
}

3.3.5 copy_net_ns

如果没有设置标志位 CLONE_NEWNET,则返回老的 old_net。

// linux-4.10.1/net/core/net\_namespace.c

struct net \*copy\_net\_ns(unsigned long flags,
			struct user\_namespace \*user_ns, struct net \*old_net)
{
	struct ucounts \*ucounts;
	struct net \*net;
	int rv;

	//返回老的 old\_net
	if (!(flags & CLONE_NEWNET))
		return get\_net(old_net);

	ucounts = inc\_net\_namespaces(user_ns);
	if (!ucounts)
		return ERR\_PTR(-ENOSPC);

	//分配一个新的 struct net 结构
	net = net\_alloc();
	if (!net) {
		dec\_net\_namespaces(ucounts);
		return ERR\_PTR(-ENOMEM);
	}

	get\_user\_ns(user_ns);
	
	......
	//setup\_net runs the initializers for the network namespace object.
	rv = setup\_net(net, user_ns);
	if (rv == 0) {
		rtnl\_lock();
		list\_add\_tail\_rcu(&net->list, &net_namespace_list);
		rtnl\_unlock();
	}
	......
	return net;
}

如果设置标志位 CLONE_NEWNET了,新建一个 network namespace。
copy_net_ns 会调用 net = net_alloc(),分配一个新的 struct net 结构:

// linux-4.10.1/net/core/net\_namespace.c

static struct kmem\_cache \*net_cachep;

static struct net \*net\_alloc(void)
{
	struct net \*net = NULL;
	......
	net = kmem\_cache\_zalloc(net_cachep, GFP_KERNEL);
	......
}


**先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前在阿里**

**深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/355b9ce7163eb69b94719f90576a682b.png)
![img](https://img-blog.csdnimg.cn/img_convert/232ae1ea05b00bbea7a0006f378e1e17.png)
![img](https://img-blog.csdnimg.cn/img_convert/d832fd756ff335abdc1249c591b9cd77.png)
![img](https://img-blog.csdnimg.cn/img_convert/3e865ef9ac7bf039e76696754e25c9d6.png)
![img](https://img-blog.csdnimg.cn/img_convert/3db53ccd4f6e4cb1dda476bf1ece7b94.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。**
[外链图片转存中...(img-1e6X133K-1715225247077)]
[外链图片转存中...(img-SQvPZ0E1-1715225247077)]
[外链图片转存中...(img-InzMIyUq-1715225247078)]
[外链图片转存中...(img-EqjlschQ-1715225247078)]
[外链图片转存中...(img-8uY7X8BS-1715225247078)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值