本文以“ virsh pool-info vg10 ” 命令为例,分两个部分分析了virsh 命令的代码。(libvirt 版本为1.1.2 )
第一部分:综述virsh 命令的组成部分以及各结构体的关系;
第二部分:分析virsh pool-info vg10 命令的执行流程;
第一部分:
libvirt中的virsh 命令被分成了若干组,如:将对存储池(pool)进行操作的所有命令分到一个组,组名为:VSH_CMD_GRP_STORAGE_POOL。
再如:将对存储卷(volume )进行操作的所有命令分到一个组,组名为:VSH_CMD_GRP_STORAGE_VOL。
组的结构体如下:
struct _vshCmdGrp {
const char *name; /* name of group, or NULL for list end */
const char *keyword; /* help keyword */
const vshCmdDef *commands;
};
结构体中,其中第一个成员name 是组名,第三个成员commands指针指向了本组中的第一个命令,命令结构如下:
/*
* vshCmdDef - command definition
*/
struct _vshCmdDef {
const char *name; /* name of command, or NULL for list end */
bool (*handler) (vshControl *, const vshCmd *); /* command handler */
const vshCmdOptDef *opts; /* definition of command options */
const vshCmdInfo *info; /* details about command */
unsigned int flags; /* bitwise OR of VSH_CMD_FLAG */
};
其中的name 是命令名,handler是处理此命令的具体函数(函数指针)。
1.1.2 版本中所有组定义在virsh.c 中,如下:
static const vshCmdGrp cmdGroups[] = {
{VSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds},
{VSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds},
{VSH_CMD_GRP_HOST_AND_HV, "host", hostAndHypervisorCmds},
{VSH_CMD_GRP_IFACE, "interface", ifaceCmds},
{VSH_CMD_GRP_NWFILTER, "filter", nwfilterCmds},
{VSH_CMD_GRP_NETWORK, "network", networkCmds},
{VSH_CMD_GRP_NODEDEV, "nodedev", nodedevCmds},
{VSH_CMD_GRP_SECRET, "secret", secretCmds},
{VSH_CMD_GRP_SNAPSHOT, "snapshot", snapshotCmds},
{VSH_CMD_GRP_STORAGE_POOL, "pool", storagePoolCmds},
{VSH_CMD_GRP_STORAGE_VOL, "volume", storageVolCmds},
{VSH_CMD_GRP_VIRSH, "virsh", virshCmds},
{NULL, NULL, NULL}
};
其中可见操作存储池的组,即 {VSH_CMD_GRP_STORAGE_POOL, "pool", storagePoolCmds}。
如上所述,其中的storagePoolCmds(第三个成员) 即指向操作存储池的组,它定义在virsh-pool.c文件中,如下:
const vshCmdDef storagePoolCmds[] = {
{.name = "find-storage-pool-sources-as",
.handler = cmdPoolDiscoverSourcesAs,
.opts = opts_find_storage_pool_sources_as,
.info = info_find_storage_pool_sources_as,
.flags = 0
},
{.name = "find-storage-pool-sources",
.handler = cmdPoolDiscoverSources,
.opts = opts_find_storage_pool_sources,
.info = info_find_storage_pool_sources,
.flags = 0
},
{.name = "pool-autostart",
.handler = cmdPoolAutostart,
.opts = opts_pool_autostart,
.info = info_pool_autostart,
.flags = 0
},
{.name = "pool-build",
.handler = cmdPoolBuild,
.opts = opts_pool_build,
.info = info_pool_build,
.flags = 0
},
{.name = "pool-create-as",
.handler = cmdPoolCreateAs,
.opts = opts_pool_X_as,
.info = info_pool_create_as,
.flags = 0
},
{.name = "pool-create",
.handler = cmdPoolCreate,
.opts = opts_pool_create,
.info = info_pool_create,
.flags = 0
},
{.name = "pool-define-as",
.handler = cmdPoolDefineAs,
.opts = opts_pool_X_as,
.info = info_pool_define_as,
.flags = 0
},
{.name = "pool-define",
.handler = cmdPoolDefine,
.opts = opts_pool_define,
.info = info_pool_define,
.flags = 0
},
{.name = "pool-delete",
.handler = cmdPoolDelete,
.opts = opts_pool_delete,
.info = info_pool_delete,
.flags = 0
},
{.name = "pool-destroy",
.handler = cmdPoolDestroy,
.opts = opts_pool_destroy,
.info = info_pool_destroy,
.flags = 0
},
{.name = "pool-dumpxml",
.handler = cmdPoolDumpXML,
.opts = opts_pool_dumpxml,
.info = info_pool_dumpxml,
.flags = 0
},
{.name = "pool-edit",
.handler = cmdPoolEdit,
.opts = opts_pool_edit,
.info = info_pool_edit,
.flags = 0
},
{.name = "pool-info",
.handler = cmdPoolInfo,
.opts = opts_pool_info,
.info = info_pool_info,
.flags = 0
},
{.name = "pool-list",
.handler = cmdPoolList,
.opts = opts_pool_list,
.info = info_pool_list,
.flags = 0
},
{.name = "pool-name",
.handler = cmdPoolName,
.opts = opts_pool_name,
.info = info_pool_name,
.flags = 0
},
{.name = "pool-refresh",
.handler = cmdPoolRefresh,
.opts = opts_pool_refresh,
.info = info_pool_refresh,
.flags = 0
},
{.name = "pool-start",
.handler = cmdPoolStart,
.opts = opts_pool_start,
.info = info_pool_start,
.flags = 0
},
{.name = "pool-undefine",
.handler = cmdPoolUndefine,
.opts = opts_pool_undefine,
.info = info_pool_undefine,
.flags = 0
},
{.name = "pool-uuid",
.handler = cmdPoolUuid,
.opts = opts_pool_uuid,
.info = info_pool_uuid,
.flags = 0
},
{.name = NULL}
};
其中含有pool-info ,并且对应的处理函数是cmdPoolInfo() .
第二部分 :
virsh pool-info vg10 命令在gdb 下调试获得的流程如下:
main()
vshParseArgv(ctl, argc, argv)
vshCommandArgvParse(ctl, argc - optind, argv + optind);
vshCommandParse(ctl, &parser);
vshCmddefSearch(tkdata)
vshCommandRun(ctl, ctl->cmd);
其中的函数vshCmddefSearch定义如下:
vshCmddefSearch(const char *cmdname)
{
const vshCmdGrp *g;
const vshCmdDef *c;
for (g = cmdGroups; g->name; g++) {
for (c = g->commands; c->name; c++) {
if (STREQ(c->name, cmdname))
return c;
}
}
return NULL;
}
过程解析: pool-info作为命令名传到各级函数中,最后在vshCmddefSearch()中,首先定义了const vshCmdDef *c; 然后通过两个for循环遍历第一部分中定义的所有组和组中所有的命令,将每一个遍历到的命令赋值给c,需要注意的是:此时c就包含的下面结构体中的所有成员:
struct _vshCmdDef {
const char *name; /* name of command, or NULL for list end */
bool (*handler) (vshControl *, const vshCmd *); /* command handler */
const vshCmdOptDef *opts; /* definition of command options */
const vshCmdInfo *info; /* details about command */
unsigned int flags; /* bitwise OR of VSH_CMD_FLAG */
};
接下来 if (STREQ(c->name, cmdname)) 语句很简单,如果当前遍历到的命令c 的第一个成员(即 名字)与命令名pool-info一样 ,则返回c ,由此即返回了处理此命令的handler .
回到main函数,即执行
vshCommandRun(ctl, ctl->cmd);
其中的ctl->cmd即是返回的命令。接下来vshCommandRun(ctl, ctl->cmd);中调用下面的语句进行具体的处理。
cmd->def->handler(ctl, cmd);
至此,过程结束。