一口气看完45个寄存器,CPU核心技术大揭秘
作者 | 轩辕之风O
来源 | 编程技术宇宙
头图 | CSDN下载自视觉中国
自1946年冯·诺伊曼领导下诞生的世界上第一台通用电子计算机ENIAC至今,计算机技术已经发展了七十多载。
从当初专用于数学计算的庞然大物,到后来大型机服务器时代,从个人微机技术蓬勃发展,到互联网浪潮席卷全球,再到移动互联网、云计算日新月异的当下,计算机变的形态各异,无处不在。
这七十多年中,出现了数不清的编程语言,通过这些编程语言,又开发了无数的应用程序。
可无论什么样的应用程序,什么样的编程语言,最终的程序逻辑都是要交付给CPU去执行实现的(当然这里有些不严谨,除了CPU,还有协处理器、GPU等等)。所以了解和学习CPU的原理都是对计算机基础知识的夯实大有裨益。
在七十多年的漫长历程中,也涌现了不少架构的CPU。
MIPS
PowerPC
x86/x64
IA64
ARM
······
这篇文章就以市场应用最为广泛的x86-x64架构为目标,通过学习了解它内部的100个寄存器功能作用,来串联阐述CPU底层工作原理。
通过这篇文章,你将了解到:
CPU指令执行原理
内存寻址技术
软件调试技术原理
中断与异常处理
系统调用
CPU多任务技术
什么是寄存器?
寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果以及一些CPU运行需要的信息。
x86架构CPU走的是复杂指令集(CISC) 路线,提供了丰富的指令来实现强大的功能,与此同时也提供了大量寄存器来辅助功能实现。这篇文章将覆盖下面这些寄存器:
通用寄存器
标志寄存器
指令寄存器
段寄存器
控制寄存器
调试寄存器
描述符寄存器
任务寄存器
MSR寄存器
通用寄存器
首当其冲的是通用寄存器,这些的寄存器是程序执行代码最最常用,也最最基础的寄存器,程序执行过程中,绝大部分时间都是在操作这些寄存器来实现指令功能。
所谓通用,即这些寄存器CPU没有特殊的用途,交给应用程序“随意”使用。注意,这个随意,我打了引号,对于有些寄存器,CPU有一些潜规则,用的时候要注意。
eax: 通常用来执行加法,函数调用的返回值一般也放在这里面
ebx: 数据存取
ecx: 通常用来作为计数器,比如for循环
edx: 读写I/O端口时,edx用来存放端口号
esp: 栈顶指针,指向栈的顶部
ebp: 栈底指针,指向栈的底部,通常用ebp+偏移量的形式来定位函数存放在栈中的局部变量
esi: 字符串操作时,用于存放数据源的地址
edi: 字符串操作时,用于存放目的地址的,和esi两个经常搭配一起使用,执行字符串的复制等操作
在x64架构中,上面的通用寄存器都扩展成为64位版本,名字也进行了升级。当然,为了兼容32位模式程序,使用上面的名字仍然是可以访问的,相当于访问64位寄存器的低32位。
rax rbx rcx rdx rsp rbp rsi rdi
除了扩展原来存在的通用寄存器,x64架构还引入了8个新的通用寄存器:
r8-r15
在原来32位时代,函数调用时,那个时候通用寄存器少,参数绝大多数时候是通过线程的栈来进行传递(当然也有使用寄存器传递的,比如著名的C++ this指针使用ecx寄存器传递,不过能用的寄存器毕竟不多)。
进入x64时代,寄存器资源富裕了,参数传递绝大多数都是用寄存器来传了。寄存器传参的好处是速度快,减少了对内存的读写次数。
当然,具体使用栈还是用寄存器传参数,这个不是编程语言决定的,而是编译器在编译生成CPU指令时决定的,如果编译器非要在x64架构CPU上使用线程栈来传参那也不是不行,这个对高级语言是无感知的。
标志寄存器
标志寄存器,里面有众多标记位,记录了CPU执行指令过程中的一系列状态,这些标志大都由CPU自动设置和修改:
CF 进位标志
PF 奇偶标志
ZF 零标志
SF 符号标志
OF 补码溢出标志
TF 跟踪标志
IF 中断标志
······
在x64架构下,原来的eflags寄存器升级为64位的rflags,不过其高32位并没有新增什么功能,保留为将来使用。
指令寄存器
eip: 指令寄存器可以说是CPU中最最重要的寄存器了,它指向了下一条要执行的指令所存放的地址,CPU的工作其实就是不断取出它指向的指令,然后执行这条指令,同时指令寄存器继续指向下面一条指令,如此不断重复,这就是CPU工作的基本日常。
而在漏洞攻击中,黑客想尽办法费尽心机都想要修改指令寄存器的地址,从而能够执行恶意代码。
同样的,在x64架构下,32位的eip升级为64位的rip寄存器。
段寄存器
段寄存器与CPU的内存寻址技术紧密相关。
早在16位的8086CPU时代,内存资源宝贵,CPU使用分段式内存寻址技术:
16位的寄存器能寻址的范围是64KB,通过引入段的概念,将内存空间划分为不同的区域:分段,通过段基址+段内偏移段方式来寻址。
这样一来,段的基地址保存在哪里呢?8086CPU专门设置了几个段寄存器用来保存段的基地址,这就是段寄存器段的由来。
段寄存器也是16位的。
段寄存器有下面6个,前面4个是早期16位模式就引入了,到了32位时代,又新增了fs和gs两个段寄存器。
cs: 代码段
ds: 数据段
ss: 栈段
es: 扩展段
fs: 数据段
gs: 数据段
段寄存器里面存储的内容与CPU当前工作的内存寻址模式紧密相关。
当CPU处于16位实地址模式下时,段寄存器存储段的基地址,寻址时,将段寄存器内容左移4位(乘以16)得到段基地址+段内偏移得到最终的地址。
当CPU工作于保护模式下,段寄存器存储的内容不再是段基址了,此时的段寄存器中存放的是段选择子,用来指示当前这个段寄存器“指向”的是哪个分段。
注意我这里的指向打了引号,段寄存器中存储的并不是内存段的直接地址,而是段选择子,它的结构如下:
16个bit长度的段寄存器内容划分了三个字段:
PRL: 特权请求级,就是我们常说的ring0-ring3四个特权级。
TI: 0表示用的是全局描述符表GDT,1表示使用的是局部描述符表LDT。
Index: 这是一个表格中表项的索引值,这个表格叫内存描述符表,它的每一个表项都描述了一个内存分段。
这里提到了两个表,全局描述符表GDT和局部描述符表LDT,关于这两个表的介绍,下面介绍描述符寄存器时再详述,这里只需要知道,这是CPU支持分段式内存管理需要的表格,放在内存中,表格中的每一项都是一个描述符,记录了一个内存分段的信息。
保护模式下的段寄存器和段描述符到最后的内存分段,通过下图的方式联系在一起:
通用寄存器、段寄存器、标志寄存器、指令寄存器,这四组寄存器共同构成了一个基本的指令执行环境,一个线程的上下文也基本上就是这些寄存器,在执行线程切换的时候,就是修改它们的内容。
控制寄存器
控制寄存器是CPU中一组相当重要的寄存器,我们知道eflags寄存器记录了当前运行线程的一系列关键信息。
那CPU运行过程中自身的一些关键信息保存在哪里呢?答案是控制寄存器!
32位CPU总共有cr0-cr4共5个控制寄存器,64位增加了cr8。他们各自有不同的功能,但都存储了CPU工作时的重要信息:
cr0: 存储了CPU控制标记和工作状态
cr1: 保留未使用
cr2: 页错误出现时保存导致出错的地址
cr3: 存储了当前进程的虚拟地址空间的重要信息——页目录地址
cr4: 也存储了CPU工作相关以及当前人任务的一些信息
cr8: 64位新增扩展使用
其中,CR0尤其重要,它包含了太多重要的CPU信息,值得单独关注一下:
一些重要的标记位含义如下:
PG: 是否启用内存分页
AM: 是否启用内存对齐自动检查
WP: 是否开启内存写保护,若开启,对只读页面尝试写入时将触发异常,这一机制常常被用来实现写时复制功能
PE: 是否开启保护模式
除了CR0,另一个值得关注的寄存器是CR3,它保存了当前进程所使用的虚拟地址空间的页目录地址,可以说是整个虚拟地址翻译中的顶级指挥棒,在进程空间切换的时候,CR3也将同步切换。
调试寄存器
在x86/x64CPU内部,还有一组用于支持软件调试的寄存器。
调试,对于我们程序员是家常便饭,必备技能。但你想过你的程序能够被调试背后的原理吗?
程序能够被调试,关键在于能够被中断执行和恢复执行,被中断的地方就是我们设置的断点。那程序是如何能在遇到断点的时候停下来呢?
对于一些解释执行(PHP、Python、JavaScript)或虚拟机执行(Java)的高级语言,这很容易办到,因为它们的执行都在解释器/虚拟机的掌控之中。
而对于像C、C++这样的“底层”编程语言,程序代码是直接编译成CPU的机器指令来执行的,这就需要CPU来提供对于调试的支持了。
对于通常的断点,也就是程序执行到某个位置下就停下来,这种断点实现的方式,在x86/x64上,是利用了一条软中断指令:int 3来进行实现的。
注意,这里的int不是指高级语言里面的整数,而是表示interrupt中断的意思,是一条汇编指令,int 3则表示中断向量号为3的中断。
在我们使用调试器下断点时,调试器将会把对应位置的原来的指令替换为一个int 3指令,机器码为0xCC。这个动作对我们是透明的,我们在调试器中看到的依然是原来的指令,但实际上内存中已经不是原来的指令了。
顺便提一句,两个0xCC是汉字【烫】的编码,在一些编译器里,会给线程的栈中填充大量的0xCC,如果程序出错的时候,我们经常会看到很多烫烫烫出现,就是这个原因。
言归正传,CPU在执行这条int 3指令时,将自动触发中断处理流程(虽然这实际上不是一个真正的中断),CPU将取出IDTR寄存器指向的中断描述符表IDT的第3项,执行里面的中断处理函数。
而这个中断描述符表,早在操作系统启动之初,就已经提前安排好了,所以执行这条指令后,操作系统的中断处理函数将介入,来处理这一事件。
后面的过程就多了,简单来说,操作系统会把触发这一事件的进程冻结起来,随后将这一事件发送到调试器,调试器拿到之后就知道目标进程触发断点了。这个时候,咱们程序员就能通过调试器的UI交互界面或者命令行调试接口来调试目标进程,查看堆栈、查看内存、变量都随你。
如果我们要继续运行,调试器将会把之前修改的int 3指令给恢复回去,然后告知操作系统:我处理完了,把目标进程解冻吧!
上面简单描述了一下普通断点的实现原理。现在思考一个场景:我们发现一个bug,某个全局整数型变量的值老是莫名其妙被修改,但你发现有很多线程,很多函数都有可能会去修改这个变量,你想找出到底谁干的,怎么办?
这个时候上面的普通断点就没办法了,你需要一种新的断点:硬件断点。
这时候就该本小节的主人公调试寄存器登场表演了。
在x86架构CPU内部,提供了8个调试寄存器DR0~DR7。
DR0~DR3:这是四个用于存储地址的寄存器
DR4~DR5:这两个有点特殊,受前面提到的CR4寄存器中的标志位DE位控制,如果CR4的DE位是1,则DR4、DR5是不可访问的,访问将触发异常。如果CR4的DE位是0,则DR4和DR5将会变成DR6和DR7的别名,相当于做了一个软链接。这样做是为了将DR4、DR5保留,以便将来扩展调试功能时使用。
DR6:这个寄存器中存储了硬件断点触发后的一些状态信息
DR7:调试控制寄存器,这里面记录了对DR0-DR3这四个寄存器中存储地址的中断方式(是对地址的读,还是写,还是执行)、数据长度(1/2/4个字节)以及作用范围等信息
通过调试器的接口设置硬件断点后,CPU在执行代码的过程中,如果满足条件,将自动中断下来。
回答前面提出的问题,想要找出是谁偷偷修改了全局整形变量,只需要通过调试器设置一个硬件写入断点即可。
描述符寄存器
所谓描述符,其实就是一个数据结构,用来记录一些信息,‘描述’一个东西。把很多个描述符排列在一起,组成一个表,就成了描述符表。再使用一个寄存器来指向这个表,这个寄存器就是描述符寄存器。
在x86/x64系列CPU中,有三个非常重要的描述符寄存器,它们分别存储了三个地址,指向了三个非常重要的描述符表。
gdtr: 全局描述符表寄存器,前面提到,CPU现在使用的是段+分页结合的内存管理方式,那系统总共有那些分段呢?这就存储在一个叫全局描述符表(GDT)的表格中,并用gdtr寄存器指向这个表。这个表中的每一项都描述了一个内存段的信息。
ldtr: 局部描述符表寄存器,这个寄存器和上面的gdtr一样,同样指向的是一个段描述符表(LDT)。不同的是,GDT是全局唯一,LDT是局部使用的,可以创建多个,随着任务段切换而切换(下文介绍任务寄存器会提到)。
GDT和LDT中的表项,就是段描述符,描述了一个内存分段的信息,其结构如下:
一个表项占据8个字节(32位CPU),里面存储了一个内存分段的诸多信息:基地址、大小、权限、类型等信息。
除了这两个段描述符寄存器,还有一个非常重要的描述符寄存器:
idtr: 中断描述符表寄存器,指向了中断描述符表IDT,这个表的每一项都是一个中断处理描述符,当CPU执行过程中发生了硬中断、异常、软中断时,将自动从这个表中定位对应的表项,里面记录了发生中断、异常时该去哪里执行处理函数。
IDT中的表项称为Gate,中文意思为门,因为这是应用程序进入内核的主要入口。虽然表的名字叫中断描述符表,但表中存储的不全是中断描述符,IDT中的表项存在三种类型,对应三种类型的门:
任务门
陷阱门
中断门
三种描述符中都存储了处理这个中断/异常/任务时该去哪里处理的地址。三种门用途不一,其中中断门是真正意义上的中断,而像前面提到的调试指令int 3以及老式的系统调用指令int 2e/int 80都属于陷阱门。任务门则用的较少,要了解任务门,先了解下任务寄存器。
任务寄存器
现代操作系统,都是支持多任务并发运行的,x86架构CPU为了顺应时代潮流,在硬件层面上提供了专门的机制用来支持多任务的切换,这体现在两个方面:
CPU内部设置了一个专用的寄存器——任务寄存器TR,它指向当前运行的任务。
定义了描述任务的数据结构TSS,里面存储了一个任务的上下文(一系列寄存器的值),下图是一个32位CPU的TSS结构图:
x86CPU的构想是每一个任务对应一个TSS,然后由TR寄存器指向当前的任务,执行任务切换时,修改TR寄存器的指向即可,这是硬件层面的多任务切换机制。
这个构想其实还是很不错的,然而现实却打了脸,包括Linux和Windows在内的主流操作系统都没有使用这个机制来进行线程切换,而是自己使用软件来实现多线程切换。
所以,绝大多数情况下,TR寄存器都是指向固定的,即便线程切换了,TR寄存器仍然不会变化。
注意,我这里说的的是绝大多数情况,而没有说死。虽然操作系统不依靠TSS来实现多任务切换,但这并不意味着CPU提供的TSS操作系统一点也没有使用。还是存在一些特殊情况,如一些异常处理会使用到TSS来执行处理。
下面这张图,展示了控制寄存器、描述符寄存器、任务寄存器构成的全貌:
模型特定寄存器
从80486之后的x86架构CPU,内部增加了一组新的寄存器,统称为MSR寄存器,中文直译是模型特定寄存器,意思是这些寄存器不像上面列出的寄存器是固定的,这些寄存器可能随着不同的版本有所变化。这些寄存器主要用来支持一些新的功能。
随着x86CPU不断更新换代,MSR寄存器变的越来越多,但与此同时,有一部分MSR寄存器随着版本迭代,慢慢固化下来,成为了变化中那部分不变的,这部分MSR寄存器,Intel将其称为Architected MSR,这部分MSR寄存器,在命名上,统一加上了IA32的前缀。
这里选取三个代表性的MSR简单介绍一下:
IA32_SYSENTER_CS
IA32_SYSENTER_ESP
IA32_SYSENTER_EIP
这三个MSR寄存器是用来实现快速系统调用。
在早期的x86架构CPU上,系统调用依赖于软中断实现,类似于前面调试用到的int 3指令,在Windows上,系统调用用到的是int 2e,在Linux上,用的是int 80。
软中断毕竟还是比较慢的,因为执行软中断就需要内存查表,通过IDTR定位到IDT,再取出函数进行执行。
系统调用是一个频繁触发的动作,如此这般势必对性能有所影响。在进入奔腾时代后,就加上了上面的三个MSR寄存器,分别存储了执行系统调用后,内核系统调用入口函数所需要的段寄存器、堆栈栈顶、函数地址,不再需要内存查表。快速系统调用还提供了专门的CPU指令sysenter/sysexit用来发起系统调用和退出系统调用。
在64位上,这一对指令升级为syscall/sysret。
总结
以上就是全部要介绍的寄存器了,需要说明一下的是,这并不是x86CPU全部所有的寄存器,除了这些,还存在XMM、MMX、FPU浮点数运算等其他寄存器。
这篇文章以x86/x64架构CPU为目标,通过对CPU内部寄存器的阐述,串讲了CPU执行代码机制、内存寻址技术、中断与异常处理、多任务管理、系统调用、调试原理等多种计算机底层知识。
更多精彩推荐
或许,人工智能比你还要老
魏永明:我们的目标是取代物联网中的安卓
最新!百度首发 OCR 自训练平台 EasyDL OCR
面向全场景的鸿蒙操作系统能有多安全?
上线两天用户 10W+,这款 AI 知识图谱小程序有多牛?
相关文章:

用友公司Java面试题(含答案)
为什么80%的码农都做不了架构师?>>> 用友公司Java面试题(含答案) 1.Hashtable和HashMap有什么区别? a.Hashtable是继承自陈旧的Dictionary类的,HashMap继承自AbstractMap类同时是Java 1.2引进的Map接口…

使用memcache做web缓存
为什么80%的码农都做不了架构师?>>> 下载: memcached server [密码: vTI8, 安装启动和调用, 内部有说明] 下载: python-memcached 1.57 现在准备用web.py写个网站, 缓存这块一直没想好用哪个, 今天终于想好了, 直接用memcache # coding: utf-8import me…

Asp.net中DataGrid控件的自定义分页
使用实现起来虽然比较方便,但是效率不高,每次都需要读取所有页(整个记录集),而加载的只是其中一页,造成了资源的浪费,记录多又会使效率变得很低。下面通过DataGrid的自定义分页功能来减少资源使…

实战:在Windows Server2008上配置NLB
1.1 在Windows Server2008上配置NLB 试验环境: DCServer是ESS.COM域的域控制器。 Fileserver和Research属于ESS.COM域,安装有Windows Server 2008企业版。 Sales计算机是ESS.COM域的成员,安装Vista企业版。 试验要求: 实现FileSer…
无人驾驶矿山赛道单笔最大融资:踏歌智行完成2亿元B轮融资
10月30日,矿山无人驾驶运输企业踏歌智行完成了2亿元的B轮融资,本轮融资由前海母基金和宝通投资共同领投,清研资本、蓝焱资本等跟投。踏歌智行继2019年连续完成三轮融资后,再创行业新高。据了解,踏歌智行2019年签订了超…

Python加密—RSA加密
为什么80%的码农都做不了架构师?>>> 公钥加密,私钥解密。 import rsa import base64 from Crypto.PublicKey import RSA # RSA加密解密pubkey -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcB4zYqi3mjdP3E2f9jyPuF0X…

在asp.net中为Web用户控件添加属性和事件
在90年代初,Microsoft为Web程序员提供的 Active Server Pages(ASP)革命性地改变了Web的编程。它可以利用十分易用的模型在Web服务器上动态生成HTML,并且很容易的实现了对数据库的访问,就当时来说,这是一项多么吸引人的技术&#x…
1024 鲲鹏开发者技术沙龙·福州站圆满收官!给程序员的福利你收到了吗?
10月24日,由华为技术有限公司与福建鲲鹏生态创新中心联合主办的“1024鲲鹏开发者技术沙龙”在福州顺利举行。在沙龙上,来自福建鲲鹏生态创新中心运营总监宋宗佑为活动进行致辞,福建鲲鹏生态创新中心生态总监朱晓彤对鲲鹏生态创业中心进行介绍…

IPsec ××× 配置實例
試驗top:ipsec ***的配置包括一下幾個步驟:1.配置ike的協商2.配置ipsec的協商3.配置端口的應用4ike的調試和排錯按照步驟建立ike 的協商策略和參數R1<config>#crypto isakmp policy 編號<1-10000>編號越低優先級越高#hash { md5 | sha1 } 此命令表明設置密匙認…

springMVC参数绑定与数据回显
简单例子:修改商品信息的jsp页面: 参数绑定过程: 1.2.1 默认支持的参数类型 处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。 1.1.1 HttpServletRequest 通过request对象获取请求信息 1.1.2 HttpServletResponse 通…

使用Qt编写模块化插件式应用程序
动态链接库技术使软件工程师们兽血沸腾,它使得应用系统(程序)可以以二进制模块的形式灵活地组建起来。比起源码级别的模块化,二进制级别的模块划分使得各模块更加独立,各模块可以分别编译和链接,模块的升级…

datagrid的正反双向排序
在asp.net中利用datagrid控件按列进行排序很是方便。可是我们只能单项排序!如果我们需要正反排序那么就需要加入一些代码控制一下。 首先我们需要将datagird控件的属性设置为 AllowSorting"True",且需要排序列需要制定排序表达式 eg: SortExpr…
比Python 3.8快20%,Pyston v2正式发布
作者 | 写代码的明哥来源 | Python编程时光头图 | CSDN付费下载于视觉中国Pyston 自从 2017 年发布 0.6.1 版本后,已经淡出了人们的视线三年多了,导致现在新人都很少听过它的大名。前两天(2020年10月28日)Pyston 在官方博客上&…

基于Netty实现的轻量级分布式服务框架
对分布式技术比较感兴趣,于是在闲暇时间写了一个简单的RPC框架娱乐一下,项目持续更新中...... GitHub项目地址: Pudding 如果感觉Pudding对你有帮助可以顺手点个Star哦......哈哈 直接看一下示例代码吧 第一步: 启动注册中心 public class RegistryTest…

在分页状态下删除纪录的问题
在使用DataGrid分页的时候,正常情况下,绑定数据库列表纪录时会自动产生分页的效果,然而我发觉在删除纪录的时候总会发生"无效的 CurrentPageIndex 值。它必须大于等于 0 且小于 PageCount。"的异常,其实解决这个问题很简…
老码农90%的程序猿都是瞎努力,这份路线教你成为高手!
数据正在变得越来越常见,小到我们每个人的社交网络、消费信息、运动轨迹……,大到企业的销售、运营数据,产品的生产数据,交通网络数据……如何从海量数据中获得别人看不见的知识,如何利用数据来武装营销工作、优化产品…

android 365手机秘书源代码
2019独角兽企业重金招聘Python工程师标准>>> 应用到的知识还算挺多的,网络编程,xml解析,通知,广播,联系人,服务等,希望对新手有帮助 运行环境: 在android 2.3.3 运行…

centos安装及网络配置
感谢老师传授,共同学习!谢谢!仅供自己日后复习之用!centos安装关键点:创建分区:/ 系统分区/boot 启动分区SWAP 交换分区,虚拟内存。主要是缓解物理内存不足。虚拟化软件:VMware work…

使用DataGrid动态绑定DropDownList
简单的使用模板列绑定DropDownList,初学者想必都会了,但有时候,我们要做的就是在编辑的时候想让某一列定制为DropDownList,并且根据正常情况下显示的值自动变换DropDownList中所选的值,然后保存选择后的值到数据库或XM…
隐私数据在隐私AI框架中的安全流动
作者 | Rosetta技术团队责编 | 晋兆雨出品 | AI科技大本营本文中,我们将介绍为了保护用户的隐私数据,在隐私 AI 框架的计算任务全流程中,数据是如何以密文形式流动,同时仍正确完成加法、乘法等计算步骤的。隐私 AI 系统存在的目…

MS Sql Server 作业编辑报错以及不显示历史记录
如下图: 解决方法: 1.开始->运行,输入dcomcnfg.exe 2.打开组件服务器->计算机->我的电脑->DCOM配置; 3.找到Microsoft Office Excel或者Microsoft Office Word点击右键->属性 4.选择安全,将启动和激活权…

基于Guava实现的文件复制
需求:现需要将文件D:\A\B\C\abc.txt进行一下操作 1.在文件夹D:\A\B\C下,没有以abc命名的文件夹则创建 2.将目标文件D:\A\B\C\abc.txt复制到abc下 实现代码: /*** 以目标文件名创建文件夹,并将目标文件复制到该文件夹下** param sr…
破局传统算法痛点,腾讯安全首提基于跨模态检索的二进制代码-源代码匹配
整理 | 高卫华出品 | AI科技大本营头图 | CSDN付费下载自视觉中国近日,在NeurIPS 2020正式发布的论文入选名单中,腾讯安全科恩实验室聚焦解决二进制安全问题的《CodeCMR: Cross-Modal Retrieval For Function-Level Binary Source Code Matching》&#…

DataGrid删除确认及Item颜色交替
有时候我们需要在删除DataGrid中Item相对应的数据时,需要弹出一个确认对话框来提示使用者,其实这个功能非常简单,下面的代码可以在DataGrid的Item 中产生颜色交替的效果。 private void dg_ItemDataBound(object sender, DataGridItemEve…

SharePoint 2010 自定义日志
7/6/2012 How to log to the SharePoint ULS Logs(Clean Debugging and Error Logging broken down into steps) By: Philip Stathis 原文地址 http://www.thesharepointblog.net/Lists/Posts/Post.aspx?ID122 This article is meant to introduce a simple error logging rou…

使用Bot Service创建Bot Framework
创建Bot Service:进入至Azure控制台中,新建Bot Service,如不知道Bot Service在哪个选项中,可以先查找Bot Service再创建 在弹出的查询结果中,选择Bot Service,点击后会进入至下一个步骤 在弹出的Bot Servic…

[转]大三下,我们该做什么?一篇被转万次的日志,你值得一看
大三下,我们该做什么?一篇被转万次的日志,你值得一看 还有几个月,你就不得不参加考研、就业大军了,你做好准备了么?你知道211学校、985学校和非211、985的主要区别么?找工作机会一定是不均等的。…

如何在DataGrid里面产生滚动条而不滚动题头
作者Blog:http://blog.csdn.net/cuike519/ 我们在开发的时候一定遇到,使用DataGrid的时候由于不想分页(数据没有那么多)但是又显示不在一页里面,此时我们希望在DataGrid里面出现一个滚动条,可以上下滚动Dat…
“小霸王学习机”再现?树莓派400正式发布,售价70美元
整理 | 高卫华出品 | AI科技大本营头图 | CSDN 下载自视觉中国11月2日,树莓派 4 的制造商正式推出了树莓派 400,这是一款集成了 4GB 内存树莓派 4 的紧凑型键盘。有了树莓派 400,只需使用其两个微型HDMI端口之一,将其插入电视或显…

display的block、none、inline属性及解释
常会用到display对应值有block、none、inline这三个值 参数: block :块对象的默认值。用该值为对象之后添加新行。之前也添加一行。 none :隐藏对象。与visibility属性的hidden值不同,其不为被隐藏的对象保留其物理空间 inline :内联对象的默认值。用该值…