ImageVerifierCode 换一换
格式:DOCX , 页数:18 ,大小:22.99KB ,
资源ID:10138540      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/10138540.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(虚拟化.docx)为本站会员(b****7)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

虚拟化.docx

1、虚拟化目录-一、使用gdb分析QEMU代码二、参数解析用到的数据结构QEMU链表Error和QErrorGMainLoop三、QEMUOption、QemuOpt及QEMU参数解析一、使用gdb分析QEMU代码使用gdb不仅可以很好地调试代码,也可以利用它来动态地分析代码。使用gdb调试QEMU需要做一些准备工作:1, 编译QEMU时需要在执行configure脚本时的参数中加入enable-debug。2, 从QEMU官方网站上下载一个精简的镜像linux-0.2.img。linux-0.2.img只有8MB大小,启动后包含一些常用的shell命令,用于QEMU的测试。$wget http:

2、/wiki.qemu.org/download/linux-0.2.img.bz2$bzip2 -d ./linux-0.2.img.bz23, 启动gdb调试QEMU:gdb -args qemu-system-x86_64 -enable-kvm -m 4096 -smp 4 linux-0.2.img-smp指定处理器个数。二、参数解析用到的数据结构QEMU系统模拟的主函数位于vl.c文件,无论是qemu-system-x86_64还是qemu-system-ppc64,都是从vl.c中的main函数开始执行。下面先介绍main函数涉及到的一些数据结构。QEMU链表QEMU的链表在inc

3、lude/qemu/queue.h文件中定义,分为四种类型: 单链表(singly-linked list):单链表适用于大的数据集,并且很少有删除节点或者移动节点的操作,也适用于实现后进先出的队列。 链表(list):即双向链表,除了头节点之外每个节点都会同时指向前一个节点和后一个节点。 简单队列(simple queue):简单队列类似于单链表,只是多了一个指向链表尾的一个表头,插入节点的时候不仅可以像单链表那样将其插入表头或者某节点之后,还可以插入到链表尾。 尾队列(tail queue):类似于简单队列,但节点之间是双向指向的。这里不一一介绍各种链表的用法,只通过NotifierLis

4、t的定义来说明QEMU链表(list)的用法。在main函数的开头定义的DisplayState结构体使用到了NotifiereList,NotifierList就用到了链表。a. 表头及节点的定义定义表头需要用到QLIST_HEAD,定义如下: 86 #define QLIST_HEAD(name, type) 87 struct name 88 struct type *lh_first; /* first element */ 89 NotifierList就采用了QLIST_HEAD来定义表头: 27 typedef struct NotifierList 28 29 QLIST_HE

5、AD(, Notifier) notifiers; 30 NotifierList;定义节点需要用到QLIST_ENTRY,定义如下: 94 #define QLIST_ENTRY(type) 95 struct 96 struct type *le_next; /* next element */ 97 struct type *le_prev; /* address of previous next element */ 98 Notifier的节点定义如下: 21 struct Notifier 22 23 void (*notify)(Notifier *notifier, void

6、*data); 24 QLIST_ENTRY(Notifier) node; 25 ;b. 初始化表头初始化表头用到QLIST_INIT:103 #define QLIST_INIT(head) do 104 (head)-lh_first = NULL; 105 while (/*CONSTCOND*/0)初始化NotifierList就可以这样进行: 19 void notifier_list_init(NotifierList *list) 20 21 QLIST_INIT(&list-notifiers); 22 c. 在表头插入节点将节点插入到表头使用QLIST_INSERT_HEA

7、D:122 #define QLIST_INSERT_HEAD(head, elm, field) do 123 if (elm)-field.le_next = (head)-lh_first) != NULL) 124 (head)-lh_first-field.le_prev = &(elm)-field.le_next;125 (head)-lh_first = (elm); 126 (elm)-field.le_prev = &(head)-lh_first; 127 while (/*CONSTCOND*/0)插入Notifier到NotifierList: 24 void not

8、ifier_list_add(NotifierList *list, Notifier *notifier) 25 26 QLIST_INSERT_HEAD(&list-notifiers, notifier, node); 27 d. 遍历节点遍历节点使用QLIST_FOREACH或者QLIST_FOREACH_SAFE,QLIST_FOREACH_SAFE是为了防止遍历过程中删除了节点,从而导致le_next被释放掉,中断了遍历。147 #define QLIST_FOREACH(var, head, field) 148 for (var) = (head)-lh_first); 149

9、 (var); 150 (var) = (var)-field.le_next)151 152 #define QLIST_FOREACH_SAFE(var, head, field, next_var) 153 for (var) = (head)-lh_first); 154 (var) & (next_var) = (var)-field.le_next), 1); 155 (var) = (next_var)NotifierList在执行所有的回调函数时就用到了QLIST_FOREACH_SAFE: 34 void notifier_list_notify(NotifierList *

10、list, void *data) 35 36 Notifier *notifier, *next; 37 38 QLIST_FOREACH_SAFE(notifier, &list-notifiers, node, next) 39 notifier-notify(notifier, data); 40 41 Error和QError为了方便的处理错误信息,QEMU定义了Error和QError两个数据结构。Error在qobject/qerror.c中定义:101 struct Error102 103 char *msg;104 ErrorClass err_class;105 ;包含了

11、错误消息字符串和枚举类型的错误类别。错误类别有下面几个: 139 typedef enum ErrorClass 140 141 ERROR_CLASS_GENERIC_ERROR = 0, 142 ERROR_CLASS_COMMAND_NOT_FOUND = 1, 143 ERROR_CLASS_DEVICE_ENCRYPTED = 2, 144 ERROR_CLASS_DEVICE_NOT_ACTIVE = 3, 145 ERROR_CLASS_DEVICE_NOT_FOUND = 4, 146 ERROR_CLASS_K_V_M_MISSING_CAP = 5, 147 ERROR_C

12、LASS_MAX = 6, 148 ErrorClass;QEMU在util/error.c中定义了几个函数来对Error进行操作:error_set /根据给定的ErrorClass以及格式化字符串来给Error分配空间并赋值error_set_errno /除了error_set的功能外,将指定errno的错误信息追加到格式化字符串的后面error_copy /复制Error error_is_set /判断Error是否已经分配并设置error_get_class /获取Error的ErrorClass error_get_pretty /获取Error的msgerror_free /释

13、放Error及msg的空间另外,QEMU定义了QError来处理更为细致的错误信息: 22 typedef struct QError 23 QObject_HEAD; 24 Location loc; 25 char *err_msg; 26 ErrorClass err_class; 27 QError;QError可以通过一系列的宏来给err_msg及err_class赋值: 39 #define QERR_ADD_CLIENT_FAILED 40 ERROR_CLASS_GENERIC_ERROR, Could not add client 41 42 #define QERR_AMB

14、IGUOUS_PATH 43 ERROR_CLASS_GENERIC_ERROR, Path %s does not uniquely identify an object 44 45 #define QERR_BAD_BUS_FOR_DEVICE 46 ERROR_CLASS_GENERIC_ERROR, Device %s cant go on a %s bus 47 48 #define QERR_BASE_NOT_FOUND 49 ERROR_CLASS_GENERIC_ERROR, Base %s not found.Location记录了出错的位置,定义如下: 20 typedef

15、 struct Location 21 /* all members are private to qemu-error.c */ 22 enum LOC_NONE, LOC_CMDLINE, LOC_FILE kind; 23 int num; 24 const void *ptr; 25 struct Location *prev; 26 Location;GMainLoopQEMU使用glib中的GMainLoop来实现IO多路复用,关于GMainLoop可以参考博客GMainLoop的实现原理和代码模型。由于GMainLoop并非QEMU本身的代码,本文就不重复赘述。三、QEMUOpt

16、ion、QemuOpt及QEMU参数解析QEMU定义了QEMUOption来表示执行qemu-system-x86_64等命令时用到的选项。在vl.c中定义如下:2123 typedef struct QEMUOption 2124 const char *name; /选项名,如 -device, name的值就是device2125 int flags; /标志位,表示选项是否带参数,可以是0,或者HAS_ARG(值为0x0001)2126 int index; /枚举类型的值,如-device,该值就是QEMU_OPTION_device2127 uint32_t arch_mask;

17、/ 选项支持架构的掩码2128 QEMUOption;vl.c中维护了一个QEMUOption数组qemu_options来存储所有可用的选项,并利用qemu-options-wrapper.h和qemu-options.def来给该数组赋值。赋值语句如下:2130 static const QEMUOption qemu_options = 2131 h, 0, QEMU_OPTION_h, QEMU_ARCH_ALL ,2132 #define QEMU_OPTIONS_GENERATE_OPTIONS2133 #include qemu-options-wrapper.h2134 NUL

18、L ,2135 ;#define QEMU_OPTIONS_GENERATE_OPTIONS选择qemu-options-wrapper.h的操作,qemu-options-wrapper.h可以进行三种操作:QEMU_OPTIONS_GENERATE_ENUM: 利用qemu-options.def生成一个枚举值列表,就是上面提到的QEMU_OPTION_device等QEMU_OPTIONS_GENERATE_HELP: 利用qemu-options.def生成帮助信息并输出到标准输出QEMU_OPTIONS_GENERATE_OPTIONS: 利用qemu-options.def生成一组

19、选项列表可以通过下面的方法来展开qemu-options-wrapper.h来查看上述操作的结果,以生成选项为例。1 在qemu-options-wrapper.h第一行写入#define QEMU_OPTIONS_GENERATE_OPTIONS.2 执行命令gcc -E -o options.txt qemu-options-wrapper.h3 查看文件options.txt即可给qemu_options数组赋值后,QEMU就有了一个所有可用选项的集合。之后在vl.c中main函数的一个for循环根据这个集合开始解析命令行。for循环的框架大致如下: 1 for(;) 2 if (opt

20、ind = argc) 3 break; 4 if (argvoptind0 != -) 5 hda_opts = drive_add(IF_DEFAULT, 0, argvoptind+, HD_OPTS); 6 else 7 const QEMUOption *popt; 8 9 popt = lookup_opt(argc, argv, &optarg, &optind); 10 if (!(popt-arch_mask & arch_type) 11 printf(Option %s not supported for this targetn, popt-name); 12 exit

21、(1); 13 14 switch(popt-index) 15 case QEMU_OPTION_M: 16 . 17 case QEMU_OPTION_hda: 18 . 19 case QEMU_OPTION_watchdog: 20 . 21 default: 22 os_parse_cmd_args(popt-index, optarg); 23 24 25 QEMU会把argv中以-开头的字符串当作选项,然后调用lookup_opt函数到qemu_options数组中查找该选项,如果查找到的选项中flags的值是HAS_ARG,lookup_opt也会将参数字符串赋值给optarg

22、。找到选项和参数之后,QEMU便根据选项中的index枚举值来执行不同的分支。对于一些开关性质的选项,分支执行时仅仅是把相关的标志位赋值而已,如:3712 case QEMU_OPTION_old_param:3713 old_param = 1;3714 break;也有一些选项没有子选项,分支执行时就直接把optarg的值交给相关变量:3822 case QEMU_OPTION_qtest:3823 qtest_chrdev = optarg;3824 break;对于那些拥有子选项的选项,如”-drive if=none,id=DRIVE-ID”,QEMU的处理会更为复杂一些。它会调用q

23、emu_opts_parse来解析子选项,如realtime选项的解析:3852 case QEMU_OPTION_realtime:3853 opts = qemu_opts_parse(qemu_find_opts(realtime), optarg, 0);3854 if (!opts) 3855 exit(1);3856 3857 configure_realtime(opts);3858 break;对子选项的解析涉及到4个数据结构:QemuOpt, QemuDesc, QemuOpts, QemuOptsList. 它们的关系如下图所示:QemuOpt存储子选项,每个QemuOpt

24、有一个QemuOptDesc来描述该子选项名字、类型、及帮助信息。两个结构体定义如下: 32 struct QemuOpt 33 const char *name; /子选项的名字 34 const char *str; /字符串值 35 36 const QemuOptDesc *desc; 37 union 38 bool boolean; /布尔值 39 uint64_t uint; /数字或者大小 40 value; 41 42 QemuOpts *opts; 43 QTAILQ_ENTRY(QemuOpt) next; 44 ; 95 typedef struct QemuOptDe

25、sc 96 const char *name; 97 enum QemuOptType type; 98 const char *help; 99 QemuOptDesc;子选项的类型可以是: 88 enum QemuOptType 89 QEMU_OPT_STRING = 0, / 字符串 90 QEMU_OPT_BOOL, / 取值可以是on或者off 91 QEMU_OPT_NUMBER, / 数字 92 QEMU_OPT_SIZE, / 大小,可以有K, M, G, T等后缀 93 ;QEMU维护了一个QemuOptsList*的数组,在util/qemu-config.c中定义:10

26、 static QemuOptsList *vm_config_groups32;在main函数中由qemu_add_opts将各种QemuOptsList写入到数组中:2944 qemu_add_opts(&qemu_drive_opts);2945 qemu_add_opts(&qemu_chardev_opts);2946 qemu_add_opts(&qemu_device_opts); 2947 qemu_add_opts(&qemu_netdev_opts);2948 qemu_add_opts(&qemu_net_opts);2949 qemu_add_opts(&qemu_rt

27、c_opts);2950 qemu_add_opts(&qemu_global_opts);2951 qemu_add_opts(&qemu_mon_opts);2952 qemu_add_opts(&qemu_trace_opts);2953 qemu_add_opts(&qemu_option_rom_opts);2954 qemu_add_opts(&qemu_machine_opts);2955 qemu_add_opts(&qemu_smp_opts);2956 qemu_add_opts(&qemu_boot_opts);2957 qemu_add_opts(&qemu_sandb

28、ox_opts);2958 qemu_add_opts(&qemu_add_fd_opts);2959 qemu_add_opts(&qemu_object_opts);2960 qemu_add_opts(&qemu_tpmdev_opts);2961 qemu_add_opts(&qemu_realtime_opts);2962 qemu_add_opts(&qemu_msg_opts);每个QemuOptsList存储了大选项所支持的所有小选项,如-realtime大选项QemuOptsList的定义: 507 static QemuOptsList qemu_realtime_opts

29、 = 508 .name = realtime, 509 .head = QTAILQ_HEAD_INITIALIZER(qemu_realtime_opts.head), 510 .desc = 511 512 .name = mlock, 513 .type = QEMU_OPT_BOOL, 514 , 515 /* end of list */ 516 , 517 ;-realtime只支持1个子选项,且值为bool类型,即只能是on或者off。在调用qemu_opts_parse解析子选项之前,QEMU会调用qemu_find_opts(“realtime”),把QemuOptsLis

30、t *从qemu_add_opts中找出来,和optarg一起传递给qemu_opts_parse去解析。QEMU可能会多次使用同一个大选项来指定多个相同的设备,在这种情况下,需要用id来区分。QemuOpts结构体就表示同一id下所有的子选项,定义如下: 46 struct QemuOpts 47 char *id; 48 QemuOptsList *list; 49 Location loc; 50 QTAILQ_HEAD(QemuOptHead, QemuOpt) head; 51 QTAILQ_ENTRY(QemuOpts) next; 52 ;其中list是同一个大选项下不同id的QemuOpts链表。如果调试qemu,gdb是一个不错的工具,跟踪研究qemu的工作机制,以及查看问题,都是很不错的。

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1