windows驱动开发-PCI和中断(二)

谈到中断使用PCI总线来作为例子是最合适的,在Windows发展过程中,PCI作为最成功的底层总线,集成了大量的外设,不夸张的说,目前PCI几乎是唯一的总线选择,故大部分情况下,只有PCI设备驱动程序会遇到需要使用中断的情况。

PCI的升级版本是PCIe,但是在协议上,PCIe兼容PCI总线,在编程上,这两者几乎一致,PCIe兼容了PCI的结构和定义,以至于仅看代码很难理解这些知识,这个文档推迟了很久,是因为我还需要一些PCI方面的文档。

直接架构于PCI总线的设备类型包括: USB总线、1394总线、网络适配器、显示适配器等等,PCI可能目前最广泛的总线体系,这套体系不仅仅是软件也是硬件。

PCI总线最早采用的中断机制是INTx,这是基于边带信号的。后续的PCI/PCI-X版本,为了消除边带信号,降低系统的硬件设计复杂度,逐渐采用了MSI/MSI-X的中断机制。INTx一般被称为传统的(Legacy)PCI中断机制,MSI和MSIX中断则属于PCIE后来使用的中断方式。

注意: 由于PCI和PCIe是前向兼容的,所以在下面的讨论中,讨论PCI就是指PCIe。

PCI配置空间

PCI配置空间是PCI(Peripheral Component Interconnect)设备特有的一个物理空间,它支持即插即用功能,允许操作系统自动配置PCI设备的参数。PCI配置空间总长度为256个字节,分为两部分:

配置首部:前64个字节的配置空间称为配置头,对于所有设备都一样,主要功能是用来识别设备、定义主机访问PCI卡的方式(I/O访问或存储器访问,以及中断信息);
本地配置空间:其余的192个字节称为本地配置空间,主要定义卡上局部总线的特性、本地空间基地址及范围等;

PCI分为两种,一种成为PCI设备,另一种则是PCI桥,PCI设备容易理解,PCI桥则是用于将几个PCI总线连接起来的,一般认为每个PCI桥上可以有多个PCI设备。它们都使用相同的首部,不过PCI设备的首部称为type 0,PCI桥则是type 1,不过,遗憾的是,PCI的配置空间实际上是0x0或者0x81,位于PCI配置空间的14字节。

学习PCI最好实际看看PCI的配置空间,下图是我截取我系统上的PCI网卡的配置空间图;:

同样的PCI桥的配置空间如下:

在后续的文档中会详细解释这部分。 遗憾的是,我无法直接修改PCI的中断寄存器,无法演示两种中断模式。

INTx中断

在设备早期的开发中,很可能采用这种方式的中断,但是这种有边带信号触发的中断,有许多缺点: 系统其实无法准确分辨是PCI桥中具体哪一个设备引发的中断,需要调用所有的中断处理程序、系统每次收到中断的时候,数据其实并未准备好;每个设备最多有4个引脚可以触发传统中断。

但是无论如何,这种中断方式在硬件体系设计的早期是非常方便的,设备设计的早些版本很容易触发一个INTx中断,从而传输数据,从INTx中断升级到MSIX中断非常简单,并且对于主机侧来说,代码改动并不算太复杂,也不会改变程序结构。

目前的PCIe设备之间的中断信息传输中使用的并非边带信号INTx,而是基于消息(Message)的。其中Assert_INTx消息表示INTx信号的下降沿;Dessert_INTx消息表示INTx信号的上升沿。当发送这两种消息时,PCIe设备还会将配置空间的相关中断状态bit的值也进行更新。接收中断的物理设备的驱动程序会注册一个或多个中断服务例程, ISR为中断提供服务。 系统每次收到该中断时都会调用 ISR。

当驱动程序收到 IRP_MN_START_DEVICE 请求时,驱动程序将可以处理 IO_STACK_LOCATION 结构的 Parameters.StartDevice.AllocatedResources 和 Parameters.StartDevice.AllocatedResourcesTranslated 成员中的原始中断和已转换的硬件资源。 为了连接其中断,驱动程序使用 AllocatedResourcesTranslated.List.PartialResourceList.PartialDescriptors[] 中的资源。 驱动程序必须扫描部分描述符数组中 CmResourceTypeInterrupt 类型的资源。

如果驱动程序为 SpinLock 提供存储,则必须先调用 KeInitializeSpinLock ,然后才能将其中断旋转锁传递给 IoConnectInterrupt。

从成功调用 IoConnectInterrupt 返回后,如果在驱动程序的设备上启用了中断,或者 ShareVector 设置为 TRUE,则可以调用调用方 ISR。 在 IoConnectInterrupt 返回之前,驱动程序不得启用中断。

注意: 原始中断和已经分配的中断都是系统提供的,一般都会存储它们来为后面的一系列操作做准备。

有两个中断函数,不过先介绍第一个中断函数:

NTSTATUS IoConnectInterrupt([out]          PKINTERRUPT       *InterruptObject,[in]           PKSERVICE_ROUTINE ServiceRoutine,[in, optional] PVOID             ServiceContext,[in, optional] PKSPIN_LOCK       SpinLock,[in]           ULONG             Vector,[in]           KIRQL             Irql,[in]           KIRQL             SynchronizeIrql,[in]           KINTERRUPT_MODE   InterruptMode,[in]           BOOLEAN           ShareVector,[in]           KAFFINITY         ProcessorEnableMask,[in]           BOOLEAN           FloatingSave
);
  • [out] InterruptObject: 指向指向一组中断对象的指针的驱动程序提供的存储地址的指针。 此指针必须在后续调用 KeSynchronizeExecution 中传递;
  • [in] ServiceRoutine: 指向驱动程序提供的 InterruptService 例程的入口点的指针;
  • [in, optional] ServiceContext: 指向驱动程序确定的上下文的指针,该上下文将在调用时提供给 InterruptService 例程。 ServiceContext 区域必须位于驻留内存中:在驱动程序创建的设备对象的设备扩展中、驱动程序创建的控制器对象的控制器扩展中,或位于设备驱动程序分配的非分页池中;
  • [in, optional] SpinLock:指向初始化的旋转锁的指针,驱动程序为其提供存储,该锁将用于同步对由其他驱动程序例程共享的驱动程序确定数据的访问。 如果 ISR 处理多个向量或驱动程序有多个 ISR,则此参数是必需的。 否则,驱动程序无需为中断旋转锁分配存储,并且输入指针为 NULL;
  • [in] Vector: 指定在CM_PARTIAL_RESOURCE_DESCRIPTOR的 u.Interrupt.Vector 成员处的中断资源中传递的中断向量;
  • [in] Irql: 指定在 CM_PARTIAL_RESOURCE_DESCRIPTOR 的 u.Interrupt.Level 成员处的中断资源中 传递的 DIRQL;
  • [in] SynchronizeIrql: 指定运行 ISR 的 DIRQL。 如果 ISR 处理多个中断向量或驱动程序具有多个 ISR,则此值必须是每个中断资源中 u.Interrupt.Level 传递的 Irql 值的最高值。 否则, Irql 和 SynchronizeIrql 值是相同的;
  • [in] InterruptMode: 指定设备中断是 LevelSensitive 还是 Latched;
  • [in] ShareVector: 指定中断向量是否可共享;
  • [in] ProcessorEnableMask: 指定一个 KAFFINITY 值,该值表示此平台中设备中断可能发生的处理器集。 此值在 u.Interrupt.Affinity 处的中断资源中传递;
  • [in] FloatingSave: 指定在驱动程序的设备中断时是否保存浮点堆栈。 对于基于 x86 和基于 Itanium 的平台,此值必须设置为 FALSE; 

 中断例程函数原型如下:

KSERVICE_ROUTINE KserviceRoutine;BOOLEAN KserviceRoutine([in] _KINTERRUPT *Interrupt,[in] PVOID ServiceContext
)
{...}

 下面我们从一个实际的案例来看中断:

NTSTATUS
NICMapHWResources(__in PFDO_DATA FdoData,__in PIRP Irp)
{PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceTrans;PCM_PARTIAL_RESOURCE_LIST       partialResourceListTranslated;PIO_STACK_LOCATION              stack;ULONG                           i;NTSTATUS                        status = STATUS_SUCCESS;DEVICE_DESCRIPTION              deviceDescription;ULONG                           MaximumPhysicalMapping;PDMA_ADAPTER                    DmaAdapterObject;ULONG                           maxMapRegistersRequired, miniMapRegisters;ULONG                           MapRegisters;BOOLEAN bResPort = FALSE, bResInterrupt = FALSE, bResMemory = FALSE;ULONG                           numberOfBARs = 0;
#if defined(DMA_VER2) // To avoid  unreferenced local variables errorULONG                           SGMapRegsisters;ULONG                           ScatterGatherListSize;
#endifstack = IoGetCurrentIrpStackLocation (Irp);PAGED_CODE();if (NULL == stack->Parameters.StartDevice.AllocatedResourcesTranslated) {status = STATUS_DEVICE_CONFIGURATION_ERROR;goto End;}partialResourceListTranslated = &stack->Parameters.StartDevice.\AllocatedResourcesTranslated->List[0].PartialResourceList;resourceTrans = &partialResourceListTranslated->PartialDescriptors[0];// 此处我们可以遍历所有IRP提供的硬件资源,一般是寄存器、端口、中断这三类for (i = 0;i < partialResourceListTranslated->Count;i++, resourceTrans++) {switch (resourceTrans->Type) {case CmResourceTypePort:numberOfBARs++;DebugPrint(LOUD, DBG_INIT,"I/O mapped CSR: (%x) Length: (%d)\n",resourceTrans->u.Port.Start.LowPart,resourceTrans->u.Port.Length);if(numberOfBARs != 2) {DebugPrint(ERROR, DBG_INIT, "I/O mapped CSR is not in the right order\n");status = STATUS_DEVICE_CONFIGURATION_ERROR;goto End;}FdoData->IoBaseAddress = ULongToPtr(resourceTrans->u.Port.Start.LowPart);FdoData->IoRange = resourceTrans->u.Port.Length;FdoData->ReadPort = NICReadPortUShort;FdoData->WritePort = NICWritePortUShort;bResPort = TRUE;FdoData->MappedPorts = FALSE;break;case CmResourceTypeMemory:numberOfBARs++;if(numberOfBARs == 1) {DebugPrint(LOUD, DBG_INIT, "Memory mapped CSR:(%x:%x) Length:(%d)\n",resourceTrans->u.Memory.Start.LowPart,resourceTrans->u.Memory.Start.HighPart,resourceTrans->u.Memory.Length);ASSERT(resourceTrans->u.Memory.Length == 0x1000);FdoData->MemPhysAddress = resourceTrans->u.Memory.Start;FdoData->CSRAddress = MmMapIoSpace(resourceTrans->u.Memory.Start,NIC_MAP_IOSPACE_LENGTH,MmNonCached);if(FdoData->CSRAddress == NULL) {DebugPrint(ERROR, DBG_INIT, "MmMapIoSpace failed\n");status = STATUS_INSUFFICIENT_RESOURCES;goto End;}DebugPrint(LOUD, DBG_INIT, "CSRAddress=%p\n", FdoData->CSRAddress);bResMemory = TRUE;} else if(numberOfBARs == 2){DebugPrint(LOUD, DBG_INIT,"I/O mapped CSR in Memory Space: (%x) Length: (%d)\n",resourceTrans->u.Memory.Start.LowPart,resourceTrans->u.Memory.Length);FdoData->IoBaseAddress = MmMapIoSpace(resourceTrans->u.Memory.Start,resourceTrans->u.Memory.Length,MmNonCached);if(FdoData->IoBaseAddress == NULL) {DebugPrint(ERROR, DBG_INIT, "MmMapIoSpace failed\n");status = STATUS_INSUFFICIENT_RESOURCES;goto End;}FdoData->ReadPort = NICReadRegisterUShort;FdoData->WritePort = NICWriteRegisterUShort;FdoData->MappedPorts = TRUE;bResPort = TRUE;} else if(numberOfBARs == 3){DebugPrint(LOUD, DBG_INIT, "Flash memory:(%x:%x) Length:(%d)\n",resourceTrans->u.Memory.Start.LowPart,resourceTrans->u.Memory.Start.HighPart,resourceTrans->u.Memory.Length);} else {DebugPrint(ERROR, DBG_INIT,"Memory Resources are not in the right order\n");status = STATUS_DEVICE_CONFIGURATION_ERROR;goto End;}break;case CmResourceTypeInterrupt:ASSERT(!bResInterrupt);bResInterrupt = TRUE;//// 将所有中断特定信息保存在设备中// 因为我们稍后在电源暂停和恢复期间需要它来断开和连接//FdoData->InterruptLevel = (UCHAR)resourceTrans->u.Interrupt.Level;FdoData->InterruptVector = resourceTrans->u.Interrupt.Vector;FdoData->InterruptAffinity = resourceTrans->u.Interrupt.Affinity;if (resourceTrans->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {FdoData->InterruptMode = Latched;} else {FdoData->InterruptMode = LevelSensitive;}ASSERT(FdoData->InterruptMode == LevelSensitive);DebugPrint(LOUD, DBG_INIT,"Interrupt level: 0x%0x, Vector: 0x%0x, Affinity: 0x%x\n",FdoData->InterruptLevel,FdoData->InterruptVector,(UINT)FdoData->InterruptAffinity); // casting is done to keep WPP happybreak;default:DebugPrint(LOUD, DBG_INIT, "Unhandled resource type (0x%x)\n",resourceTrans->Type);break;}}if (!(bResPort && bResInterrupt && bResMemory)) {status = STATUS_DEVICE_CONFIGURATION_ERROR;goto End;}NICDisableInterrupt(FdoData);IoInitializeDpcRequest(FdoData->Self, NICDpcForIsr);status = IoConnectInterrupt(&FdoData->Interrupt,NICInterruptHandler,FdoData,                   // ISR ContextNULL,FdoData->InterruptVector,FdoData->InterruptLevel,FdoData->InterruptLevel,FdoData->InterruptMode,TRUE, // shared interruptFdoData->InterruptAffinity,FALSE);if (status != STATUS_SUCCESS){DebugPrint(ERROR, DBG_INIT, "IoConnectInterrupt failed %x\n", status);goto End;}MP_SET_FLAG(FdoData, fMP_ADAPTER_INTERRUPT_IN_USE);RtlZeroMemory(&deviceDescription, sizeof(DEVICE_DESCRIPTION));#if defined(DMA_VER2)deviceDescription.Version = DEVICE_DESCRIPTION_VERSION2;
#elsedeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
#endifdeviceDescription.Master = TRUE;deviceDescription.ScatterGather = TRUE;deviceDescription.Dma32BitAddresses = TRUE;deviceDescription.Dma64BitAddresses = FALSE;deviceDescription.InterfaceType = PCIBus;miniMapRegisters = ((NIC_MAX_PACKET_SIZE * 2 - 2) / PAGE_SIZE) + 2;maxMapRegistersRequired = FdoData->NumTcb * NIC_MAX_PHYS_BUF_COUNT;MaximumPhysicalMapping = (maxMapRegistersRequired-1) << PAGE_SHIFT;deviceDescription.MaximumLength = MaximumPhysicalMapping;DmaAdapterObject = IoGetDmaAdapter(FdoData->UnderlyingPDO,&deviceDescription,&MapRegisters);if (DmaAdapterObject == NULL){DebugPrint(ERROR, DBG_INIT, "IoGetDmaAdapter failed\n");status = STATUS_INSUFFICIENT_RESOURCES;goto End;}if(MapRegisters < miniMapRegisters) {DebugPrint(ERROR, DBG_INIT, "Not enough map registers: Allocated %d, Required %d\n",MapRegisters, miniMapRegisters);status = STATUS_INSUFFICIENT_RESOURCES;goto End;}FdoData->AllocatedMapRegisters = MapRegisters;FdoData->NumTcb = MapRegisters/miniMapRegisters;FdoData->NumTcb = min(FdoData->NumTcb, NIC_MAX_TCBS);DebugPrint(TRACE, DBG_INIT, "MapRegisters Allocated %d\n", MapRegisters);DebugPrint(TRACE, DBG_INIT, "Adjusted TCB count is %d\n", FdoData->NumTcb);FdoData->DmaAdapterObject = DmaAdapterObject;MP_SET_FLAG(FdoData, fMP_ADAPTER_SCATTER_GATHER);#if defined(DMA_VER2)status = DmaAdapterObject->DmaOperations->CalculateScatterGatherList(DmaAdapterObject,NULL,0,MapRegisters * PAGE_SIZE,&ScatterGatherListSize,&SGMapRegsisters);ASSERT(NT_SUCCESS(status));ASSERT(SGMapRegsisters == MapRegisters);if (!NT_SUCCESS(status)){status = STATUS_INSUFFICIENT_RESOURCES;goto End;}FdoData->ScatterGatherListSize = ScatterGatherListSize;#endifFdoData->AllocateCommonBuffer =*DmaAdapterObject->DmaOperations->AllocateCommonBuffer;FdoData->FreeCommonBuffer =*DmaAdapterObject->DmaOperations->FreeCommonBuffer;
End:return status;
}

可以看到,在上面的例子中,我们会调用IoConnectInterrupt函数来连接中断。

这里的代码中有一部分遗憾,由于没有使用自旋锁,所以调用SpinLock之前需要初始化自旋锁这一点并未体现。

还有另外一个遗憾就是,中断本身适合处理器相关的,但是IoConnectInterrupt的返回值中,可能出现STATUS_INVALID_PARAMETER,这个错误的本质是没有指定合适的处理器,但是它的返回值上没有说明这一点,参数ProcessorEnableMask指的就是处理器组,它的解释如下: 

KAFFINITY 类型在 32 位版本的 Windows 上为 32 位,在 64 位版本的 Windows 上为 64 位。

如果组包含 n 个逻辑处理器,则处理器的编号从 0 到 n-1。 组中的处理器编号 i 由关联掩码中的位 i 表示,其中 i 在 0 到 n-1 的范围内。 与逻辑处理器不对应的关联掩码位始终为零。

例如,如果 KAFFINITY 值标识组中的活动处理器,如果处理器处于活动状态,则处理器的掩码位为 1;如果处理器不处于活动状态,则为0。

关联掩码中的位数确定组中逻辑处理器的最大数目。 对于 64 位版本的 Windows,每个组的最大处理器数为 64。 对于 32 位版本的 Windows,每个组的最大处理器数为 32。 调用 KeQueryMaximumProcessorCountEx 例程以获取每个组的最大处理器数。 此数字取决于多处理器系统的硬件配置,但永远不能超过 64 位和 32 位版本的 Windows 分别设置的固定 64 处理器和 32 处理器限制。

GROUP_AFFINITY 结构包含一个相关性掩码和一个组编号。 组号标识应用相关性掩码的组。

禁止和启用中断

和我们的直觉相反,禁止和启用中断非常频繁,这有几个原因:

首先,中断等级直接等于代码运行等级,windows明确规定中断不会被同级别的中断打断,只能被更高级别的中断打断;其次,边带信号可以理解为引脚上拉这种操作,这对于硬件那边也很好设计;最后,中断可以共享,因为系统内可以使用的中断向量只有两百多个,但是明显系统需要使用的中断远多于这个,故共享中断的时候,也需要注意避免中断之间的干扰。

中断的禁用也非常简单:

__inline VOID
NICDisableInterrupt(__in PFDO_DATA FdoData)
{FdoData->CSRAddress->ScbCommandHigh = SCB_INT_MASK;
}KSYNCHRONIZE_ROUTINE NICEnableInterrupt;__inline 
BOOLEAN
NICEnableInterrupt(PVOID Context)
{PFDO_DATA FdoData = Context;FdoData->CSRAddress->ScbCommandHigh = 0;return TRUE;
}

此处解释一下, CSRAddress->ScbCommandHigh是已经映射进系统空间的中断寄存器的地址,对它修改之后,系统会检测到这种行为并设置,硬件那边在发起中断之前可以查询这个寄存器来决定要不要中断。

我们由此也能知道什么时候禁用和启用中断:

在中断例程正在执行时;

系统进入深度睡眠(< S4)同时设备不需要唤醒时;

设备禁用和启用;设备进入D3同时不需要唤醒时;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/12558.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java项目之企业资产管理系统(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的企业资产管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 管理员功能有个人中心&…

SQL Server (MSSQLSERVER) 服务无法启动

解决方法&#xff1a; 打开服务&#xff0c;右键SQL Server (MSSQLSERVER) ->属性->登录&#xff0c;改为本地系统用户

小果网页---套利系统添加了可以套利模块,提供数据api

最近在小果套利系统里面添加了一下可以套利模块&#xff0c;同时实现了盘中自动更新&#xff0c;30分钟更新一次。给大家提交交易参考&#xff0c;可以套利模块的选择 dfdf[df[申购状态] !暂停申购]dfdf[df[申购限额] !无限额]df[溢价率]df[溢价率].astype(float)df[成交量]df…

Nginx的正向代理与反向代理

你好呀&#xff0c;我是赵兴晨&#xff0c;文科程序员。 今天&#xff0c;我们将一起了解什么是Nginx的正向代理&#xff1f;什么是Nginx的反向代理&#xff1f;并实际动手实践。 以下内容都是满满的干货&#xff0c;绝对不容错过。我建议先收藏这篇文章&#xff0c;然后找一…

项目管理-案例重点知识(整合管理)

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 一、整合管理 案例重点 重点内容&#xff1a; &#xff08;1&#xff09;项目章程内容和作用 &#xff08;2&#xff09;项目管理计划…

linux系统介绍和安装教程(含安装链接放在最下面了)

一、linux系统简介 在Linux和C语言的发展历程中&#xff0c;有几位关键人物为它们的诞生和推广做出了重要贡献。 首先&#xff0c;肯汤姆森&#xff08;Ken Thompson&#xff09;是一位在AT&T公司工作的员工&#xff0c;他不仅发明了B语言&#xff0c;还创造了Unix系统。…

python怎么读取xml

引入XML组件&#xff1a;import xml.dom.minidom。 创建一个xml文件&#xff0c;<?xml version"1.0" encoding"utf-8"?>。 加载读取XML文件&#xff0c;xml.dom.minidom.parse(abc.xml)&#xff0c;这是xml文件的对象。 获取XML文档对象&#xff0…

RT-DETR改进教程|加入SCNet中的SCConv[CVPR2020]自校准卷积模块!

⭐⭐ RT-DETR改进专栏|包含主干、模块、注意力机制、检测头等前沿创新 ⭐⭐ 一、 论文介绍 论文链接&#xff1a;http://mftp.mmcheng.net/Papers/20cvprSCNet.pdf 代码链接&#xff1a;https://gitcode.com/MCG-NKU/SCNet/ 文章摘要&#xff1a; CNN的最新进展主要致力于设计更…

微信小程序 - - - - - custom-tab-bar使用自定义tabbar

custom-tab-bar使用自定义tabbar 1. 创建custom-tab-bar组件2. 修改app.json3. tabbar对应页面调整 1. 创建custom-tab-bar组件 各个文件代码如下 /custom-tab-bar/data.js export default [{text: 流水笺,iconPath: /assets/icon/bill.png,selectedIconPath: /assets/icon/bi…

前端 performance api使用 —— mark、measure计算vue3页面echarts渲染时间

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐Performance api计算持续时间&#x1f496; mark用法&#x1f496; measure用法 ⭐计算echarts渲染的持续时间⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于 前端 performance api使用 —— mark、measure计…

Java网络编程之TCP协议核心机制(一)

目录 题外话 正题 TCP协议核心机制 1.确认应答机制 2.超时重传 3.连接管理 三次握手(建立数据连接)和四次挥手(断开连接) 三次握手 三次握手的意义 为什么不能是四次挥手和两次挥手呢??? 四次挥手(断开连接) 四次挥手的意义 四次挥手能变为三次挥手吗? 小结 题…

string功能介绍(普及版)

目录 1。初始化&#xff08;好几种方式&#xff09;&#xff0c;npos和string的使用说明 2。string的拷贝&#xff0c;隐式类型转换&#xff0c;[]&#xff0c;size&#xff0c;iterator&#xff0c;begin&#xff0c;end&#xff0c;reverse&#xff0c;reverse_iterator&am…

基于springboot实现的家具销售电商平台

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&…

DELL服务器配置ILO(idrac)地址、修改管理员密码

服务器型号&#xff1a;DELL PowerEdge R630 1、重启服务器选择F2进入BIOS 2、重启服务器选择F2进入BIOS 3、选择“Network” 4、配置iDRAC的IP&#xff0c;掩码网关&#xff0c;DNS等信息 5、Esc返回&#xff0c;下滑选择“User Configuration” 6、配置iDRAC的用户名密码以及…

Spring Boot集成Ldap快速入门Demo

1.Ldap介绍 LDAP&#xff0c;Lightweight Directory Access Protocol&#xff0c;轻量级目录访问协议. LDAP是一种特殊的服务器&#xff0c;可以存储数据数据的存储是目录形式的&#xff0c;或者可以理解为树状结构&#xff08;一层套一层&#xff09;一般存储关于用户、用户…

WD—C++课前作业—30题

怎么会手和脚都在桌子上 目录 31&#xff0c;声明一个类 String,其数据成员为 char head[100],构造函数 String(char*Head)实现 head 的初始化&#xff0c;成员函数 void reverse()实现 head 内字符串的逆序存放&#xff0c;成员函数 void print()实现 head 内字符串的输出。…

01基础篇

1、初识 JVM 1.1 什么是 JVM JVM 全称是 Java Virtual Machine&#xff0c;中文译名 Java虚拟机。JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件。 Java源代码执行流程如下&#xff1a; 分为三个步骤&#xff1a; 编写Java源代码文件。使用…

Python轻量级Web框架Flask(14)—— 自己做Flask项目总结

0、前言&#xff1a; 本文意在记录自己在做毕业Flask项目开发时遇到的一些问题&#xff0c;并将问题解决方案记录下来&#xff0c;可做日后查询本文也会记录自己做FLask项目时实现的一些功能&#xff0c;作为开发工作的进程记录注意&#xff1a;用Flask开发的前提是已经设计好…

Java | Leetcode Java题解之第89题格雷编码

题目&#xff1a; 题解&#xff1a; class Solution {public List<Integer> grayCode(int n) {List<Integer> ret new ArrayList<Integer>();for (int i 0; i < 1 << n; i) {ret.add((i >> 1) ^ i);}return ret;} }

mongodb备份还原指南

MongoDB 提供的命令行实用程序mongodump和mongorestore创建备份和恢复数据的过程。 一、数据备份 mongorestore和mongodump实用程序可处理BSON数据转储&#xff0c;对于创建小型部署的备份非常有用。要实现弹性且无中断的备份&#xff0c;请将文件系统快照或区块级磁盘快照与…