1、背景
html
因項目需求,須要調用lxc部分API(用於系統開發,相比於直接使用shell命令,更爲規範),並修改lxc部分代碼。由於需求比較具體,所以目前沒有閱讀lxc全部代碼,只是閱讀了相關部分。爲了便於積累,這裏把代碼閱讀的一些成果進行記錄總結,從此如有機會閱讀lxc所有代碼,將會進行補充。shell
2、lxc源代碼簡介數據結構
lxc版本:0.7.5app
下載:http://sourceforge.net/projects/lxc/files/lxc/ide
源碼文件名總覽函數
af_unix.c console.h lxc-destroy.in lxc_unfreeze.c parse.coop af_unix.h error.c lxc_execute.c lxc_unshare.c parse.h測試 arguments.c error.h lxc_freeze.c lxc-version.in restart.cui arguments.h freezer.c lxc.h lxc_wait.c rtnl.cspa 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 |
3、代碼流程圖
圖太大了,不直接上圖了。
圖不是我畫的,原圖連接http://www.cppblog.com/smagle/archive/2013/10/30/204008.html
圖清晰展現了lxc核心命令lxc-start的代碼流程。
4、核心源碼文件解析
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_define,lxc_log_define在lxc整個源碼中常常用到,因此這裏進行解釋。lxc_log_define的源碼以下,主要調用lxc_log_category_define,lxc_log_category_define則根據字符串name、parent,來定義一個名爲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,其parent爲lxc_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_priority if (lxc_priority == LXC_LOG_PRIORITY_NOTSET) { ERROR("invalid log priority %s", priority); return -1; } } lxc_log_category_lxc.priority = lxc_priority; // 優先級 定義到變量lxc_log_category_lxc lxc_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、-1、0之類的,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] = '\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
描述:定義了配置文件讀取函數
六、文件名: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 }
七、文件名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中。
八、自行開發
我以前項目一個模塊須要實現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 error LOG4CPLUS_ERROR(logger, "Failed to read configuration file"); } lxc_start(GetName().c_str(), m_exe_array, conf); // 啓動lxc,參數包括lxc的名字,執行命令,以及配置文件 free(conf);
截止目前,該代碼運行測試正常。