qemu内存访问分析

qemu-kvm 设备虚拟化----I/O 端口和 I/O 内存 qemu-kvm 设备虚拟化----I/O 端口和 I/O 内存 操作设备存在两种接口:I/O 端口和 I/O 内存,下面分析虚拟机如何截获和模拟这两种情况的。 1.用户空间访问内核数据结构信息 内存映射可被实现来提供用户程序对设备内存的直接存取,KVM 内核代表每个 VCPU 的 struct kvm_run 数据结构被 mmap用户空间,从而用户空间可以读取 struct kvm 阅读详情
qemu  TLB表项:

       qemu 内存访问过程:guest virtual addr (GVA) → guest physical addr (GPA) → host virtualaddr (HVA)。其中 GVA->HVA 由 qemu 负责完成,HVA->HPA 由 host 操作系统完成。tlb 的结构如下,addr_xxx 表示 GVA 地址,同时也表示了执行权限;addrend =gpa_base – gva_base;

typedef struct CPUTLBEntry {
target_ulong addr_read; // 可读
target_ulong addr_write; // 可写
target_ulong addr_code; // 可执行
unsigned long addend;

} CPUTLBEntry;
    1.get_page_addr_code 会首先查看 tlb 是否命中,如果没有命中就 ldub_code 走 mmu 翻译这个分支,否则直接获取 hva。
313 /* NOTE: this function can trigger an exception */
314 /* NOTE2: the returned address is not exactly the physical address: it
315    is the offset relative to phys_ram_base */
316 tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
317 {   
318     int mmu_idx, page_index, pd;
319     void *p;
320     MemoryRegion *mr;
321                                       
322     page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
323     mmu_idx = cpu_mmu_index(env1);    
324     if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code !=
325                  (addr & TARGET_PAGE_MASK))) {
326 #ifdef CONFIG_TCG_PASS_AREG0
327         cpu_ldub_code(env1, addr);
328 #else
329         ldub_code(addr);
330 #endif
331     }
332     pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK;
333     mr = iotlb_to_region(pd);
334     if (memory_region_is_unassigned(mr)) {
335 #if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC)
336         cpu_unassigned_access(env1, addr, 0, 1, 0, 4);
337 #else
338         cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x"
339                   TARGET_FMT_lx "\n", addr);
340 #endif
341     }
342     p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend);
343     return qemu_ram_addr_from_host_nofail(p);
344 }
2.TLB 没有命中时,会通过 ldub_code,这个函数是由下面的宏产生。这个宏首先会在 tlb 检查下是
否命中,如果命中直接从 hva 地址中返回,否则还是通过 mmu 来获取。

 95 static inline RES_TYPE
 96 glue(glue(glue(CPU_PREFIX, ld), USUFFIX), MEMSUFFIX)(ENV_PARAM
 97                                                      target_ulong ptr)
 98 {
 99     int page_index;
100     RES_TYPE res;
101     target_ulong addr;
102     int mmu_idx;
103 
104     addr = ptr;
105     page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
106     mmu_idx = CPU_MMU_INDEX;
107     if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
108                  (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
109         res = glue(glue(glue(HELPER_PREFIX, ld), SUFFIX), MMUSUFFIX)(ENV_VAR
110                                                                      addr,
111                                                                      mmu_idx);
112     } else {
113         uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
114         res = glue(glue(ld, USUFFIX), _raw)(hostaddr);
115     }
116     return res;
117 }
3.在这个模板中会对 tlb 进行查询,看是否命中,如果命中,还要根据是 io 还是 ram 进行分别处理;
如果没有命中,则需要通过 mmu 获取该虚拟地址所对应的物理地址,对 tlb 进行填充。
106 DATA_TYPE
107 glue(glue(glue(HELPER_PREFIX, ld), SUFFIX), MMUSUFFIX)(ENV_PARAM
108                                                        target_ulong addr,
109                                                        int mmu_idx)
110 {
111     DATA_TYPE res;
112     int index;
113     target_ulong tlb_addr;
114     target_phys_addr_t ioaddr;
115     uintptr_t retaddr;
116 
117     /* test if there is match for unaligned or IO access */
118     /* XXX: could done more in memory macro in a non portable way */
119     index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
120  redo:
121     tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
122     if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
123         if (tlb_addr & ~TARGET_PAGE_MASK) {
124             /* IO access */
125             if ((addr & (DATA_SIZE - 1)) != 0)
126                 goto do_unaligned_access;
127             retaddr = GETPC();
128             ioaddr = env->iotlb[mmu_idx][index];
129             res = glue(io_read, SUFFIX)(ENV_VAR ioaddr, addr, retaddr);
130         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
131             /* slow unaligned access (it spans two pages or IO) */
132         do_unaligned_access:
133             retaddr = GETPC();
134 #ifdef ALIGNED_ONLY
135             do_unaligned_access(ENV_VAR addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
136 #endif
137             res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(ENV_VAR addr,
138                                                          mmu_idx, retaddr);
139         } else {
140             /* unaligned/aligned access in the same page */
141             uintptr_t addend;
142 #ifdef ALIGNED_ONLY
143             if ((addr & (DATA_SIZE - 1)) != 0) {
144                 retaddr = GETPC();
145                 do_unaligned_access(ENV_VAR addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
146             }
147 #endif
148             addend = env->tlb_table[mmu_idx][index].addend;
149             res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(intptr_t)
150                                                 (addr + addend));
151         }
152     } else {
153         /* the page is not in the TLB : fill it */
154         retaddr = GETPC();
155 #ifdef ALIGNED_ONLY
156         if ((addr & (DATA_SIZE - 1)) != 0)
157             do_unaligned_access(ENV_VAR addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
158 #endif
159         tlb_fill(env, addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
160         goto redo;
161     }
162     return res;
163 }
4.以 arm 为例,tlb_fill 会通过 cpu_arm_handle_mmu_fault 对虚实地址转换进行处理,如果该虚拟
地址没有对应的物理地址或者权限不够等情况,cpu 就会出现 page_fault 异常。

 72 /* try to fill the TLB and return an exception if error. If retaddr is
 73    NULL, it means that the function was called in C code (i.e. not
 74    from generated code or from helper.c) */
 75 /* XXX: fix it to restore all registers */
 76 void tlb_fill(CPUARMState *env1, target_ulong addr, int is_write, int mmu_idx,
 77               uintptr_t retaddr)
 78 {
 79     TranslationBlock *tb;
 80     CPUARMState *saved_env;
 81     int ret;
 82 
 83     saved_env = env;
 84     env = env1;
 85     ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx);
 86     if (unlikely(ret)) {
 87         if (retaddr) {                 
 88             /* now we have a real cpu fault */
 89             tb = tb_find_pc(retaddr);  
 90             if (tb) {
 91                 /* the PC is inside the translated code. It means that we have
 92                    a virtual CPU fault */
 93                 cpu_restore_state(tb, env, retaddr);
 94             }
 95         }
 96         raise_exception(env->exception_index);
 97     }
 98     env = saved_env;
 99 }
5. cpu_arm_handle_mmu_fault 里面主要是 page_walk,检查是否存在对应的物理地址和权限。如
果有,更新 tlb;否则,保存出错信息。

2122 int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address,
2123                               int access_type, int mmu_idx)
2124 {
2125     uint32_t phys_addr;
2126     target_ulong page_size;
2127     int prot;
2128     int ret, is_user;
2129        
2130     is_user = mmu_idx == MMU_USER_IDX;
2131     ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot,
2132                         &page_size);
2133     if (ret == 0) {
2134         /* Map a single [sub]page.  */
2135         phys_addr &= ~(uint32_t)0x3ff;
2136         address &= ~(uint32_t)0x3ff; 
2137         tlb_set_page (env, address, phys_addr, prot, mmu_idx, page_size);
2138         return 0;
2139     }
2140    
2141     if (access_type == 2) {
2142         env->cp15.c5_insn = ret;
2143         env->cp15.c6_insn = address;
2144         env->exception_index = EXCP_PREFETCH_ABORT;
2145     } else {
2146         env->cp15.c5_data = ret;
2147         if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6))
2148             env->cp15.c5_data |= (1 << 11);
2149         env->cp15.c6_data = address;
2150         env->exception_index = EXCP_DATA_ABORT;
2151     }
2152     return 1;
2153 }
        qemu 的内存访问模拟,qemu 并没有将内存访问用 IR 表示,这样一方面会更加翻译难度,同时也占
用大量的 code_buffer。Qemu 使用了外调函数来实现这个功能。在对 target 翻译过程中,对于内存访问操
作会生成如下形式的中间码:

tmp = gen_ld16s(addr, IS_USER(s));
783 static inline TCGv gen_ld16s(TCGv addr, int index)
784 {
785 TCGv tmp = tcg_temp_new_i32();
786 tcg_gen_qemu_ld16s(tmp, addr, index);
787 return tmp;
788 }
接下来在 tcg 翻译将中间码翻译成 host 机器码时,会首先查询 tlb,如果命中就直接返回结果;否则就调用
以下 qemu_ld/st_helpers 中的函数进行 target 的虚拟地址和物理地址转换的工作。

928 #ifdef CONFIG_SOFTMMU
929
930 #include "../../softmmu_defs.h"
931
932 #ifdef CONFIG_TCG_PASS_AREG0
933 /* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr,
934 int mmu_idx) */
935 static const void * const qemu_ld_helpers[4] = {
936 helper_ldb_mmu,
937 helper_ldw_mmu,
938 helper_ldl_mmu,
939 helper_ldq_mmu,
940 };
941
942 /* helper signature: helper_st_mmu(CPUState *env, target_ulong addr,
943 uintxx_t val, int mmu_idx) */
944 static const void * const qemu_st_helpers[4] = {
945 helper_stb_mmu,
946 helper_stw_mmu,
947 helper_stl_mmu,
948 helper_stq_mmu,
949 };
950 #else
951 /* legacy helper signature: __ld_mmu(target_ulong addr, int
952 mmu_idx) */
953 static void *qemu_ld_helpers[4] = {
954 __ldb_mmu,
955 __ldw_mmu,
956 __ldl_mmu,
957 __ldq_mmu,
958 };
959
960 /* legacy helper signature: __st_mmu(target_ulong addr, uintxx_t val,
961 int mmu_idx) */
962 static void *qemu_st_helpers[4] = {
963 __stb_mmu,
964 __stw_mmu,
965 __stl_mmu,
966 __stq_mmu,
967 };
968 #endif
969 #endif

 971 #define TLB_SHIFT       (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS)
 972 
 973 static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
 974 {
 975     int addr_reg, data_reg, data_reg2, bswap;
 976 #ifdef CONFIG_SOFTMMU
 977     int mem_index, s_bits;
 978 # if TARGET_LONG_BITS == 64
 979     int addr_reg2;
 980 # endif
 981     uint32_t *label_ptr;
 982 #endif
 983 
 984 #ifdef TARGET_WORDS_BIGENDIAN
 985     bswap = 1;
 986 #else
 987     bswap = 0;
 988 #endif
 989     data_reg = *args++;
 990     if (opc == 3)
 991         data_reg2 = *args++;
 992     else
 993         data_reg2 = 0; /* suppress warning */
 994     addr_reg = *args++;
 995 #ifdef CONFIG_SOFTMMU
 996 # if TARGET_LONG_BITS == 64
 997     addr_reg2 = *args++;
 998 # endif
 999     mem_index = *args;
1000     s_bits = opc & 3;
1001 
1002     /* Should generate something like the following:
1003      *  shr r8, addr_reg, #TARGET_PAGE_BITS
1004      *  and r0, r8, #(CPU_TLB_SIZE - 1)   @ Assumption: CPU_TLB_BITS <= 8
1005      *  add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS
1006      */
1007 #  if CPU_TLB_BITS > 8
1008 #   error
1009 #  endif
1010     tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_R8,
1011                     0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS));
1012     tcg_out_dat_imm(s, COND_AL, ARITH_AND,
1013                     TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1);
1014     tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_AREG0,
1015                     TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS));
1016     /* In the
1017      *  ldr r1 [r0, #(offsetof(CPUArchState, tlb_table[mem_index][0].addr_read))]
1018      * below, the offset is likely to exceed 12 bits if mem_index != 0 and
1019      * not exceed otherwise, so use an
1020      *  add r0, r0, #(mem_index * sizeof *CPUArchState.tlb_table)
1021      * before.
1022      */
1023     if (mem_index)
1024         tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0,
1025                         (mem_index << (TLB_SHIFT & 1)) |
1026                         ((16 - (TLB_SHIFT >> 1)) << 8));
1027     tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0,
1028                     offsetof(CPUArchState, tlb_table[0][0].addr_read));
1029     tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1,
1030                     TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS));
1031     /* Check alignment.  */
1032     if (s_bits)
1033         tcg_out_dat_imm(s, COND_EQ, ARITH_TST,
1034                         0, addr_reg, (1 << s_bits) - 1);
1035 #  if TARGET_LONG_BITS == 64
1036     /* XXX: possibly we could use a block data load or writeback in
1037      * the first access.  */
1038     tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0,
1039                     offsetof(CPUArchState, tlb_table[0][0].addr_read) + 4);
1040     tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0,
1041                     TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0));
1042 #  endif
1043     tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0,
1044                     offsetof(CPUArchState, tlb_table[0][0].addend));
1045 
1046     switch (opc) {
1047     case 0:
1048         tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
1049         break;
1050     case 0 | 4:
1051         tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
1052         break;
1053     case 1:
1054         tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
1055         if (bswap) {
1056             tcg_out_bswap16(s, COND_EQ, data_reg, data_reg);
1057         }
1058         break;
1059     case 1 | 4:
1060         if (bswap) {
1061             tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
1062             tcg_out_bswap16s(s, COND_EQ, data_reg, data_reg);
1063         } else {
1064             tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
1065         }
1066         break;
1067     case 2:
1068     default:
1069         tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
1070         if (bswap) {
1071             tcg_out_bswap32(s, COND_EQ, data_reg, data_reg);
1072         }
1073         break;
1074     case 3:
1075         if (bswap) {
1076             tcg_out_ld32_rwb(s, COND_EQ, data_reg2, TCG_REG_R1, addr_reg);
1077             tcg_out_ld32_12(s, COND_EQ, data_reg, TCG_REG_R1, 4);
1078             tcg_out_bswap32(s, COND_EQ, data_reg2, data_reg2);
1079             tcg_out_bswap32(s, COND_EQ, data_reg, data_reg);
1080         } else {
1081             tcg_out_ld32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg);
1082             tcg_out_ld32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4);
1083         }
1084         break;
1085     }
1086 
1087     label_ptr = (void *) s->code_ptr;
1088     tcg_out_b_noaddr(s, COND_EQ);
1089 
1090     /* TODO: move this code to where the constants pool will be */
1091     if (addr_reg != TCG_REG_R0) {
1092         tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1093                         TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0));
1094     }
1095 # if TARGET_LONG_BITS == 32
1096     tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R1, 0, mem_index);
1097 # else
1098     tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1099                     TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0));
1100     tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index);
1101 # endif
1102 #ifdef CONFIG_TCG_PASS_AREG0
1103     /* XXX/FIXME: suboptimal and incorrect for 64 bit */
1104     tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1105                     tcg_target_call_iarg_regs[2], 0,
1106                     tcg_target_call_iarg_regs[1], SHIFT_IMM_LSL(0));
1107     tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1108                     tcg_target_call_iarg_regs[1], 0,
1109                     tcg_target_call_iarg_regs[0], SHIFT_IMM_LSL(0));
1110 
1111     tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1112                     tcg_target_call_iarg_regs[0], 0, TCG_AREG0,
1113                     SHIFT_IMM_LSL(0));
1114 #endif
1115     tcg_out_call(s, (tcg_target_long) qemu_ld_helpers[s_bits]);
1116 
1117     switch (opc) {
1118     case 0 | 4:
1119         tcg_out_ext8s(s, COND_AL, data_reg, TCG_REG_R0);
1120         break;
1121     case 1 | 4:
1122         tcg_out_ext16s(s, COND_AL, data_reg, TCG_REG_R0);
1123         break;
1124     case 0:
1125     case 1:
1126     case 2:
1127     default:
1128         if (data_reg != TCG_REG_R0) {
1129             tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1130                             data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0));
1131         }
1132         break;
1133     case 3:
1134         if (data_reg != TCG_REG_R0) {
1135             tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1136                             data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0));
1137         }
1138         if (data_reg2 != TCG_REG_R1) {
1139             tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
1140                             data_reg2, 0, TCG_REG_R1, SHIFT_IMM_LSL(0));
1141         }
1142         break;
1143     }
1144 
1145     reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr);
1146 #else /* !CONFIG_SOFTMMU */
1147     if (GUEST_BASE) {
1148         uint32_t offset = GUEST_BASE;
1149         int i;
1150         int rot;
1151 
1152         while (offset) {
1153             i = ctz32(offset) & ~1;
1154             rot = ((32 - i) << 7) & 0xf00;
1155 
1156             tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R8, addr_reg,
1157                             ((offset >> i) & 0xff) | rot);
1158             addr_reg = TCG_REG_R8;
1159             offset &= ~(0xff << i);
1160         }
1161     }
1162     switch (opc) {
1163     case 0:
1164         tcg_out_ld8_12(s, COND_AL, data_reg, addr_reg, 0);
1165         break;
1166     case 0 | 4:
1167         tcg_out_ld8s_8(s, COND_AL, data_reg, addr_reg, 0);
1168         break;
1169     case 1:
1170         tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0);
1171         if (bswap) {
1172             tcg_out_bswap16(s, COND_AL, data_reg, data_reg);
1173         }
1174         break;
1175     case 1 | 4:
1176         if (bswap) {
1177             tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0);
1178             tcg_out_bswap16s(s, COND_AL, data_reg, data_reg);
1179         } else {
1180             tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0);
1181         }
1182         break;
1183     case 2:
1184     default:
1185         tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0);
1186         if (bswap) {
1187             tcg_out_bswap32(s, COND_AL, data_reg, data_reg);
1188         }
1189         break;
1190     case 3:
1191         /* TODO: use block load -
1192          * check that data_reg2 > data_reg or the other way */
1193         if (data_reg == addr_reg) {
1194             tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4);
1195             tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0);
1196         } else {
1197             tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0);
1198             tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4);
1199         }
1200         if (bswap) {
1201             tcg_out_bswap32(s, COND_AL, data_reg, data_reg);
1202             tcg_out_bswap32(s, COND_AL, data_reg2, data_reg2);
1203         }
1204         break;
1205     }
1206 #endif
1207 }


       上面的内存访问中将查找tlb部分直接翻译成了host指令,而对于target 的mmu转换则使用了外调相送的函数来实现的。这样对提升速度是有好处的。







QEMU 模拟 ARM 与 VMware 虚拟 x86:3大性能瓶颈实测与选型指南 本文深入对比了QEMU模拟ARM架构与VMware虚拟x86架构的性能差异,通过系统启动时间、磁盘I/O性能和CPU计算能力三大维度的实测数据,揭示了QEMU跨架构模拟的三大性能瓶颈。文章为技术决策者提供了详细的选型指南和优化建议,帮助在不同场景下选择最适合的虚拟化方案,特别适用于需要在WIN10环境下进行跨架构开发和测试的技术团队。 阅读详情

相关推荐

Android跨架构跑分实测:QEMU vs Houdini,谁才是效率王者?

本文深入对比了Android跨架构运行的两大技术方案QEMU与Houdini的性能表现。通过p7zip跑分测试显示,Houdini在x86→ARM转换中效率达82.3%,显著优于QEMU的26.8%,揭示了不同场景下的最佳效率选择方案。

weixin_30439067的博客 333

QEMU 6.2 源代码分析之 [ 6 ] —— MMU 虚拟化

接着,再通过 section.mr,得到MemoryRegion, 再通过 mr->ram_block,得到 ram_block,再 ram_block->host + 0x2a68000,而 ram_block->host 是0x7fa793e00000,所以最终得到的HVA就是( 0x7fa793e00000 + 0x2a68000 )成功时返回 true。否则,如果正在探测,返回 false。假设已经知道了 GPA 地址,接下来,又该如何读写到里面的数据呢?

weixin_43082062的博客 776

qemu内存模型(6)mm实现(一)实模式

前边分析了qemu对内存的建模, 整个过程有三种内存地址 gpa, hva, gva, 在qemu中 如何表现这三种内存呢, 首先qemu把所有的ram片段(由MemoryRegion生成的RamBlock)平坦的铺开,串联起来, 放在 ram_list 链表里面, 用于寻址ram, 这个平坦的ram地址用ram_addr_t表示(这里的ram不光指ram还要mmio和rom地址), hva呢用u...

woai110120130的专栏 2458

qemu-tcg -s2e mmu学习笔记2

声明:本文纯属自我学习记录,如果错误,请指正!谢谢!

luke_lx的博客 1302

qemu内存模型(7)地址空间的读写

对于tcg模式,读内存将执行accel/tcg/softmmu_template.h下的函数 WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, uintptr_t retaddr) # define helper_le_ld_na...

woai110120130的专栏 2362

qemu tcg mmp分析

tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) { int mmu_idx, index, pd; void *p; MemoryRegion *mr; CPUState *cpu = ENV_GET_CPU(env); CPUIOTLBEntry *iotlbe...

woai110120130的专栏 670

QEMU插件系统教程:如何使用TCG插件分析和优化虚拟机性能

前往项目官网免费下载:[https://ar.openeuler.org/ar/](https://ar.openeuler.org/ar/?utm_source=csdn_blog) QEMU作为一款强大的开源机器模拟器和虚拟化工具,其插件系统为开发者提供了扩展功能的灵活途径。本文将详细介绍如何利用TCG(Tiny Code Generator)插件来分析和优化虚拟机性能,帮助你深入了解虚拟机

gitblog_06832的博客 180

Ubuntu 上 1Panel 的安装与高效运维实践

本文详细介绍了在Ubuntu系统上安装和高效运维1Panel面板的完整实践指南。内容涵盖了一键安装、Docker容器可视化部署、计划任务自动化以及网站数据库管理等核心功能,并提供了安全加固、资源监控与数据备份等进阶技巧,旨在帮助用户快速搭建并管理一个强大易用的服务器环境。

weixin_29210759的博客 142

qemu的埋点技术

QEMU 的埋点技术(Tracing)是一种用于监控和分析 QEMU 虚拟机内部行为的机制。通过埋点,开发者可以收集 QEMU 执行过程中的各种事件信息,例如函数调用、内存访问、设备 I/O 等,从而帮助调试、性能分析和优化。QEMU 的埋点技术是一种强大的工具,可以帮助开发者深入理解虚拟机的内部行为。通过静态埋点、动态埋点和自定义埋点,可以灵活地监控和分析 QEMU 的执行过程。动态埋点允许在运行时动态添加或移除埋点,适用于需要灵活监控的场景。静态埋点通过 QEMU 的。QEMU 的埋点技术基于。

远方雪的专栏 1492

VS2022 中 std::mutex 静态初始化陷阱:一次诡异崩溃的排查记录

VS2022中使用std::mutex时遇到的静态初始化陷阱问题。该问题源于VS2022 17.4版本为std::mutex添加constexpr支持后,全局静态初始化可能导致系统资源未就绪时锁失效或崩溃。文章分析了问题现象、影响范围,并提供了解决方案。最后给出了排查清单和核心建议

qq_34552942的博客 951

嵌入式开发:用状态机实现STM32按键消抖,告别阻塞延时

在嵌入式系统开发中,处理异步事件和实时响应是核心挑战。状态机作为一种强大的编程模型,通过定义有限的状态、事件和状态转换规则,将复杂的、非线性的控制逻辑转化为清晰、确定且易于维护的代码结构。其技术价值在于实现逻辑的模块化、异步化和非阻塞化,能显著提升代码的可读性、可扩展性和系统实时性。这一思想广泛应用于协议解析、用户界面交互、传感器数据处理等场景。本文以嵌入式开发中基础的按键处理为例,针对传统“延时消抖”方法会阻塞主循环、影响实时性的痛点,深入讲解如何运用状态机思维重构按键消抖逻辑。通过分析状态机的核心三要素

weixin_33377602的博客 339

QEMU RISCV TCG 详解九 -- 初见SoftMMU (软内存管理器)

Guest Virtual Address -> Guest Physical Address -> Host Virtual Address。

sinat_36821938的博客 1146

通讯软件013——分分钟学会Kepware OPC AE Server仿真配置

本文介绍如何使用Kepware软件仿真OPC AE Server配置。

zhouwl72的博客 2226

qemu_kvm 内存相关

一、qemu中物理内存的注册 cpu_register_physical_memory -->cpu_notify_set_memory ---->kvm_client_set_memory ------>kvm_set_phys_mem -------->kvm_set_user_memory_region ---------->kvm_vm_ioctl(进入内核) 内核中会调用k

wenjieky的专栏 1074

DisplayPort 1.4数据扰码实战:16bit LFSR并行优化技巧(附Verilog代码)

本文深入探讨了DisplayPort 1.4协议中16bit数据扰码的并行化实现。针对高带宽视频传输需求,文章详细推导了16位LFSR(线性反馈移位寄存器)的单周期16步并行计算公式,并提供了可直接使用的Verilog代码,解决了传统8bit串行处理吞吐量不足的瓶颈,为高速接口设计提供了关键优化技巧。

y4z5a6b7的博客 258

Handler相关面试题

常见面试题: Handler相关 一.谈一谈你对handler的认识,例如:机制和实现等。 handler机制图解: handler常见使用过程: ①.子线程向主线程发送消息: 在主线程中新建Handler实例,并重写handleMessage()方法用于处理子线程传过来的message; 通过Message.obtain()方法获取到消息msg,然后将需要传递给主线程的信息通过msg的what、obj等字段传递; 通过handler的sendMessage发送msg消息,然后在hand

龚礼鹏的博客 3170

1.1Qemu 用户态架构

本节首先分析Qemu的初始化的顶层流程;从而引出Qemu各大功能模块的描述; 最后分析Qemu与内核态KVM的通讯接口。

wanthelping的博客 6446

九天揽月带你玩转Ardupilot 的EKF2纸老虎(1)

目录 目录 摘要 1.kalman基础知识储备 摘要 本文主要记录自己学习ardupilot的ekf2代码的过程,相信很多人想移植或者学习ekf2,看到眼花缭乱的代码无从下手。九天揽月将带你玩转Ardupilot 的EKF2纸老虎 1.kalman基础知识储备 (1)先来看下最基础也是最重要的线性kalman 总结:不要害怕kalman,只要你可以懂得线性kal...

陌城烟雨 1万+

qemu cracks when shutdown vm

如上图,Qemu创建的XP虚拟机,用spicec连接到该虚拟机的桌面,关闭该虚拟机时,Qemu的主线程异常。(qemu版本:1.3.1,spice版本:0.12.5) 用gdb 查看crash dump文件,发现是非法访问内存地址0x100000000导致,'Cannot access memory at address 0x100000000',该地址已经超过进程可以访问的范围 根据

somyjun的专栏 812

华为OD最新机试真题-池化资源共享

摘要:该问题要求管理n台设备的资源池,处理资源申请和释放操作。申请操作需返回满足条件且剩余资源最少的设备ID,释放操作需将指定申请的资源归还。采用线段树数据结构维护各设备当前最大可用资源值,支持快速查询和更新。线段树实现了单点修改、区间最大值查询以及查找最左满足条件的节点功能。算法时间复杂度为O((n+m)logn),空间复杂度O(n),适用于大规模操作。提供了C++、Java和Python三种实现方案。

面试高手的博客 324

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Linux虚拟化KVM-Qemu分析(五)之内存虚拟化

Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化》非虚拟化下的内存的访问CPU访问物理内存前,需要先建立页表映射(虚拟地址到物理地址的映射),最终通过查表的方式来完成访问。在ARMv8中,内核页表基地址存放在TTBR1_EL1中,用户空间页表基地址存放在TTBR0_EL0中;虚拟化下的内存访问虚拟化情况下,内存的访问会分为两个StageHypervisor通过Stage 2来控制虚拟机的内存视图,控制虚拟机是否可以访问某块物理内存,进而达到隔离的目的;Stage 1。

生活需要深度 1560

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

QIRA时间旅行调试器:逆向分析与漏洞挖掘的终极利器

逆向分析是安全研究、漏洞挖掘和软件理解的核心技术,它通过剖析二进制程序的执行逻辑与数据流,揭示其内部工作机制。传统动态调试工具存在状态易失、操作割裂等痛点,难以高效追踪复杂执行路径。时间旅行调试技术通过记录程序执行的完整轨迹,实现了执行历史的无损回溯与任意时间点的状态分析,极大提升了逆向工程与漏洞分析的效率。QIRA作为一款革命性的时间旅行调试器,基于QEMU模拟器实现全量执行记录,提供直观的Web界面进行动态分析与数据追溯,广泛应用于CTF竞赛、恶意软件分析和软件漏洞挖掘等场景。

weixin_42557803的博客 933

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

wpf在page中如何切换page_关于QEMU中对EPT实现机制

首先开宗明义:在QEMU中,当processor进入VM环境,对基于EPT机制的内存虚拟化是完全支持的,当VM中的GuestOS在执行内存访问操作时,会通过EPT-page-structure将linear-address先转换成guest-physical-address,然后再将guest-physical-address转换成实际的物理地址physical-address;得到了最终的实际物...

weixin_39688750的博客 293

QEMU测试框架使用指南:如何编写和运行QEMU测试用例

前往项目官网免费下载:[https://ar.openeuler.org/ar/](https://ar.openeuler.org/ar/?utm_source=csdn_blog) QEMU作为一款功能强大的开源机器模拟器和虚拟化工具,拥有完善的测试框架来确保代码质量和功能稳定性。本文将为您详细介绍QEMU测试框架的完整使用指南,帮助您快速掌握如何编写和运行各种类型的QEMU测试用例。无论您

gitblog_06861的博客 266

qemu + GDB 如何单步调试内核

在阅读 linux 内核源码的过程中,我们经常希望能单步运行调试内核函数的运行,通过 systemtap 或者 bpf 等技术可以窥探到一些细节但还是不如单步来得直接,这篇文章我们来看看 qemu + GDB 如何单步调试内核,当我们对进程管理、内存管理、文件系统的实现有什么困惑就可以动手 debug 一下,实际出真知。QEMU (Quick Emulator) 是大神 Fabrice Bellard 开源的一款功能强大的开源虚拟化软件,可以在一台计算机上模拟运行另一个操作系统。

一个时代的落幕,一个时代的开启。 29
上一篇: qemu执行流程分析
下一篇: gdb 使用
Xalloc
博客等级 码龄16年 25粉丝 55原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值