Study KVM internal 2
The C Preprocessor
- เพื่อให้ทำความเข้าใจ Code ของ KVM ได้ดียิ่งขึ้น จึงศึกษาเกี่ยวกับ Preprocessor เพิ่มเติม ที่ The C Preprocessor ยกตัวอย่างบางส่วนที่น่าสนใจและมีอยู่ใน Code ของ KVM คือ Macros ซึ่งมีสัญลักษณ์แปลก ๆ เช่น ## ที่ปรากฏอยู่ในหัวข้อ Concatenation
- เพื่อให้เข้าใจถึงกลไกของ Compiler ในการจัดการกับ Preprocessor ให้มากขึ้น จึงทดลองเขียนโปรแกรมที่มี Preprocessor อยู่ใน Code และทดลองใช้ Compiler ทำการ Compile แล้วดูผลลัพธ์ที่ได้จาก Preprocessor
- เขียนโปรแกรมภาษา C ง่าย ๆ

- ทำการ Compile โดยใช้คำสั่ง gcc -E เพื่อให้ Compiler ทำงานเฉพาะในส่วนของ Preprocessor ได้ผลดังภาพ

- สังเกตผลที่ได้ และ นำความรู้ในส่วนนี้ ไปประยุกต์ใช้ในการศึกษา Code ของ KVM ในหัวข้อต่อ ๆ ไป
Study block_init()
- ศึกษา block_init() จากไฟล์ module.h
16 ...
17 /* This should not be used directly. Use block_init etc. instead. */
18 #define module_init(function, type)
19 static void __attribute__((constructor)) do_qemu_init_ ## function(void) {
20 register_module_init(function, type);
21 }
22
23 typedef enum {
24 MODULE_INIT_BLOCK,
25 MODULE_INIT_DEVICE,
26 MODULE_INIT_MACHINE,
27 MODULE_INIT_MAX
28 } module_init_type;
29
30 #define block_init(function) module_init(function, MODULE_INIT_BLOCK)
31 #define device_init(function) module_init(function, MODULE_INIT_DEVICE)
32 #define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
33
34 void register_module_init(void (*fn)(void), module_init_type type);
35 ...
- เขียนโปรแกรมอย่างง่าย โดยทำการ #include “module.h” เข้าไปด้วย บันทึกชื่อ block_init.c
- เรียกใช้ block_init(bdrv_qcow2_init) และ block_init(bdrv_raw_init)

- ใช้ Compiler ตรวจสอบการทำงานของ Preprocessor ด้วยคำสั่ง gcc -E block_init.c

- ผลที่ได้พบว่าเมื่อเรียกใช้ block_init(bdrv_qcow2_init) จะถูกเปลี่ยนเป็น
static void __attribute__((constructor)) do_qemu_init_bdrv_qcow2_init(void) {
register_module_init(bdrv_qcow2_init, MODULE_INIT_BLOCK);
};
- และ block_init(bdrv_raw_init) ก็เช่นกัน
static void __attribute__((constructor)) do_qemu_init_bdrv_raw_init(void) {
register_module_init(bdrv_raw_init, MODULE_INIT_BLOCK);
};
- ศึกษาเกี่ยวกับ Program Library Howto ในส่วนของ ((constructor)) พบว่า ฟังก์ชันที่ได้จาก Preprocessor ดังกล่าว อันได้แก่ do_qemu_init_bdrv_qcow2_init() และ do_qemu_init_bdrv_raw_init() จะทำงานในขณะโปรแกรมกำลังโหลดเข้าสู่ระบบ นั่นคือ ก่อนที่ dlopen() จะ return หรือก่อนเข้าฟังก์ชัน main()
- เพื่อให้เข้าใจมากขึ้นจึงเขียนโปรแกรม ที่มีการเรียกใช้ ((constructor))

- ทำการ Compile และ Run พบว่า ฟังก์ชัน hello() ทำงานก่อน main()

Study bdrv_open() and bdrv_close()
- ศึกษา Data Structure ที่เกี่ยวข้องกับฟังก์ชัน bdrv_open() และ bdrv_close() มีดังนี้
...
356 int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
357 BlockDriver *drv)
...
...
507 void bdrv_close(BlockDriverState *bs)
...
- มี 4 ตัว แต่ที่โครงสร้างค่อนข้างซับซ้อนมี 2 ตัว ได้แก่ BlockDriverState และ BlockDriver
- ศึกษา BlockDriverState จาก block_int.h

125
126 struct BlockDriverState {
127 int64_t total_sectors; /* if we are reading a disk image, give its
128 size in sectors */
129 int read_only; /* if true, the media is read only */
130 int removable; /* if true, the media can be removed */
131 int locked; /* if true, the media cannot temporarily be ejected */
132 int encrypted; /* if true, the media is encrypted */
133 int valid_key; /* if true, a valid encryption key has been set */
134 int sg; /* if true, the device is a /dev/sg* */
135 /* event callback when inserting/removing */
136 void (*change_cb)(void *opaque);
137 void *change_opaque;
138
139 BlockDriver *drv; /* NULL means no media */
140 void *opaque;
141
142 char filename[1024];
143 char backing_file[1024]; /* if non zero, the image is a diff of
144 this file image */
145 char backing_format[16]; /* if non-zero and backing_file exists */
146 int is_temporary;
147 int media_changed;
148
149 BlockDriverState *backing_hd;
150 /* async read/write emulation */
151
152 void *sync_aiocb;
153
154 /* I/O stats (display with "info blockstats"). */
155 uint64_t rd_bytes;
156 uint64_t wr_bytes;
157 uint64_t rd_ops;
158 uint64_t wr_ops;
159
160 /* Whether the disk can expand beyond total_sectors */
161 int growable;
162
163 /* the memory alignment required for the buffers handled by this driver
164 int buffer_alignment;
165
166 /* do we need to tell the quest if we have a volatile write cache? */
167 int enable_write_cache;
168
169 /* NOTE: the following infos are only hints for real hardware
170 drivers. They are not used by the block driver */
171 int cyls, heads, secs, translation;
172 int type;
173 char device_name[32];
174 unsigned long *dirty_bitmap;
175 BlockDriverState *next;
176 void *private;
177 };
178
- และศึกษา BlockDriver
47
48 struct BlockDriver {
49 const char *format_name;
50 int instance_size;
51 int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename
52 int (*bdrv_probe_device)(const char *filename);
53 int (*bdrv_open)(BlockDriverState *bs, const char *filename, int flags);
54 int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
55 uint8_t *buf, int nb_sectors);
56 int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
57 const uint8_t *buf, int nb_sectors);
58 void (*bdrv_close)(BlockDriverState *bs);
59 int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
60 void (*bdrv_flush)(BlockDriverState *bs);
61 int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
62 int nb_sectors, int *pnum);
63 int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
64 int (*bdrv_make_empty)(BlockDriverState *bs);
65 /* aio */
66 BlockDriverAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs,
67 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
68 BlockDriverCompletionFunc *cb, void *opaque);
69 BlockDriverAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs,
70 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
71 BlockDriverCompletionFunc *cb, void *opaque);
72 BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs,
73 BlockDriverCompletionFunc *cb, void *opaque);
74
75 int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs,
76 int num_reqs);
77 int (*bdrv_merge_requests)(BlockDriverState *bs, BlockRequest* a,
78 BlockRequest *b);
79
80
81 const char *protocol_name;
82 int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
83 int64_t (*bdrv_getlength)(BlockDriverState *bs);
84 int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
85 const uint8_t *buf, int nb_sectors);
86
87 int (*bdrv_snapshot_create)(BlockDriverState *bs,
88 QEMUSnapshotInfo *sn_info);
89 int (*bdrv_snapshot_goto)(BlockDriverState *bs,
90 const char *snapshot_id);
91 int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_i
92 int (*bdrv_snapshot_list)(BlockDriverState *bs,
93 QEMUSnapshotInfo **psn_info);
94 int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
95
96 int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
97 int64_t pos, int size);
98 int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf,
99 int64_t pos, int size);
100
101 /* removable device specific */
102 int (*bdrv_is_inserted)(BlockDriverState *bs);
103 int (*bdrv_media_changed)(BlockDriverState *bs);
104 int (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
105 int (*bdrv_set_locked)(BlockDriverState *bs, int locked);
106
107 /* to control generic scsi devices */
108 int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf
109 BlockDriverAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs,
110 unsigned long int req, void *buf,
111 BlockDriverCompletionFunc *cb, void *opaque);
112
113 /* List of options for creating images, terminated by name == NULL */
114 QEMUOptionParameter *create_options;
115
116
117 /* Returns number of errors in image, -errno for internal errors */
118 int (*bdrv_check)(BlockDriverState* bs);
119
120 /* Set if newly created images are not guaranteed to contain only zeros
121 int no_zero_init;
122
123 struct BlockDriver *next;
124 };
125
- สังเกต ตัวแปรที่ขีดเส้นใต้ จะเห็นได้ว่าเป็น Pointer และส่วนใหญ่เป็น Pointer ที่ชี้หา Data Type ชนิดเดียวกัน ดังนั้น Data Structure จึงเป็นไปในลักษณะ Linked List โดย BlockDriver และ BlockDriverState มีความสัมพันธ์กันดังนี้
+----+----------------------+----+
| | | |
*backing_hd | | | | *next
... <-----------------o | BlockDriverState | o-----------------> ...
| | | |
+----+----------------------+----+
| o |
+----------------|---------------+
| *drv
|
V
+----------------------+----+
| | |
| BlockDriver | | *next
| | o-----------------> ...
| | |
+----------------------+----+
- Find out ‘WHEN” and “WHERE” the bdrv_open() function is called (!!! กำลังดำเนินการ ยังไม่แล้วเสร็จ !!!)
- Draw the function call graph for the bdrv_open() function (!!! กำลังดำเนินการ ยังไม่แล้วเสร็จ !!!)
- Describe what happen to the data structure in 2) when the bdrv_open() function is called to open an overlay hard disk image (!!! กำลังดำเนินการ ยังไม่แล้วเสร็จ !!!)
- ศึกษา bdrv_open() โดยเพิ่ม Code ในฟังก์ชัน bdrv_open2() ดังนี้
356 int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
357 BlockDriver *drv)
358 {
359 // Add by Phithak Thaenkaew
360 int phithak_seq = rand() % 10000; // Sequence number
361 printf("Enter bdrv_open2(), Seq %04d, Flag %2d, Filename: %s\n",
362 phithak_seq, flags, filename);
363
364 printf(" Before --\n");
365 printf(" filename: %s\n", bs->filename);
366 printf(" backing_file: %s\n", bs->backing_file);
367 printf(" backing_format: %s\n", bs->backing_format);
368 printf(" read_only: %d\n", bs->read_only);
369 printf(" removable: %d\n", bs->removable);
370
371 printf(" *drv: %s\n", (bs->drv==NULL)?"NULL":"NOT NULL");
372 if (!(bs->drv==NULL)) {
373 printf(" ->format_name: %s\n", bs->drv->format_name);
374 printf(" ->protocol_name: %s\n", bs->drv->protocol_name);
375 }
376
377 printf(" *backing_hd: %s\n", (bs->backing_hd==NULL)?"NULL":"NOT NULL");
378 if (!(bs->backing_hd==NULL)) {
379 printf(" ->filename: %s\n", bs->backing_hd->filename);
380 printf(" ->backing_file: %s\n", bs->backing_hd->backing_file);
381 printf(" ->backing_format: %s\n", bs->backing_hd->backing_format);
382 printf(" ->read_only: %d\n", bs->backing_hd->read_only);
383 printf(" ->removable: %d\n", bs->backing_hd->removable);
384 }
385
386 printf(" *next: %s\n", (bs->next==NULL)?"NULL":"NOT NULL");
387 if (!(bs->next==NULL)) {
388 printf(" ->filename: %s\n", bs->next->filename);
389 printf(" ->backing_file: %s\n", bs->next->backing_file);
390 printf(" ->backing_format: %s\n", bs->next->backing_format);
391 printf(" ->read_only: %d\n", bs->next->read_only);
392 printf(" ->removable: %d\n", bs->next->removable);
393 }
...
...
...
540 printf(" After -- \n");
541 printf(" filename: %s\n", bs->filename);
542 printf(" backing_file: %s\n", bs->backing_file);
543 printf(" backing_format: %s\n", bs->backing_format);
544 printf(" read_only: %d\n", bs->read_only);
545 printf(" removable: %d\n", bs->removable);
546
547 printf(" *drv: %s\n", (bs->drv==NULL)?"NULL":"NOT NULL");
548 if (!(bs->drv==NULL)) {
549 printf(" ->format_name: %s\n", bs->drv->format_name);
550 printf(" ->protocol_name: %s\n", bs->drv->protocol_name);
551 }
552
553 printf(" *backing_hd: %s\n", (bs->backing_hd==NULL)?"NULL":"NOT NULL");
554 if (!(bs->backing_hd==NULL)) {
555 printf(" ->filename: %s\n", bs->backing_hd->filename);
556 printf(" ->backing_file: %s\n", bs->backing_hd->backing_file);
557 printf(" ->backing_format: %s\n", bs->backing_hd->backing_format);
558 printf(" ->read_only: %d\n", bs->backing_hd->read_only);
559 printf(" ->removable: %d\n", bs->backing_hd->removable);
560 }
561
562 printf(" *next: %s\n", (bs->next==NULL)?"NULL":"NOT NULL");
563 if (!(bs->next==NULL)) {
564 printf(" ->filename: %s\n", bs->next->filename);
565 printf(" ->backing_file: %s\n", bs->next->backing_file);
566 printf(" ->backing_format: %s\n", bs->next->backing_format);
567 printf(" ->read_only: %d\n", bs->next->read_only);
568 printf(" ->removable: %d\n", bs->next->removable);
569 }
570 // Add by Phithak
571 printf(" Exit bdrv_open2(), Seq %04d, Flag %2d, Filename: %s\n",
572 phithak_seq, flags, filename);
573 printf("----------------------------------------------------------\n");
574
575 return 0;
576 }
- ผลที่ได้จากจากเรียกใช้โปรแกรมที่ทำการแก้ไข โดยกำหนดให้ใช้ centos_ovl2.img คือ
phithak@phithak-laptop hw04 $ qemu-system-x86_64 -hda centos_ovl2.img -boot c -m 256
Enter bdrv_open2(), Seq 9383, Flag 0, Filename: centos_ovl2.img
Before --
filename:
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NULL
*backing_hd: NULL
*next: NULL
Enter bdrv_open2(), Seq 0886, Flag 16, Filename: centos_ovl2.img
Before --
filename:
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NULL
*backing_hd: NULL
*next: NULL
After --
filename: centos_ovl2.img
backing_file:
backing_format:
read_only: 1
removable: 0
*drv: NOT NULL
->format_name: raw
->protocol_name: (null)
*backing_hd: NULL
*next: NULL
Exit bdrv_open2(), Seq 0886, Flag 16, Filename: centos_ovl2.img
----------------------------------------------------------
Enter bdrv_open2(), Seq 2777, Flag 18, Filename: centos_ovl2.img
Before --
filename:
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NULL
*backing_hd: NULL
*next: NULL
After --
filename: centos_ovl2.img
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NOT NULL
->format_name: raw
->protocol_name: (null)
*backing_hd: NULL
*next: NULL
Exit bdrv_open2(), Seq 2777, Flag 18, Filename: centos_ovl2.img
----------------------------------------------------------
Enter bdrv_open2(), Seq 6915, Flag 2, Filename: centos_base.img
Before --
filename:
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NULL
*backing_hd: NULL
*next: NULL
Enter bdrv_open2(), Seq 7793, Flag 16, Filename: centos_base.img
Before --
filename:
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NULL
*backing_hd: NULL
*next: NULL
After --
filename: centos_base.img
backing_file:
backing_format:
read_only: 1
removable: 0
*drv: NOT NULL
->format_name: raw
->protocol_name: (null)
*backing_hd: NULL
*next: NULL
Exit bdrv_open2(), Seq 7793, Flag 16, Filename: centos_base.img
----------------------------------------------------------
Enter bdrv_open2(), Seq 8335, Flag 18, Filename: centos_base.img
Before --
filename:
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NULL
*backing_hd: NULL
*next: NULL
After --
filename: centos_base.img
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NOT NULL
->format_name: raw
->protocol_name: (null)
*backing_hd: NULL
*next: NULL
Exit bdrv_open2(), Seq 8335, Flag 18, Filename: centos_base.img
----------------------------------------------------------
After --
filename: centos_base.img
backing_file:
backing_format:
read_only: 0
removable: 0
*drv: NOT NULL
->format_name: qcow2
->protocol_name: (null)
*backing_hd: NULL
*next: NULL
Exit bdrv_open2(), Seq 6915, Flag 2, Filename: centos_base.img
----------------------------------------------------------
After --
filename: centos_ovl2.img
backing_file: centos_base.img
backing_format:
read_only: 0
removable: 0
*drv: NOT NULL
->format_name: qcow2
->protocol_name: (null)
*backing_hd: NOT NULL
->filename: centos_base.img
->backing_file:
->backing_format:
->read_only: 0
->removable: 0
*next: NULL
Exit bdrv_open2(), Seq 9383, Flag 0, Filename: centos_ovl2.img
----------------------------------------------------------
Tools
References
– http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html
– http://www.faqs.org/docs/Linux-HOWTO/Program-Library-HOWTO.html#INIT-AND-CLEANUP
Mirror Site
- http://phithak.com/study-kvm-internal-2
- http://phithak.wordpress.com/2010/08/26/study-kvm-internal-2/
(ที่มา: การบ้าน หัวข้อ CS797 Exercise 6 – Study KVM internal 2 วิชา CS797 Advanced Topics in Computer Science – Introduction to Computer Virtualization ภาคเรียนที่ 1/2553 อาจารย์ผู้สอน ดร.กษิดิศ ชาญเชี่ยว ภาควิชาวิทยาการคอมพิวเตอร์ คณะวิทยาศาสตร์และเทคโนโลยี มหาวิทยาลัยธรรมศาสตร์)