文件系统
文件系统是操作系统用于管理磁盘上文件的方法和数据结构,简单点说就是在磁盘上如何组织文件的方法
任何东西存在硬盘上就是一堆0和1,但是文件怎么存,以什么格式存就是文件系统说了算。不同的操作系统都有自己用来管理文件的方法和数据结构,我们称之为文件系统
我们如何来看我们的盘是什么系统呢
在盘右键点击属性就能看到
这是我主机的C盘的文件系统,D盘(我主机就两个盘)
这是我的U盘的文件系统
|
NTFS |
FAT32 |
磁盘分区容量 |
2T |
32G |
单个文件容量 |
4G以上 |
最大4G |
EFS加密 |
支持 |
不支持 |
磁盘配额 |
支持 |
不支持 |
这里就列出这两个文件的容易理解的区别
EFS加密就是,比如我在C盘创建了两个文本文件,分别写上一些文字,保存然后右键属性,点击高级,有个加密的选项
这个加密就是EFS加密,确定以后,会发现文件的颜色都变了
这个加密对当前用户是没有影响的,当我切换一个用户,就会提示拒绝访问
磁盘配额就是限制别的用户在使用本盘的大小
这里并不是学习NTFS和FAT32是怎么组织的,只是学习相关的API,虽然这两者有许多的区别,但是在API层面是没有太大的差别
分层
在文件系统中,不论是哪种文件系统,最上层的组织形式为卷
逻辑驱动器,就是假设你的硬盘有80G,在装系统的时候把这80G分成4个每个20G,那这个每个20G就是一个卷,这就是文件系统最顶层的组织形式
卷之后就是目录,目录后面可能还有目录
目录之后是文件了
所以文件系统的三层结构:卷、目录、文件
头文件相关解释
在写代码开始之前,需要说明一下需要自定义头文件的事情,下面的代码就是自定义头文件来创建新函数,这里可以自己搜一搜,我这直接提供我写的,有关函数里具体的操作是在FileSystem.cpp文件,而文件系统.cpp是来调用创建的函数的
//FileSystem.h
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
void TestVolume();
void TestDirectory();
void TestFile();
#endif
我的编译器有StdAfx.h文件,如果是VS高版本的话,可能默认没有,不过也不影响,只是要重复加几个头文件罢了
//StdAfx.h 注释给删了,有点占位置
#if !defined(AFX_STDAFX_H__EB17CBDE_4691_4C5B_8747_8F00823AFAFA__INCLUDED_)
#define AFX_STDAFX_H__EB17CBDE_4691_4C5B_8747_8F00823AFAFA__INCLUDED_
#include<stdio.h>
#include<windows.h>
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// 文件系统.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "FileSystem.h"
int main(int argc, char* argv[])
{
//卷的相关API
//TestVolume();
//目录相关API
//TestDirectory();
//文件相关API
TestFile();
return 0;
}
卷相关API
1.获取卷
GetLogicalDrives()
这个函数是没有参数的,返回值是有用的
Return Values
If the function succeeds, the return value is a bitmask representing the currently available disk drives. Bit position 0 (the least-significant bit) is drive A, bit position 1 is drive B, bit position 2 is drive C, and so on.
If the function fails, the return value is zero. To get extended error information, call [GetLastError](JavaScript:hhobj_1.Click()).
返回值
如果函数成功执行,其返回值是一个位掩码,表示当前可用的磁盘驱动器。位位置0(最低有效位)代表驱动器A,位位置1代表驱动器B,位位置2代表驱动器C,以此类推。
如果函数执行失败,返回值为0。要获取扩展的错误信息,请调用GetLastError函数。
就比如说,我运行一下
#include "stdafx.h"
#include "FileSystem.h"
void TestVolume()
{
//1.获取卷名
DWORD volumeName = GetLogicalDrives();
}//下断点,看VolumeName的值
得到一个十进制数,转换成16进制为:80 000C
拆成二进制
1000 0000 0000 0000 0000 1100
从低位开始:A、B、C、D、……,依次
所以是C、D、X是有效的
也确实如此
2.获取一个卷的盘符的字符串
GetLogicalDriveStrings
DWORD GetLogicalDriveStrings(
DWORD nBufferLength, // size of buffer
LPTSTR lpBuffer // drive strings buffer
);
nBufferLength
[in] Specifies the maximum size, in characters, of the buffer pointed to by lpBuffer. This size does not include the terminating null character.
[输入] 指定由 lpBuffer
指向的缓冲区可以容纳的最大字符数。这个大小不包括终止的空字符
lpBuffer
[out] Pointer to a buffer that receives a series of null-terminated strings, one for each valid drive in the system, that end with a second null character.
[输出] 指向一个缓冲区的指针,该缓冲区接收一系列以空字符(null)结尾的字符串,每个字符串对应于系统中的一个有效驱动器,并且以一个额外的空字符结尾。
#include "stdafx.h"
#include "FileSystem.h"
void TestVolume()
{
//2.获取卷的标识符
DWORD BufLen = 100;
char lpBuff[100];
GetLogicalDriveStrings(BufLen,lpBuff);
}
3.获取卷的类型
GetDriveType
UINT GetDriveType(
LPCTSTR lpRootPathName // root directory
);
lpRootPathName
[in] Pointer to a null-terminated string that specifies the root directory of the disk to return information about. A trailing backslash is required. If lpRootPathName is NULL, the function uses the root of the current directory.
[输入] 指向一个以空字符(null)结尾的字符串的指针,该字符串指定了要返回其信息的磁盘的根目录。末尾需要有一个反斜杠(\)。如果 lpRootPathName
为 NULL,则该函数将使用当前目录的根目录。
#include "stdafx.h"
#include "FileSystem.h"
void TestVolume()
{
//3.获取卷的类型
UINT uType = GetDriveType("X:\\");
}
得到的值是5,然后再去文档查
从0开始往下数,也就是DRIVE_CDROM
虚拟机的C盘和D盘都是3,DRIVE_FIXED
该磁盘无法从驱动器中移除。
下面的是用主机来测试U盘的效果(我的虚拟机不读U盘T_T),得到2,DRIVE_REMOVABLE
该磁盘可以从驱动器中移除。
4.获取卷的详细信息
GetVolumeInformation
BOOL GetVolumeInformation(
LPCTSTR lpRootPathName, // IN 磁盘驱动器字符串
LPTSTR lpVolumeNameBuffer, // OUT 磁盘驱动器卷标名称
DWORD nVolumeNameSize, // IN 磁盘驱动器卷标名称长度
LPDWORD lpVolumeSerialNumber, // OUT 磁盘驱动器卷标序列号
LPDWORD lpMaximumComponentLength, // OUT 系统允许的最大文件名长度
LPDWORD lpFileSystemFlags, // OUT 文件系统标识
LPTSTR lpFileSystemNameBuffer, // OUT 文件操作系统名称
DWORD nFileSystemNameSize // IN 文件操作系统名称长度
);
lpRootPathName
查看那个盘,就写那个盘的标识,比如"C:\\"
lpVolumeNameBuffer
这是个OUT参数,意思为卷标,也就是盘符的别名
这里我就改成了123,右键属性里可以修改
nVolumeNameSize
卷标的长度,假设卷标有20,但你设置了10,那么就只会取这10
lpVolumeSerialNumber
卷标序列号,并不是硬盘序列号,硬盘序列号是生产厂家设置的,用来区别产品,设置好了就不会改变。而卷标就是硬盘划分而来的,如果重装系统,重新格式化硬盘的话,卷标就会发生变化
lpMaximumComponentLength
允许的文件或文件夹名字的长度
lpFileSystemFlags
会得到里面的宏,这个可以自行查阅
lpFileSystemNameBuffer
文件系统名称,就是NTFS和FAT32
nFileSystemNameSize
文件操作系统名称长度
#include "stdafx.h"
#include "FileSystem.h"
void TestVolume()
{
//4.获取卷的信息
TCHAR volumeName[260] = {0};
DWORD volumeSerial = 0;
DWORD MaxLength = 0;
DWORD FileSysFlags = 0;
TCHAR FileSystName[260] = {0};
GetVolumeInformation(
"C:\\",
volumeName,
260,
&volumeSerial,
&MaxLength,
&FileSysFlags,
FileSystName,
260
);
printf("盘符:%s\n卷标序列号:%08X\n最大文件长度:%d\n文件系统标识:%08X\n文件系统名称:%s\n",volumeName,volumeSerial,MaxLength,FileSysFlags,FileSystName);
}
这样就能得到相关信息
目录相关API
1.创建目录
CreateDirectory
BOOL CreateDirectory(
LPCTSTR lpPathName, // directory name
LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD
);
第一个参数是创建目录的路径加上名字,第二个是安全描述符
#include "stdafx.h"
#include "FileSystem.h"
void TestDirectory()
{
//1.创建目录
CreateDirectory("C:\\A",NULL);
}
执行后会发现多了个A文件夹
2.删除目录
RemoveDirectory
BOOL RemoveDirectory( LPCTSTR lpPathName // directory name);
参数是创建目录的路径加上名字
#include "stdafx.h"
#include "FileSystem.h"
void TestDirectory()
{
//2.删除目录
RemoveDirectory("C:\\A");
}
执行后发现A目录就删除了,如果目录里有东西就删不掉了
3.修改目录名称
MoveFile
BOOL MoveFile(
LPCTSTR lpExistingFileName, // file name
LPCTSTR lpNewFileName // new file name
);
第一个原目录名称,第二个新目录名称
void TestDirectory()
{
//3.修改目录名称
MoveFile("C:\\B","C:\\C");
}
先在C盘下创建B文件夹,执行后修改成了C,并且,里面有文件也可修改
4.获取并设置程序(进程)当前目录
GetCurrentDirectory(获取)
这个就参考进程章节里的模块路径和工作路径,在这章节是讲过的,就不重复了
void TestDirectory()
{
//4.获取程序当前目录
CHAR CurrDirectory[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, CurrDirectory);
printf("当前目录:%s\n", CurrDirectory);
}
当我不设置路径创建目录时,就会在这个目录下创建
SetCurrentDirectory(设置)
当我不想在上面的目录下创建,但又不想写全路径,就可以用这个函数
void TestDirectory()
{
//5.设置程序当前目录
SetCurrentDirectory("C:\\");
CreateDirectory("A",NULL);
}
可以看到在C盘下创建了,而不是文件系统这个目录下创建的
文件相关API
1.创建文件
CreateFile
HANDLE CreateFile(
LPCTSTR lpFileName, // file name
DWORD dwDesiredAccess, // access mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to template file
);
lpFileName
文件路径
dwDesiredAccess
文件的权限
可读还是可写
dwShareMode
指定该对象的共享类型
当设置为0时,就是排他的,就是除了我别人都不能使用,只有等我用完了关掉后,别人才能对这个有操作,有点互斥的意思了
当我想共享时,就要设置成如下
看名字就能猜到是干啥的,比如说第一个,可共享删除,当我执行后,还没关闭之前,别的程序是可以删掉这个文件的,但是也仅限于删除,其它的也同理
lpSecurityAttributes
安全描述符
dwCreationDisposition
以什么形式创建
CREATE_NEW:创建一个新文件,如果已经存在则失败
CREATE_ALWAYS:创建一个新文件,如果已经存在,则覆盖
OPEN_EXISTING:打开文件,如果不存在则失败
dwFlagsAndAttributes
文件的属性
有点小多就不截完,看名字也能猜出来,这里就自行查阅
hTemplateFile
很少用到,不用管,直接填空
#include "stdafx.h"
#include "FileSystem.h"
void TestFile()
{
//1.创建文件
HANDLE hFile = CreateFile(
"C:\\A.txt",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_HIDDEN, //隐藏属性
NULL
);
//2.关闭文件
CloseHandle(hFile);
}
3.获取文件长度
GetFileSize
DWORD GetFileSize(
HANDLE hFile, // handle to file
LPDWORD lpFileSizeHigh // high-order word of file size
);
第一个是文件句柄,第二个参数返回文件大小的高32位字,这个参数对32位没用
返回值是文件大小的低序双字,详情看MSDN
void TestFile()
{
//1.创建文件
HANDLE hFile = CreateFile(
"C:\\notepad.exe",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_HIDDEN, //隐藏属性
NULL
);
//3.获取文件长度
DWORD High_32 = 0;
DWORD Sizelow = GetFileSize(hFile,&High_32);
CloseHandle(hFile);
}
返回的值与大小一样,执行成功
4.获取文件的属性和信息
GetFileAttributesEx
BOOL GetFileAttributesEx(
LPCTSTR lpFileName, // file or directory name
GET_FILEEX_INFO_LEVELS fInfoLevelId, // attribute
LPVOID lpFileInformation // attribute information
);
lpFileName
文件路径
fInfoLevelId
可以看到只有这一个可选参数
lpFileInformation
[out] Pointer to a buffer that receives the attribute information. The type of attribute information stored into this buffer is determined by the value of fInfoLevelId.
[输出] 指向一个缓冲区的指针,该缓冲区用于接收属性信息。存储在此缓冲区中的属性信息的类型由 fInfoLevelId
的值确定。
所以去看看上面的唯一的可选参数
后面意思是,返回值会返回在WIN32_FILE_ATTRIBUTE_DATA结构体中,所以第三个参数就是这个类型了
WIN32_FILE_ATTRIBUTE_DATA
typedef struct _WIN32_FILE_ATTRIBUTE_DATA{
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA;
dwFileAttributes
定义文件或目录的属性信息,属性有点多,就不截图了,自行查阅文档
ftCreationTime
指定文件或目录被创建的时间
ftLastAccessTime
对于文件,指定了最后一次被读取或写入的时间,对于目录是创建的时间
ftLastWriteTime
对于文件,指定了最后一次被写入的时间,对于目录是创建的时间
由于以上的三个成员都是FILETIME结构体的,并不直接是时间戳,所以要转换成时间,还要看看FILETIME结构体是怎么说的
若要将 FILETIME 结构转换为易于向用户显示的时间,请使用 FileTimeToSystemTime 函数。
nFileSizeHigh
文件大小的高32位
nFileSizeLow
文件大小的低32位
void TestFile()
{
//4.获取文件的属性和信息
WIN32_FILE_ATTRIBUTE_DATA data = {0};
SYSTEMTIME SystTime;
GetFileAttributesEx("C:\\A.txt",GetFileExInfoStandard,&data);
printf("属性:%d\n",data.dwFileAttributes);
printf("创建时间:%x\n",data.ftCreationTime);
printf("最后访问时间:%x\n",data.ftLastAccessTime);
printf("最后写入时间:%x\n",data.ftLastWriteTime);
printf("文件大小高32位:%d\n",data.nFileSizeHigh);
printf("文件大小低32位:%d\n",data.nFileSizeLow);
FileTimeToSystemTime(&data.ftCreationTime, &SystTime);
printf("%d年%d月%d日-%d时%d分%d秒\n", SystTime.wYear, SystTime.wMonth, SystTime.wDay, SystTime.wHour, SystTime.wMinute, SystTime.wSecond);
}
说一下属性,就是属性的宏组合相加而得出的值
这里我运算出的时间,却与我在文件的属性中相比不一样,只有小时不一样
还请大佬解释一下,我百思不得其解
5.读取文件
ReadFile
BOOL ReadFile(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer
);
这里就快速过一遍
文件句柄、存的数据空间、读多少个、读了多少个、网络编程相关,填空
void TestFile()
{
//1.创建文件
HANDLE hFile = CreateFile(
"C:\\A.txt",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_HIDDEN, //隐藏属性
NULL
);
//5.读取文件
//5.1分配空间
LPSTR buff = (LPSTR)malloc(10);
ZeroMemory(buff,10);
//5.2设置当前读取位置
SetFilePointer(hFile,0,NULL,FILE_BEGIN);
//5.3读取数据
DWORD ReadLen = 0;
ReadFile(hFile,buff,10,&ReadLen,NULL);
printf("%s\n",buff);
//5.4释放内存
free(buff);
//5.5关闭句柄
CloseHandle(hFile);
}
这里注意别越界了加上NULL不要超过10
6.写入文件
WriteFile
BOOL WriteFile(
HANDLE hFile, // handle to file
LPCVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToWrite, // number of bytes to write
LPDWORD lpNumberOfBytesWritten, // number of bytes written
LPOVERLAPPED lpOverlapped // overlapped buffer
);
这些参数我相信大家都能理解了,就不多说了
void TestFile()
{
//1.创建文件
HANDLE hFile = CreateFile(
"C:\\A.txt",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_HIDDEN, //隐藏属性
NULL
);
//6.写入文件
TCHAR buff[] = TEXT("吾爱破解");
DWORD WriteLen = 0;
WriteFile(hFile,buff,strlen(buff),&WriteLen,NULL);
//关闭文件
CloseHandle(hFile);
}
7.拷贝文件
CopyFile
BOOL CopyFile(
LPCTSTR lpExistingFileName, // name of an existing file
LPCTSTR lpNewFileName, // name of new file
BOOL bFailIfExists // operation if file exists
);
这里说说第三个参数
比如CopyFile(A,B,FALSE),这里为FALSE的话,不管原来是否存在B,直接覆盖,如果存在且此处为TRUE,就返回失败
void TestFile()
{
//7.拷贝文件
CopyFile("C:\\A.txt","C:\\B.txt",FALSE);
}
8.删除文件
void TestFile()
{
//8.删除文件
DeleteFile("C:\\B.txt");
}
9.查找文件
FindFirstFile
HANDLE FindFirstFile(
LPCTSTR lpFileName, // file name
LPWIN32_FIND_DATA lpFindFileData // data buffer
);
FindNextFile
BOOL FindNextFile(
HANDLE hFindFile, // search handle
LPWIN32_FIND_DATA lpFindFileData // data buffer
);
可以自行查阅其用法,来查找相关文件
void TestFile()
{
//9.查找文件
WIN32_FIND_DATA FindData;
HANDLE hFile = FindFirstFile("C:\\*.*",&FindData);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("没有找到文件,错误代码:%d\n",GetLastError());
}
else
{
printf("找到文件\n");
// printf("%s\n",&FindData.cFileName);
}
BOOL Result = FALSE;
int i = 0;
while(!Result)
{
if(!FindNextFile(hFile,&FindData))
{
Result = TRUE;
return;
}
printf("%s\n",FindData.cFileName);
i++;
}
}
这里就是循环查找C盘下的所有文件和目录,搞了好久才弄懂FindNextFile,弄得太晚了,脑袋都不灵光了,意思就是看你查找下一个的还有这种类型的文件吗?比如说我C盘下只有一个A.txt,如果我还去查有无其它的txt,那么就打印不出A.txt,即便你有两个比如A和B两个txt,那也只会打印出B(这里是按照ASCII编码顺序打印的),因为B在A的后面,所以这个函数是只会找满足条件的下一个文件
内存映射文件
什么是内存映射文件
把硬盘上的文件直接映射在物理内存上,这个物理内存也映射在虚拟内存上,这样操作这个进程就相当于在操作这个文件
这样操作就避免了一些麻烦的打开文件、关闭文件等繁琐操作
最重要的优点是当我们读取的文件较大时,内存映射文件的性能较好
代码实现
这里建议自行写,因为都是前面说过的,可以用来巩固一边,翻看以前的笔记来编写,加深印象
#include "stdafx.h"
#include "MappingFile.h"
#define MAPNAME "共享内存"
DWORD MapFile(LPSTR lpcFile)
{
HANDLE hFile;
HANDLE hMap;
LPVOID lpAdr;
//1.得到文件的句柄
hFile = CreateFile(
lpcFile,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("文件句柄获取失败:%d\n",GetLastError);
return 0;
}
//2.创建物理页并关联文件
hMap = CreateFileMapping(
hFile,
NULL,
PAGE_READWRITE,
0,
0,
MAPNAME
);
if(hMap == NULL)
{
printf("物理页与文件关联失败:%d\n",GetLastError);
return 0;
}
//3.映射到虚拟内存
lpAdr = MapViewOfFile(
hMap,
FILE_MAP_COPY,//FILE_MAP_COPY FILE_MAP_ALL_ACCESS
0,0,0
);
if(lpAdr == NULL)
{
printf("映射虚拟内存失败:%d\n",GetLastError);
CloseHandle(hFile);
CloseHandle(hMap);
return 0;
}
//4.读取文件
DWORD Test1 = *(PDWORD)lpAdr;
DWORD Test2 = *((PDWORD)lpAdr+0x40FC);
printf("%x %x\n",Test1,Test2);
return 0;
}
1.得到文件的句柄:这部分没什么说的
2.创建物理页并关联文件:CreateFileMapping函数在以前的学习中是用-1的宏来写的,意为不要文件只要物理页,这里就要传文件句柄了
3.映射到虚拟内存:这里没什么讲的,至于FILE_MAP_COPY,后面会讲到
4.读取文件:上面说过了只要映射了,操作内存就是操作文件,所以这里的*(PDWORD)lpAdr就是文件开始地方
*((PDWORD)lpAdr+0x40FC)是文件末
可以看到并不是0x40FC,这是因为lpAdr本就是一个指针类型
LPVOID A 32-bit pointer to an unspecified type.
一个指向未指定类型的32位指针
相当于是4字节的指针,所以指针运算的时候需要先砍掉一颗星,在与数字乘以砍掉一颗星后的宽度运算
比如
int* x = (int*)1;
x++;
//得到的x的值是5
再回到这里就能理解了,原来是0x103F0除以4就得到0x40FC
运行结果
可以得知完全一样
内存映射文件之共享
内存映射文件也是可以共享的,并且系统中绝大多数都是这样的
//5.写入文件
*(PDWORD)lpAdr = 0x41414141;
printf("写入A %x\n",*(PDWORD)lpAdr);
getchar();
//强制更新缓存
//FlushViewOfFile(((PDWORD)lpAdr),4) //当修改文件的时候,数据并不是立即修改,而是在关闭资源中做收尾工作,要想立即修改就用本函数
//6.关闭资源
UnmapViewOfFile(lpAdr);
CloseHandle(hFile);
CloseHandle(hMap);
在3.映射到虚拟内存中FILE_MAP_COPY换成FILE_MAP_ALL_ACCESS
原因后面再说,然后在另写一个测试代码
#include<stdio.h>
#include<windows.h>
#define MAPNAME "共享内存"
int main()
{
HANDLE hMap;
LPVOID lpAdr;
//1.打开对象
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,MAPNAME);
if(hMap == NULL)
{
printf("打开失败:%d\n",GetLastError);
return 0;
}
//2.映射到内存
lpAdr = MapViewOfFile(
hMap,
FILE_MAP_ALL_ACCESS,//FILE_MAP_COPY FILE_MAP_ALL_ACCESS
0,0,0
);
if(lpAdr == NULL)
{
printf("映射虚拟内存失败:%d\n",GetLastError);
CloseHandle(hMap);
return 0;
}
//3.读取数据
DWORD Test = *(PDWORD)lpAdr;
printf("读取A %x\n",Test);
//4.释放资源
UnmapViewOfFile(lpAdr);
CloseHandle(hMap);
return 0;
}
这里面很简单的,只要学过了就知道了,即便不知道查查文档就可以了
运行
两个进程共享了同一个物理页
两个进程都运行结束去看看文件
发现也是成功的
写拷贝:FILE_MAP_COPY
如图,每个进程都会用到图中的三个dll,这些也是映射了物理内存供大家共享,这样的话,(左为A,右为B)AB进程都能读,但如果能写呢?
那A进程对dll修改了,那B进程使用的dll不也就被修改了吗?
这里可以做个实验,用OD去载入一个进程,停在一个断点,都知道断点为CC(int 3),如果我重新去运行它却又能运行起来,不应该被断下来吗?
要实现这个现象就要使用到写拷贝即FILE_MAP_COPY
在MapViewOfFile函数中的参数,只用看第一句话,写的时候拷贝、复制
什么意思,看一眼下面的图
每当进程执行写的操作时,就会拷贝一份新的物理页,原来的内容就不会改变,别的进程访问也不会变,就相当与有个存档,玩闯关游戏需要的就是存档了
代码就是上面的,就不贴了,记得把参数改一下
结果
可以看到没有影响,还是读取原来的0,结束后看看文件
也是没有改变