【QEMU系統分析之實例篇(十七)】

系列文章目錄

第十七章 QEMU系統仿真的機器創建分析實例


文章目錄

  • 系列文章目錄
    • 第十七章 QEMU系統仿真的機器創建分析實例
  • 前言
  • 一、QEMU是什么?
  • 二、QEMU系統仿真的機器創建分析實例
    • 1.系統仿真的命令行參數
    • 2.配置加速器
      • configure_accelerators()
        • do_configure_icount()
        • 設置加速器順序
        • do_configure_accelerator()
    • 3.調試輸出
  • 總結


前言

本文以 QEMU 8.2.2 為例,分析其作為系統仿真工具的工作過程,并為讀者展示各種 QEMU 系統仿真的啟動配置實例。
本文讀者需要具備一定的 QEMU 系統仿真使用經驗,并對 C 語言編程有一定了解。


一、QEMU是什么?

QEMU 是一個通用且開源的機器模擬器和虛擬機。
其官方主頁是:https://www.qemu.org/


二、QEMU系統仿真的機器創建分析實例

1.系統仿真的命令行參數

QEMU 作為系統仿真工具,其入口代碼在 system/main.c 文件中,初始化函數 qemu_init() 的實現在 system/vl.c 文件中。
前文完成創建目標機器的過程分析,本文將繼續后續運行過程的分析,讀者需要對 QEMU 系統啟動過程的程序代碼有所了解,相關內容可以參考《QEMU系統分析之啟動篇》系列文章。

..\qemu\8.2.2-qkd\qemu-system-x86_64.exe -cpu "Penryn" -M  "q35,accel=whpx,smm=off" -m "6G" -display "sdl" -audio "sdl,model=hda" -vga "std" -L "data"

2.配置加速器

這部分代碼在 system/vl.c 文件中,實現如下:

int qemu_init(int argc, char **argv)
{
...configure_accelerators(argv[0]);
...
}

前文分析了應用當前機器設置項的過程,本文繼續完成加速器的配置過程。


configure_accelerators()

函數 configure_accelerators() 代碼如下:

static void configure_accelerators(const char *progname)
{bool init_failed = false;qemu_opts_foreach(qemu_find_opts("icount"),do_configure_icount, NULL, &error_fatal);if (QTAILQ_EMPTY(&qemu_accel_opts.head)) {char **accel_list, **tmp;if (accelerators == NULL) {/* Select the default accelerator */bool have_tcg = accel_find("tcg");bool have_kvm = accel_find("kvm");if (have_tcg && have_kvm) {if (g_str_has_suffix(progname, "kvm")) {/* If the program name ends with "kvm", we prefer KVM */accelerators = "kvm:tcg";} else {accelerators = "tcg:kvm";}} else if (have_kvm) {accelerators = "kvm";} else if (have_tcg) {accelerators = "tcg";} else {error_report("No accelerator selected and"" no default accelerator available");exit(1);}}accel_list = g_strsplit(accelerators, ":", 0);for (tmp = accel_list; *tmp; tmp++) {/** Filter invalid accelerators here, to prevent obscenities* such as "-machine accel=tcg,,thread=single".*/if (accel_find(*tmp)) {qemu_opts_parse_noisily(qemu_find_opts("accel"), *tmp, true);} else {init_failed = true;error_report("invalid accelerator %s", *tmp);}}g_strfreev(accel_list);} else {if (accelerators != NULL) {error_report("The -accel and \"-machine accel=\" options are incompatible");exit(1);}}if (!qemu_opts_foreach(qemu_find_opts("accel"),do_configure_accelerator, &init_failed, &error_fatal)) {if (!init_failed) {error_report("no accelerator found");}exit(1);}if (init_failed && !qtest_chrdev) {error_report("falling back to %s", current_accel_name());}if (icount_enabled() && !tcg_enabled()) {error_report("-icount is not allowed with hardware virtualization");exit(1);}
}

首先,如果命令行參數中有 “icount” 則調用函數 do_configure_icount() 做配置。


do_configure_icount()

代碼如下:

void icount_configure(QemuOpts *opts, Error **errp)
{const char *option = qemu_opt_get(opts, "shift");bool sleep = qemu_opt_get_bool(opts, "sleep", true);bool align = qemu_opt_get_bool(opts, "align", false);long time_shift = -1;if (!option) {if (qemu_opt_get(opts, "align") != NULL) {error_setg(errp, "Please specify shift option when using align");}return;}if (align && !sleep) {error_setg(errp, "align=on and sleep=off are incompatible");return;}if (strcmp(option, "auto") != 0) {if (qemu_strtol(option, NULL, 0, &time_shift) < 0|| time_shift < 0 || time_shift > MAX_ICOUNT_SHIFT) {error_setg(errp, "icount: Invalid shift value");return;}} else if (icount_align_option) {error_setg(errp, "shift=auto and align=on are incompatible");return;} else if (!icount_sleep) {error_setg(errp, "shift=auto and sleep=off are incompatible");return;}icount_sleep = sleep;if (icount_sleep) {timers_state.icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,icount_timer_cb, NULL);}icount_align_option = align;if (time_shift >= 0) {timers_state.icount_time_shift = time_shift;icount_enable_precise();return;}icount_enable_adaptive();/** 125MIPS seems a reasonable initial guess at the guest speed.* It will be corrected fairly quickly anyway.*/timers_state.icount_time_shift = 3;/** Have both realtime and virtual time triggers for speed adjustment.* The realtime trigger catches emulated time passing too slowly,* the virtual time trigger catches emulated time passing too fast.* Realtime triggers occur even when idle, so use them less frequently* than VM triggers.*/timers_state.vm_clock_warp_start = -1;timers_state.icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT,icount_adjust_rt, NULL);timer_mod(timers_state.icount_rt_timer,qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);timers_state.icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,icount_adjust_vm, NULL);timer_mod(timers_state.icount_vm_timer,qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +NANOSECONDS_PER_SECOND / 10);
}static int do_configure_icount(void *opaque, QemuOpts *opts, Error **errp)
{icount_configure(opts, errp);return 0;
}

設置加速器順序

代碼如下:

static void configure_accelerators(const char *progname)
{
...if (QTAILQ_EMPTY(&qemu_accel_opts.head)) {char **accel_list, **tmp;if (accelerators == NULL) {/* Select the default accelerator */bool have_tcg = accel_find("tcg");bool have_kvm = accel_find("kvm");if (have_tcg && have_kvm) {if (g_str_has_suffix(progname, "kvm")) {/* If the program name ends with "kvm", we prefer KVM */accelerators = "kvm:tcg";} else {accelerators = "tcg:kvm";}} else if (have_kvm) {accelerators = "kvm";} else if (have_tcg) {accelerators = "tcg";} else {error_report("No accelerator selected and"" no default accelerator available");exit(1);}}accel_list = g_strsplit(accelerators, ":", 0);for (tmp = accel_list; *tmp; tmp++) {/** Filter invalid accelerators here, to prevent obscenities* such as "-machine accel=tcg,,thread=single".*/if (accel_find(*tmp)) {qemu_opts_parse_noisily(qemu_find_opts("accel"), *tmp, true);} else {init_failed = true;error_report("invalid accelerator %s", *tmp);}}g_strfreev(accel_list);} else {if (accelerators != NULL) {error_report("The -accel and \"-machine accel=\" options are incompatible");exit(1);}}
...
}

如果程序名后綴是 kvm 則設置 KVM 加速器優先,設置完加速器順序后完成加速器的配置。
加速器配置通過函數 do_configure_accelerator() 來完成。


do_configure_accelerator()

代碼如下:

static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp)
{bool *p_init_failed = opaque;const char *acc = qemu_opt_get(opts, "accel");AccelClass *ac = accel_find(acc);AccelState *accel;int ret;bool qtest_with_kvm;if (!acc) {error_setg(errp, QERR_MISSING_PARAMETER, "accel");goto bad;}qtest_with_kvm = g_str_equal(acc, "kvm") && qtest_chrdev != NULL;if (!ac) {if (!qtest_with_kvm) {error_report("invalid accelerator %s", acc);}goto bad;}accel = ACCEL(object_new_with_class(OBJECT_CLASS(ac)));object_apply_compat_props(OBJECT(accel));qemu_opt_foreach(opts, accelerator_set_property,accel,&error_fatal);/** If legacy -singlestep option is set, honour it for TCG and* silently ignore for any other accelerator (which is how this* option has always behaved).*/if (opt_one_insn_per_tb) {/** This will always succeed for TCG, and we want to ignore* the error from trying to set a nonexistent property* on any other accelerator.*/object_property_set_bool(OBJECT(accel), "one-insn-per-tb", true, NULL);}ret = accel_init_machine(accel, current_machine);if (ret < 0) {if (!qtest_with_kvm || ret != -ENOENT) {error_report("failed to initialize %s: %s", acc, strerror(-ret));}goto bad;}return 1;bad:*p_init_failed = true;return 0;
}

3.調試輸出

首先,添加跟蹤調試信息,修改后的代碼如下:

static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp)
{
...accel = ACCEL(object_new_with_class(OBJECT_CLASS(ac)));HUEDBG("accel=0x%llx\n", (unsigned long long int)accel);huedbg_dump_AccelClass(ac, 9);
...
}static void configure_accelerators(const char *progname)
{
...HUEDBG("accelerators=[%s]\n", accelerators);accel_list = g_strsplit(accelerators, ":", 0);
...
}int accel_init_machine(AccelState *accel, MachineState *ms)
{AccelClass *acc = ACCEL_GET_CLASS(accel);int ret;HUEDBG("accel=0x%llx\n", (unsigned long long int)accel);ms->accelerator = accel;*(acc->allowed) = true;ret = acc->init_machine(ms);if (ret < 0) {HUEDBG("\n");ms->accelerator = NULL;*(acc->allowed) = false;object_unref(OBJECT(accel));} else {HUEDBG("\n");object_set_accelerator_compat_props(acc->compat_props);}huedbg_dump_AccelClass(acc, 9);return ret;
}int qemu_init(int argc, char **argv)
{
.../** Note: uses machine properties such as kernel-irqchip, must run* after qemu_apply_machine_options.*/huedbg_flag = 1;HUEDBG("\n");configure_accelerators(argv[0]);HUEDBG("\n");huedbg_flag = 0;phase_advance(PHASE_ACCEL_CREATED);
...
}

運行后,輸出信息如下:

[21096]../system/vl.c/qemu_init(2849):
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptsList(183):<<<deep>>>=[9] list=[00007ff7ee3825c0]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptsList(185):name=[drive]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptsList(186):implied_opt_name=[(null)]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptsList(187):merge_lists=[0]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptsList(188):head=[00007ff7ee3825d8]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptsList(192):desc=[00007ff7ee3825e8]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptDesc(160):<<<deep>>>=[8] desc=[00007ff7ee3825e8]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptDesc(162):name=[(null)]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptDesc(163):type=[0]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptType(116):<<<deep>>>=[7] idx=[0]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptType(127):[0]=QEMU_OPT_STRING
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptDesc(167):help=[(null)]
[21096]../util/huedbg-option.c/huedbg_dump_QemuOptDesc(168):def_value_str=[(null)]
[21096]../system/vl.c/qemu_init(2851):
[21096]../system/vl.c/qemu_init(3824):
[21096]../system/vl.c/configure_accelerators(2419):accelerators=[whpx]
[21096]../qom/object.c/object_class_by_name(1095):enter
[21096]../qom/object.c/type_table_lookup(103):lookup type(whpx-accel) in hash table
[21096]../qom/object.c/object_class_by_name(1105):class(whpx-accel) return
[21096]../qom/object.c/type_table_lookup(103):lookup type(accel) in hash table
[21096]../qom/object.c/type_get_parent(194):parent_type(accel)
[21096]../qom/object.c/object_class_by_name(1095):enter
[21096]../qom/object.c/type_table_lookup(103):lookup type(whpx-accel) in hash table
[21096]../qom/object.c/object_class_by_name(1105):class(whpx-accel) return
[21096]../qom/object.c/object_new_with_class(818):klass->type->name=[whpx-accel]
[21096]../qom/object.c/object_new_with_type(789):try type_initialize(whpx-accel)
[21096]../qom/object.c/object_new_with_type(799):obj(whpx-accel) alloc
[21096]../qom/object.c/object_new_with_type(808):try object_initialize_with_type(whpx-accel)
[21096]../qom/object.c/object_initialize_with_type(568):obj with type(whpx-accel) enter
[21096]../qom/object.c/object_initialize_with_type(576):mapping obj(whpx-accel).class with type(whpx-accel).class
[21096]../qom/object.c/object_initialize_with_type(579):try object_class_property_init_all(whpx-accel)
[21096]../qom/object.c/object_class_property_init_all(552):obj(whpx-accel) enter
[21096]../qom/object.c/object_class_property_iter_init(1440):objclass{whpx-accel} enter
[21096]../qom/object.c/object_class_get_parent(1130):enter
[21096]../qom/object.c/type_get_parent(194):parent_type(accel)
[21096]../qom/object.c/object_class_get_parent(1138):objclass(whpx-accel) has parent(accel)
[21096]../qom/object.c/object_class_get_parent(1141):objclass(whpx-accel) return
[21096]../qom/object.c/object_class_property_iter_init(1443):objclass{whpx-accel} return
[21096]../qom/object.c/object_class_property_init_all(555):prop name=[kernel-irqchip] type=[on|off|split] desc=[Configure WHPX in-kernel irqchip] init=[0000000000000000]
[21096]../qom/object.c/object_class_get_parent(1130):enter
[21096]../qom/object.c/type_get_parent(194):parent_type(object)
[21096]../qom/object.c/object_class_get_parent(1138):objclass(accel) has parent(object)
[21096]../qom/object.c/object_class_get_parent(1141):objclass(accel) return
[21096]../qom/object.c/object_class_get_parent(1130):enter
[21096]../qom/object.c/type_get_parent(196):no parent_type
[21096]../qom/object.c/object_class_get_parent(1134):objclass(object) has no parent return
[21096]../qom/object.c/object_class_property_init_all(555):prop name=[type] type=[string] desc=[(null)] init=[0000000000000000]
[21096]../qom/object.c/object_class_property_init_all(563):obj(whpx-accel) return
[21096]../qom/object.c/object_initialize_with_type(583):try object_init_with_type(whpx-accel)
[21096]../qom/object.c/object_init_with_type(416):obj->class->type->name=[whpx-accel] ti->name=[whpx-accel] enter
[21096]../qom/object.c/type_get_parent(194):parent_type(accel)
[21096]../qom/object.c/object_init_with_type(416):obj->class->type->name=[whpx-accel] ti->name=[accel] enter
[21096]../qom/object.c/type_get_parent(194):parent_type(object)
[21096]../qom/object.c/object_init_with_type(416):obj->class->type->name=[whpx-accel] ti->name=[object] enter
[21096]../qom/object.c/object_init_with_type(427):obj->class->type->name=[whpx-accel] ti->name=[object] return
[21096]../qom/object.c/object_init_with_type(427):obj->class->type->name=[whpx-accel] ti->name=[accel] return
[21096]../qom/object.c/object_init_with_type(423):name=[whpx-accel] ti->instance_init() before
[21096]../qom/object.c/object_init_with_type(425):name=[whpx-accel] ti->instance_init() after
[21096]../qom/object.c/object_init_with_type(427):obj->class->type->name=[whpx-accel] ti->name=[whpx-accel] return
[21096]../qom/object.c/object_initialize_with_type(585):try object_post_init_with_type(whpx-accel)
[21096]../qom/object.c/object_post_init_with_type(433):obj->class->type->name=[whpx-accel] ti->name=[whpx-accel] enter
[21096]../qom/object.c/type_get_parent(194):parent_type(accel)
[21096]../qom/object.c/object_post_init_with_type(433):obj->class->type->name=[whpx-accel] ti->name=[accel] enter
[21096]../qom/object.c/type_get_parent(194):parent_type(object)
[21096]../qom/object.c/object_post_init_with_type(433):obj->class->type->name=[whpx-accel] ti->name=[object] enter
[21096]../qom/object.c/object_post_init_with_type(444):return
[21096]../qom/object.c/object_post_init_with_type(444):return
[21096]../qom/object.c/object_post_init_with_type(444):return
[21096]../qom/object.c/object_initialize_with_type(587):obj(whpx-accel) return
[21096]../qom/object.c/object_new_with_type(812):obj(whpx-accel) return
[21096]../qom/object.c/type_table_lookup(103):lookup type(accel) in hash table
[21096]../qom/object.c/type_get_parent(194):parent_type(accel)
[21096]../system/vl.c/do_configure_accelerator(2353):accel=0x1dc0cf9d320
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(76):<<<deep>>>=[9] ac=[000001dc0cf6d2e0]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(79):name=[WHPX]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(80):init_machine=[00007ff7edfa3d60]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(82):setup_post=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(83):has_memory=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(85):cpu_common_realize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(86):cpu_common_unrealize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(88):gdbstub_supported_sstep_flags=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(89):allowed=[00007ff7eefb19e9]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(91):*allowed=[0]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(93):compat_props=[0000000000000000]
[21096]../qom/object.c/type_table_lookup(103):lookup type(accel) in hash table
[21096]../qom/object.c/type_get_parent(194):parent_type(accel)
[21096]../accel/accel-system.c/accel_init_machine(40):accel=0x1dc0cf9d320
[21096]../qom/object.c/type_table_lookup(103):lookup type(machine) in hash table
[21096]../qom/object.c/type_get_parent(194):parent_type(object)
[21096]../qom/object.c/type_get_parent(196):no parent_type
[21096]../qom/object.c/type_get_parent(194):parent_type(generic-pc-machine)
[21096]../qom/object.c/type_get_parent(194):parent_type(x86-machine)
[21096]../qom/object.c/type_get_parent(194):parent_type(machine)
[21096]../target/i386/whpx/whpx-all.c/whpx_accel_init_machine(2778):mc=0x1dc0cf8b9f0,s=0x1dc0cf9d320,whpx=0x7ff7eefb1980
WHPX: setting APIC emulation mode in the hypervisor
Windows Hypervisor Platform accelerator is operational
[21096]../accel/accel-system.c/accel_init_machine(50):
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(76):<<<deep>>>=[9] ac=[000001dc0cf6d2e0]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(79):name=[WHPX]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(80):init_machine=[00007ff7edfa3d60]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(82):setup_post=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(83):has_memory=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(85):cpu_common_realize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(86):cpu_common_unrealize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(88):gdbstub_supported_sstep_flags=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(89):allowed=[00007ff7eefb19e9]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(91):*allowed=[1]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(93):compat_props=[0000000000000000]
[21096]../system/vl.c/qemu_init(3826):

注意 AccelClass 信息變化如下:

...
[21096]../system/vl.c/do_configure_accelerator(2353):accel=0x1dc0cf9d320
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(76):<<<deep>>>=[9] ac=[000001dc0cf6d2e0]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(79):name=[WHPX]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(80):init_machine=[00007ff7edfa3d60]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(82):setup_post=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(83):has_memory=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(85):cpu_common_realize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(86):cpu_common_unrealize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(88):gdbstub_supported_sstep_flags=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(89):allowed=[00007ff7eefb19e9]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(91):*allowed=[0]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(93):compat_props=[0000000000000000]
...
[21096]../accel/accel-system.c/accel_init_machine(50):
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(76):<<<deep>>>=[9] ac=[000001dc0cf6d2e0]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(79):name=[WHPX]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(80):init_machine=[00007ff7edfa3d60]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(82):setup_post=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(83):has_memory=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(85):cpu_common_realize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(86):cpu_common_unrealize=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(88):gdbstub_supported_sstep_flags=[0000000000000000]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(89):allowed=[00007ff7eefb19e9]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(91):*allowed=[1]
[21096]../util/huedbg-accel.c/huedbg_dump_AccelClass(93):compat_props=[0000000000000000]
[21096]../system/vl.c/qemu_init(3826):

未調用 accel_init_machine() 前 *allowed = 0,調用后 *allowed = 1


總結

以上分析了系統初始化過程中加速器的配置過程。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/10090.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/10090.shtml
英文地址,請注明出處:http://en.pswp.cn/web/10090.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

藍橋杯13屆JAVA A組 國賽

??????? package 藍橋杯國賽; // 貪心選個數最少的進行擺 // 2:1 ,3:1, 4:1,5 : 3,6:3,7:1 // 選 1&#xff0c;7&#xff0c;4&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;9 // 然后都選滿10個 public class 火彩棒數字 {public static void main(String[] a…

【CTFHub】HTTP 請求方式 302跳轉 cookie WP

1.請求方式 打開給出的URL進入一個頁面&#xff0c;提示原方法是GET&#xff0c;用CTFHUB方法就能獲得flag 思路&#xff1a;抓包&#xff0c;將GET方法改成CTFHUB方法進行重新發送請求&#xff0c;查看響應情況 1.打開代理服務器 2.打開BurpSuite 刷新頁面獲得攔截 3.發送…

【Excel VBA】深入理解 VBA 中的 CallByName 函數

動態調用方法與屬性&#xff1a;深入理解 VBA 中的 CallByName 函數 昨天在介紹Choose函數在復雜的VBA應用程序開發中&#xff0c;有時我們需要根據運行時的情況來決定調用哪個對象的哪個方法或屬性。這種靈活性可以通過 CallByName 函數實現。在本篇博客中&#xff0c;田辛老…

網絡Http和Socket通訊(基于面試)

每日一句&#xff1a;想得到世上最好的東西&#xff0c;先得讓世界看到最好的你 目錄 面試問OSI或TCP/IP,怎么回答&#xff1f; 面試問HTTP&#xff1f; 面試問Get類型&#xff0c;Pot類型區別&#xff1f; 面試什么是Socket套接字&#xff1f; 面試問什么是數據粘包&…

qt for android 的架構原理

qt for android實現架構&#xff0c;分享這幾幅很不錯圖。來自于 《Qt 林斌&#xff1a;整合Android IVI平臺打造統一的Qt數字座艙體驗》 1.實現架構圖 2.qt for android能力 3.java 和 qt混合開發 4. AutoMotive

46寸硅片為什么要留平邊(flat)?

知 識星球&#xff08;星球名&#xff1a; 芯片制造與封測社區&#xff0c;星球號&#xff1a; 63559049&#xff09;里的學員問&#xff1a; 2&#xff0c; 4&#xff0c; 6寸硅片都有 平 邊&#xff0c;它們有什么作用 &#xff1f; 硅片的平邊&#xff08;Flat&…

經典權限五張表案例分析

文章目錄 模塊分析模塊分析 描述五張表的關系重要知識講解抽取成一個BaseServletSpringIOC思想(底層)實現代碼IOC概述 SPI機制(為學習框架做思想和技術鋪墊)SPI引入1. 標準/規范2. 具體的實現3. 調用 SPI介紹SPI練習JDBC4.0免注冊驅動原理Servlet實現方式三 ServletContainerIn…

idea使用前的全局配置,一次配置,多次使用

前提&#xff1a;每次導入一個新的項目&#xff0c;就需要重新設置編碼、maven、jdk、git版本等信息。實際每個項目所用到的配置信息是一致的&#xff0c;除非換一家公司&#xff0c;不然不會改動到這些內容。 idea版本&#xff1a;2024.1.1 1.1、全局Maven配置 IDEA啟動頁面…

微火一文盤點:為何全域運營系統會成為創業新風口?

當前&#xff0c;微火全域運營已經成為創業的新風口&#xff0c;想要做微火全域運營服務商的創業者數量日益增多。據目前了解到的最新消息&#xff0c;微火全域運營系統的市場占有率已經超過了48%&#xff0c;并且還在持續不斷地上漲中。 所謂微火全域運營系統&#xff0c;就是…

InLine Chat功能優化對標Github Copilot,CodeGeeX帶來更高效、更直觀的編程體驗!

VSCode中的CodeGeeX 插件上線InLine Chat功能后&#xff0c;收到不少用戶的反饋&#xff0c;大家對行內交互編程這一功能非常感興趣。近期我們針對這個功能再次進行了深度優化&#xff0c;今天詳細介紹已經在VSCode插件v2.8.0版本上線的 CodeGeeX InLine Chat功能&#xff0c;以…

藍橋杯成績已出

藍橋杯的成績早就已經出來了&#xff0c;雖然沒有十分驚艷 &#xff0c;但是對于最終的結果我是心滿意足的&#xff0c;感謝各位的陪伴&#xff0c;關于藍橋杯的刷題筆記我已經堅持更新了49篇&#xff0c;但是現在即將會告別一段落&#xff0c;人生即將進入下一個規劃。我們一起…

Spring框架學習筆記(一):Spring基本介紹(包含IOC容器底層結構)

1 官方資料 1.1 官網 https://spring.io/ 1.2 進入 Spring5 下拉 projects, 進入 Spring Framework 進入 Spring5 的 github 1.3 在maven項目中導入依賴 <dependencies><!--加入spring開發的基本包--><dependency><groupId>org.springframework<…

jni 返回二維byte數組

在JNI中返回二維byte數組&#xff0c;你需要在Java中準備一個相應的二維數組來接收這個返回值。在JNI層面&#xff0c;你可以創建一個二維的byte數組&#xff0c;并使用GetByteArrayRegion和SetByteArrayRegion來操作它。 以下是一個簡單的例子&#xff1a; public class Jni…

基于STC12C5A60S2系列1T 8051單片機實現一主單片機與一從單片機進行雙向串口通信功能

基于STC12C5A60S2系列1T 8051單片機實現一主單片機與一從單片機進行雙向串口通信功能 STC12C5A60S2系列1T 8051單片機管腳圖STC12C5A60S2系列1T 8051單片機串口通信介紹STC12C5A60S2系列1T 8051單片機串口通信的結構基于STC12C5A60S2系列1T 8051單片機串口通信的特殊功能寄存器…

QT---day5,通信

1、思維導圖 2、TCp 服務器 #ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> #include <QTcpServer> #include <QList> #include <QTcpSocket> #include <QMessageBox> #include <QDebug> #include <QTcpServer> QT_B…

鐳速實現利用Libarchive實現高效、智能的文件傳輸和管理

在前一篇報道中&#xff0c;我們闡述了Libarchive這一開源庫的強大功能&#xff0c;它專門用于處理歸檔文件。通過整合Libarchive&#xff0c;鐳速在包括Windows和Linux在內的多個操作系統上提供了在線解壓縮服務&#xff0c;為企業構建了一個既強大又安全的文件傳輸系統&#…

【代碼分享】使用HTML5的Canvas繪制編碼說明圖片

最急在工作中遇到一個需求&#xff0c;根據給定的編碼生成編碼說明&#xff0c;像下面這樣的效果。 不同含義的編碼用橫杠分割&#xff0c;然后每個編碼下面用箭頭指明具體的含義。下面是我使用canvas實現的代碼。具體的編碼寬度大家可以根據實際情況進行調整&#xff0c;目前…

Ranger 面試題及答案整理,最新面試題

Ranger 的安全模型是如何設計的&#xff1f; Ranger的安全模型設計主要基于訪問控制和安全策略的管理&#xff0c;它通過以下幾個關鍵組件實現&#xff1a; 1、策略管理&#xff1a; Ranger 提供了一個中央管理平臺&#xff0c;用于定義、更新和管理安全策略。這些策略根據資…

基于RT-Thread的HC-SR04超聲波驅動

前言 本次驅動用的是這款超聲波&#xff0c;超聲波的驅動大同小異&#xff0c;均可參考 一、引腳定義 引腳功能VCC接直流5V電TRIG接外部電路的TRIG端&#xff0c;向此引腳輸入10us以上的高電平可觸發超聲波測距ECHO接外部電路的ECHO端&#xff0c;測距結束時該引腳會輸出一…