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

最近较关心LinuxContainer 的启动流程,所以就从lxc_start.c这个文件看起。

首先进入源文件,直接到main程序来,本人喜欢按照程序执行的顺序来看代码,所以看个人喜好了。

	int main(int argc, char *argv[])
{
    int err = 1;
    struct lxc_conf *conf;    //初始化config结构
    char *const *args;		 //传递的参数
    char *rcfile = NULL;		 //指定配置文件
    char *const default_args[] = {    //默认的args参数
        "/sbin/init",
        NULL,
    };  
struct lxc_container *c;    //lxc-container 的结构体
….
}

看下lxc_conf这个数据结构

struct lxc_conf {
    int is_execute;		//容器是否在执行
    char *fstab;			//fstab?
    int tty;				//tty的个数
    int pts;				//pts的个数?
    int reboot;			//重启?
    int need_utmp_watch;		//字面翻译 需要utmp 查看
    signed long personality;		//字面翻译 特点	
    struct utsname *utsname;	//ustname
    struct lxc_list cgroup;		//cgroup list lxc_list只是简单的链表结构
    struct lxc_list id_map;		//id_map list
    struct lxc_list network;		//network list
    struct saved_nic *saved_nics;//saved_nics 结构
    int num_savednics;			//savednics数量?
    int auto_mounts;			//auto_mounts?
    struct lxc_list mount_list;		//mount_list list?
    struct lxc_list caps;			//caps list?
    struct lxc_list keepcaps;		//keepcaps list?
    struct lxc_tty_info tty_info;	//tty的相关信息
    struct lxc_console console;	//console的结构体
    struct lxc_rootfs rootfs;		//rootfs的结构体
    char *ttydir;					//tty目录
int close_all_fds;			//关闭所有fd
struct lxc_list hooks[NUM_LXC_HOOKS];	//hooks 函数

    char *lsm_aa_profile;   //?
    char *lsm_se_context;	//?
    int tmp_umount_proc;	//?
    char *seccomp;  // filename with the seccomp rules
#if HAVE_SCMP_FILTER_CTX
    scmp_filter_ctx seccomp_ctx;
#endif
    int maincmd_fd;		//?
    int autodev;  // if 1, mount and fill a /dev at start
    int haltsignal; // signal used to halt container
    int stopsignal; // signal used to hard stop container
    int kmsg;  // if 1, create /dev/kmsg symlink
    char *rcfile;   // Copy of the top level rcfile we read

    // Logfile and loglevel can be set in a container config file.
    // Those function as defaults.  The defaults can be overriden
    // by command line.  However we don't want the command line
    // specified values to be saved on c->save_config().  So we
    // store the config file specified values here.
    char *logfile;  // the logfile as specifed in config
    int loglevel;   // loglevel as specifed in config (if any)

    int inherit_ns_fd[LXC_NS_MAX];

    int start_auto;
    int start_delay;
    int start_order;
    struct lxc_list groups;
    int nbd_idx;

    /* set to true when rootfs has been setup */
    bool rootfs_setup;
};

lxc_info 的结构体看完,下面要看下lxc-container 这个重头戏。

下面来看下lxc_container的结构体

/*!
 * An LXC container.
 */
struct lxc_container {
    // private fields
   char *name; 		 //container 的名字
   char *configfile; 		 // configuration file 的路径
   char *pidfile;   		 // 存储pid 的文件名
   struct lxc_lock *slock;  //Container semaphore lock. 容器的信号锁
    struct lxc_lock *privlock;//容器的私有信号锁
    int numthreads;			//容器的引用数量,由privlock保护
    struct lxc_conf *lxc_conf;

    // public fields
    char *error_string;		//全局变量 可读的最后显示的error
    int error_num;			//最后error的数字
    bool daemonize;		//容器是否希望开启守护进程
char *config_path;		// configuration file 的路径 和上面的区别? 全局?
…….					//一堆成员函数   暂不看
}

好了, lxc_container暂时看到这个位置,后面需要的话再一个一个的详解

 

lxc_list_init(&defines);             //初始化list

defines定义在文件开始,为全局变量

static structlxc_list defines;

if(lxc_caps_init())                    //caps初始化

        return err;

到这个函数里看一下。

int lxc_caps_init(void)
{
    uid_t uid = getuid();
    gid_t gid = getgid();
    uid_t euid = geteuid();	//有效uid

    if (!uid) {				//root权限运行的话就省了后面的步骤了
        INFO("command is run as 'root'");
        return 0;
    }   

    if (uid && !euid) {
        INFO("command is run as setuid root (uid : %d)", uid);

        if (prctl(PR_SET_KEEPCAPS, 1)) {		//prctl 设置进程的选项,为下面set?
            ERROR("failed to 'PR_SET_KEEPCAPS': %m");
            return -1; 
        }   

        if (setresgid(gid, gid, gid)) {
            ERROR("failed to change gid to '%d': %m", gid);
            return -1; 
        }   

        if (setresuid(uid, uid, uid)) {
            ERROR("failed to change uid to '%d': %m", uid);
            return -1; 
        }   

        if (lxc_caps_up()) {
            ERROR("failed to restore capabilities: %m");
            return -1; 
        }   
    }   

if (uid == euid)
INFO("command is run as user '%d'", uid);

return 0;
}

接着就是读传过来的参数

if(lxc_arguments_parse(&my_args, argc, argv))

        return err;

这个函数就没细看,只需知道将参数传给my_args

判断有没有指定 初始执行的参数,没有的话指定默认参数

if (!my_args.argc)

        args = default_args;

    else       

        args = my_args.argv;

 

初始化一堆log的,暂时也没细看

if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
             my_args.progname, my_args.quiet, my_args.lxcpath[0]))
        return err;
lxc_log_options_no_override();

const char *lxcpath = my_args.lxcpath[0];		//lxcpath 很有意思
// lxc_global_config_value("lxc.lxcpath")这个写的还是比较复杂的,总之lxcpath会是默认的路径
//指定config的位置,如果没指定,则使用默认的路径的config,通过配置创建新的
/*
     * rcfile possibilities:
     * 1. rcfile from random path specified in cli option
     * 2. rcfile not specified, use $lxcpath/$lxcname/config
     * 3. rcfile not specified and does not exist.
     */
    /* rcfile is specified in the cli option */
    if (my_args.rcfile) {
        rcfile = (char *)my_args.rcfile;
        c = lxc_container_new(my_args.name, lxcpath);
        if (!c) {
            ERROR("Failed to create lxc_container");
            return err;
        }
        c->clear_config(c);
        if (!c->load_config(c, rcfile)) {
            ERROR("Failed to load rcfile");
            lxc_container_put(c);
            return err;
        }
}
 } else {
        int rc;

        rc = asprintf(&rcfile, "%s/%s/config", lxcpath, my_args.name);
        if (rc == -1) {
            SYSERROR("failed to allocate memory");
            return err;
        }
        INFO("using rcfile %s", rcfile);

        /* container configuration does not exist */
        if (access(rcfile, F_OK)) {
            free(rcfile);
            rcfile = NULL;
        }
        c = lxc_container_new(my_args.name, lxcpath);
        if (!c) {
            ERROR("Failed to create lxc_container");
            return err;
        }
}

里面最主要的函数c = lxc_container_new(my_args.name, lxcpath);

struct lxc_container *lxc_container_new(const char *name, const char *configpath)
{
    struct lxc_container *c;			//结构体lxc_container 前面分析过了

    c = malloc(sizeof(*c));			//创建
    if (!c) {
        fprintf(stderr, "failed to malloc lxc_container\n");
        return NULL;
    }    
    memset(c, 0, sizeof(*c));		   //初始0

    if (configpath)
        c->config_path = strdup(configpath);			//config_path
    else 
        c->config_path = strdup(lxc_global_config_value("lxc.lxcpath"));

    if (!c->config_path) {
        fprintf(stderr, "Out of memory\n");
        goto err; 
    }    

    remove_trailing_slashes(c->config_path);
    c->name = malloc(strlen(name)+1);
    if (!c->name) {
        fprintf(stderr, "Error allocating lxc_container name\n");
        goto err; 
    }    
    strcpy(c->name, name);

    c->numthreads = 1; 
	 // lock这部分没细看
    if (!(c->slock = lxc_newlock(c->config_path, name))) {
        fprintf(stderr, "failed to create lock\n");
        goto err; 
    }
	 if (!(c->privlock = lxc_newlock(NULL, NULL))) {
        fprintf(stderr, "failed to alloc privlock\n");
        goto err;
    }
	 // set config path
	 if (!set_config_filename(c)) {
        fprintf(stderr, "Error allocating config file pathname\n");
        goto err;
    }
	 //load config path
    if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL))
        goto err;
	 //判断容器是否创建失败
	 if (ongoing_create(c) == 2) {
        ERROR("Error: %s creation was not completed", c->name);
        lxcapi_destroy(c);
        lxcapi_clear_config(c);
    }
    c->daemonize = true;
    c->pidfile = NULL;
	 …… 				//后面都是成员函数赋值
}
现在回到lxc_start 的main函数中
//判断容器是否在运行
if (c->is_running(c)) {
        ERROR("Container is already running.");
        err = 0;
        goto out; 
}
/*       
 * We should use set_config_item() over &defines, which would handle
  * unset c->lxc_conf for us and let us not use lxc_config_define_load()
  */
//加载config文件
 if (!c->lxc_conf)
     c->lxc_conf = lxc_conf_init();
 conf = c->lxc_conf;
if (lxc_config_define_load(&defines, conf))
        goto out;
//提示信息
if (!rcfile && !strcmp("/sbin/init", args[0])) {
        ERROR("Executing '/sbin/init' with no configuration file may crash the host");
        goto out;
    }

    if (ensure_path(&conf->console.path, my_args.console) < 0) {
        ERROR("failed to ensure console path '%s'", my_args.console);
        goto out;
    }

    if (ensure_path(&conf->console.log_path, my_args.console_log) < 0) {
        ERROR("failed to ensure console log '%s'", my_args.console_log);
        goto out;
    }
	// pid 文件
    if (my_args.pidfile != NULL) {
        if (ensure_path(&c->pidfile, my_args.pidfile) < 0) {
            ERROR("failed to ensure pidfile '%s'", my_args.pidfile);
            goto out;
        }
    }
    //一些share_ns 的配置,未细看
	 int i;
    for (i = 0; i < LXC_NS_MAX; i++) {
        if (my_args.share_ns[i] == NULL)
            continue;

        int pid = pid_from_lxcname(my_args.share_ns[i], lxcpath);
        if (pid < 1)
            goto out;

        int fd = open_ns(pid, ns_info[i].proc_name);
        if (fd < 0)
            goto out;
        conf->inherit_ns_fd[i] = fd;
    }
    //初始化为1
    if (!my_args.daemonize) {
        c->want_daemonize(c, false);
    }

    if (my_args.close_all_fds)
        c->want_close_all_fds(c, true);

    err = c->start(c, 0, args) ? 0 : 1;

    if (err) {
        ERROR("The container failed to start.");
        if (my_args.daemonize)
            ERROR("To get more details, run the container in foreground mode.");
        ERROR("Additional information can be obtained by setting the "
              "--logfile and --logpriority options.");
        err = c->error_num;
        lxc_container_put(c);
        return err;
    }

out:
    lxc_container_put(c);
    return err;
}

直接到c->start 过程start是调用 lxcapi_start 这个函数指针,现在去看下这个函数到底是怎么讲lxc container 启动起来的。

    传过来的参数是container c,useinit 0,argv=args 即指定的初始化程序

static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv[])
{
    int ret;
    struct lxc_conf *conf;
    bool daemonize = false;				//守护进程为false
    FILE *pid_fp = NULL;				//pid_file文件的指针
    char *default_args[] = {				//又是default_args
        "/sbin/init",
        NULL,
    };

    /* container exists */
    if (!c)								//判断容器是否存在
        return false;
    /* container has been setup */
    if (!c->lxc_conf)						//config加载完美
        return false;

    if ((ret = ongoing_create(c)) < 0) {		//容器是否创建完整
        ERROR("Error checking for incomplete creation");
        return false;
    }
    if (ret == 2) {
        ERROR("Error: %s creation was not completed", c->name);
        c->destroy(c);
        return false;
    } else if (ret == 1) {
        ERROR("Error: creation of %s is ongoing", c->name);
        return false;
    }

	 /* is this app meant to be run through lxcinit, as in lxc-execute? */
    if (useinit && !argv)					//还是判断
        return false;

    if (container_mem_lock(c))			//lock
        return false;
    conf = c->lxc_conf;					//conf赋值
    daemonize = c->daemonize;			//true
    container_mem_unlock(c);		//unlock

    if (useinit) {						//0
        ret = lxc_execute(c->name, argv, 1, conf, c->config_path);
        return ret == 0 ? true : false;
    }

    if (!argv)
        argv = default_args;			//又重新判断 args 是否为空,空即赋值
	 /*
    * say, I'm not sure - what locks do we want here?  Any?
    * Is liblxc's locking enough here to protect the on disk
    * container?  We don't want to exclude things like lxc_info
    * while container is running...
	 * 这段注释给跪了,还是老老实实看他想干嘛吧
    */
    if (daemonize) {					//true
        lxc_monitord_spawn(c->config_path);	//start好像跟前面的版本差别

        pid_t pid = fork();
        if (pid < 0)
            return false;

        if (pid != 0) {
            /* Set to NULL because we don't want father unlink
             * the PID file, child will do the free and unlink.
             */
            c->pidfile = NULL;
            return wait_on_daemonized_start(c, pid);		//等下进去,里面有waitpid,所以先看后面
        }

        /* second fork to be reparented by init */
        pid = fork();										//两次fork
        if (pid < 0) {
            SYSERROR("Error doing dual-fork");
            return false;
        }
        if (pid != 0)
            exit(0);
        /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */
        if (chdir("/")) {									//root目录
            SYSERROR("Error chdir()ing to /.");
            return false;
        }
        lxc_check_inherited(conf, -1);
        close(0);					//pipe file?
close(1);
        close(2);
        open("/dev/zero", O_RDONLY);
        open("/dev/null", O_RDWR);
        open("/dev/null", O_RDWR);
        setsid();
    } else {
        if (!am_single_threaded()) {
            ERROR("Cannot start non-daemonized container when threaded");
            return false;
        }
    }
/* We need to write PID file after daeminize, so we always
     * write the right PID.
     */
    if (c->pidfile) {								//写入pid 到pidfile
        pid_fp = fopen(c->pidfile, "w");
        if (pid_fp == NULL) {
            SYSERROR("Failed to create pidfile '%s' for '%s'",
                 c->pidfile, c->name);
            return false;
        }

        if (fprintf(pid_fp, "%d\n", getpid()) < 0) {
            SYSERROR("Failed to write '%s'", c->pidfile);
            fclose(pid_fp);
            pid_fp = NULL;
            return false;
        }

        fclose(pid_fp);
        pid_fp = NULL;
    }

reboot:
		…..     			
}

现在到 wait_on_daemonized_start(c, pid) 里面看看函数调用的情况

这个就是主线程的pid 在等待其他子线程工作完,然后执行,只能硬着头皮继续看了。

static bool wait_on_daemonized_start(struct lxc_container *c, int pid)
{
    /* we'll probably want to make this timeout configurable? */
    int timeout = 5, ret, status;

    /*
     * our child is going to fork again, then exit.  reap the
     * child
     */
    ret = waitpid(pid, &status, 0);
    if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
        DEBUG("failed waiting for first dual-fork child");
    return lxcapi_wait(c, "RUNNING", timeout);
}

函数很简单 直接调用了lxcapi_wait。

static bool lxcapi_wait(struct lxc_container *c, const char *state, int timeout)
{
    int ret;

    if (!c)
        return false;

    ret = lxc_wait(c->name, state, timeout, c->config_path);
    return ret == 0;
}

这个依旧很简单又跳走了。。。lxc_wait了

这个函数现在先不细说了,只是检查容器创建是否超时的问题。

 

Reboot这货还是很奇葩,我们这一代都在灌输少用或者不用goto,导致我见到这个一直很疑惑goto在哪,结果找了半天没找到,好吧 他是单独的一个程序块,瞅瞅去,重头戏都在这里。


ok 下篇再看reboot吧,重头戏都在里面




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值