LXC1.0.7-- lxc-start 源码分析 03

前面的过程就不赘述了,看一下lxc-start 一些流程中重要的过程,有些无关紧要的函数还是依旧跳过。

OK。

1、首先就是第一个lxc_check_inherited函数

dir = opendir("/proc/self/fd");

    if (!dir) {

        WARN("failed to opendirectory: %m");

        return -1;

}

此函数是根据配置将/proc/self/fd下,关闭fd。

然后就跳到__lxc_start中

2、看下lxc-init

在init中 设置一些关于LXC_XXX的环境变量,猜测用于后面的使用。

可以再lxc启动的时候加一些脚本。

会在hook中先执行pre-start的前缀的脚本

if (run_lxc_hooks(name, "pre-start", conf,handler->lxcpath, NULL)) {

        ERROR("failed to runpre-start hooks for container '%s'.", name);

        goto out_aborting;

}

继续,后面有调用lxc_create_tty,细致研究发现,这个函数是根据conf中设置tty的个数,通过opentty函数来创建pts给容器使用。

ret = openpty(&pty_info->master, &pty_info->slave,pty_info->name,NULL, NULL);

这个可以再config文件中设置tty的个数

tty的作用是,如果容器配置了根文件系统和inittab文件设置启动gettty,同时在inittab中gettty的个数不能超过设置的tty的个数,否则会出问题

同理 lxc_console_create 也是一样

如果容器配置了根文件系统和inittab文件设置使用控制台,您可能希望指定该控制台的输出。可以在config中设置lxc.console.logfile来指定输出的位置,lxc.console指定console的个数

然后通过ttys_shift_ids来设置tty的owner。

这样init的初始化过程就结束了。

3、然后到must_drop_cap_sys_boot(handler->conf)这个步骤中。

这个函数会读系统中/proc/sys/kernel/ctrl-alt-del这个文件,判断确定cmd的命令,cmd = v ?LINUX_REBOOT_CMD_CAD_ON : LINUX_REBOOT_CMD_CAD_OFF;

然后会系统调用clone,其中函数指针为container_reboot_supported,最终会调用reboot这个函数,

通过man reboot可以看到细节

LINUX_REBOOT_CMD_CAD_OFF

             (RB_DISABLE_CAD,  0).   CAD is  disabled.   This means  that  the CAD keystroke will cause a SIGINT signalto be sent to init

              (process 1),whereupon this process may decide upon a proper action (maybe: kill allprocesses, sync, reboot).

 

       LINUX_REBOOT_CMD_CAD_ON

              (RB_ENABLE_CAD,0x89abcdef).  CAD is enabled.  This means that the CAD keystroke willimmediately cause the  action  associated

              withLINUX_REBOOT_CMD_RESTART.

那么,问题来了,到底reboot什么东西,系统?还是container?一个已经启动,一个正在start过程。

暂时还没搞懂,是不是NEWPID|NEWUSER 启动的新的namespace的空间中的东西,可能发SIGINT信号给主机的init的进程。将以前启动的container剩余的部分重新启动?先mark一下。

4、然后判断if (geteuid() == 0&& !lxc_list_empty(&conf->id_map)),id_map是空的,因为目前所有的的流程,都是以privilegecontainer说的,所有非root的用户就不分析了。

检查rootfs_is_blockdev(conf) 感觉函数是在判断rootfs的路径是否为blockdev,然后remount_all_slave打开/proc/self/mountinfo然后将shared enties 改变到slave中,就看当前的系统有没有share entries了。

然后调用do_rootfs_setup(conf, name,lxcpath) 将container rootfs 挂载上去。同时也通过pre-mount的脚本将自定义的一些mount 加进去,因此,这个地方也可以自己自定义,复用一些东西

然后调用setup_rootfs,先是调用mount("","/", NULL, MS_SLAVE|MS_REC, 0),mount /,调用bdev_init,初始化rootfs。

5、然后进去lxc-spawn这个函数中,在别的地方很多次见到spawn这个函数,只知道spawn的英文意思是产卵的意思。这个函数上次分析,里面有很多事在做。

首先将以前的cloneflag 保存,记得start的刚开始初始化的时候如果没设置,ns_info中都设置默认的-1,然后就是同步handler,没什么好说的。

然后就是讲handler的clone_flags设置CLONE_NEWXXX,获取物理网络,等等设置一堆东西, 然后就要想办法将cgroup与namespace联系到一块了,到cgroup_init里面看看是什么流程。

首先,前面一直迷惑的ops怎么被初始化的问题,

__attribute__((constructor))

void cgroup_ops_init(void)

这个结构,在函数未调用之前就被执行了,这个回头会在杂篇中讲到,首先程序会根据系统中是否有cgmanager 来使用不同的初始化函数,本文就默认没有cgmanager,调用通用的cgfs_ops_init;返回一个引用值,返回静态变量cgfs_ops;将一些指针赋值,ok,看cgroup_init初始化过程,init指向cgfs_init,因此到cgfs_init这个函数中看一下

首先初始化cgfs_data的数据结构,然后设置cgroup_pattern为全局变量中lxc.cgroup.pattern即在编译中的DEFAULT_CGROUP_PATTERN,默认的是/lxc/%n,这个暂时不知道含义。继续看

然后调用lxc_cgroup_load_meta加载metadata,函数中会判断cgroup的使用情况,然后会调用lxc_cgroup_load_meta2的函数,会查找子系统的白名单,或者指定的hierarchies。

最终返回给handler->cgroup_data。

然后调用cgroup_create(handler)来创建cgroup,调用ops的create,create的指针指向cgfs_create,是个内联函数,最终调用lxc_cgroupfs_create,lxc_cgroupfs_create(d->name,d->cgroup_pattern, md, NULL)用来创建new cgroup

/* we will modify the result of this operation directly,

     * so we don't have to copythe data structure

     */

   base_info = (path_pattern[0]== '/') ?

    lxc_cgroup_process_info_get_init(meta_data) :    //pattern为/lxc/%n

     lxc_cgroup_process_info_get_self(meta_data);

    if (!base_info)

        return NULL;

其中get_init为returnlxc_cgroup_process_info_get(1, meta);pid 为1号进程get数据,根据/proc/1/cgroup中的信息添加到cgroup_process_info的链表中。

new_cgroup_paths = calloc(meta_data->maximum_hierarchy + 1,sizeof(char *));

    if (!new_cgroup_paths)

        goto out_initial_error;

 

    new_cgroup_paths_sub =calloc(meta_data->maximum_hierarchy + 1, sizeof(char *));

    if (!new_cgroup_paths_sub)

        goto out_initial_error;

分配空间

/* find mount points we can use */

    for (info_ptr = base_info;info_ptr; info_ptr = info_ptr->next) {

        h =info_ptr->hierarchy;

        mp =lxc_cgroup_find_mount_point(h, info_ptr->cgroup_path, true);

        if (!mp) {

            ERROR("Could notfind writable mount point for cgroup hierarchy %d while trying to createcgroup.", h->index);

            gotoout_initial_error;

        }

        info_ptr->designated_mount_point= mp;

 

        if(lxc_string_in_array("ns", (const char **)h->subsystems))

            continue;

        if(handle_cgroup_settings(mp, info_ptr->cgroup_path) < 0) {

            ERROR("Could notset clone_children to 1 for cpuset hierarchy in parent cgroup.");

            gotoout_initial_error;

        }

}

/* normalize the path */

    cgroup_path_components =lxc_normalize_path(path_pattern);

    if (!cgroup_path_components)

        goto out_initial_error;

来看主要的find_name_on_this_level程序块

/* determine name of the path component we should create */

        if (contains_name&& suffix > 0) {

            char *buf =calloc(strlen(name) + 32, 1);

            if (!buf)

                gotoout_initial_error;

            snprintf(buf, strlen(name)+ 32, "%s-%u", name, suffix);

            current_component =lxc_string_replace("%n", buf, p_eff);

            free(buf);

        } else {

            current_component =contains_name ? lxc_string_replace("%n", name, p_eff) : p_eff;

        }

        parts[0] = path_so_far;

        parts[1] =current_component;

        parts[2] = NULL;

        current_subpath =path_so_far ? lxc_string_join("/", (const char **)parts, false) :current_component;

/* Now go through each hierarchy and try to create the

         * corresponding cgroup

         */

其中最主要的是

r = create_cgroup(info_ptr->designated_mount_point,current_entire_path);来创建cgroup的目录层级。

理一下头绪,cgroup通过cgroup.patternd 的模式,然后读取/proc/1/cgroup下去创建相应的cgroup层级,最后创建cgroup的目录。

6、回到lxc-spawn中,然后到通过一些网络的netpipepair设置,这些都不是我们关心的。

最后调用lxc_clone函数调用do_start来对container进行一系列的初始化操作,首先是lxc_setup 前面也介绍了,通过初始化,mount rootfs,网络,autodev,自动挂载/proc,/sys等文件,然后设置tty,console等设置标准输入输出的位置,等等。

然后可以设置if(run_lxc_hooks(handler->name, "start", handler->conf,handler->lxcpath, NULL)) start脚本来辅助工作,这个也是可以自定义的内容

最后在do_start函数中调用handler->ops->start(handler,handler->data);

ops为lxc的operation中的内容,来看看想干嘛。execvp(arg->argv[0],arg->argv);执行start container了,这里面,我们用到的是/init不是默认的/sbin/init,因为我们的容器不是标准的容器,所以这点是不同的。

里面注释也谈到了,当我们执行这个/init的时候,函数就不会返回来了,那么后面的程序怎么办?

所以在do_start中子进程一直等到父进程完成工作和配置。

/* Tell the parent task it can begin to configure the

     * container and wait for itto finish

     */

    if(lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE))

        return -1;

然后父进程进行一系列的配置,其中最主要的就是cgroup的配置,如果容器没有cgroup的话,资源划分就成问题了,

cgroup_setup_limits 资源限制,cgroup_enter将pid进程加入task任务中,等等设置cgroup

然后还是配置网络,将container加入到veth当中,这当年还是要看自己config网络相关的配置,so,网络配置有很多,就忽略网络的问题了。

然后又告诉子进程继续初始化过程

/* Tell the child to continue its initialization.  we'll get

     * LXC_SYNC_CGROUP when it isready for us to setup cgroups

     */

    if(lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE))

        goto out_delete_net;

然后当子进程setup过程完成之后,让父进程设置cgroup,同时父进程设置完cgroup时,也通知子进程完成,此时子进程就真正进入到container的init的进程了。

一直没发现这个LXC_SYNC_POST_CGROUPwait 子进程的信号谁发给他,这个比较疑惑?

最后发现是do_stat这个函数if判断失败后goto的,则表示中间会error,最后还有个post_cgroup,注释是这样说道。

/* Tell the child to complete its initialization and wait for

     * it to exec or return anerror.  (the child will never

     * returnLXC_SYNC_POST_CGROUP+1.  It will eitherclose the

     * sync pipe, causinglxc_sync_barrier_child to return

     * success, or return adifferent value, causing us to error

     * out).

     */

    if(lxc_sync_barrier_child(handler, LXC_SYNC_POST_CGROUP))

        return -1;

然后就是调用post-start,NOTICE 运行的pid,最后设置container的状态为RUNNING,至此spawn就结束了。

回到__lxc_start中,get_netns_fd获得network的状态,然后进入lxc_poll中.后面没什么好说的,现在主要考虑lxc 在exec container的init的进程过后,lxc是如何继续接管程序的。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值