linux内核知识之namespace

namespace概念:

   namespace是linux自带的功能用来隔离内核资源的机制,如进程pid,主机名与域名,网络设备端口等。什么是容器?容器其实就是一个虚拟化的独立的沙箱环境,和宿主机或者其他的容器之间是独立隔离的,他有自己的网络环境,用户权限,进程pid等。容器是依托于一个物理机或者一个虚拟机的,在此之上可以有多个容器,容器之间是感受不到彼此的存在的,在网络中可以被看做是一个独立的个体存在,而namespace起到的作用就是让资源之间互相隔离,成为一个独立的存在。

  说穿了,其实容器就是一个轻量级的虚拟化服务器,你可以在一台物理机上面运行两个容器,然后在两个容器中运行tomcat,端口都是8080,而不会有端口占用的问题,因为两台容器的都有自己网络环境,他们的NetWork namespace是不同的,网络环境被隔离了起来让容器不会感受到彼此的存在,在容器中的tomcat以为只有自己一个tomcat。

 

linux支持的六大隔离:

  linux提供了六种namespace,这六种namespace在不同的方面进行隔离,互相配合,以此模拟出一个相对独立的容器环境。

     linux的每个进程都有以上六种namespace,如果两个进程的对应的namespace值是一样的,那么他们就处于同一个namespace,彼此之间是可见的。反之,他们在不同的容器中,彼此是不可见的。

 

ll /proc/[pid]/ns   

    执行以上命令会显示上图,左边的红色部分就是这个进程的六大namespace,右边的中括号中的数值就是具体的某个namespace。比如net,表示当前进程处于4026531956这个namespace下。当其他的进程如果net也是这个数值,这里两个进程就是处于同一容器中。

    一旦上述文件被打开,只要打开

namespace操作:

    namespace的API包括clone(),setns()以及unshare(),还有/proc下的部分文件。为了确定隔离的到底是哪6项namespace,在使用这些api时,通常需要指定以下6个参数中的一个或多个,通过|(位或)操作来实现。这6个参数分别是CLONE_NEWIPC、CLONE_NEWNS、CLONE_NEWNET、CLONE_NEWPID、CLONE_NEWUSERS和CLONE_NEWUTS。

    clone(): 使用clone()在创建新进程的同时创建namespace。

      这是最常见的做法,也是docker使用namespace最基本的方法。调用过程如下:

      int clone(int (*child_func)(void *),void *child_stack,int flags,void *arg);

      clone()实际上是Linux系统调用fork()的一种更通用的实现方式,它可以通过flags来控制使用多少功能。一共有20多种CLONE_*的flag(标志位)参数来控制clone进程的方方面面(如是否与父进程共享虚拟内存等)

      child_func:子进程运行的程序主函数。

      child_stack:子进程使用的栈空间。

      flag:表示试用哪些CLONE_*标志位,与namespace相关的主要包括CLONE_NEWIPC、CLONE_NEWNS、CLONE_NEWNET、CLONE_NEWPID、CLONE_NEWUSERS和CLONE_NEWUTS。

      args:用户参数。

   setns():通过setns()加入一个已经存在的namespace。

    上文提到,在进程都已经结束的情况下,也可以通过挂载的形式把namespace保留下来,保留namespace的目的是为以后有进程加入做准备。在docker中,使用docker exec命令在已经运行着的容器中执行一个新的命令,就需要用到改方法。通过setns()系统调用,进程从原先的namespace加入某个已经存在的namespace,使用方法如下,通常为了不影响进程的调用者,也为了使新加入的pid namespace生效,会在setns()函数执行后使用clone()创建子进程继续执行命令,让原先的进程结束。

    int setns(int fd,int nstype);

    fd:表示要加入namespace的文件描述符。上文提到,它是/proc/[pid]/ns目录的文件描述符,可以通过直接打开该目录下的链接或者打开一个挂载了该目录下链接的文件得到。

   nstype:让调用者可以检查fd指向的namespace类型是否符合实际要求。参数为0时小时不检查。

  unshare():在原先进程上进行namespace隔离。

    它与clone()很像,不同的是,unshare()运行在原先的进程上,不需要启动一个新进程。

    int unshare(int flags);

    调用unshare()的主要作用是,不启动新进程就可以起到隔离的效果,相当于跳出原先的namespace进行操作。这样,就可以在原进程进行一些需要隔离的操作。Linux中自带的unshare命令,就是通过unshare()系统调用实现的。docker目前并没有使用这个系统调用。

  fork():系统调用      

    当程序调用fork()函数时,系统会创建新的进程,为其分配资源,例如存储数据和代码的空间,然后把原先进程的所有值都复制到新进程中,只有少量数值与原来的进程值不同,相当于复制了本身,后续代码逻辑根据fork()的返回值用来区分是当前线程是新进程还是父进程。

  1. 父进程中,fork()返回新创建子进程的进程id。

  2.在子进程中,fork()返回0;

  3.如果出现错误,fork()返回一个负值。

    使用fork()后,父进程有义务监控子进程的运行状态,并在子进程退出后自己才能正常退出,否则子进程就会成为“孤儿”进程。如果子进程真的成为了“孤儿”进程,则init进程会接管子进程,进行资源的销毁以及释放。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值