Bochs源码分析 - 17:分析Bochs对于int指令的实现

Bochs源码分析 - 8:bochs的CPU代码初步分析 前言 上一篇文章,我们分析了floppy设备的初始化,本来我们希望看看floppy的工作原理,但是调试时发现其运行cpu,通过BIOS启动指令来调用floppy。(从现在来看这是肯定的,floppy是外设,要想调用必须通过cpu,还是自己对底层不太熟悉) Bochs的CPU模块特别复杂,函数众多,我们是第一次分析,对其结构还是不太熟悉,我们先尝试来看其执行流程。在分析前,我浏览了一遍喻强老师的《Bochs项目源码分析与注释》,其CPU模块的核心图如下。 CPU模块的... 阅读详情

前言

        在《x86/x64编程体系探索及编程》的第207页,其举了一个使用中断服务例程的例子,我们现在来分析其源码以及探究bochs是如何实现的(重点探究int指令)。

代码分析

        其首先设置好调用set_user_interrupt_handler来调用中断向量,内容如下:

        mov esi, SYSTEM_SERVICE_VECTOR // 0x40
        mov edi, system_service        // lib
        call set_user_interrupt_handler

        set_user_interrupt_handler 地址只有一个 jmp 指令,跳转到 __set_user_interrupt_handler,在该函数中先调用sidt来获取idt表地址,存储到 [___idt__pointer] 所指向的内存中,之后根据esi作为索引找到对应的值,将 system_service 存储进去,这很好理解的。

set_user_interrupt_handler:     jmp     DWORD __set_user_interrupt_handler

;------------------------------------------------------
; set_user_interrupt_handler(int vector, void(*)()handler)
; input:
;       esi: vector,  edi: handler
;------------------------------------------------------
__set_user_interrupt_handler:
        sidt [__idt_pointer]        
        mov eax, [__idt_pointer + 2]
        mov [eax + esi * 8 + 4], edi                           ; set offset [31:16]
        mov [eax + esi * 8], di                                ; set offset [15:0]
        mov DWORD [eax + esi * 8 + 2], kernel_code32_sel       ; set selector
        mov WORD [eax + esi * 8 + 5], 0E0h | INTERRUPT_GATE32  ; Type=interrupt gate, P=1, DPL=3
        ret

        system_service函数中存在一个__system_service函数,在这里直接从系统服务表中获取对应的值,然后直接call进去即可。

system_service:                 jmp     DWORD __system_service

;-------------------------------------------------------
; system_service(): 系统服务例程,使用中断0x40号调用进入 
; input:
;                eax: 系统服务例程号
;--------------------------------------------------------
__system_service:
        mov eax, [system_service_table + eax * 4]
        call eax                                ; 调用系统服务例程
        iret

;******** 系统服务例程函数表 ***************
system_service_table:
        dd __puts                                       ; 0 号
        dd __read_gdt_descriptor                        ; 1 号
        dd __write_gdt_descriptor                       ; 2 号
        dd 0                                            ; 3 号
        dd 0                                            ; 4 号
        dd 0                                            ; 5 号
        dd 0                                            ; 6 号
        dd 0
        dd 0
        dd 0

        之后的__puts函数向video内存中写入对应的值,而不是使用bios来输出,这些关于外设的我们可能之后分析,现在这不是重点。

__write_char:
        push ebx
        mov ebx, video_current
        or si, 0F00h
        cmp si, 0F0Ah                                ; LF
        jnz do_wirte_char
        call __get_current_column

Bochs源码分析

        先用IDA来逆向,找出其调用int指令的地址,如下。

         之后通过bochs-dbg定位到该处,然后在visual studio中设置对应的软件断点。

         如下代码是当遇到int指令时所产生的替换指令,这部分还是很好理解的。注意其type为BX_SOFTWARE_INTERRUPT,含义是软件所触发的中断,我们之后分析interrupt(..)函数时会用到。

void BX_CPP_AttrRegparmN(1) BX_CPU_C::INT_Ib(bxInstruction_c *i)
{

  Bit8u vector = i->Ib();

    ...
    ...


  interrupt(vector, BX_SOFTWARE_INTERRUPT, 0, 0);

  BX_INSTR_FAR_BRANCH(BX_CPU_ID, BX_INSTR_IS_INT,
                      FAR_BRANCH_PREV_CS, FAR_BRANCH_PREV_RIP,
                      BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value, RIP);

}

        interrupt(..)函数主要完成两件事:发生中断事件的类型,CPU当前所在的模式。现在我们正在32位保护模式下,因此走的是 protected_mode_int(vector, soft_int, push_error, error_code) 这个函数,我们继续往下分析。


void BX_CPU_C::interrupt(Bit8u vector, unsigned type, bx_bool push_error, Bit16u error_code)
{
  ....

  bx_bool soft_int = 0;
  switch(type) {
    ...
    case BX_SOFTWARE_EXCEPTION:
      soft_int = 1;
      break;
    ....
  }
  ...

  if (long_mode()) {
    long_mode_int(vector, soft_int, push_error, error_code);
  }
  else
  {
    // software interrupt can be redirected in v8086 mode
    if (type != BX_SOFTWARE_INTERRUPT || !v8086_mode() ||!v86_redirect_interrupt(vector))
    {
      if(real_mode()) {
        real_mode_int(vector, push_error, error_code); 
      }
      else {
        protected_mode_int(vector, soft_int, push_error, error_code); // <---
      }
    }
  }


  RSP_COMMIT;

    ....

  BX_CPU_THIS_PTR EXT = 0;
}

BX_CPU_C::protected_mode_int(...)函数分析

        该函数内容有点多,不过没关系,我们慢慢来分解。

        还记得我们之前分析的idtr寄存器嚒,其存储着idt表的idt表的基质和限长。该函数上来先从该寄存器中来获取限长来进行对比,判断其是否超出限长。

  // interrupt vector must be within IDT table limits,
  // else #GP(vector*8 + 2 + EXT)
  if ((vector*16 + 15) > BX_CPU_THIS_PTR idtr.limit) {
    BX_ERROR(("interrupt(long mode): vector must be within IDT table limits, IDT.limit = 0x%x", BX_CPU_THIS_PTR idtr.limit));
    exception(BX_GP_EXCEPTION, vector*8 + 2);
  }

        我们现在提一下idt,曾经有一节我们分析过,在实模式下,idtr寄存器中存储着ivt表的地址,而ivt表中直接存储着中断处理函数的地址。但是我们现在是在保护模式,在idt表中存储着中断门描述符,而不是中断处理函数的地址。

         如果还是没有印象,下图是中断门描述符的属性,看到这个很容易理解,其中断处理函数存储在offset,很好查找与定位。

         结合上面这张表,我们重新回顾设置中断门描述符的代码,很好理解。首先offset被设置为 system_service 函数入口,将DPL设置为3,允许用户层代码进入,并且将Segment Selecotor设置为kernel_code32_sel,内核级代码段选择子。

        mov eax, [__idt_pointer + 2]
        mov [eax + esi * 8 + 4], edi                           ; set offset [31:16]
        mov [eax + esi * 8], di                                ; set offset [15:0]
        mov DWORD [eax + esi * 8 + 2], kernel_code32_sel       ; set selector
        mov WORD [eax + esi * 8 + 5], 0E0h | INTERRUPT_GATE32  ; Type=interrupt gate, P=1, DPL=3

        继续来分析protected_mode_int(..)函数,之后代码如下,其从idt表中解析出上述中断门描述符。(先来获取其值,然后调用parse_descriptor(..)函数解析)

  Bit64u desctmp1 = system_read_qword(BX_CPU_THIS_PTR idtr.base + vector*16);
  Bit64u desctmp2 = system_read_qword(BX_CPU_THIS_PTR idtr.base + vector*16 + 8);

    // ...

  Bit32u dword1 = GET32L(desctmp1);
  Bit32u dword2 = GET32H(desctmp1);
  Bit32u dword3 = GET32L(desctmp2);

  parse_descriptor(dword1, dword2, &gate_descriptor);

        之后来判断当前CPL是否满足中断门描述符所要求的权限(dpl),很好理解。

  // if software interrupt, then gate descriptor DPL must be >= CPL,
  // else #GP(vector * 8 + 2 + EXT)
  if (soft_int && gate_descriptor.dpl < CPL) {
    BX_ERROR(("interrupt(): soft_int && (gate.dpl < CPL)"));
    exception(BX_GP_EXCEPTION, vector*8 + 2);
  }

        之后这个很好理解,我们是BX_386_INTERRUPT_GATE,之后所有行为都是在case条件下完成的,当完成之后直接return结束函数运行。

  switch (gate_descriptor.type) {
  case BX_TASK_GATE:
    ....
  case BX_286_INTERRUPT_GATE:
  case BX_286_TRAP_GATE:
  case BX_386_INTERRUPT_GATE:
  case BX_386_TRAP_GATE:
    ..
 }

        之后来解析代码段选择子,这里是内核的代码段,这部分解析的函数我们之前已经分析过了,就不用再来继续分析了。

    parse_selector(gate_dest_selector, &cs_selector);

    // selector must be within its descriptor table limits
    // else #GP(selector+EXT)
    fetch_raw_descriptor(&cs_selector, &dword1, &dword2, BX_GP_EXCEPTION);
    parse_descriptor(dword1, dword2, &cs_descriptor);

        之后来进行常规的代码段权限检查,这些检查内容很好理解。

    // descriptor AR byte must indicate code seg
    // and code segment descriptor DPL<=CPL, else #GP(selector+EXT)
    if (cs_descriptor.valid==0 || cs_descriptor.segment==0 ||
        IS_DATA_SEGMENT(cs_descriptor.type) ||
        cs_descriptor.dpl > CPL)
    {
      BX_ERROR(("interrupt(): not accessible or not code segment cs=0x%04x", cs_selector.value));
      exception(BX_GP_EXCEPTION, cs_selector.value & 0xfffc);
    }

        当检查通过是,其会先来保存原来的ESP、SS、EIP、CS四个值,很好理解。

    Bit32u old_ESP = ESP;
    Bit16u old_SS  = BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value;
    Bit32u old_EIP = EIP;
    Bit16u old_CS  = BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value;

        然后来判断是否是一致代码段,这个我们已经在上篇文章中分析过了。

if(IS_CODE_SEGMENT_NON_CONFORMING(cs_descriptor.type) && cs_descriptor.dpl < CPL)

        这里关键的来了,其从TSS中获取ESP0,SS0。我们以前仅知道Windows只使用TSS结构体来保存SSP0与SS0,其实intel内部本来就使用这两个数据结构,其值就存储在这里面。

 // check selector and descriptor for new stack in current TSS
      get_SS_ESP_from_TSS(cs_descriptor.dpl, &SS_for_cpl_x, &ESP_for_cpl_x);

        绕过对ss数据段的权限检查和解析,下面就是调用函数准备新的栈,代码如下,很好理解。

      // Prepare new stack segment
      bx_segment_reg_t new_stack;
      new_stack.selector = ss_selector;
      new_stack.cache = ss_descriptor;
      new_stack.selector.rpl = cs_descriptor.dpl;
      // add cpl to the selector value
      new_stack.selector.value = (0xfffc & new_stack.selector.value) | new_stack.selector.rpl;

        现在重点来了,开始往栈中压入数据,可以看到其栈的结构。并且可以看到error_code并不一定必须压住栈,如果有就压,如果没有就不压!

        if (gate_descriptor.type>=14) { // 386 int/trap gate
          // push long pointer to old stack onto new stack
          write_new_stack_dword(&new_stack, temp_ESP-4,  cs_descriptor.dpl, old_SS);
          write_new_stack_dword(&new_stack, temp_ESP-8,  cs_descriptor.dpl, old_ESP);
          write_new_stack_dword(&new_stack, temp_ESP-12, cs_descriptor.dpl, read_eflags());
          write_new_stack_dword(&new_stack, temp_ESP-16, cs_descriptor.dpl, old_CS);
          write_new_stack_dword(&new_stack, temp_ESP-20, cs_descriptor.dpl, old_EIP);
          temp_ESP -= 20;

          if (push_error) {
            temp_ESP -= 4;
            write_new_stack_dword(&new_stack, temp_ESP, cs_descriptor.dpl, error_code);
          }

    ESP = temp_ESP;

        之后调用load_cs和load_ss这两个函数加载代码段寄存器的栈段寄存器。这个内容比较简单,直接对寄存器赋值即可,没有想的那么复杂。

      // load new CS:eIP values from gate
      // set CPL to new code segment DPL
      // set RPL of CS to CPL
      load_cs(&cs_selector, &cs_descriptor, cs_descriptor.dpl);

      // load new SS:eSP values from TSS
      load_ss(&ss_selector, &ss_descriptor, cs_descriptor.dpl);


BX_CPU_C::load_cs(bx_selector_t *selector, bx_descriptor_t *descriptor, Bit8u cpl)
{
    ...
  BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector = *selector;
  BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].cache = *descriptor;
  BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.rpl = cpl;
  BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].cache.valid  = SegValidCache;
    ...
}

        最后这部分也非常重要,其EIP是gate描述符中的偏移地址。之后来清除标志位!!这个对我们帮助很大,尤其当我们一直记不清要清除哪些标志位时,看具体代码就很好记忆了。

    EIP = gate_dest_offset;

    // if interrupt gate then set IF to 0
    if (!(gate_descriptor.type & 1)) // even is int-gate
      BX_CPU_THIS_PTR clear_IF();
    BX_CPU_THIS_PTR clear_TF();
    BX_CPU_THIS_PTR clear_NT();
    BX_CPU_THIS_PTR clear_VM();
    BX_CPU_THIS_PTR clear_RF();

总结

        通过bochs代码,我们很好理清了当中断发生时具体的行为。注意,int除了可以触发fault类型事件还可以触发trap类型事件,这两种事件中存在着细微差异,我们之后分析到时会对比着来进行分析。

        下一篇文章我们将来尝试分析中断返回时使用的iret指令,与之对应的还存在一个retf,我们慢慢来分析,搞懂其内部实际调用情况即可。

Bochs源码分析 - 11:《X86探索及编程》代码初步解释 前言 现在我们来阅读邓志老师的《x86/x64体系探索及编程》,边阅读边将其代码放到bochs中运行,然后观察bochs的CPU是如何来模拟的。现在我们来详细分析ex3-2目录里面的内容。 boot.asm 代码解读 自己在工作中经常看汇编代码,但是写汇编代码真的很少写过,我们现在一边分析着这段boot汇编代码一边回顾其如何编写。 ; Int 19h 加载 sector 0 (MBR) 进入 BOOT_SEG 段, BOOT_SEG 定义为 0x7c00 ... 阅读详情

相关推荐

Bochs源码分析 - 12:这才是真正的调试姿势!!

前言 之前我们为了正确的调试bochs,采用了很多方法,一种是利用IDA进行调试,第二种是采用bochs-dbg配合调试,第三种是在cpu_loop(..)中下循环来进行调试。这三种来实际操作中都很不方便。 真正的调试姿势 IDA以二进制模式分别以0x7c00,0x8000-2为偏移打开uboot.bin与setup.bin文件,静态分析;配置调试模式编译bochs,精确下断点定位指令;再通过visual studio 在cpu_loop(..)函数中下软件断点分析指令...

WriteAnything_的博客 688

Bochs源码分析 - 5: 从启动到cpu运行之前的一系列初始化

前言 现在正式开始调试bochs源代码,在这之前我也没读过bochs源码,算是从零开始读,自己可能会走入一些误区,有些可能猜错,如果发现之后会来改正。我们先大体走一遍整个系统,不要纠结于各种复杂细节流程。 这篇文章开始分析程序在点击启动后如何开始并且一步步初始化完成初始化,完成相关配置文件。说实话,如下图bochs启动时一系列的配置参数,对像我这种新手来说是相当恐怖的,各种启动参数信息。其实调试过一遍之后,虽然里面很多细节还是不太懂,但是再看这些信息心里踏实多了。 启...

WriteAnything_的博客 1313

Bochs源码分析 - 1: Bochs工程项目的编译

编译环境 操作系统: Windows 10 IDE: Visutal Studio 2019 + SDK10 ---------------------------------------------------------------- 备注: 编译环境没啥要求,正常的vs2017,vs2019 win10SDK都是可以的。 ---------------------------------------------------------------- 下载地址 https://sou.

WriteAnything_的博客 994

Bochs源码分析-CPU类

因为学习需要,要看虚拟机Bochs的源代码。写随笔主要为了学习总结,其次是分享大家共同研究,大神勿喷,欢迎评论。 手头资料:bochs源代码,下于:bochs.sourceforge.net,还有喻强写的源码分析电纸书。 在Bochs模拟机里面CPU类是最复杂的一个类,也是最重要的一个类。原因很简单,在整个计算机体系中CPU处于中心位置,计算机其它部件都是在CPU的调控下进行有序的工作,当然C...

weixin_30635053的博客 267

Bochs + GDB 源码级调试C语言内核

Ubuntu 14.04 64bit Bochs 2.6.6 GDB 7.7 想要源码级调试C内核,需要在编译Bochs时加上 --enable-gdb-stub 参数 sudo ./configure --enable-all-optimizations --enable-long-phy-address --enable-disasm --enable-alignmen

分享技术,分享知识 3534

Bochs源码分析 - 4: 使用ida来调试bochs

前言 bochs内部提供了一个调试器,但是这个命令行调试器相当难用,想找一个gui的debugger。第一次是在百度百科的bochs词条中介绍的peter-bochs-debugger调试器(java编写),但是后来发现ida也有一个bochs调试器,因为对ida比较熟悉,就选用此作为bochs调试器。 在配置过程中参考博客 https://blog.youkuaiyun.com/cssxn/article/details/90738498 中的配置,但是这是直接发布版本,拿源码编译...

WriteAnything_的博客 2755

一个简单多任务内核实例的分析【转】

转自:http://blog.youkuaiyun.com/rosetta/article/details/8933228 一个简单多任务内核实例的分析 转载请注明出处:http://blog.youkuaiyun.com/rosetta 简介 Linux-0.00是由LinusTorvalds写的Linux最初版本(未发布),只是打印AAA和BBB而没有更多的功能,比...

weixin_33756418的博客 100

Bochs项目源码分析与注释.pdf

Bochs是一个x86硬件平台的开源模拟器。它可以模拟各种硬件的配置。Bochs模拟的是整个PC平台,包括I/O设备、内存和BIOS。更为有趣的是,甚至可以不使用PC硬件来运行Bochs。事实上,它可以在任何编译运行Bochs的平台上模拟x86硬件。通过改变配置,可以指定使用的CPU(386、486或者586),以及内存大小等。一句话,Bochs是电脑里的“PC”。根据需要,Bochs还可以模拟多台PC,此外,它甚至还有自己的电源按钮。

Bochs源码分析 - 2:使用bochs运行引导代码

前言 这一篇文章中,我们尝试使用编写引导代码,然后配置bochs让其可以运行这份代码,过程中遇到的问题,为什么这么做,我会尽可能详细的描述给大家,当然自己可能存在错误,请见谅!! 安装 nasm 编译器 ...

WriteAnything_的博客 1312

AutoSar标准官网下载

AutoSar文档下载和文档命名格式。。。

m0_56208280的博客 3518

五段式S型算法笔记

又因为有 v-v0= 0.5jt通式: 所以有 vm-v0= 0.5jtm 所以有4vm-4v0= 2jtm。算法1: v=2vm-v0-0.5*jt+2jt*tm-2jtm*tm。加加速度j 自己设置 已知,vmax自己设置已知;所以有4vm-4v0= 2jtm带入算法1。

qq_36658033的博客 680

Bochs源码分析 - 6: bochs的物理内存初始化

前言 上一篇文章,我们走马观花地介绍了bochs从启动到CPU执行流程的大体流程,里面相关数据结构没详细分析。这篇文章我们尝试来详细分析一下bochs的物理内存初始化,首先有个概念需要理清(相信来看bochs源码的人肯定会知道):bochs只负责处理物理内存,而虚拟内存是运行在bochs中运行的OS来负责的,我们只需要关注物理内存。 找到内存初始化函数 在上篇文章中,我们提到过一个比较重要的函数bx_init_hardware(),该函数负责初始化各种硬件(内存,Io...

WriteAnything_的博客 941

机械革命重装Win11详细教程

通过以上步骤,你可以在机械革命电脑上成功重装Windows 11。虽然整个过程可能看起来有些复杂,但只要你按照步骤逐一进行,就能够顺利完成安装并享受到全新的操作系统带来的便利和性能提升。希望本文能够帮助到你,如果你有任何问题或疑问,请随时联系我们的技术支持团队。

mmoo_python的博客 3130

OS_lab——bochs源码的编译与安装

1.认真阅读章节资料 2.在实验机上安装虚拟运行环境,并安装ubuntu 3.安装ubuntu开发环境,32位环境 4.下载bochs源码,编译并安装bochs环境 5.使用bochs自带工具bximage创建虚拟软驱 6.阅读、编译boot.asm,并反汇编阅读 7.修改bochsrc,运行并调试你的第一个程序 8.完成实验练习要求

weixin_49816179的博客 880

GPOPSII 对控制量变化率进行约束,避免控制量振荡

在这篇文章中,在GPOPSII中通过对控制量变化的约束,优化求解得到更平滑的控制量曲线,更符合真实控制约束。

Dannyfomer的博客 1869

基于深度学习的智能监考系统(UI界面 + YOLOv8/v7/v6/v5代码 + 训练数据集)

行为检测与识别:能够自动识别考生在考试中的异常行为,如作弊、看手机、交头接耳等。实时监控:在考试期间,系统能够对考生的行为进行实时监控,及时发出警报。用户交互:通过简洁易用的UI界面,便于监考人员进行实时监控、查看警报和视频回放等操作。行为分析与反馈:系统能够识别出考生的违规行为,并提供反馈。本文详细介绍了如何构建基于YOLOv5的智能监考系统,从数据集的准备、YOLO模型的训练,到UI界面的设计与实现,展示了一个完整的深度学习监控系统。

YOLO 781

Bochs源码分析-MEM类

因为学习需要,要看虚拟机Bochs的源代码。写随笔主要为了学习总结,其次是分享大家共同研究,大神勿喷,欢迎评论。 手头资料:bochs源代码,下于:bochs.sourceforge.net,还有喻强写的源码分析电纸书。 计算机系统最主要的两个部件是CPU和内存。CPU负责不断读取内存里面的指令进行工作。一块固定大小的内存条(2G)和能由很多ROM,RAM,SDRAM通过位扩展法、字扩展法共同...

weixin_30725315的博客 271

【Unity动画优化插件】BT - OptiAnimX —— AAA级动画优化框架深度剖析

BT - OptiAnimX 是一款面向 Unity 的高性能动画优化框架,核心目标是解决多角色动画场景中的性能瓶颈问题。该插件对标《战争机器5》和《巫师3》等 AAA 游戏中的动画优化技术,通过一整套自动化与智能化的优化策略,将动画性能提升与视觉质量控制结合在一起。它适用于从移动端、VR 到高端 PC 的多平台项目,尤其在“多人角色 + 高频动画”的场景中表现突出,例如 MMO、开放世界、动作游戏等。BT - OptiAnimX 本质上是一套“动画性能调度系统”,而不仅仅是优化工具。

2403_88403568的博客 433

Bochs源码分析-调试功能

因为学习需要,要看虚拟机Bochs的源代码。写随笔主要为了学习总结,其次是分享大家共同研究,大神勿喷,欢迎评论。 手头资料:bochs源代码,下于:bochs.sourceforge.net,还有喻强写的源码分析电纸书。 Bochs虚拟机一个很重要的功能就是调试,有助于系统程序的开发与研究,虚拟机可以让操作系统在任何时候停止运行,并查看此时计算机内部所有部件的内容与数据状态。在Bochs源码中...

weixin_30423977的博客 380

图解目标检测 之 YOLO 算法 最全原理详解

YOLO 一.背景 YOLO(You Only Look Once: Unified, Real-Time Object Detection),是Joseph Redmon和Ali Farhadi等人于2015年提出的基于单个神经网络的目标检测系统。在2017年CVPR上,Joseph Redmon和Ali Farhadi又发表的YOLO 2,进一步提高了检测的精度和速度。 二.YOLO算法的网...

DFCED的博客 26万+

【OpenClaw从入门到精通】第19篇:OpenClaw电商实战:从零搭建比价+库存监控+自动调价全自动化系统(2026实测附完整代码)

2026年OpenClaw成为全球最火开源项目,电商行业因其场景高频、数据标准化成为其核心落地领域。本文针对电商从业者面临的比价繁琐、库存混乱、调价滞后三大痛点,基于OpenClaw的“接入层-执行层-决策层”三层架构,从零拆解智能比价、多平台库存监控、动态自动调价三大核心场景的实现逻辑。文中包含完整的环境配置、技能安装、代码实现(含Python脚本与YAML配置)、执行结果验证,同时补充关键算法原理与避坑指南,兼顾新手入门与进阶提升。读者可直接参考本文实操流程,快速搭建属于自己的电商自动化系统,实现7×2

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 2237

Bochs源码分析-初始化部分

因为学习需要,要看虚拟机Bochs的源代码。写随笔主要为了学习总结,其次是分享大家共同研究,大神勿喷,欢迎评论。 手头资料:bochs源代码,下于:bochs.sourceforge.net,还有喻强写的源码分析电纸书。 从程序运行到模拟机真正开始工作,即模拟程序代码的运行,这一部分称为程序的初始化。其中主要包括1:获取用户参数决定程序运行方式,2:获取模拟器配置信息(主要放在配置文件boch...

weixin_30919571的博客 337

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Bochs源码分析 - 19:分析Bochs对于bound,into的实现

前言 在《x86/x64体系探索及编程》的ex10-7代码中,使用了int3,bound,into来加载CS寄存器。我们现在来尝试分析bochs对于这几条指令的实现。 into指令分析 关于into以及处理OF异常的代码如下,比较好理解。into指令只是判断eflag的of位,这位标志着是否存在溢出发生,如果存在溢出,则触发OF异常。 ;; 测试 INTO 指令 mov eax, 0x80000000 mov ebx, eax ...

WriteAnything_的博客 871

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Bochs源码分析 - 23:页表构建的汇编分析

前言 上一篇文章,我们使用C++来简单模拟了分页机制,现在我们来看汇编代码,看看其如何来构建汇编代码的,这里如果新手可能会犯糊涂,很久之前我被这份代码搞晕了好久,现在来一起解释一下。 页表构建代码分析 代码如下,在《x86/x64体系探索及编程》的ex11-2\protected.asm中,来负责初始化页表。 ;--------------------------------------------- ; init_32bit_paging(): 建立 32-b...

WriteAnything_的博客 441

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

上一篇: Bochs源码分析 - 16:conforming与non-conforming代码段区别以及Bochs代码的实现
下一篇: Bochs源码分析 - 18:分析Bochs对于iret指令的实现
WriteAnything_
博客等级 码龄5年 66粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值