前言
参考 使用kvmtool启动一台最小虚拟机
./lkvm run -k bzImage -i root_fs.cpio -m 2048
源码阅读工具: source insight 4.0
main
kvm-tools是一个普通的应用程序,所以入口是main函数
// main.c
static int handle_kvm_command(int argc, char **argv){
return handle_command(kvm_commands, argc, (const char **) &argv[0]);
}
int main(int argc, char *argv[]){
// kvm__set_dir("%s/%s", getenv("HOME"), "/.lkvm/");
kvm__set_dir("%s/%s", HOME_DIR, KVM_PID_FILE_PATH);
return handle_kvm_command(argc - 1, &argv[1]); // 默认main argv[0] 文件路径
}
kvm 命令
// kvm-cmd.c
struct cmd_struct kvm_commands[] = {
{ "pause", kvm_cmd_pause, kvm_pause_help, 0 },
{ "resume", kvm_cmd_resume, kvm_resume_help, 0 },
{ "debug", kvm_cmd_debug, kvm_debug_help, 0 },
{ "balloon", kvm_cmd_balloon, kvm_balloon_help, 0 },
{ "list", kvm_cmd_list, kvm_list_help, 0 },
{ "version", kvm_cmd_version, NULL, 0 },
{ "--version", kvm_cmd_version, NULL, 0 },
{ "stop", kvm_cmd_stop, kvm_stop_help, 0 },
{ "stat", kvm_cmd_stat, kvm_stat_help, 0 },
{ "help", kvm_cmd_help, NULL, 0 },
{ "setup", kvm_cmd_setup, kvm_setup_help, 0 },
{ "run", kvm_cmd_run, kvm_run_help, 0 }, // 运行一个虚拟机
{ "sandbox", kvm_cmd_sandbox, kvm_run_help, 0 },
{ NULL, NULL, NULL, 0 },
};
// include/kvm/kvm-cmd.h
struct cmd_struct {
const char *cmd; // 命令名字
int (*fn)(int, const char **, const char *); // 命令执行函数指针
void (*help)(void); // 命令帮助信息
int option; // 可选项
};
命令解析
// kvm-cmd.c
int handle_command(struct cmd_struct *command, int argc, const char **argv){
struct cmd_struct *p;
const char *prefix = NULL;
int ret = 0;
// 没有参数
if (!argv || !*argv) {
p = kvm_get_command(command, "help");
BUG_ON(!p);
return p->fn(argc, argv, prefix);
}
p = kvm_get_command(command, argv[0]);
if (!p) {
// 用户输入无效命令,打印帮助信息
p = kvm_get_command(command, "help");
BUG_ON(!p);
p->fn(0, NULL, prefix);
// EINVAL 是定义在 errno.h 中的一个宏,是错误代码的一个取值。表示 无效的参数,即为 invalid argument ,包括参数值、类型或数目无效等
return EINVAL;
}
ret = p->fn(argc - 1, &argv[1], prefix); // 执行相关函数
if (ret < 0) {
if (errno == EPERM)
die("Permission error - are you root?");
}
return ret;
}
struct cmd_struct *kvm_get_command(struct cmd_struct *command, const char *cmd){
struct cmd_struct *p = command;
while (p->cmd) {
if (!strcmp(p->cmd, cmd))
return p;
p++;
}
return NULL;
}
判断错误
BUG_ON:
// include/kvm/util.h
// 满足条件,构造故障,打印相关堆栈信息
#define BUG_ON(condition) BUG_ON_HANDLER((condition))
#ifndef BUG_ON_HANDLER
# define BUG_ON_HANDLER(condition) \
do { \
if ((condition)) { \
pr_err("BUG at %s:%d", __FILE__, __LINE__); \
raise(SIGABRT); \
} \
} while (0)
#endif
指针检查
linux驱动IS_ERR_VALUE、IS_ERR
// inclued/kvm/err.h
static inline bool __must_check IS_ERR(__force const void *ptr){
return IS_ERR_VALUE((unsigned long)ptr);
}
#define MAX_ERRNO 4095 /* 取补码 F001 */
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) /*大于等于0xFFFFF001的指针为非法指针*/
kvm_cmd_run
// builtin-run.c
int kvm_cmd_run(int argc, const char **argv, const char *prefix){
// 传递后参数 (6, {-k bzImage -i root_fs.cpio -m 2048} , NULL)
int ret = -EFAULT; // /* Bad address */
struct kvm *kvm;
// 进行跑虚拟机前的初始化工作
kvm = kvm_cmd_run_init(argc, argv);
if (IS_ERR(kvm))
return PTR_ERR(kvm);
ret = kvm_cmd_run_work(kvm); // 跑guest
kvm_cmd_run_exit(kvm, ret); // 做清理工作
return ret;
}
static struct kvm *kvm_cmd_run_init(int argc, const char **argv)
{
static char default_name[20];
unsigned int nr_online_cpus;
// 配置需要保存在 kvm 结构体中传递给各个初始化模块。因此需要先构建一个 kvm
struct kvm *kvm = kvm__new();
if (IS_ERR(kvm)) // 判断指针是否合法
return kvm;
// sysconf 库函数用来获取系统执行的配置信息。_SC_NPROCESSORS_ONLN:当前可获得的处理器个数
nr_online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
kvm->cfg.custom_rootfs_name = "default";
while (argc != 0) {
BUILD_OPTIONS(options, &kvm->cfg, kvm); // 该宏定义 struct option options [] = {} 里面包含一些 kvm_config 选项配置说明
// 修改 options ,也修改kvm_config (&kvm->cfg)
argc = parse_options(argc, argv, options, run_usage,
PARSE_OPT_STOP_AT_NON_OPTION |
PARSE_OPT_KEEP_DASHDASH);
if (argc != 0) {
/* Cusrom options, should have been handled elsewhere */
if (strcmp(argv[0], "--") == 0) {
if (kvm_run_wrapper == KVM_RUN_SANDBOX) {
kvm->cfg.sandbox = DEFAULT_SANDBOX_FILENAME;
kvm_run_write_sandbox_cmd(kvm, argv+1, argc-1);
break;
}
}
if ((kvm_run_wrapper == KVM_RUN_DEFAULT && kvm->cfg.kernel_filename) ||
(kvm_run_wrapper == KVM_RUN_SANDBOX && kvm->cfg.sandbox)) {
fprintf(stderr, "Cannot handle parameter: %s\n", argv[0]);
usage_with_options(run_usage, options);
free(kvm);
return ERR_PTR(-EINVAL);
}
if (kvm_run_wrapper == KVM_RUN_SANDBOX) {
/* first unhandled parameter is treated as sandbox command */
kvm->cfg.sandbox = DEFAULT_SANDBOX_FILENAME;
kvm_run_write_sandbox_cmd(kvm, argv, argc);
} else {
/** first unhandled parameter is treated as a kernel image */
kvm->cfg.kernel_filename = argv[0];
}
argv++;
argc--;
}
}
kvm_run_validate_cfg(kvm);
.... // 用户没有相关配置选项,则使用默认选项
if (init_list__init(kvm) < 0)
die ("Initialisation failed");
return kvm;
}
struct kvm *kvm__new(void){
// calloc (size_t num, size_t size) 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0
struct kvm *kvm = calloc(1, sizeof(*kvm));
if (!kvm)
return ERR_PTR(-ENOMEM);
mutex_init(&kvm->mem_banks_lock);
// 将 sys_fd 和 vm_fd 初始化为 -1 即可。这两个 fd 是控制虚拟机的接口,必须初始化为无效值
kvm->sys_fd = -1;
kvm->vm_fd = -1;
...
}