LXC1.0.7-- lxc-start 源码分析 01
2015-08-25 11:31
806 查看
最近较关心LinuxContainer 的启动流程,所以就从lxc_start.c这个文件看起。
首先进入源文件,直接到main程序来,本人喜欢按照程序执行的顺序来看代码,所以看个人喜好了。
看下lxc_conf这个数据结构
lxc_info 的结构体看完,下面要看下lxc-container 这个重头戏。
下面来看下lxc_container的结构体
好了, lxc_container暂时看到这个位置,后面需要的话再一个一个的详解
lxc_list_init(&defines); //初始化list
defines定义在文件开始,为全局变量
static structlxc_list defines;
if(lxc_caps_init()) //caps初始化
return err;
到这个函数里看一下。
接着就是读传过来的参数
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的,暂时也没细看
直接到c->start 过程start是调用 lxcapi_start 这个函数指针,现在去看下这个函数到底是怎么讲lxc container 启动起来的。
传过来的参数是container c,useinit 0,argv=args 即指定的初始化程序
现在到 wait_on_daemonized_start(c, pid) 里面看看函数调用的情况
这个就是主线程的pid 在等待其他子线程工作完,然后执行,只能硬着头皮继续看了。
函数很简单 直接调用了lxcapi_wait。
这个依旧很简单又跳走了。。。lxc_wait了
这个函数现在先不细说了,只是检查容器创建是否超时的问题。
Reboot这货还是很奇葩,我们这一代都在灌输少用或者不用goto,导致我见到这个一直很疑惑goto在哪,结果找了半天没找到,好吧 他是单独的一个程序块,瞅瞅去,重头戏都在这里。
ok 下篇再看reboot吧,重头戏都在里面
首先进入源文件,直接到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吧,重头戏都在里面
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- ruby 单态方法 分析
- 典型入侵日志分析
- 浅析Ruby的源代码布局及其编程风格
- 一根网线内的8根线哪4根是传输数据的,哪四根是防干扰的
- asp.net 抓取网页源码三种实现方法
- JavaScript分析、压缩工具JavaScript Analyser
- JavaScript 组件之旅(一)分析和设计
- JS小游戏之仙剑翻牌源码详解
- Ajax 的六个误区小结分析
- JS小游戏之宇宙战机源码详解
- jquery的总体架构分析及实现示例详解
- jQuery源码分析之jQuery中的循环技巧详解
- 本人自用的global.js库源码分享
- java中原码、反码与补码的问题分析
- 百度最近不收站分析,为什么不收录你的站
- PHP5.2下chunk_split()函数整数溢出漏洞 分析
- 远程分析IIS设置
- 分析 JavaScript 中令人困惑的变量赋值
- json跟xml的对比分析