Windows管理内存的3种方式——堆、虚拟内存、共享内存
一、操作系统管理内存概述
在 Windows 操作系统中,每个进程都被分配了 4GB 的虚拟地址空间,这被称为进程的虚拟地址空间。虚拟地址空间提供了一个抽象的地址空间,使得每个进程都可以认为它拥有自己的独立内存空间。这个虚拟地址空间被分为两个部分,即用户空间和内核空间。这个虚拟地址空间的前 2GB 是给操作系统内核使用的,这个部分被称为内核空间。内核空间是由操作系统内核所控制和管理的。它包含了操作系统所需的内存,例如内核代码、驱动程序和系统数据结构等。在 Windows 操作系统中,内核空间的大小是固定的,且不可更改。
现在我来讲一下重点内容:
有很多同学搞不懂虚拟内存是个什么东西,首先我声明一下,我们这里讲的是每个进程拥有的独立的4GB虚拟内存空间,这4GB虚拟内存空间是操作系统给每个进程许诺的一张空头支票,平时这4GB空间不会真的分配给你,而是当你程序的一部分真正需要运行的时候,操作系统才会为你分配物理页,也就是真正的物理内存。再说通俗一点:虚拟内存就是你代码编译的时候,编译器给你写死的一个地址,你的变量存放的位置(比如数组变量,int类型的变量之类的)在编译的时候已经写死了,包括你通过malloc动态申请的内存,这些虚拟地址写死在exe里面,真正执行的时候操作系统需要将虚拟内存给物理内存之间建立一一映射关系,这种映射关系就记录在操作系统的PCB里面,然后上处理机执行的时候会通过MMU自动将虚拟地址转换为物理地址,这里不再赘述,可以简单提示一下,MMU无非就是从PCB里面取出来当前进程的基地址,然后从虚拟地址里获取偏移就可以正确映射物理地址了,至于怎么获取偏移感兴趣的同学可以自行查找资料。这里顺便说一下,你64位的程序也可以把虚拟地址设置为4GB,你只要映射的时候物理地址的基地址选取好,他的寻址空间同样可以扩充到8G,16G,32G等等,这里的4GB虚拟地址空间和你的寻址范围没有什么必然联系!
而虚拟地址空间的后 2GB 则是给用户进程使用的,这个部分被称为用户空间。用户空间中存放着进程所需的代码、数据和堆栈等。用户空间的大小是由操作系统所分配的内存大小决定的,它是可以动态调整的,也就是说当进程需要更多的内存时,操作系统会自动为进程分配更多的空间。
在 Windows 操作系统中,虚拟地址空间中的每一个地址都是虚拟地址,每一个虚拟内存空间都是和操作系统的物理页一一对应的。即虚拟内存通过操作系统内核来将其转换为物理地址,从而能够真正访问对应的内存空间。这个地址转换过程是透明的,进程并不需要关心物理地址是什么。这样的虚拟地址转换机制,保证了每个进程在使用内存时的独立性,也避免了进程之间相互干扰的情况发生。为什么能够保证内存物理空间上的隔离?这是因为操作系统在为进程分配内存的时候首先选择一块空闲的真实物理内存页,然后将分配页的基址以及分配空间的大小写在进程的PCB当中,这样每个程序加上偏移所得到的虚拟地址与物理内存一一映射,从而保证了进程之间的相互隔离。
二、堆内存
我们都知道:栈是操作系统给运行中的程序的分的一块确定大小的内存,这个内存大小是在编译的过程中就已经写死了,由编译器提交给OS来分配确定大小的内存空间,而堆是当我们在程序运行的过程当中想使用一块儿不确定大小内存时动态申请的,通常是在堆里面进行分配内存,即从虚拟内存中割出来一小块作为堆内存来使用。
我们真正想要申请内存只有两个办法,一是通过VirtualAlloc,二是通过CreateFileMapping这两个函数,我们用的HeapAlloc其实也是一种假申请,我们平常用的new(new=malloc+构造函数)和malloc他俩也和内存申请没有关系,他们名义上是指申请内存,实际上根本就不是,malloc是从已经申请好的虚拟内存中割出来一小块供自己使用,VirtualAlloc则可以看成是向操作系统批发物理页的一个过程,在这个过程中的内存申请是真实存在的,而malloc是假申请,它是在这块虚拟内存本身就已经申请过了,归你了的基础上,自己从中割出一小块内存拿来使用。堆内存和栈内存都是在进程启动时,操作系统就已经替我们申请好了。换句话说,就是在你HeapAlloc一小块内存之前,这块内存就已经存在了。
待补充。。。(有时间会更新)
在 Windows 操作系统中,多个进程可能会需要访问同一份数据,例如共享的 DLL 库、共享内存、共享文件等等。为了节约内存,Windows 采用了一种称为“共享页面”的机制,尽量保证同一份数据在物理内存中只有一份,并分别映射到多个进程中。
当多个进程需要访问同一份数据时,Windows 会将这份数据对应的物理内存页面标记为“可共享”的状态,这个过程可以通过一些系统调用(如CreateFileMapping、MapViewOfFile等)来实现。然后,为每个进程分配一个虚拟地址空间,并将这些虚拟地址空间映射到同一个物理内存页面上。这样,每个进程在访问这份数据时,实际上是访问同一个物理内存页面,而不是各自拥有一份独立的拷贝。
三、虚拟内存
1、内存分页的概念:
内存分页是一种将物理内存划分成固定大小的块,并将虚拟内存映射到这些物理内存块的技术。在内存分页的实现中,每个物理内存块被称为一个页框,每个虚拟内存块被称为一个页面。操作系统将虚拟地址空间划分为固定大小的页面,并将其映射到物理地址空间中的页面框中。
内存分页的主要目的是实现虚拟内存,以提高系统的内存利用率。由于虚拟内存允许将页面换入换出到磁盘上,因此在物理内存不足时,可以将不常用的页面换出到磁盘上,从而释放物理内存,并为正在执行的进程提供足够的内存空间。同时,内存分页还可以使操作系统更加灵活地管理内存,以便实现内存保护和共享等功能。
在内存分页的实现中,操作系统通常会将每个页面映射到一个页表项中。页表项包含了虚拟页面的地址、页面的状态信息、页面的访问权限、页面所属的进程等信息。当CPU访问一个虚拟地址时,操作系统会根据该地址对应的页表项,将虚拟地址映射到物理地址,并检查对应的页面是否已经在物理内存中。如果页面不在物理内存中,操作系统就会触发一个缺页异常,将页面从磁盘上读取到物理内存中,并更新页表项。
内存分页是一种将虚拟内存映射到物理内存的技术,它实现了虚拟内存和内存保护等功能,提高了系统的内存利用率,并为操作系统提供了更加灵活的内存管理手段。
2、操作系统分页概述:
操作系统如何管理内存?
》操作系统管理内存是将内存分成一页一页来管理的,每一页的大小是4K也就是0x1000
4G的内存共有1M个页
使用了分页机制之后,4G的地址空间被分成了固定大小的页,每一页或者被映射到物理内存,或者被映射到硬盘上的交换文件中,或者没有映射任何东西。对于一般程序来说,4G的地址空间,只有一小部分映射了物理内存,大片大片的部分是没有映射任何东西。CPU用来把虚拟地址转换成物理地址的信息存放在叫做页目录和页表的结构里。
3、虚拟内存状态:
状态 | |
空闲(FREE) | 内存页不可用 |
保留(Reserve) | 内存页被预定了,但为与物理内存做映射,还是不可用 |
提交(Commit) | 内存被分配,并且与物理内存进行了映射,进程可以使用了 |
当虚拟内存映射到物理内存时,有三种映射方式
4、内存映射方式:
映射方式 | 描述 |
Private | 进程私有内存,不被其他进程所共享,一般是堆,栈 |
Mapped | 从别的内存映射而来 |
Image | 从程序的PE映像映射而来。 |
虚拟内存管理-内存属性:
Windows中,内存管理的最小单元是一个内存页 通常是0x1000=4kb
5、内存分页属性:
ReadOnly | 只读 |
READ_WRITE | 读写 |
EXECUTE | 执行 |
EXECUTE_READ_WRITE | 可读可写可执行 |
WRITE_COPY | 写时拷贝 |
6、页交换文件逻辑:
程序访问虚拟内存地址,操作系统判断数据是否在内存中,如果在就从虚拟地址映射到的物理地址,如果不在就判断是否在页交换文件当中,如果在就查看物理内存是否有闲置空间,有的话,就将页交换文件载入到物理内存,如果没有闲置内存,就从物理内存中找到一个可以释放的页,然后将页保存到页交换文件中。
7、虚拟内存相关API
VirtualAlloc | 分配或者预定一块虚拟内存 |
VirtualAllocEx | 可以在其他进程分配或者预定一块内存 |
VirtualFree | 释放内存 |
VirtualFreeEx | 可以释放其他进程内存 |
VirtualLock | 锁定内存不能交换到硬盘 |
VirtualUnLock | 解锁 |
VirtualProtect | 修改内存读写执行属性 |
VirtualProtectEx | 可以修改其他进程内存属性 |
ReadProcessMemory | 读取远程进程内存 |
WriteProcessMemory | 写入数据到远程进程内存 |
VirtualQuery | 查询内存状态 |
VirtualQueryEx |
8、虚拟内存小案例
下面我们利用虚拟内存的特点来修改C++当中的常量:没错!就是那些你以为不可能被改动的const char*那些玩意🙂🙂
事情是这样的:浮沉最近经常摆烂,他感觉这样不好,于是偷偷黑入了我的电脑,把我虚拟内存常量区的《浮沉学习记录表》偷偷改了,从此,浮沉变成了一个爱学习的乖宝宝😂😂😂
以下是完整的代码部分:(他的代码忘记删了,我复制了一份)
#include <iostream>
#include<Windows.h>
#include<cstring>
int main()
{const char* str = "浮沉今天在好好摆烂!\n";std::cout << "字符串常量修改前:" << str << std::endl;// 获取字符串所在内存区域的地址const void* addr = static_cast<const void*>(str);// 修改内存保护属性为可写属性//记得保存一下内存区域的原有访问属性,以便后续恢复他的属性,避免报错DWORD old_protect;VirtualProtect((char*)addr, strlen(str), PAGE_EXECUTE_READWRITE, &old_protect);// 修改字符串内容char* writable_str = const_cast<char*>(str);char* index = std::strstr(writable_str, "摆烂");if (index != nullptr) {std::memcpy(index, "学习!\0", 7);}// 输出修改后的字符串内容std::cout << "字符串常量修改后:" << str << std::endl;// 恢复内存保护属性为只读属性VirtualProtect((char*)addr, strlen(str), old_protect, &old_protect);return 0;
}
四、文件映射
1、文件映射的概念
文件映射(File Mapping)是Windows系统中一种高效的文件访问方式。它通过将文件数据与进程的虚拟地址空间相关联,这样访问虚拟内存就是直接访问文件了,让进程可以直接读写文件数据,避免了传统的文件I/O操作带来的频繁的磁盘I/O,提高了文件访问的效率。
注:这里的所谓和虚拟地址建立联系,本质就是和物理内存建立联系!表面上来看修改虚拟内存就直接修改磁盘上的文件了,本质上还是要通过物理内存来连接滴😐
2、文件映射的原理
1)文件映射首先通过系统调用创建一个FileMapping映射对象(内核对象)
2)然后将映射对象关联到一个文件或其他I/O设备(关联我们准备操作的文件)
3)最后将映射对象的内容映射到进程的虚拟地址空间中
通过文件映射的方式使得进程可以通过访问内存的方式直接读写文件或设备的数据,修改这个内容之后由操作系统来负责自动同步磁盘上文件内容的更新,所以有时候你在内存修改文件的时候,磁盘上的源文件可能并没有同步更新,这时候我们可以使用Windows提供的另外一个接口FlushViewOfFile来刷新缓冲区,实现将映射在内存当中的文件立即写回到磁盘当中。
这个过程中,操作系统会将映射对象分页,每一页映射到虚拟地址空间的一个页面上,并且会对访问的数据进行合适的缓存,从而提高了文件访问的效率。
3、下面我来详细解释一下为什么通过文件映射可以提高访问文件的访问效率:
首先我们需要明确两点:一是在读取大文件的时候通过文件映射才可以提高访问效率,其实原因很简单,你文件太小的话,两种方式都会直接把整个文件全部读到内存当中去,启动一次磁盘,没什么效率上的区别。二是所谓提高文件访问效率本质就是减少启动磁盘的次数!
因此,我们不难发现,采用传统的方式操作文件,属于一种颗粒度比较大的读取,而且没有通过操作系统进行统一集中管理,比如在操作文件的时候,发现文件的内容没有被加载进内存,通过传统的方式就是要把相关的块一股脑地全部加载进来,但是你很有可能只需要使用其中很少的一部分内容,而且长时间不用还有读回磁盘,这就又需要额外启动磁盘,我们都知道IO设备的访问速度远远慢于内存,你直接按照块去加载会启动不止一次磁盘,而且每一层加载很多内容速率也慢,同时会造成额外的空间开销,而使用文件映射之后,就相当于是把磁盘当中内存来使用了,一样是按页来进行装载,一个页大小4KB大小,十分轻量,就算读取IO也不会浪费太多时间,而且文件映射对象是通过内核进行管理的,可以十分精确地把没有加载的内容装载进来,而不会造成额外的开销!
4、文件映射的应用场景
-
需要频繁地读写大文件或大数据的场景,比如视频编辑、图像处理等,使用文件映射可以提高访问效率,减少磁盘I/O带来的性能瓶颈。
-
需要多个进程共享同一个数据源的场景,比如IPC(进程间通信),使用文件映射可以让多个进程共享同一个数据源,避免了复制数据和进程间通信的开销。
-
实现内存映射文件(Memory-Mapped Files),可以将一个文件映射到内存中,通过读写内存的方式来读写文件数据。内存映射文件可以实现对文件的共享访问,使得多个进程可以共享同一个文件数据。
我们以前操作文件,都是将文件从磁盘当中先读入内存当中,然后在内存当中对文件的内容进行操作,操作完成之后再将文件内容从内存写回磁盘当中,这样的操作方式的效率无疑十分低下。
因此Windows当中提供了一种文件映射内存的方式操作文件,也就是建立虚拟内存和磁盘文件的映射,在这里这个磁盘文件我们就相当于当成是物理内存来用,从虚拟内存的角度来看,磁盘文件和物理内存本质就是一个东西,只不过物理内存的读写速度更快,仅此而已。
5、文件映射常用API:
CreateFileMapping | 创建一个Mapping对象 |
OpenFileMapping | 打开一个Mapping对象 |
MapViewOfFile | 将maping对象的文件映射到内存中 |
UnmapViewOfFile | 取消文件映射 |
FlushViewOfFile | 刷新缓存区,将映射在内存中的文件写回到硬盘中 |
6、下面我们来梳理一下:
正常操作文件:
CreateFile--》文件句柄---》通过文件句柄将文件内容读到虚拟内存,修改内容--》重写写入到文件。
文件映射:
CreateFile打开文件---》创建文件映射对象---》将文件映射到内存---》对内存的操作直接映射到文件当中。
文件映射还有一个重要的特点:可以在不同的进程间共享数据。(跨进程)!!!
下面我们来重点介绍一下文件映射用于共享内存的功能
7、共享内存小案例:
我们可以通过一个小案例来说明一下如何利用共享内存来实现限制程序多开!
事情是这样的,我有一个朋友叫浮沉,他很喜欢摆烂,他经常在电脑上使用一款名叫《只因你太美.exe》的舞蹈软件来欣赏哥哥的舞姿,现在我要在物理上让他实现科学上网:我们都知道”事不过三“这个Old Wisdom,当他打开《只因你太美.exe》软件超过三次,软件就会打开一个弹窗,提醒他停止摆烂,赶紧去学习,从而达到科学上网的目的。
先说思路:我们利用映射内核对象跨进程共享的属性,每次打开一个进程,就在共享内存的这块区域把计数器+1(计数器就存放在共享内存当中),这样新打开的进程就知道在他之前以及打开多少个进程了。注意!这个操作要执行在main函数的最前面,即在开始我们的业务之前进行执行,这样一旦多开数量超过我们设置的阈值,后面的业务程序就不会被执行了。
具体步骤就是:
1)首先通过OpenFileMappingA函数,根据共享内存对象的名称打开我们创建的共享内存对象,如果返回NULL,我们就通过CreateFileMappingA创建一个共享内存对象
2)将共享内存对象的句柄传入MapViewOfFile函数,调用函数后会返回我们创建的共享内存的首地址
3)然后我们通过一个指针来拿到这块内存地址,直接对其进行操作就好了,对于跨进程的程序,如果有必要的话,还应考虑同步,互斥的问题
以下是完整代码:
#include <iostream>
#include<Windows.h>//打开超过3个exe就关闭
//创建一个共享内存,每打开一个进程就操作共享缓冲区的计数器,给他+1
//如果number>阈值就return掉即可#define BUF_SIZE 4096
#define ERROR -1int main()
{//修改控制台输出颜色HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);HANDLE hFileMapping;hFileMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, NULL, "只因你太美");if (hFileMapping == NULL) {hFileMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, // 使用页文件创建文件映射NULL, // 默认安全性PAGE_READWRITE, // 可读可写0, // 文件映射的大小(高位)BUF_SIZE, // 文件映射的大小(低位)"只因你太美" // 共享内存对象名);}if (hFileMapping == NULL) {printf("CreateFileMapping failed (error %lu)\n", GetLastError());return ERROR;}LPVOID lpMapView = MapViewOfFile(hFileMapping, // 共享内存对象句柄FILE_MAP_WRITE, // 可读可写0, // 文件偏移高位0, // 文件偏移低位BUF_SIZE // 映射视图大小);if (lpMapView == NULL) {printf("MapViewOfFile failed (error %lu)\n", GetLastError());CloseHandle(hFileMapping);return ERROR;}//在共享缓冲区写内容int ProcessCnt = 0;DWORD* pBuffer = (DWORD*)lpMapView;*pBuffer += 1;ProcessCnt = *pBuffer;if (ProcessCnt > 3) {MessageBoxA(0, "浮沉摆烂时间大于3天,请立即滚回去学习!", "严重警告!!!", MB_OK);*pBuffer -= 1;return 0;}printf("*****************浮沉摆烂监控程序已启动!*****************\n");printf("应用进程正常加载!但是:\n");printf("今天是浮沉摆烂的第%d天\n", ProcessCnt);system("pause");*pBuffer -= 1;printf("浮沉竟然竟然好好了一天!\n");printf("*****************浮沉摆烂监控被干掉一个!*****************\n");system("pause");SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);return 0;
}
相关文章:

Windows管理内存的3种方式——堆、虚拟内存、共享内存
一、操作系统管理内存概述 在 Windows 操作系统中,每个进程都被分配了 4GB 的虚拟地址空间,这被称为进程的虚拟地址空间。虚拟地址空间提供了一个抽象的地址空间,使得每个进程都可以认为它拥有自己的独立内存空间。这个虚拟地址空间被分为两…...

PCM/FM解调原理与Matlab算法仿真
调制的作用是将调制信息的频谱从低频搬移到高频,以适合信道传输。关于调制的原理,在上一节中已经讲过了。在这一节中,主要讲解FM的解调原理。与调制相对应的是在接收端需要解调过程将调制信息复原,所以解调是影响通信系统性能的重要技术。 解调方法按照是否需要载波恢复的…...

我的『1024』创作纪念日
目录 ◐机缘 ◑收获 ◐日常 ◑成就 ◐憧憬 记得,2020年07月22日我撰写了第1篇技术博客:《遗传算法实例解析》在这平凡的一天,我赋予了它不平凡的意义也许是立志成为一名专业T作者、也许是记录一段刚实践的经验但在那一刻,我已…...

Python ---> 衍生的数据技术
我的个人博客主页:如果’真能转义1️⃣说1️⃣的博客主页 关于Python基本语法学习---->可以参考我的这篇博客:《我在VScode学Python》 随着人工智能技术的发展,挖掘和分析商业运用大数据已经成为一种推动应用, 推动社会发展起着…...

【27】linux进阶——rpm软件包的管理
大家好,这里是天亮之前ict,本人网络工程大三在读小学生,拥有锐捷的ie和红帽的ce认证。每天更新一个linux进阶的小知识,希望能提高自己的技术的同时,也可以帮助到大家 另外其它专栏请关注: 锐捷数通实验&…...

HTTP第六讲——键入网址再按下回车,后面究竟发生了什么?
使用 IP 地址访问 Web 服务器 首先我们运行 www 目录下的“start”批处理程序,启动本机的 OpenResty 服务器,启动后可以用“list”批处理确认服务是否正常运行。 然后我们打开 Wireshark,选择“HTTP TCP port(80)”过滤器,再鼠标…...

layui目录和项目引入
1.目录结构如下 ├─css //css目录 │ │─modules //模块css目录(一般如果模块相对较大,我们会单独提取,比如下面三个:) │ │ ├─laydate │ │ ├─layer │ │ └─layim │ └─layui.css //核心样式文件…...

Ubuntu22.04 将EFI启动分区迁移到另一块硬盘
机器上有两块硬盘, 一块已经安装了Win10, 另一块新装Ubuntu22.04, 在新硬盘上划分分区的时候, 有分出256M给 BOOT EFI, 但是安装的时候没注意, 启动分区不知道怎的跑到 Windows 所在的硬盘上了 记录一下将 /boot/efi 分区迁移至 Ubuntu 所在硬盘, 并创建 Grub 的记录. 预留的…...

只要学会这些AI工具,一个人就是一家营销咨询公司
本教程收集于:AIGC从入门到精通教程 只要学会这些AI工具,一个人就是一家营销咨询公司 随着AI工具的不断涌现,您只需掌握市面上热门的AI工具,便可独自开展营销咨询公司。通过一系列AI工具,您可以为企业提供全案服务,收获丰厚回报。 例如,在协助一家美妆初创公司出海时,…...

[离散数学] 函数
文章目录 函数判断函数的条件复合函数复合函数的性质 逆函数 函数 判断函数的条件 dom F A ⇔ \Leftrightarrow ⇔所有x 都有 F(x)与之对应 有唯一的与其对应 < x , y > ∈ f ∧ < y , z > ∈ f ⇒ y z <x,y>\in f \land <y,z…...

好家伙,又一份牛逼笔记面世了...
最近网传的一些裁员的消息,搞的人心惶惶。已经拿到大厂offer的码友来问我:大厂还能去,去了会不会被裁。 还在学习的网友来问我:现在还要冲互联网么? 我是认为大家不用恐慌吧,该看啥看啥,该学啥…...

基于nodejs+vue3 的高仿网易云音乐
大家好,我是小寻,欢迎大家关注我的公众号:工具优选,加入前端、java群聊哦! 今天给大家分享一个超高水准的项目:基于nodejsvue3研发的高仿网易云音乐,项目内容出自寻码网! 技术栈&a…...

MySQL数据库用户管理以及数据库用户授权
一、数据库用户管理 1、新建用户 CREATE USER 用户名来源地址 [IDENTIFIED BY [PASSWORD] 密码]; ---------------------------------------------------------------------------------------------------------- 用户名:指定将创建的用户名 来源地址:…...

全面分析生物技术的优缺点以及应用场景
一、 引言 生物识别技术具有不可撤销性、高度便利性和较低错误率等优势,在安全领域中也备受瞩目。然而,对于生物识别技术在应对安全挑战方面的可靠性和有效性,但争议并未被完全解决 二、生物识别技术的介绍 所谓生物识别技术就是,…...

OpenAI是什么?
OpenAI是一家人工智能技术公司,成立于2015年,总部位于美国旧金山。它的创始人包括埃隆马斯克等多名知名人士,公司的目标是推进人工智能技术的发展,同时确保人工智能的发展不会对人类造成负面影响。 OpenAI在研究和开发各种人工智能…...

量子计算——新兴领域的前沿技术
随着人类社会文明的不断进步,计算技术也在不断发展。传统计算机在过去的几十年中快速发展,计算速度、存储能力等方面发生了天翻地覆的变化。但随着大数据、人工智能、区块链等新兴领域的迅速崛起,传统计算机的发展似乎面临了瓶颈。在这样的背…...

.Net平台下OpenGL绘制图形(1)(VS2019,Winform,C#)
1、介绍 OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由近350个不同的函数调用组成…...

Casso的创作纪念日
机缘 注册CSDN的时候才刚上大学,到现在使用CSDN已经四年了,距发布第一篇文章却只刚过去一百多天,刚看到这个提醒消息的时候只感慨时间过得真快,自己也在慢慢成长着,当初刚开始学习的时候,查资料用得最多的就…...

Bernhard‘s Talk on Towards Causal NLP 笔记
因果学习系列笔记 这是我的 GitHub 因果学习笔记仓库 https://github.com/xin007-kong/ryCausalLearning,欢迎 star🤩 讲者是 Bernhard Schlkopf talk 链接:(41) Bernhard Schoelkopf | Towards Causal NLP | KeynoteEMNLP 2021 Causal Infer…...

ES6模块化规范
在没有ES6模块化规范前,有像AMD、CMD这样的浏览器模块化规范,还有像CommonJS这样的服务端模块化规范。 2015年,JS终于推出了官方的模块化规范,为了统一各种规范,我们简称ES6 模块化。 ES6目前作为JS的内置模块化系统&a…...

红黑树下岗,内核新数据结构上场:maple tree!
在外界看来,Linux 内核的内部似乎变化很少,尤其是像内存管理子系统(memory-management subsystem)这样的子系统。然而,开发人员时常需要更换内部接口来解决某些长期存在的问题。比如,其中一个问题就是用来保…...

Angular开发之——Angular打包部署项目(04)
一 概述 ng build 构建应用lite-server介绍及安装lite-server部署应用IIS管理器部署应用 二 ng build 构建应用 2.1 执行如下指令构建应用 ng build2.2 构建完成后,会创建一个 dist 文件夹 2.3 直接打开index.html会出错(需要借助于服务器部署) 三 lite-server介…...

深度优先搜索算法思想,题型总结与题目清单(不断更新)
深度优先搜索 深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。这个名称直接来自于这个算法的操作方式:它沿着某一路径深入遍历直到无法继续,然后再回溯进行下一条路径的遍历。 DFS的主要…...

网页三剑客之 CSS
css 在这里不会介绍太多,我们主要重点介绍两个:选择器和盒子模型就够用了。这里看个乐就好了,没有那么多重点,只是简单的认识一下下CSS。 CSS 是什么 CSS 是层叠样式表 (Cascading Style Sheets)的缩写它存在的意义就是…...

Maven(1)--- Maven入门指南
当然,我可以为你提供Maven的详细介绍,并按照6篇文章的方式进行详细展开。下面是第一篇的内容,采用Markdown格式输出: Maven入门指南 什么是Maven? Maven是一个强大的项目管理工具,被广泛应用于Java项目开…...

C# 实现 Websocket通讯聊天 (管用、超好使,点个赞)
1、背景 WebSocket出现之前,Web端为了实现即时通讯,所用的技术都是Ajax轮询(polling)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器…...

知识点回顾(一)
1.final,finally ,finalize final?修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final&…...

verflow属性的常用值详解
什么是overflow 在CSS中,overflow是“溢出”的意思,该属性规定当内容溢出元素框时发生的事情,设置内容是否会被修剪,溢出部分是否会被隐藏;例如当属性值设置为“visible”则内容不会被修剪,为“hidden”则内…...

算法怎么算:贪心算法
总有人在小白面前说:我是搞算法的,不是码农。又或者在想要进阶的时候,有人问你:你懂算法吗? 所有,算法到底是什么? 从目的性来说:它是计算方法,用来达到自己目的的方式…...

【UDS】ISO15765-2之网络时间参数
文章目录 简介分类1. N_As2. N_Ar3. N_Bs4. N_Br5. N_Cs5. N_Cr 总结 ->返回总目录<- 简介 网络层定时参数定义了N_As、N_Ar、N_Bs、N_Br、N_Cs、N_Cr六个参数。 这些时间参数在多帧传输中可以定义在下图的过程中 分类 1. N_As 方向: 发送方->接收方 …...