kvmtool 学习(一)——初始化

kvmtool 学习(一)——初始化

前言

参考 使用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;
...
}

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值