一、背景

因项目需求,需要调用lxc部分API(用于系统开发,相比于直接使用shell命令,更为规范),并修改lxc部分代码。因为需求比较具体,因此目前没有阅读lxc所有代码,只是阅读了相关部分。为了便于积累,这里把代码阅读的一些成果进行记录总结,今后若有机会阅读lxc全部代码,将会进行补充。



二、lxc源代码简介

lxc版本:0.7.5

下载:http://sourceforge.net/projects/lxc/files/lxc/



源码文件名总览

af_unix.c     console.h           lxc-destroy.in  lxc_unfreeze.c  parse.c

af_unix.h     error.c             lxc_execute.c   lxc_unshare.c   parse.h

arguments.c   error.h             lxc_freeze.c    lxc-version.in  restart.c

arguments.h   freezer.c           lxc.h           lxc_wait.c      rtnl.c

caps.c        genl.c              lxc_info.c      mainloop.c      rtnl.h

caps.h        genl.h              lxc_init.c      mainloop.h      start.c

cgroup.c      list.h               lxc_kill.c      Makefile.am     start.h

cgroup.h      log.c               lxc-ls.in       Makefile.in     state.c

checkpoint.c  log.h               lxc_monitor.c   monitor.c       state.h

commands.c    lxc_attach.c        lxc-netstat.in  monitor.h       stop.c

commands.h    lxc_cgroup.c        lxc-ps.in       namespace.c     sync.c

conf.c        lxc-checkconfig.in  lxc_restart.c   namespace.h     sync.h

conf.h        lxc_checkpoint.c    lxc-setcap.in   network.c       utils.c

confile.c     lxc-clone.in        lxc-setuid.in   network.h       utils.h

confile.h     lxc_console.c       lxc_start.c     nl.c            utmp.c

console.c     lxc-create.in       lxc_stop.c      nl.h            utmp.h



make install后,安装到/usr/local/include/lxc内的头文件

caps.h    conf.h      error.h     list.h  lxc.h       namespace.h  state.h

cgroup.h  console.h   log.h   monitor.h  start.h       utils.h



三、代码流程图

图太大了,不直接上图了。

图不是我画的,原图链接http://www.cppblog.com/smagle/archive/2013/10/30/204008.html

   图清晰展示了lxc核心命令lxc-start的代码流程。



四、核心源码文件解析



1、文件名:lxc.h

描述:类似于API声明的一个头文件,声明了大部分核心方法

重点包括

extern int  lxc_start(const char *name, char *const argv[], struct lxc_conf *conf);
extern int  lxc_stop(const char *name);
extern int  lxc_cgroup_set(const  char *name, const char *filename, const char *value);
extern int  lxc_cgroup_get(const char *name, const char *filename,char *value, size_t  len);
extern int  lxc_restart(const char *, int, struct lxc_conf *, int);



2、文件名:list.h

描述:定义了一个数据结构

struct  lxc_list {void *elem;struct lxc_list *next;struct lxc_list *prev;
};

并基于数据结构lxc_list构造了一个lxc的链表,猜测lxc-ls后台可能调用了相关方法



3、文件名:log.h 与log.c

log.h

描述:lxc内部日志的处理代码,定义了一些与日志相关的数据结构,比较重要的数据结构是lxc_log_category,用于描述日志类别的实体,维护日志的优先级(priority)和输出附加器(appender)。

lxc_log_category

/* log  category object */
struct  lxc_log_category {const char                      *name;int                             priority;struct lxc_log_appender         *appender;const struct lxc_log_category   *parent;
};

   文件中还有一个对lxc_log_category进行操作的宏定义lxc_log_definelxc_log_definelxc整个源码中经常用到,所以这里进行解释。lxc_log_define的源码如下,主要调用lxc_log_category_definelxc_log_category_define则根据字符串nameparent,来定义一个名为lxc_log_category_##name,父亲为 lxc_log_category_##parent的变量。

/** Helper macro to define and use static  categories.*/
#define  lxc_log_category_define(name, parent)                           extern struct lxc_log_category  lxc_log_category_##parent;       struct lxc_log_category  lxc_log_category_##name = {              #name,                                                   LXC_LOG_PRIORITY_NOTSET,                                NULL,                                                    &lxc_log_category_##parent                              };
#define  lxc_log_define(name, parent)                                    lxc_log_category_define(name,  parent)                           \lxc_log_priority_define(&lxc_log_category_##name,  TRACE)        lxc_log_priority_define(&lxc_log_category_##name,  DEBUG)        lxc_log_priority_define(&lxc_log_category_##name,  INFO)         lxc_log_priority_define(&lxc_log_category_##name,  NOTICE)       lxc_log_priority_define(&lxc_log_category_##name,  WARN)         lxc_log_priority_define(&lxc_log_category_##name,  ERROR)        lxc_log_priority_define(&lxc_log_category_##name,  CRIT)         lxc_log_priority_define(&lxc_log_category_##name,  ALERT)        lxc_log_priority_define(&lxc_log_category_##name,  FATAL)



最后声明一个日志初始化函数

extern int  lxc_log_init(const char *file, const char *priority,const char *prefix,  int quiet);



log.c

   在log.c中,“作者”写到lxc_log_define(lxc_log, lxc);这是定义了一个lxc_log_category数据结构,名为lxc_log_category_lxc_log,其parentlxc_log_category_lxc

根据log.c中其他两个重要变量,可以得出lxc_log_category”child-parent应当是lxc_log_category_lxc_log-->  lxc_log_category_lxc -->log_root

static  struct lxc_log_category log_root = {.name           = "root",.priority       = LXC_LOG_PRIORITY_ERROR,.appender       = NULL,.parent         = NULL,
};
struct  lxc_log_category lxc_log_category_lxc = {.name           = "lxc",.priority       = LXC_LOG_PRIORITY_ERROR,.appender       = &log_appender_stderr,.parent         = &log_root
};



   然后log.c中定义了日志初始化函数

extern int lxc_log_init(const char *file, const char *priority,const char *prefix, int quiet)
{int lxc_priority = LXC_LOG_PRIORITY_ERROR;if (priority) {lxc_priority = lxc_log_priority_to_int(priority);  // 初始化lxc_priorityif (lxc_priority == LXC_LOG_PRIORITY_NOTSET) {ERROR("invalid log priority %s", priority);return -1;}}lxc_log_category_lxc.priority = lxc_priority;   // 优先级 定义到变量lxc_log_category_lxclxc_log_category_lxc.appender = &log_appender_logfile; // log_appender_logfile是已经定义的lxc_log_appender类型的变量,指明日志的输出文件if (!quiet)lxc_log_category_lxc.appender->next = &log_appender_stderr;if (prefix)lxc_log_setprefix(prefix);if (file) {int fd;fd = log_open(file);if (fd == -1) {ERROR("failed to initialize log service");return -1;}lxc_log_fd = fd;}return 0;
}



4、文件名:conf.h与conf.c

conf.h

描述:定义数据结构struct lxc_conf

声明了lxc conf初始化函数extern struct lxc_conf *lxc_conf_init(void);



conf.c

描述:其中有很多具体的配置函数,涉及很多细节,1700+行代码

重点看函数struct lxc_conf *lxc_conf_init(void),其中核心内容如下。由代码来看,初始化中设置参数非常简单,多为NULL-10之类的,lxc_conf_init其实只是在形式上初始化了配置文件,并没有读入真正的配置文件。

struct lxc_conf *new;
new =   malloc(sizeof(*new));
if (!new) {ERROR("lxc_conf_init :  %m");return NULL;
}
memset(new, 0, sizeof(*new));
new->personality = -1;
new->console.path = NULL;
new->console.peer = -1;
new->console.master = -1;
new->console.slave = -1;
new->console.name[0] = '';
new->rootfs.mount =  LXCROOTFSMOUNT;
lxc_list_init(&new->cgroup);
lxc_list_init(&new->network);
lxc_list_init(&new->mount_list);
lxc_list_init(&new->caps);
return new;





5、文件名:confile.h与conf.c

描述:声明了配置文件读取函,这个函数是真正读入了用户输入的配置文件。

extern intlxc_config_read(const char *file, struct lxc_conf *conf);// 读取具体的配置文件,类似于lxc-xxx ... -f confile
extern intlxc_config_readline(char *buffer, struct lxc_conf *conf);



文件名:confile.c

描述:定义了配置文件读取函数



6、文件名:lxc_start.c与lxc_execute.c

描述:核心命令lxc-start和lxc-execute对应的源文件,二者大体流程相似,lxc-execute代码较为简单,这里简要介绍lxc_execute.c的代码(注释)。

int main(int argc, char *argv[])
{static char **args;char *rcfile;struct lxc_conf *conf;lxc_list_init(&defines);if (lxc_caps_init())return -1;if (lxc_arguments_parse(&my_args, argc, argv))return -1;if (lxc_log_init(my_args.log_file, my_args.log_priority,my_args.progname, my_args.quiet)) //日志初始化return -1;args = lxc_arguments_dup(LXCINITDIR "/lxc-init", &my_args);if (!args)return -1;/* rcfile is specified in the cli option */if (my_args.rcfile)  // 保存配置文件路径rcfile = (char *)my_args.rcfile;else {int rc;rc = asprintf(&rcfile, LXCPATH "/%s/config", my_args.name);if (rc == -1) {SYSERROR("failed to allocate memory");return -1;}/* container configuration does not exist */if (access(rcfile, F_OK)) {free(rcfile);rcfile = NULL;}}conf = lxc_conf_init(); // 初始化配置文件(数据结构,分配内存)if (!conf) {ERROR("failed to initialize configuration");return -1;}if (rcfile && lxc_config_read(rcfile, conf)) { // 读入配置文件内容ERROR("failed to read configuration file");return -1;}if (lxc_config_define_load(&defines, conf))return -1;return lxc_start(my_args.name, args, conf); // 启动lxc
}



7、文件名start.c

描述:无论是lxc_start.c还是lxc_execute.c的main函数,最后都调用了函数lxc_start(),函数lxc_start()的具体定义就在文件start.c中。以lxc_start()为起点,比较核心的函数调用关系如下

lxc_start --> __lxc_start --> lxc_spawn  --> lxc_clone

在函数lxc_spawn中,正式调用lxc_clone之前,先设置clone_flags,lxc代码设置为CLONE_NEWUTS|CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS;

clone_flags相关信息,则在文件namespace.h和namespace.c中。



8、自行开发

我之前项目一个模块需要实现lxc的启动等,在阅读lxc代码基础上,我使用“伪API”进行开发,核心代码如下:

char *rcfile = (char *)m_conf_path.c_str(); // 报错配置文件路径
lxc_conf* conf = lxc_conf_init();   // 初始化配置文件,分配内存。
if (!conf) {LOG4CPLUS_ERROR(logger, "Failed to initialize configuration");exit(-1);
}
if (rcfile && lxc_config_read(rcfile, conf)) {// 读取配置文件,相当与读取lxc-xxx -f confile文件// 0 is true, other err number means errorLOG4CPLUS_ERROR(logger, "Failed to read configuration file");
}
lxc_start(GetName().c_str(), m_exe_array, conf);  // 启动lxc,参数包括lxc的名字,执行命令,以及配置文件
free(conf);

截止目前,该代码运行测试正常。