LXC代碼閱讀筆記

一、背景

因項目需求,需要調用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_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-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] = '\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 error
    LOG4CPLUS_ERROR(logger, "Failed to read configuration file");
}
lxc_start(GetName().c_str(), m_exe_array, conf);  // 啓動lxc,參數包括lxc的名字,執行命令,以及配置文件
free(conf);

截止目前,該代碼運行測試正常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章