您的位置:首页 > 其它

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

2015-08-25 11:31 806 查看
最近较关心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();
co
bbfe
nf = 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吧,重头戏都在里面
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  lxc 分析 源码