cyxyrq-code-loading

 

Win32学习6

21、共享内存的申请释放

①共享内存

HANDLE CreateFileMapping(
HANDLE hFile, //handle to file
LPSECURITY ATTRIBUTES lpAttributes,// security
DWORD flProtect, // protection
DWORD dwMaximumSizeHigh, // high-order DWORD of size
DWORD dwMaximumSizeLow, // low-order DWORD of size
LPCTSTR 1pName // object name
);

这个创建共享内存的函数也可以创建一个内核对象,而这个对象可以不仅仅可以提供一块可以映射的内存,还可以将对应的文件映射到对应的内存上去。即第一个成员 hFile ,如果提供了对应的文件,还可以将对应的文件映射到对应的内存中。如果不指定文件的话,那么只会申请一块儿内存,而不会关联文件。 lpAttributes 安全描述符,一般填 NULL 。 flProtect 保护模式,有只读和可读可写等。 dwMaximumSizeHigh 这是高32位的值,dwMaximumSizeLow 这是低32位的值,但在 X86 下,dwMaximumSizeHigh 我们是用不上的。二者都是用来指定需要多大的一块物理内存。 1pName 给内核对象起名字,当多个对象同时使用一个内核对象的时候,可以给它起个名字。

#define MapFileName "共享内存"
#define BUF_SIZE 0x1000
HANDLE g_hMapFile;
LPTSTR g_lpBuff;

int main()
{
getchar();
//内核对象:物理页
g_hMapFile = CreateFileMapping(INUALID_HANDLE_UALUE,NULL,PAGE_READWRITE,o,BUF_SIZE,MapFileName)
//将物理页与线性地址进行映射
9_lpBuff = (LPTSTR)MapUiewOfFile(g_hMapFile,FILE_MAP_MRITE,0,8,BUF_SIZE );
DWORD eror GetLastError();
*(PDWORD)g_1pBuff = 0x12345678;
printf("%p",g_lpBuff);

   getchar();
   //关闭映射
UnmapViewOfFile(g_lpBuff);
   CloseHandle(g_hMapFile);
   getchar();
   return 0;
}

CreateFileMapping 首先利用这个函数准备好物理页,MapUiewOfFile 再利用这个函数将物理页和虚拟内存创建联系。然后就可以通过这个虚拟内存来使用物理页了。

LPVOID MapViewofFile(
HANDLE hFileMappingObject, // handle to file-mapping object
DWORD dwDesiredAccess, // access mode
DWORD dwFileOffsetHigh, // high-order DWORD of offset
DWORD dwFileOffsetLow, // low-order DWORD of offset
SIZE_T dwNumberOfBytesToMap //number of bytes to map
);

hFileMappingObject 这个成员指的是要映射的文件的句柄。 dwDesiredAccess 这个成员指的是访问模式,这个访问模式不同于CreateFileMapping 中的 flProtect,前者指的是虚拟内存的访问模式,后者指的是物理页的属性。而需要知道的是对于线性地址(虚拟内存)的权限只能比物理页的属性更加的严格,而不能比物理页的属性更宽松。(可读可写 - > 只读 )。否则映射过程就会失败。 dwFileOffsetHigh 和 dwFileOffsetLow 指明了从哪里开始映射,即物理页的地址偏移,如果为 0 的话,那么就是整个物理页。

通过映射方式创建的内存,可以有多个映射,实际上映射内存的数量要多于私有内存的数量。

使用完毕之后,需要先关闭映射(即断开虚拟内存与物理页之间的联系),UnmapViewOfFile(),之后再关闭句柄CloseHandle(),同其他的内核对象一样,关闭句柄并不代表,物理页就消失了,因为可能其他的进程也在使用这块内存。直到所有进程都不使用的时候,才会真正的释放这块内存。

22、文件系统

①文件系统

文件系统是操作系统用于管理磁盘上文件的方法和数据结构; 简单点说就是 在磁盘上如何组织文件的方法。(并非属于硬件而是属于软件)

image-20230219213539008

EFS加密能够保护文件在同一操作系统中不被别的用户随意访问。配额而是限制其他用户使用的文件使用的容量。

不论是NTFS还是FAT32,最上层的组成形式都是卷,这个卷实际上就是逻辑硬盘(C盘,D盘...),或者称之为虚拟逻辑驱动器。下一层就是文件的目录。最后一层才是文件。

②卷相关API <1>获取卷 GetLogicalDrives() <2>获取一个所卷的盘符的字符串 GetLogicalDrives() <3> 获取卷的类型 GetLogicalType() <4> 获取卷的详细信息 GetVolumelnformation()

GetLogicalDrives() 这个函数原型为 DWORD WINAPI GetLogicalDrives(void); 调用该函数可以获得当前系统的卷,如果函数成功,则返回值是表示当前可用磁盘驱动器的位掩码。该位掩码为十进制的数,将其转为二进制,位位置 0(最低有效位)是驱动器 A,位位置 1 是驱动器 B,位位置 2 是驱动器 C,依此类推。(例如1101 就代表了驱动器A C D)如果函数失败,则返回值是零。

GetLogicalDrives()这个函数原型为

DWORD WINAPI GetLogicalDriveStrings(
 __in   DWORD nBufferLength,
 __out  LPTSTR lpBuffer
);

该函数的作用为检索此计算机上格式为“<驱动器号>:\”的逻辑驱动器的名称。 第一个参数nBufferLength的含义是:lpBuffer 指向的缓冲区的最大大小,以 TCHAR 为单位。此大小不包括终止空字符。如果此参数为零,则不使用lpBuffer。第二个参数lpBuffer的含义是:指向缓冲区的指针,该缓冲区接收一系列以 null 结尾的字符串,每个字符串对应系统中每个有效驱动器,外加一个附加 null 字符。每个字符串都是一个设备名称。

GetDriveType()这个函数的含义就是获取卷的类型,返回值就是对应的一个表格由0开始由上到下,例如返回值如果是3对应的类型就是DRIVE_FIXED

image-20230709170653995

GetVolumelnformation()这个函数如下使用

GetUolumeInformation(
"C:\\", //IN磁盘驱动器代码字符串
szUolumneName, //0UT磁盘驱动器卷标名称(就是我们平时命名的软件,信息等这类名字)
260, //IN磁盘驱动器卷标名称长度(取名字的长度范围)
&dwolunneSerial, //0UT磁盘驱动器眷标序列导(不是硬盘序列号,该号是硬盘出厂时生产厂家为区别产品而设置的,就像人的身份证
&dwtaxLength, //0UT 系统允许的最大文件名长度
&dwFileSystem, //0UT文件系统标识(文件系统的特性 )
szFileSysten, //0UT文件操作系统名称(NTFS 或 FAT32)
260 //IN 文件操作系统名称长度
);

③目录相关API <1>创建目录 CreateDirectory(); <2>删除目录 RemoveDirectory(); <3> 修改目录名称 MoveFile(); <4> 获取程序当前目录 GetCurrentDirectory(); <5> 设置程序当前目录 SetCurrentDirectory();

CreateDirectory()这个函数原型为

BOOL CreateDirectory(
LPCTSTR lpPathName, //directory name
LPSECURITY_ATTRIBUTES lpSecurityAttributes //SD
);

lpPathName 这个参数可以指定路径以及文件的名字,

RemoveDirectory();这个函数就是删除指定路径和名字的文件。 MoveFile();这个函数就是将指定路径和文件名的文件修改对应的文件名, GetCurrentDirectory();这个函数是获取当前运行这个API 的程序的所在的目录(利用这个我们可以在创建目录或者删除修改目录的时候不加上绝对路径,这个时候就会在当前程序的目录中进行创建、删除或者修改)。 SetCurrentDirectory();而这个函数就可以将当前这个程序的目录进行设置修改,但是并非移动当前程序,只是将该程序对应的目录修改。(即仅仅修改信息)。

④文件相关API <1> 创建文件 CreateFile(); <2>关闭文件的 CloseHandle(); <3> 获取文件长度 GetFileSize(); <4> 获取文件的属性和信息 GetFileAttributes()/GetFileAttributesEx(); <5> 读/写/拷贝/删除 文件 ReadFile()/WriteFile()/CopyFile()/DeleteFile(); <6> 查找文件 FindFirstFile()/FindNextFile();

HANDLE WINAPI CreateFile(
 __in      LPCTSTR lpFileName, //文件名(还可以设置文件路径)
 __in      DWORD dwDesiredAccess, //访问模式(GENERIC_READ只读,GENERIC_WRITE只写,GENERIC_READ|GENERIC_WRITE可写可读)
 __in      DWORD dwShareMode, //分享模式(在文件关闭之前别的程序可以如何操作)
 __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,//SD
 __in      DWORD dwCreationDisposition, //以什么方式创建
 __in      DWORD dwFlagsAndAttributes, //创建文件的属性
 __in_opt  HANDLE hTemplateFile //一般填null
);

CloseHandle();用于关闭文件 GetFileSize(); 该函数的原型为

DWORD WINAPI GetFileSize(
 __in       HANDLE hFile,
 __out_opt  LPDWORD lpFileSizeHigh
);

lpFileSizeHigh指向返回文件大小的高阶双字的变量的指针。如果应用程序不需要高阶双字,则此参数可以为 NULL。如果函数成功,则返回值是文件大小的低阶双字,如果 lpFileSizeHigh 为非 NULL,则该函数将文件大小的高阶双字放入该参数指向的变量中.如果函数失败并且 lpFileSizeHigh 为 NULL,则返回值为 INVALID_FILE_SIZE。

GetFileAttributes()/GetFileAttributesEx();

BOOL WINAPI GetFileAttributesEx(
 __in   LPCTSTR lpFileName,
 __in   GET_FILEEX_INFO_LEVELS fInfoLevelId,
 __out  LPVOID lpFileInformation
);

lpFileName第一个参数是名字,第二个参数只填GetFileExInfoStandard这样一个宏。lpFileInformation第三个参数就代表了,获取的属性就会存在这个第三个参数这个指针的结构体中。

//5.读取文件
//5.1分配空间间
LPSTR pszBuffer = (LPSTR)malloc( 10);
ZeroMemory( pszBuffer,10 );
//5.2设置当前读取的位置
SetFilePointer( hFile, 1, NULL, FILE_BEGIN );
//5.3 读取数据
DWORD duReadLength= 8;
ReadFile( hFile, pszBuffer, 10,&dwReadLength, NULL );
printf( "%s", pszBuffer );
//5.4 释放内存
free( pszBuffer );
//5.5 关闭文件
CloseHandle( hFile );
//6.写入文件
//6.1 写文件
TCHAR szBuffer[] = TEXT("中国");
DWORD dwWritten = 0;
WriteFile( hFile, szBuffer, strlen(szBuffer), &dwWritten, NULL );
//6.2关闭文件
CloseHandle( hFile );
//7.拷贝文件
CopyFile( "C:\\old.txt", "C:\\new.txt", FALSE ); */
//8.删除文件
DeleteFile( "C:\\old.txt" ); */
//9.查找文件
HANDLE FindFirstFile(
LPCTSTR lpFileName, // file name
LPWIN32_FIND_DATA 1pFindFileData // data buffer
);
BOOL FindNextFile(
HANDLE hFindFile, // search handle
LPWIN32_FIND_DATA lpFindFileData // data buffer
);

如上就是读/写/拷贝/删除/查找 文件等操作,都可以以一推三,查询文档实现,不做过多赘述。

23、内存映射文件

①什么是内存映射文件?

image-20230220185636803

内存映射文件的底层实现,把一个硬盘上的文件直接映射到物理页上,再将这个物理页直接映射到一个进程的虚拟内存里。这样我们在虚拟内存的各种操作就可以直接对应到硬盘中的文件中了。(如直接读写修改文件)更有的优点,是相比于一般的IO操作效率更加的高效。

可以简单利用一个流程 1、创建一个文件并得到文件句柄 2、紧接着创建FileMapping对象,创建分配物理页的同时,将文件映射到物理页 3、映射到虚拟内存(MapViewOfFile) *4、读取文件 *5、写文件 6、关闭资源

而我们在修改文件的时候,并非是实时更新的,而是储存在虚拟内存的缓冲区中,在我们关闭资源的时候,进行的更新缓存即将缓冲区中的内容写入写入文件。

②内存映射文件之共享

image-20230226190025498

通过内存映射文件的方式,我们可以实现文件共享,可以在A进程中修改文件中的内容,再通过B进程读取文件中的数值。实际上的与共享内存的方式和形式大致相同,只是因为文件映射到了物理页,而共享内存指向的同一块物理页,所以多个文件可以同时操作同一份文件。

③内存映射文件之写拷贝

image-20230226195119137

一个进程可以大致理解成就是由一个exe和多个dll组成的,而绝大多数的进程都拥有一些dll,如kernel32.dll,user32.dll和ntdll.dll 等,而操作系统不可能每个进程都重复的创建一个物理内存来给每个进程使用这些文件。实际上每个进程都是通过映射的方式,将这些dll,映射到物理页,并通过共享的同一个物理内存的方式,共同使用这些文件。

而因此就会出现这样一个问题,就是两个进程指向的是同一块内存,如果A进程将物理页中的数据进行修改,B进程读取这些dll的时候,就会造成一些不安全的问题。而我们在实际操作的时候,我们会发现,我们对于这些较为常用的系统dll进行修改的时候,只会对于当前的进程造成影响,而不会对于其他的进程造成任何影响。就是因为我们在使用MapViewOfFile这个函数映射的时候,我们期中的dwDesiredAccess这个参数的值我们使用的是 FILE_MAP_COPY 这个值的意思就是说:当我们想要写这块物理内存的时候,操作系统就会进行“写拷贝”的操作,创建一个新的物理页,将原来的物理页复制过去。然后将该进程指向新的这块物理页。这个的好处就在于,不会影响原来的文件,也不会影响别的进程。

24、静态链接库

静态链接库实际上就是一种软件模块化的解决方案。

①编写静态链接库文件 <1>在vc6.0中创建新项目,项目类型: Win32 Static Library. <2>在项目中创建xxxx.h和xxxx.cpp编写代码然后构建即可. 在项目目录中会生成xxxx.lib文件

②静态链接库的使用 <1>将生成的.h与.lib文件复制到项目根目录,然后在代码中引用: @include "xxxx.h" #pragma comment(lib, "xxxx.lib") <2>将xxxx.H与xxxx.LIB文件复制到VC6安装目录,与库文件放在一起 C:\Program Files\Microsoft Visual Studio\VC98\Include(xxxx.h) C:\Program Files\Microsoft Visual Studio\VC98\Lib(xxxx.lib) 然后在工程->设置->连接->对象/库模块中添加xxxx.lib

③ 静态链接库的缺点 <1>使用静态链接生成的可执行文件体积较大 <2>包含相同的公共代码,造成浪费

posted on 2023-07-17 07:58  清雨中欣喜  阅读(13)  评论(0编辑  收藏  举报

导航