Linux容器间共享内存,容器基本原理——Linux namespace

Linux Namespace

Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法。Unix 中有一个名为 chroot 的系统调用(通过修改根目录把用户 jail 到一个特定目录下),chroot 提供了一种简单的隔离模式:chroot 内部的文件系统无法访问外部的内容。Linux Namespace 在此基础上,提供了对 UTS、IPC、Mount、PID、Network、User 等六种隔离机制。

基础程序

本文中后续程序都是在基础程序基础上形成的,基础程序如下。

#define _GNU_SOURCE

#include

#include

#include

#include

#include

#include

/* 定义一个给 clone 用的栈,栈大小1M */

#define STACK_SIZE (1024 * 1024)

static char container_stack[STACK_SIZE];

char* const container_args[] = {

"/bin/bash",

NULL

};

int container_main(void* arg)

{

printf("Container[%d] - inside the container!\n", getpid());

/* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */

execv(container_args[0], container_args);

printf("Something's wrong!\n");

return 1;

}

int main()

{

printf("Parent[%d] - start a container!\n", getpid());

/* 调用clone函数,其中传出一个函数,还有一个栈空间的(为什么传尾指针,因为栈是反着的) */

int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD, NULL);

/* 等待子进程结束 */

waitpid(container_pid, NULL, 0);

printf("Parent - container stopped!\n");

return 0;

}

UTS Namespace

UTS 实现主机名隔离,在程序中使用如下:

int container_main(void* arg)

{

...

sethostname("container", 10); /* 设置主机名 */

printf("Something's wrong!\n");

return 1;

}

int main()

{

...

/* 调用clone函数,其中传出一个函数,还有一个栈空间的(为什么传尾指针,因为栈是反着的) */

int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWUTS |

SIGCHLD, NULL); /* 加上 UTS 参数 */

...

}

IPC Namespace

IPC 全称 Inter-Process Communication,是Unix/Linux下进程间通信的一种方式,IPC有共享内存、信号量、消息队列等方法。IPC 隔离实现如下:

int main()

{

...

/* 调用clone函数,其中传出一个函数,还有一个栈空间的(为什么传尾指针,因为栈是反着的) */

int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWUTS |

CLONE_NEWIPC | SIGCHLD, NULL); /* 加上 PID 参数 */

...

}

检验 IPC 隔离,clone 时先不指定 CLONE_NEWIPC 参数。使用 ipcmk -Q 创建一个 IPC 队列,使用 ipcs -q 查看队列创建是否成功,如下图所示。

57d36934b1e9

ipc 队列创建与查看

运行未指定 CLONE_NEWIPC 参数的程序,使用 ipcs -q 查看能否看到刚才创建的队列,结果如下,发现能够访问到。

57d36934b1e9

未使用 CLONE_NEWIPC

指定 CLONE_NEWIPC 参数后重新编译执行,继续使用 ipcs -q 查看,结果如下,已经看不到创建的 IPC 队列了,实现了 IPC 隔离。

57d36934b1e9

使用了 CLONE_NEWIPC 后

PID Namespace

PID Namespace 将子进程 PID 设置为 1。在传统的UNIX系统中,PID为1的进程是init,地位非常特殊。他作为所有进程的父进程,有很多特权(比如:屏蔽信号等)。另外,其还会为检查所有进程的状态,如果某个子进程脱离了父进程(父进程没有wait它),那么init就会负责回收资源并结束这个子进程。所以,要做到进程空间的隔离,首先要创建出PID为1的进程,最好就像 chroot 那样,把子进程的 PID 在容器内变成 1 。

在 clone 时将 CLONE_NEWPID传入即可, 可在程序内使用 getpid() 获取进程 PID 并打印,对比使用 CLONE_NEWPID 前后,子进程的 PID 有无变化。

Mount Namespace

加上 CLONE_NEWPID 子进程内使用 ps、top 等命令依然可以查看所有进程,因为子进程仍旧和父进程共享 /proc 文件系统,在 clone 时传入 CLONE_NEWNS 标志位,且在子进程内重新挂载 /proc 文件系统可以解决这一问题,修改如下(这里通过实践发现原文中两个小错误,一是如果不在父进程内重新 mount -t proc proc /proc,子进程结束后再在系统中使用 ps 就会报错;二是很奇怪,即使不指定 CLONE_NEWNS 标志,单纯的在子进程内 mount 也能实现同样功能。)

int container_main(void* arg)

{

...

sethostname("container", 10); /* 设置主机名 */

system("mount -t proc proc /proc");

...

}

int main()

{

...

/* 调用clone函数,其中传出一个函数,还有一个栈空间的(为什么传尾指针,因为栈是反着的) */

int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID |

CLONE_NEWIPC | CLONE_NEWNS | SIGCHLD, NULL); /* 加上 NS */

/* 等待子进程结束 */

waitpid(container_pid, NULL, 0);

system("mount -t proc proc /proc"); /* 不加这行,退出后使用 ps 报错 */

...

}

Network Namespace

User Namespace

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值