network怎么断点调试_Windows 网络编程:调试 API

一次性进群,长期免费索取教程,没有付费教程。

教程列表见微信公众号底部菜单

进微信群回复公众号:微信群;QQ群:460500587


7819cb0485083fca4174ca475f1802c1.png

微信公众号:计算机与网络安全

ID:Computer-network

在Windows中有这么一些API函数是专门用来进行调试的,这些函数被称作为Debug API,或者是调试API。利用这些函数可以进行调试器的开发,调试器通过创建有调试关系的父子进程来进行调试,被调试进程的底层信息、即时的寄存器、指令等信息都可以被获取,进而用来分析。

OllyDbg调试器的功能非常得强大,虽然有众多的功能,但是其基础的实现就是依赖于调试API。调试API函数的个数虽然不多,但是合理的使用会产生非常大的作用。调试器依赖于调试事件,调试事件有着非常复杂的结构体,调试器有着固定的流程,由于实时需要等待调试事件的发生,其过程是一个调试循环体,非常类似SDK开发程序中的消息循环。无论是调试事件,还是调试循环,对于调试或者说调试器来说,其最根本、最核心的部分是中断,或者说其最核心的部分是可以捕获中断。

一、3种断点方法

产生中断的方法是设置断点,常见的产生中断的断点方法有3种,一种是中断断点,一种是内存断点,还有一种是硬件断点。下面分别来介绍这3种断点的不同。

中断断点,这里通常指的是汇编语言中的int3指令,CPU执行该指令时会产生一个断点,因此也常称之为INT3断点。现在演示一下如何使用int 3来产生一个断点。代码如下:

c77811b8a4caa60dc9053cb6fc8db0be.png

在代码中使用了__asm,在__asm后面可以使用汇编指令,如果想添加一段汇编指令,方法是__asm{}这样的。通过__asm可以在C语言中进行内嵌汇编语言。在__asm后面直接使用的是int3指令,这样会产生一个异常,称为断点中断异常。对这段简单的代码进行编译连接,并且运行。运行后出现了错误对话框,如图1所示。

0613aaac76e0aa6fa65508c31317edd2.png

图1  异常对话框

这个对话框可能常常见到,而且见到以后多半很郁闷,通常情况是直接单击“不发送”按钮,然后关闭这个对话框。在这里,这个异常是通过int3导致的,不要忙着关掉它。通常在写自己的软件时如果出现这样的错误,应该去寻找一些更多的帮助信息来修正错误。单击“请单击此处”链接,出现如图2所示的对话框。

edc7a7a462d494970fd5da426d8e212b.png

图2“异常基本信息”对话框

弹出“异常基本信息”对话框,因为这个对话框给出的信息实在太少了,继续单击“要查看关于错误报告的技术信息”后面的“单击此处”链接,打开如图3所示的对话框。

55853f9714bcd87d348f2c7400ac967c.png

图3“错误报告内容”对话框

通常情况下,在这个报告中只关心两个内容,一个内容是Code,另一个内容是Address。在图3中,Code后面的值为0x80000003,在Address后面的值为0x0000000000401028。Code的值为产生异常的异常代码,Address是产生异常的地址。在Winnt.h中定义了关于Code的值,在这里0x80000003的定义为STATUS_BREAKPOINT,也就是断点中断。在Winnt.h中的定义为:

5d63e41a5a8d571dfdf805b0bc368b1f.png

这里给的Address可以看出是一个VA(虚拟地址),用OD打开这个程序,直接按F9键运行,如图4、图5所示。

980f864810db561077bd9deccf320969.png

图4  在OD中运行后被断下

fb7cc1aebe585a589a044622ecca983f.png

图5  OD状态栏提示

从图4所示的地方看到,程序执行停在了00401029的位置处。从图5所示的地方可以看到,INT3命令位于00401028的位置处。再看一下图3中Address后面的值,值为00401028。这也就证明了在系统的错误报告中可以给出正确的出错地址的。这样在以后写程序的过程中可以很容易地定位到自己程序中有错误的位置。

回到中断断点的话题上,中断断点是由int3产生的,那么要如何通过调试器(调试进程)在被调试进程中设置中断断点呢?看图4中00401028地址处,在地址值的后面,在反汇编代码的前面,中间那一列的内容是汇编指令对应的机器码。可以看出,INT3对应的机器码是0xCC。如果想通过调试器在被调试进程中设置INT3断点的话,那么只需要把要中断的位置的机器码改为0xCC即可,当调试器捕获到该断点异常时,修改为原来的值即可。

内存断点的方法同样是通过异常来产生的。在Win32平台下,内存是按页进行划分的,每页的大小为4KB。每一页内存都有其各自的内存属性,常见的内存属性有只读、可读写、可执行等。内存断点的原理就是通过对内存属性的修改,而导致本该进行的操作无法进行,这样便会引发异常。

在OD中关于内存断点有两种,一种是内存访问,另外一种是内存写入。用OD随便打开一个应用程序,在其“转存窗口”(或者叫“数据窗口”)中随便选中一些数据点后单击右键,在弹出的菜单中选择“断点”命令,在“断点”子命令下会看到“内存访问”和“内存写入”两种断点,如图6所示。

ea2a322f4ff2b51f0e3bb928b5eaa5a1.png

图6  内存断点类型

下面通过简单例子来看一下如何产生一个内存访问异常,代码如下:

f96dc8448e721d8786937b6262403261.png

在这个程序中,使用了VirtualProtect()函数,该函数与VirtualProtectEx()函数类似,不过VirtualProtect()是用来修改当前进程的内存属性的。

对这个程序编译连接,并运行起来。熟悉的出错界面又出现在眼前,如图7所示。

f2f9b9df090b8b07aad6b75e6a34f102.png

图7“异常基本信息”对话框

按照前面介绍的步骤打开“错误报告内容”对话框,如图8所示。

068063d7fb963ae4bcc5344776a02edc.png

图8“错误报告内容”对话框

按照上面的分析方法来看一下Code和Address这两个值。Code后面的值为0xC0000005,这个值在Winnt.h中的定义如下:

80e0e43412e13f684747779f0ab44e6a.png

这个值的意义表示访问违例。在Address后面的值为0x0000000000402fa3,这个值是地址,但是这里的地址根据程序来考虑,值是用malloc()函数申请的,用于保存数据的堆地址,而不是用来保存代码的地址。这个地址就不进行测试了,因为是动态申请,很可能每次不同,因此大家了解就可以了。

硬件断点是有硬件进行支持的。在OD中使用硬件断点的方法类似于内存断点,同样是在右键菜单中进行设置。由于是由硬件支持的,因此只能设置4个。

二、调试API函数及相关结构体

调试器的根本是依靠中断,其核心也是中断。前面也演示了两个产生中断异常的例子。下面的内容是介绍调试API函数,及其相关的调试结构体。调试API函数的数量非常少,但是其结构体是非常少有的较为复杂的,虽然说是复杂,其实只是嵌套的层级比较多,只要了解了较为常见的,剩下的可以自己对照MSDN进行学习。在介绍完调试API函数及其结构体后,再来简单演示一下,如何通过调试API捕获INT3断点和内存断点。

创建调试关系

既然是调试,那么必然存在调试和被调试。调试和被调试的这种调试关系是如何建立起来的,是我们首先要了解的内容。要使调试和被调试创建调试关系,那么就会用到两个函数中的一个,这两个函数分别是CreateProcess()和DebugActiveProcess()。如何使用CreateProcess()函数来建立一个需要被调试的进程呢?来回顾一下CreateProcess()函数。CreateProcess()函数的定义如下:

b00a6d7a200a1a5c69a77667ff909a68.png

现在要做的是创建一个被调试进程。在CreateProcess()函数中,有一个dwCreationFlags的参数,该参数的取值中有两个重要的常量,分别为DEBUG_PROCESS和DEBUG_ONLY_THIS_PROCESS。DEBUG_PROCESS的作用就是被创建的进程处于调试状态,如果一同指定了DEBUG_ONLY_THIS_PROCESS的话,那么就只能调试被创建的进程,而不能调试被调试进程创建出来的进程。只要在使用CreateProcess()函数时指定这两个常量即可。

除了CreateProcess()函数以外,还有一种创建调试关系的方法,该方法用的函数如下:

71e43d804f36c40af243e55ee0881b70.png

这个函数的功能是附加到一个进程上。该函数的参数就一个,该参数指定了被调试进程的进程ID号。从函数名与函数参数可以看出来,这个函数是和一个已经被创建的进程来建立调试关系的,跟CreateProcess()的方法是不一样的。在OD中也同样有这个功能,打开OD,选择菜单中的“文件”->“挂接”命令,就出现“选择要挂接的进程”窗口,如图9所示。

c15e333d24e03c0295981cf59a636ef7.png

图9“选择要挂接到的进程”的窗口

OD的这个功能就是通过DebugActiveProcess()函数来完成的。

调试器与被调试的目标进程可以通过前两个函数建立调试关系,但是如何使调试器与被调试的目标进程断开调试关系呢?有一个很简单的方法,关闭调试器进程,这样调试器进程与被调试的目标进程会同时结束。也可以关闭被调试的目标进程,这样也可以达到断开调试关系。那如何让调试器与被调试的目标进程断开调试关系,又保持被调试目标进程的运行呢?这里介绍一个函数,函数名为DebugActiveProcessStop(),其定义如下:

d525877a04407ca11a3730d1f7466af2.png

该函数只有一个参数,就是被调试进程的进程ID号。使用该函数可以在不影响调试器进程和被调试进程的正常运行而将两者的关系进行解除。

三、判断是否处于被调试状态

很多程序都要检测自己是否处于被调试状态,比如游戏、病毒,或者加壳后的程序。游戏为了防止被做出外挂而进行反调试,病毒为了给反病毒工程师增加分析难度而反调试,加壳程序是专门用来保护软件的,当然也会有反调试的功能(该功能仅限于加密壳,压缩壳是没有反调试功能的)。

下面不是要介绍反调试,而是要介绍一个简单的函数,这个函数是判断自身是否处于被调试状态,函数名为IsDebuggerPresent(),函数的定义如下:

6d8f98ebaa8d6b5da73c5e5d54538039.png

该函数没有参数,根据返回值来判断是否处于被调试状态。这个函数也可以用来进行反调试。不过由于这个函数的实现过于简单,很容易就能够被分析者突破,因此现在也没有软件再使用该函数来用作反调试了。

下面通过一个简单的例子来演示一下IsDebuggerPresent()函数的使用。代码如下:

f4b69ae5c479e54a45df71f2cae020d5.png

这个例子用来检测是否被调试。在进入主函数后,直接调用IsDebuggerPresent()函数,用来判断是否被调试器创建。在自定义线程函数中,一直循环检测是否被附加。只要发现自身处于被调试状态,那么就在控制台中进行输出提示。

现在用OD对这个程序进行测试。首先用OD直接打开这个程序并按F9键运行,如图10所示。

4694d89cffb0a6fae3ce6eb6eccc520b.png

图10  主函数检测到调试器

按下F9键启动以后,控制台中输出“mian func checked the debuggee”,也就是发现了调试器。

再测试一下检测OD附加的效果。先运行这个程序,然后用OD去挂接它,看其提示,如图11所示。

ee36b1057357d242f4c5c80e781da8a8.png

图11  线程函数检测到调试器

控制台中输出“thread func checked the debuggee”。可以看出用OD进行附加也能够检测到自身处于被调试状态。

进行该测试时请选用原版OD。由于该检测是否处于被调试方法过于简单,因此任何其他修改版的OD都可以将其突破,从而使测试失败。

四、断点异常函数

有时为了调试方便可能会在自己的代码中插入__asm int 3,这样当程序运行到这里时会产生一个断点,就可以用调试器进行调试了。其实微软提供了一个函数,使用该函数可以直接让程序运行到某处的时候产生INT3断点,该函数的定义如下:

070110f1a97013564170c99a84fa041f.png

修改一下前面的程序,把__asm int 3替换为DebugBreak(),编译连接并运行看一下。同样会因产生异常而出现“异常基本信息”对话框,查看它的“错误报告内容”,如图12所示。

b4ec62708ea390e2ab9e7b1d47b428c3.png

图12“错误报告内容”对话框

看一下Code的后面的值,看到值为0x80000003就应该知道是EXCEPTION_BREAKPOINT。再看Address后面的值,值为0x000000007c92120e,从这个地址可以看出,该值在系统的DLL文件中,因为调用的是系统提供的函数。

五、调试事件

调试器在调试程序的过程中,是通过不断地下断点来完成的,而断点的产生在前面的内容中提到过一部分。通过前面介绍的INT3断点和内存断点可以得知,调试器是在捕获目标进程产生的异常从而作出响应。当然,对于介绍的断点来说是这样的,不过对于调试器来说,除了对异常作出响应以外,还会对其他的一些事件作出响应,异常只是所有调试能进行响应事件的一部分。

调试器的工作主要是依赖调试事件,调试事件在系统中被定义为一个结构体,也是到目前为止要接触的最为复杂的一个结构体,因为这个结构体的嵌套关系很多。这个结构体的定义如下:

2aa44e66644dbb640c25a439c588a017.png

这个结构体非常重要,我们有必要详细地介绍一下。

(1)dwDebugEventCode:该字段指定了调试事件的类型编码。在调试的过程中可能产生的调试事件非常多,因此要根据不同的类型码进行不同的响应处理。常见的调试事件如图13所示。

5d74d7f5bc295fb5a0a812c5e30a65a4.png

图13  dwDebugEventCode取值

(2)dwProcessId:该字段指明了引发调试事件的进程ID号。

(3)dwThreadId:该字段指明了引发调试事件的线程ID号。

(4)u:该字段是一个联合体,该联合体的取值由dwDebugEventCode指定。在该联合体中包含了很多个结构体,包括EXCEPTION_DEBUG_INFO、CREATE_THREAD_DEBUG_INFO、CREATE_PROCESS_DEBUG_INFO、EXIT_THREAD_DEBUG_INFO、EXIT_PROCESS_DEBUG_INFO、LOAD_DLL_DEBUG_INFO、UNLOAD_DLL_DEBUG_INFO和OUTPUT_DEBUG_STRING_INFO。

在以上众多的结构体中,特别要介绍一下EXCEPTION_DEBUG_INFO,因为这个结构体中包含了关于异常相关的信息,而对于其他的几个结构体,使用比较简单,大家可以参考MSDN。EXCEPTION_DEBUG_INFO的定义如下:

266fba36f29f4f7d9f8f02b015023e76.png

在EXCEPTION_DEBUG_INFO中包含的EXCEPTION_RECORD结构体中保存着真正的异常信息,对于dwFirstChance里面保存着ExceptionRecord的个数。看一下关于EXCEPTION_RECORD结构体的定义:

a9dda3f1ba3529db9b52937093ff2e9a.png

(1)ExceptionCode:异常码。该值在MSDN中的定义非常多,不过我们需要使用的值只有3个,分别是EXCEPTION_ACCESS_VIOLATION (访问违例)、EXCEPTION_BREAKPOINT(断点异常)和EXCEPTION_SINGLE_STEP(单步异常)。这3个值中的前两个值对于我们来说是非常熟悉的,因为在前面已经介绍过了,关于最后一个单步异常想必也是非常熟悉的了。我们在使用OD快捷键的F7键、F8键时就是在使用单步功能,而单步异常就是有EXCEPTION_SINGLE_STEP来表示的。

(2)ExceptionRecord:指向一个EXCEPTION_RECORD的指针,异常记录是一个链表,其中可能保存着很多的异常信息。

(3)ExceptionAddress:异常产生的地址。

调试事件这个结构体DEBUG_EVENT看似非常复杂,其实也只是嵌套得比较深而已。只要大家去体会每个结构体,体会每层嵌套的含义,自然而然就觉得它没有多么复杂了。

六、调试循环

调试器在不断地对被调试目标进程进行捕获调试信息,有点类似于Win32应用程序的消息循环,但是又有所不同。调试器在捕获到调试信息后,进行相应地处理,然后恢复线程使之继续运行。

用来等待捕获被调试进程调试事件的函数是WaitForDebugEvent(),该函数的定义如下:

6f68a97750d93b0d779dc3d9dee21bc1.png

(1)lpDebugEvent:该参数用于接收保存调试事件;

(2)dwMillisenconds:该参数用于指定超时的时间,无限制等待使用INFINITE。

在调试器捕获到调试事件后会对被调试的目标进程中产生调试事件的线程进行挂起,在调试器对被调试目标进程进行相应的处理后,需要使用ContinueDebugEvent()对先前被挂起的线程进行恢复。ContinueDebugEvent()函数的定义如下:

336317fe202d67ebb507caa97402dc67.png

(1)dwProcessId:该参数表示被调试进程的进程标识符。

(2)dwThreadId:该参数表示准备恢复挂起线程的线程标识符。

(3) dwContinueStatus:该参数指定了该线程以何种方式继续执行,该参数的取值为DBG_EXCEPTION_NOT_HANDLED和DBG_CONTINUE。对于这两个值来说,在通常的情况下并没有什么差别。但是当遇到调试事件中的调试码为EXCEPTION_DEBUG_EVENT时这两个常量就会有不同的动作,如果使用DBG_EXCEPTION_NOT_HANDLED,调试器进程将会忽略该异常,Windows会使用被调试进程的异常处理函数对异常进行处理;如果使用DBG_CONTINUE的话,那么需要调试器进程对异常进行处理,然后继续运行。

由上面两个函数配合调试事件结构体,就可以构成一个完整的调试循环,以下这段调试循环的代码摘自MSDN,代码如下:

a781cd917dff5457fd6fb59c76fe08d1.png

以上就是一个完整的调试循环,不过有些调试事件对于我们来说可能是用不到的,那么就把不需要的调试事件所对应的case语句删除掉就可以了。

七、内存的操作

调试器进程通常要对被调试的目标进程进行内存的读取或写入。对于跨进程的内存读取和写入的函数是ReadProcessMemory() 和WriteProcessMemory()。

要对被调试的目标进程设置INT3断点时,就需要使用WriteProcessMemory()函数对指定的位置写入0xCC。当INT3被执行后,要在原来的位置上把原来的机器码写回去,原来的机器码需要使用ReadProcessMemory()函数来进行读取。

关于内存操作除了以上两个函数以外,还有一个就是修改内存的页面属性的函数,即VirtualProtectEx(),同样这个函数前面也介绍过了。

八、线程环境相关API及结构体

进程是用来向系统申请各种资源的,而真正被分配到CPU并执行代码的是线程。进程中的每个线程都共享着进程的资源,但是每个线程都有不同的线程上下文,或线程环境。Windows是一个多任务的操作系统,在Windows中为每一个线程分配一个时间片,当某个线程执行完其所属的时间片后,Windows会切换到另外的线程去执行。在进行线程切换以前有一步保存线程环境的工作,那就是保存在切换时线程的所有寄存器值、栈信息及描述符等相关的所有信息。只有把线程的上下文保存起来,在下次该线程被CPU再次调度时才能正确地接着上次的工作继续进行。

在Windows系统下,将线程环境定义为CONTEXT结构体,该结构体需要在Winnt.h头文件中找到,在MSDN中并没有给出定义。CONTEXT结构体的定义如下:

c9f596d2c966f033c2d2f7cf607c86af.png

这个结构体看似很大,但是也并不大,只要了解汇编语言的话,结构体中的各个字段应该是非常熟悉的。这里介绍一下ContextFlags字段的功能,该字段用于控制GetThreadContext()和SetThreadContext()能够获取或写入的环境信息。ContextFlags的取值也只能在Winnt.h头文件中找到,其取值如下:

10894ab5bfb9b796e0e6a25931f365fb.png

从这些宏定义的注释来看,能很清楚地知道这些宏可以控制GetThreadContext()和SetThreadContext()进行何种操作。大家在真正使用时进行相应的赋值就可以了。

该结构体可能会在Winnt.h中找到多个定义,因为该结构体是与平台相关的。因此,在各种不同的平台上此结构体有所不同。

线程环境在Windows中定义了一个CONTEXT的结构体,我们要进行获取或设置线程环境的话,需要使用GetThreadContext()和SetThreadContext()。这两个函数的定义分别如下:

5d277977234aa45ffaf950df56a79f85.png

这两个函数的参数都基本一样,hThread表示线程句柄,而lpContext表示指向CONTEXT的指针。所不同的是,GetThreadContext()是用来获取线程环境的,SetThreadContext()是用来进行设置线程环境的。

微信公众号:计算机与网络安全

ID:Computer-network

【推荐书籍】

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

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

相关文章

非会员只能试看20分钟_做欧包都要把铁锅烤半小时?太费电了,用我这办法只需预热几分钟...

最近迷上了做欧包,做欧包要用蒸烤箱,做好的欧包才会外脆内软,如果没有蒸烤箱的话人们又研究出了用铸铁锅来做,先把铸铁锅放在烤箱里烘烤半小时左右,然后把面包坯子放入铸铁锅盖好盖子迅速放回烤箱, 因为锅的…

顺序表归并

对两个顺序表进行合并。 思想&#xff1a;定义两个变量&#xff08;i&#xff0c;j&#xff09;分别指向顺序表 A和B当前处理的元素&#xff0c;若i元素不大于j元素&#xff08;<&#xff09;&#xff0c;则把i元素复制到新表中&#xff0c;否者将j元素复制到新表中。 View …

collection集合 多少钱_Java集合框架大汇总,建议收藏

Java集合Java集合框架&#xff1a;是一种工具类&#xff0c;就像是一个容器可以存储任意数量的具有共同属性的对象。Java集合中成员很丰富&#xff0c;常用的集合有ArrayList&#xff0c;HashMap&#xff0c;HashSet等。线程安全的有Vector&#xff0c;HashTable。线程不安全的…

python识别简单训练模型_使用已经得到的keras模型识别自己手写的数字方式

环境&#xff1a;Pythonkeras&#xff0c;后端为Tensorflow 训练集&#xff1a;MNIST 对于如何训练一个识别手写数字的神经网络&#xff0c;网上资源十分丰富&#xff0c;并且能达到相当高的精度。但是很少有人涉及到如何将图片输入到网络中并让已经训练好的模型惊醒识别&#…

程旭元系统漫画第三期:加班 !

对于苦逼的程旭元来说 加班已经变成了生活中不可缺少的部分 他的原则是不能像胖子那样贪吃 不能像销售员那样狡诈 一定要尽忠职守 精忠报国 &#xff01;老板说什么他就做什么&#xff01; 对于一个从来只说加班不加工资的boss来说 唯命是从就是存活在公司的最好方式~ 但是有…

python 的库如何开发_一篇文章入门Python生态系统

译者按&#xff1a;原文写于2011年末&#xff0c;虽然文中关于Python 3的一些说法可以说已经不成立了&#xff0c;但是作为一篇面向从其他语言转型到Python的程序员来说&#xff0c;本文对Python的生态系统还是做了较为全面的介绍。文中提到了一些第三方库&#xff0c;但是Pyth…

Nginx编译安装和平滑升级

一、Nginx的编译安装 1、安装依赖包gcc&#xff0c;gcc-c&#xff0c;pcre&#xff0c;openssl-devel 命令&#xff1a;yum -y install gcc gcc-c pcre-devel openssl-devel 2、下载Nginx源码包 Nginx下载地址&#xff1a;http://nginx.org/download/nginx-1.12.2.tar.gz …

android ListView详解

在android开发中ListView是比较常用的组件&#xff0c;它以列表的形式展示具体内容&#xff0c;并且能够根据数据的长度自适应显示。抽空把对ListView的使用做了整理&#xff0c;并写了个小例子&#xff0c;如下图。 列表的显示需要三个元素&#xff1a;1&#xff0e;ListVeiw …

Nginx网站用户认证

一、Nginx网站用户认证 用户认证&#xff1a;用户访问网页时需要输入一个用户名和密码才能打开网页。 nginx的默认网页时安装目录下的html/index.html&#xff0c;配置文件在安装目录下的conf目录中的nginx.conf 无用户认证网页 修改配置文件/usr/local/nginx/conf/nginx.conf(…

Nginx基于域名的虚拟主机

一、基于域名的虚拟主机 修改配置文件/usr/local/nginx/conf/nginx.conf 创建新的虚拟主机的根目录和默认网页index.html 重新加载nginx的配置文件 查看两个虚拟主机 因为这两个域名是随便写的&#xff0c;所以需要修改windows系统的hosts文件&#xff0c;让电脑能够解析www.a.…

【100题】第三十四 实现一个队列

一&#xff0c;题目&#xff1a; 生产者消费者线程演示 一个生产者线程将int类型的数入列&#xff0c;一个消费者线程将int类型的数出列 二&#xff0c;分析&#xff1a; 这一个&#xff0c;为操作系统上的一个经典例子&#xff0c;以下是july给出的解答 …

如何给定两个gps坐标 算出航向角_机器人开发如何配置ROS中的TF变换关系?

当我们进行机器人开发时&#xff0c;常常需要面对TF坐标转换&#xff0c;本文以 Autolabor Pro1 与思岚激光雷达为例&#xff0c;介绍ROS TF的使用。Autolabor Pro1是什么&#xff1f;Autolabor Pro1是一款室内外通用机器人移动底盘。该平台上可集成激光雷达、摄像头、GPS等传感…

Tomcat架构与原理

Tomcat架构与原理 架构图 原理 ①、用户点击网页内容&#xff0c;请求被发送到本机端口8080&#xff0c;被在那里监听的Coyote HTTP/1.1 Connector获得。 ②、Connector把该请求交给它所在的Service的Engine来处理&#xff0c;并等待Engine的回应。 ③、Engine获得请求localhos…

c语言转换为python语言_python和c语言

广告关闭 腾讯云11.11云上盛惠 &#xff0c;精选热门产品助力上云&#xff0c;云服务器首年88元起&#xff0c;买的越多返的越多&#xff0c;最高返5000元&#xff01; c语言是编译型语言&#xff0c;经过编译后&#xff0c;生成机器码&#xff0c;然后再运行&#xff0c;执行速…

COJ1196(Staginner 去爬山)

题目大意&#xff1a;给定一个n*m的只含0和1的矩阵&#xff0c;从矩阵的最后一行中的某个1出发&#xff0c;每步只能走到相邻的且是1的格子中&#xff0c;求能达到的最大高度&#xff08;最小行数&#xff09;。 这题直接DFS即可&#xff0c;复杂度为O(N*M)。 View Code 1 #in…

python网站模板下载_Python画图模板大全:从此画图不用愁

1、matplotlib具体参数介绍&#xff1a; 在介绍Python画图模板之前&#xff0c;先买个和大家卖个官司&#xff0c;首先介绍一下Python画图常用库matplotlib的参数&#xff0c;如果只介绍模板&#xff0c;如果你想改一些参数&#xff0c;首先要知道各个参数的含义&#xff0c;那…

二进制安装mysql-5.7.26

一、上传二进制 mysql-5.7.26-linux-glibc2.12-x86_64.tar.gz包 #/data 是数据盘 自己根据情况定 [rootVM_0_10_centos data]# pwd/datatar xf mysql-5.7.26-linux-glibc2.12-x86_64.tar.gzmv mysql-5.7.26-linux-glibc2.12-x86_64 mysql #做软连接 ln -s /data/mysql /usr/lo…

python给矩阵赋值_解决Python二维数组赋值问题

解决Python二维数组赋值问题 当我们采用s[[0]*3]*2初始化一个数组&#xff0c;然后对s[0][0]进行赋值&#xff0c;改变的是第一列所有的值。因为用s [[0]*3]*2 初始化数组&#xff0c;他表示的是指向这个列表的引用&#xff0c;所以当你修改一个值的时候&#xff0c;整个列表都…

python自增_python mysql自增字段AUTO_INCREMENT值的修改方式

在之前得文章中我们说过&#xff0c;如果使用delete对数据库中得表进行删除&#xff0c;那么只是把记录删除掉&#xff0c;并且id的值还会保持上次的状态。 即删除之前如果有四条数据&#xff0c;删除之后&#xff0c;再添加新的数据&#xff0c;id怎会从5开始。 但是我们显示想…