news 2026/4/15 13:50:49

KiIdleLoop线程分析之优先级为0到调用nt!SwapContext切换到NextThread的一个例子

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
KiIdleLoop线程分析之优先级为0到调用nt!SwapContext切换到NextThread的一个例子

KiIdleLoop线程分析之优先级为0到调用nt!SwapContext切换到NextThread的一个例子

0: kd> g
Breakpoint 16 hit
eax=00000001 ebx=00000102 ecx=00000002 edx=00000000 esi=f7737120 edi=00000000
eip=804ee4f8 esp=f78aac9c ebp=f78aacc0 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
hal!HalRequestSoftwareInterrupt:
804ee4f8 643a0d95000000 cmp cl,byte ptr fs:[95h] fs:0030:00000095=00
1: kd> dx -id 0,0,89831250 -r1 ((basesrv!_KPRCB *)0xffdff120)
((basesrv!_KPRCB *)0xffdff120) : 0xffdff120 [Type: _KPRCB *]
[+0x000] MinorVersion : 0x1 [Type: unsigned short]
[+0x002] MajorVersion : 0x1 [Type: unsigned short]
[+0x004] CurrentThread : 0x895f2a78 [Type: _KTHREAD *]
[+0x008] NextThread : 0x0 [Type: _KTHREAD *]
[+0x00c] IdleThread : 0x80b200c0 [Type: _KTHREAD *]

[+0x928] ReadySummary : 0x0 [Type: unsigned long]
[+0x92c] SelectNextLast : 0x0 [Type: unsigned long]
[+0x930] DispatcherReadyListHead [Type: _LIST_ENTRY [32]]
[+0xa30] DeferredReadyListHead [Type: _SINGLE_LIST_ENTRY]

1: kd> dt kthread 8952bc18
CSRSRV!KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY [ 0x8952bc28 - 0x8952bc28 ]
+0x018 InitialStack : 0xba583000 Void
+0x01c StackLimit : 0xba580000 Void
+0x020 KernelStack : 0xba582c78 Void
+0x024 ThreadLock : 0
+0x028 ContextSwitches : 0x2d7
+0x02c State : 0x3 ''
+0x02d NpxState : 0xa ''
+0x02e WaitIrql : 0 ''
+0x02f WaitMode : 1 ''
+0x030 Teb : 0x7ffd9000 Void
+0x034 ApcState : _KAPC_STATE
+0x04c ApcQueueLock : 0
+0x050 WaitStatus : 0n258
+0x054 WaitBlockList : 0x8952bd00 _KWAIT_BLOCK
+0x058 Alertable : 0 ''
+0x059 WaitNext : 0 ''
+0x05a WaitReason : 0x4 ''
+0x05b Priority : 8 ''

1: kd> dx -id 0,0,89831250 -r1 ((basesrv!_KPRCB *)0xf7737120)
((basesrv!_KPRCB *)0xf7737120) : 0xf7737120 [Type: _KPRCB *]
[+0x000] MinorVersion : 0x1 [Type: unsigned short]
[+0x002] MajorVersion : 0x1 [Type: unsigned short]
[+0x004] CurrentThread : 0xf7739fa0 [Type: _KTHREAD *]
[+0x008] NextThread : 0x8952bc18 [Type: _KTHREAD *]
[+0x00c] IdleThread : 0xf7739fa0 [Type: _KTHREAD *]

[+0x928] ReadySummary : 0x0 [Type: unsigned long]
[+0x92c] SelectNextLast : 0x0 [Type: unsigned long]
[+0x930] DispatcherReadyListHead [Type: _LIST_ENTRY [32]]
[+0xa30] DeferredReadyListHead [Type: _SINGLE_LIST_ENTRY]

1: kd> dx -id 0,0,89831250 -r1 ((basesrv!_KTHREAD *)0xf7739fa0)
((basesrv!_KTHREAD *)0xf7739fa0) : 0xf7739fa0 [Type: _KTHREAD *]
[+0x000] Header [Type: _DISPATCHER_HEADER]
[+0x010] MutantListHead [Type: _LIST_ENTRY]
[+0x018] InitialStack : 0xf78ab000 [Type: void *]
[+0x01c] StackLimit : 0xf78a8000 [Type: void *]
[+0x020] KernelStack : 0xf78aad4c [Type: void *]
[+0x024] ThreadLock : 0x0 [Type: unsigned long]
[+0x028] ContextSwitches : 0x2b9a [Type: unsigned long]
[+0x02c] State : 0x2 [Type: unsigned char]
[+0x02d] NpxState : 0xa [Type: unsigned char]
[+0x02e] WaitIrql : 0x2 [Type: unsigned char]
[+0x02f] WaitMode : 0 [Type: char]
[+0x030] Teb : 0x0 [Type: void *]
[+0x034] ApcState [Type: _KAPC_STATE]
[+0x04c] ApcQueueLock : 0x0 [Type: unsigned long]
[+0x050] WaitStatus : 0 [Type: long]
[+0x054] WaitBlockList : 0x0 [Type: _KWAIT_BLOCK *]
[+0x058] Alertable : 0x0 [Type: unsigned char]
[+0x059] WaitNext : 0x0 [Type: unsigned char]
[+0x05a] WaitReason : 0x0 [Type: unsigned char]
[+0x05b] Priority : 0 [Type: char]

[+0x113] Quantum : 114 'r' [Type: char]

1: kd> !thread 0xf7739fa0
THREAD f7739fa0 Cid 0000.0000 Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 1
Not impersonating
Owning Process 80b20320 Image: Idle
Attached Process N/A Image: N/A
Wait Start TickCount 0 Ticks: 274655302 (49:16:04:49.093)
Context Switch Count 11162 IdealProcessor: 0
UserTime 00:00:00.000
KernelTime 00:01:46.234
Stack Init f78ab000 Current f78aad4c Base f78ab000 Limit f78a8000 Call 00000000
Priority 0 BasePriority 0 PriorityDecrement 0 IoPriority 0 PagePriority 0
ChildEBP RetAddr Args to Child
f78aac98 80a3ccf7 bae6aa16 f7737dd0 f78aad50 hal!HalRequestSoftwareInterrupt (FPO: [0,0,0]) [d:\srv03rtm\base\hals\halmps\i386\mpswint.asm @ 84]
f78aaca8 804ed906 f78aacc0 00000000 00000402 nt!KiIpiServiceRoutine+0x6f (FPO: [2,3,0]) [d:\srv03rtm\base\ntos\ke\i386\mpipia.asm @ 152]
f78aaca8 bae6aa16 f78aacc0 00000000 00000402 hal!HalpIpiHandler+0xca (FPO: [0,2] TrapFrame @ f78aacc0) [d:\srv03rtm\base\hals\halmps\i386\mpipi.asm @ 718]
f78aad50 80b00a60 00000000 0000000e 00000000 processr!AcpiC1Idle+0x12 (FPO: [0,2,0]) [d:\srv03rtm\base\hals\processor\lib\i386\cstate.asm @ 134]
f78aad54 00000000 0000000e 00000000 00000000 nt!KiIdleLoop+0xc (FPO: [0,0,0]) [d:\srv03rtm\base\ntos\ke\i386\ctxswap.asm @ 1362]

1: kd> kc 2
#
00 hal!HalRequestSoftwareInterrupt
01 nt!KiIpiServiceRoutine
1: kd> g
Breakpoint 46 hit
WARNING: Process directory table base 7A00C000 doesn't match CR3 00039000
WARNING: Process directory table base 7A00C000 doesn't match CR3 00039000
eax=00000002 ebx=f7737000 ecx=00000001 edx=0000001b esi=8952bc18 edi=f7739fa0
eip=80b007f0 esp=f78aad54 ebp=80b20320 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!SwapContext:
80b007f0 51 push ecx
windbg> .open -a ffffffff804ee4f8
1: kd> kc 2
#
00 nt!SwapContext
01 nt!KiIdleLoop
windbg> .open -a ffffffff80b00a60

;++
;
; VOID
; KiIdleLoop(
; VOID
; )
;
; Routine Description:
;
; This routine continuously executes the idle loop and never returns.
;
; Arguments:
;
; ebx - Address of the current processor's PCR.
;
; Return value:
;
; None - routine never returns.
;
;--

cPublicFastCall KiIdleLoop ,0
cPublicFpo 0, 0

if DBG

xor edi, edi ; reset poll breakin counter

endif

jmp short kid20 ; Skip HalIdleProcessor on first iteration

;
; There are no entries in the DPC list and a thread has not been selected
; for execution on this processor. Call the HAL so power managment can be
; performed.
;
; N.B. The HAL is called with interrupts disabled. The HAL will return
; with interrupts enabled.
;
; N.B. Use a call instruction instead of a push-jmp, as the call instruction
; executes faster and won't invalidate the processor's call-return stack
; cache.
;

kid10: lea ecx, [ebx].PcPrcbData.PbPowerState
call dword ptr [ecx].PpIdleFunction ; (ecx) = Arg0

;
; Give the debugger an opportunity to gain control on debug systems.
;
; N.B. On an MP system the lowest numbered idle processor is the only
; processor that polls for a breakin request.
;

kid20:

if DBG
ifndef NT_UP

mov eax, _KiIdleSummary ; get idle summary
mov ecx, [ebx].PcSetMember ; get set member
dec ecx ; compute right bit mask
and eax, ecx ; check if any lower bits set
jnz short CheckDpcList ; if nz, not lowest numbered

endif

dec edi ; decrement poll counter
jg short CheckDpcList ; if g, not time to poll

POLL_DEBUGGER ; check if break in requested
endif

kid30:

if DBG

ifndef NT_UP

mov edi, 20 * 1000 ; set breakin poll interval

else

mov edi, 100 ; UP idle loop has a HLT in it

endif

endif

CheckDpcList0: ;
YIELD

;
; Disable interrupts and check if there is any work in the DPC list of the
; current processor or a target processor.
;

CheckDpcList:

;
; N.B. The following code enables interrupts for a few cycles, then
; disables them again for the subsequent DPC and next thread
; checks.
;

sti ; enable interrupts
nop ;
nop ;
cli ; disable interrupts

;
; Process the deferred procedure call list for the current processor.
;

mov eax, [ebx]+PcPrcbData+PbDpcQueueDepth ; get DPC queue depth
or eax, [ebx]+PcPrcbData+PbTimerRequest ; merge timer request

ifndef NT_UP

or eax, [ebx]+PcPrcbData+PbDeferredReadyListHead ; merge deferred list head

endif

jz short CheckNextThread ; if z, no DPC's or timers to process
mov cl, DISPATCH_LEVEL ; set interrupt level
fstCall HalClearSoftwareInterrupt ; clear software interrupt
lea ecx, [ebx].PcPrcbData ; set current PRCB address
CAPSTART <@KiIdleLoop@0,@KiRetireDpcList@4>
fstCall KiRetireDpcList ; process the current DPC list
CAPEND <@KiIdleLoop@0>

if DBG

xor edi, edi ; clear breakin poll interval

endif

;
; Check if a thread has been selected to run on the current processor.
;

CheckNextThread: ;
cmp dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; thread selected?

ifdef NT_UP

je short kid10 ; if eq, no thread selected

else

je kid40 ; if eq, no thread selected.

endif

;
; Raise IRQL to synchronization level and enable interrupts.
;

ifndef NT_UP

RaiseIrql SYNCH_LEVEL, NoOld ; raise IRQL to synchronizaiton level

endif

sti ; enable interrupts
mov edi, [ebx].PcPrcbData.PbCurrentThread ; get idle thread address

;
; Set context swap busy for idle thread and acquire the PRCB lock.
;

ifndef NT_UP

mov byte ptr [edi].ThSwapBusy, 1 ; set context swap busy
lock bts dword ptr [ebx].PcPrcbData.PbPrcbLock, 0 ; try to acquire PRCB Lock
jnc short kid33 ; if nc, PRCB lock acquired
lea ecx, [ebx].PcPrcbData.PbPrcbLock ; get PRCB lock address
fstCall KefAcquireSpinLockAtDpcLevel ; acquire current PRCB lock

endif

;
; If a thread had been scheduled for this processor but was removed from
; eligibility (e.g., an affinity change), then the new thread could be the
; idle thread.
;

kid33: mov esi, [ebx].PcPrcbData.PbNextThread ; get next thread address

ifndef NT_UP

cmp esi, edi ; check if idle thread
je short kisame ; if e, processor idle again

endif

and dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; clear next thread
mov [ebx].PcPrcbData.PbCurrentThread, esi ; set new thread address
mov byte ptr [esi]+ThState, Running ; set thread state running

;
; Clear idle schedule since a new thread has been selected for execution on
; this processor and release the PRCB lock.
;

ifndef NT_UP

and byte ptr [ebx].PcPrcbData.PbIdleSchedule, 0 ; clear idle schedule
and dword ptr [ebx].PcPrcbData.PbPrcbLock, 0 ; release current PRCB lock

endif

kid35: ;

CAPSTART <@KiIdleLoop@0,SwapContext>

mov ecx, APC_LEVEL ; set APC bypass disable
call SwapContext ; swap context

CAPEND <@KiIdleLoop@0>

ifndef NT_UP

LowerIrql DISPATCH_LEVEL ; lower IRQL to dispatch level

endif

jmp kid30 ;

;
; The new thread is the Idle thread (same as old thread). This can happen
; rarely when a thread scheduled for this processor is made unable to run
; on this processor. As this processor has again been marked idle, other
; processors may unconditionally assign new threads to this processor.
;

ifndef NT_UP

kisame: and dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; clear next thread
and dword ptr [ebx].PcPrcbData.PbPrcbLock, 0 ; release current PRCB lock
and byte ptr [edi].ThSwapBusy, 0 ; set idle thread context swap idle
jmp kid30 ;

;
; Call idle schedule if requested.
;

kid40: cmp byte ptr [ebx].PcPrcbData.PbIdleSchedule, 0 ; check if idle schedule
je kid10 ; if e, idle schedule not requested
sti ; enable interrupts
lea ecx, [ebx].PcPrcbData ; get current PRCB address
fstCall KiIdleSchedule; attempt to schedule thread
test eax, eax ; test if new thread schedule
mov esi, eax ; set new thread address
mov edi, [ebx].PcPrcbData.PbIdleThread ; get idle thread address
jnz short kid35 ; if nz, new thread scheduled
jmp kid30 ;

endif

fstENDP KiIdleLoop


1: kd> g
Breakpoint 14 hit
eax=00000041 ebx=ba582cc8 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b00720 esp=ba582c1c ebp=ba582c20 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchInterrupt:
80b00720 648b1d1c000000 mov ebx,dword ptr fs:[1Ch] fs:0030:0000001c=f7737000

1: kd> kc 5
#
00 nt!KiDispatchInterrupt
01 hal!HalpDispatchInterrupt
02 hal!KfLowerIrql
03 nt!KiSwapThread
04 nt!KeDelayExecutionThread
1: kd> dx -id 0,0,89831250 -r1 ((basesrv!_KPRCB *)0xf7737120)
((basesrv!_KPRCB *)0xf7737120) : 0xf7737120 [Type: _KPRCB *]
[+0x000] MinorVersion : 0x1 [Type: unsigned short]
[+0x002] MajorVersion : 0x1 [Type: unsigned short]
[+0x004] CurrentThread : 0x8952bc18 [Type: _KTHREAD *]
[+0x008] NextThread : 0x0 [Type: _KTHREAD *]
[+0x00c] IdleThread : 0xf7739fa0 [Type: _KTHREAD *]

[+0x928] ReadySummary : 0x0 [Type: unsigned long]

1: kd> kc
#
00 nt!KiDispatchInterrupt
01 hal!HalpDispatchInterrupt
02 hal!KfLowerIrql
03 nt!KiSwapThread
04 nt!KeDelayExecutionThread
05 nt!NtDelayExecution
06 nt!_KiSystemService
07 SharedUserData!SystemCallStub
08 ntdll!ZwDelayExecution
09 KERNEL32!SleepEx
0a KERNEL32!Sleep
WARNING: Frame IP not in any known module. Following frames may be wrong.
0b 0x0
0c 0x0
0d KERNEL32!BaseThreadStart
1: kd> .process
Implicit process is now 89802bf8
1: kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 899a2278 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 0a200000 ObjectTable: e1000e38 HandleCount: 319.
Image: System


PROCESS 89802bf8 SessionId: 0 Cid: 04b4 Peb: 7ffdf000 ParentCid: 01f4
DirBase: 7a00c000 ObjectTable: e164aa80 HandleCount: 165.
Image: msdtc.exe

1: kd> p
eax=00000041 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b00727 esp=ba582c1c ebp=ba582c20 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchInterrupt+0x7:
80b00727 fa cli
1: kd> p
eax=00000041 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b00728 esp=ba582c1c ebp=ba582c20 iopl=0 nv up di pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000046
nt!KiDispatchInterrupt+0x8:
80b00728 8b838c090000 mov eax,dword ptr [ebx+98Ch] ds:0023:f773798c=00000000
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b0072e esp=ba582c1c ebp=ba582c20 iopl=0 nv up di pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000046
nt!KiDispatchInterrupt+0xe:
80b0072e 0b83c8090000 or eax,dword ptr [ebx+9C8h] ds:0023:f77379c8=00000000
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b00734 esp=ba582c1c ebp=ba582c20 iopl=0 nv up di pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000046
nt!KiDispatchInterrupt+0x14:
80b00734 0b83500b0000 or eax,dword ptr [ebx+0B50h] ds:0023:f7737b50=00000000
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b0073a esp=ba582c1c ebp=ba582c20 iopl=0 nv up di pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000046
nt!KiDispatchInterrupt+0x1a:
80b0073a 741e je nt!KiDispatchInterrupt+0x3a (80b0075a) [br=1]
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b0075a esp=ba582c1c ebp=ba582c20 iopl=0 nv up di pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000046
nt!KiDispatchInterrupt+0x3a:
80b0075a fb sti
1: kd> p
Breakpoint 45 hit
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b0075b esp=ba582c1c ebp=ba582c20 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchInterrupt+0x3b:
80b0075b 80bbe109000000 cmp byte ptr [ebx+9E1h],0 ds:0023:f77379e1=00
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b00762 esp=ba582c1c ebp=ba582c20 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchInterrupt+0x42:
80b00762 7577 jne nt!KiDispatchInterrupt+0xbb (80b007db) [br=0]
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b00764 esp=ba582c1c ebp=ba582c20 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchInterrupt+0x44:
80b00764 83bb2801000000 cmp dword ptr [ebx+128h],0 ds:0023:f7737128=00000000
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b0076b esp=ba582c1c ebp=ba582c20 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchInterrupt+0x4b:
80b0076b 746d je nt!KiDispatchInterrupt+0xba (80b007da) [br=1]
1: kd> p
eax=00000000 ebx=f7737000 ecx=8952bc18 edx=80010031 esi=00000000 edi=804edc60
eip=80b007da esp=ba582c1c ebp=ba582c20 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchInterrupt+0xba:
80b007da c3 ret

没有任何任务需要做。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 18:42:27

【EI复现】参与调峰的储能系统配置方案及经济性分析附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2026/4/15 19:56:07

Langchain-Chatchat打造智慧图书馆服务体系

基于 Langchain-Chatchat 构建智慧图书馆服务体系 在高校与公共图书馆数字化转型的浪潮中&#xff0c;一个长期存在的矛盾日益凸显&#xff1a;馆藏资源越来越丰富&#xff0c;但读者“找得到却读不懂”“查得着却用不上”的问题却愈发严重。传统的关键词检索系统面对“贾宝玉的…

作者头像 李华
网站建设 2026/4/10 22:13:55

Vision Studio C#程序设计基础--多态函数重载、多态符号重载、抽象类、虚方法、密封类和静态类

多态函数重载面向对象四个特性: 封装 继承 多态 抽象多态: 同一个方法不同形态体现多态分静态多态和动态多态静态多态: 函数重载和符号重载动态多态:抽象和虚方法静态多态的函数重载:在同一个范围内,函数名一样,参数的类型不一样、参数的个数不一样,这样的函数就是重载仅仅只有…

作者头像 李华
网站建设 2026/4/15 23:37:13

计算机Java毕设实战-基于springboot的智能民宿预定与游玩系统设计与实现 “住宿 + 游玩” 一体化服务【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华