一、文件属性APIs
BOOL SetFileAttributes (
LPCTSTR lpFileName, // 目录/文件路径
DWORD dwFileAttributes // 目录/文件属性
);
成功返回TRUE,失败返回FALSE。
dwFileAttributes为以下值的位或:
FILE_ATTRIBUTE_ARCHIVE - 归档
FILE_ATTRIBUTE_COMPRESSED - 压缩
FILE_ATTRIBUTE_OFFLINE - 离线
FILE_ATTRIBUTE_DIRECTORY - 目录
FILE_ATTRIBUTE_ENCRYPTED - 加密
FILE_ATTRIBUTE_HIDDEN - 隐藏
FILE_ATTRIBUTE_READONLY - 只读
FILE_ATTRIBUTE_SYSTEM - 系统
FILE_ATTRIBUTE_TEMPORARY - 临时
DWORD GetFileAttributes (
LPCTSTR lpFileName // 目录/文件路径
);
成功返回目录或文件的属性,失败返回-1。
FILE_ATTRIBUTE_ARCHIVE 00000001
FILE_ATTRIBUTE_COMPRESSED 00000010
FILE_ATTRIBUTE_OFFLINE 00000100
00000111
&11111011
------------
00000011
增加文件属性:获取原属性,与需要增加的属性做位或,再设回去。
删除文件属性:获取原属性,与需要删除的属性之反做位与,再设回去。
二、文件遍历APIs
HANDLE FindFirstFile (
LPCTSTR lpFileName, // 查找路径
LPWIN32_FIND_DATA lpFindFileData // 查找信息结构
);
成功返回查找句柄,用于后续函数调用,失败返回INVALID_HANDLE_VALUE。
BOOL FindNextFile (
HANDLE hFindFile, // 由FindFirstFile返回查找句柄
LPWIN32_FIND_DATA lpFindFileData // 查找信息结构
);
成功返回TRUE,失败返回FALSE,GetLastError()返回ERROR_NO_MORE_FILES表示遍历结束。
三、Windows内存空间
1.地址空间:32操作系统地址空间0-2^32(4G)。地址空间越大程序越易于编写。
2.地址空间的划分:
1)用户地址空间:0-2G
A.空指针区:0-64K
B.用于区:64K-0x7FFEFFFF,存放用户程序的代码和数据
C.禁入区:0x7FFEFFFF-2G
2)内核地址空间:2G-4G
3.区域:连续的内存,区域大小一般是64K或64K的整数倍。每个区域都有一个特定的状态。
1)空闲:未被使用
2)私有:已被预订
3)映像:存放代码
4)映射:存放数据
4.物理内存(半导体内存,内存条)
系统实际可以使用的内存,CPU只能访问物理内存。
5.虚拟内存(磁盘交换文件)
将磁盘文件(pagefile.sys)虚拟成内存使用。CPU如果要访问虚拟内存,必须将虚拟内存中的数据放到物理内存中。经常使用的数据存放在物理内存中,不经常使用的数据存放在虚拟内存中。
6.内存页
系统进行内存管理的最小单位,内存页的大小4K字节,每个内存页都有特定的权限。
7.页目表
32位地址:
31 22 21 12 11 0
XXXXXXXXXX XXXXXXXXXX XXXXXXXXXXXX
10位 10位 12位
2^10=1K 2^10=1K 2^12=4K
页目中包含1K 页表中包含1K 页中包含4K个
个页表 个页 字节
页目中包含1K项,每项对应一个页表,页表包含1K项,每项对应一个页,页包含4K字节——1K*1K*4K=4G。
8.内存访问的过程
1)CPU首先根据地址在物理内存中查找相应的位置。如果找到,则访问其中的数据,否则执行2);
2)根据地址在虚拟内存中查找相应的位置,如果没找到(野指针),则返回错误,否则执行3);
3)将该地址所在的内存页换入物理内存,同时将原物理内存页换出到虚拟内存,执行4);
4)访问物理内存中的数据。
9.内存的分配方式
1)虚拟内存分配:专有APIs
2)堆内存分配:malloc/new
3)栈内存分配:自动完成
四、虚拟内存分配
1.特点:速度快,分配大内存效率高,将内存的分配和地址的分配分开进行,可以先分配地址,直到需要时再将地址绑定(提交)到内存。常用于大型电子表格类应用程序的实现。
2.分配虚拟内存
LPVOID VirtualAlloc (
LPVOID lpAddress, // NULL或者绑定(提交)地址
SIZE_T dwSize, // 内存大小
DWORD flAllocationType, // 分配方式
DWORD flProtect // 访问方式,一般PAGE_READWRITE
);
成功返回虚拟内存地址,失败返回NULL。
flAllocationType取值:
MEM_COMMIT - 在分配地址的同时分配内存,如买现房
MEM_RESERVE - 只分配地址不绑定到内存,如买期房
获取内存状态:
void GlobalMemoryStatus (
LPMEMORYSTATUS lpBuffer // 内存状态结构
);
typedef struct _MEMORYSTATUS {
DWORD dwLength; // 结构体字节数
DWORD dwMemroyLoad; // 内存使用率,百分之几
SIZE_T dwTotalPhys; // 物理内存总字节数
SIZE_T dwAvailPhys; // 空闲物理内存字节数
SIZE_T dwTotalPageFile; // 分页文件总字节数
SIZE_T dwAvailPageFile; // 空闲分页文件字节数
SIZE_T dwTotalVirtual; // 虚拟内存总字节数
SIZE_T dwAvailVirtual; // 空闲虚拟内存字节数
} MEMORYSTATUS, *LPMEMORYSTATUS;
3.释放虚拟内存
BOOL VirtualFree (
LPVOID lpAddress, // 虚拟内存地址
SIZE_T dwSize, // 释放字节数,0表示全部释放
DWORD dwFreeType // 释放方式
);
成功返回TRUE,失败返回FALSE。
dwFreeType取值:
MEM_DECOMMIT - 只释放内存不释放地址
MEM_RELEASE - 基释放内存也释放地址
注意:内存绑定(提交)以页(4096字节)为单位。
五、堆内存分配
1.特点:适合小内存(<1M)分配。一般每个程序都有自己的堆,默认为1M,会根据需要动态调整。每个进程都有几个缺省堆空间,malloc/new都是从这些堆中分配内存。
2.获取调用进程的首个堆
HANDLE GetProcessHeap (void);
成功返回调用进程首个堆的句柄,失败返回NULL。
3.获取调用进程的所有堆
DWORD GetProcessHeaps (
DWORD NumberOfHeaps, // 堆句柄数组的容量
PHANDLE ProcessHeaps, // 堆句柄数组
);
成功返回调用进程堆的个数,堆句柄放在ProcessHeaps数组中,失败返回0。
4.创建堆
HANDLE HeapCreate (
DWORD flOptions, // 创建选项
DWORD dwInitialSize, // 初始字节数
DWORD dwMaximumSize // 最大字节数,0表示无穷大
);
成功返回堆句柄,失败返回NULL。
flOptions取值:
HEAP_GENERATE_EXCEPTION - 堆内存分配失败则引发异常
HEAP_NO_SERIALIZE - 支持不连续存取
5.分配堆内存
LPVOID HeapAlloc (
HANDLE hHeap, // 堆句柄
DWORD dwFlags, // 分配方式
DWORD dwBytes, // 内存大小
);
成功返回堆内存地址,失败返回NULL。
dwFlags取值:
HEAP_GENERATE_EXCEPTION - 堆内存分配失败则引发异常
HEAP_NO_SERIALIZE - 支持不连续存取
(若在创建堆时已经指定此参数,则此处忽略之)
HEAP_ZERO_MEMORY - 初始化清零
6.释放堆内存
BOOL HeapFree (
HANDLE hHeap, // 堆句柄
DWORD dwFlags, // 释放方式,只能取HEAP_NO_SERIALIZE
LPVOID lpMem // 堆内存地址
);
成功返回TRUE,失败返回FALSE。
7.销毁堆
BOOL HeapDestroy (
HANDLE hHeap // 堆句柄
);
成功返回TRUE,失败返回FALSE。
当堆被销毁后,其中所有堆内存都会被自动释放。
8.Win32的malloc/new实现实际就是调用了上述堆函数。
六、栈内存
1.每个线程都有自己的栈,默认为1M字节。
2.操作系统负责自动维护栈内存的分配与释放。
3.Windows提供了_alloca函数,用于在栈中分配内存。
七、内存映射文件
1.基本概念
1)将文件映射成内存来使用,即访问内存的方式访问文件。
2)常用于实现进程通信,比直接通过文件I/O效率更高。
2.创建文件
用CreateFile创建,可读可写GENERIC_READ | GENERIC_WRITE。
3.创建映射
HANDLE CreateFileMapping (
HANDLE hFile, // 文件句柄
... // 安全属性,设NULL
DWORD flProtect, // 访问方式,PAGE_READWRITE,可读可写
DWORD dwMaximumSizeHigh, // 映射总字节数高32位
DWORD dwMaximumSizeLow , // 映射总字节数低32位
LPCTSTR lpName // 映射名,NULL表示匿名映射,其它进程无法访问
);
成功返回映射句柄,失败返回NULL。
4.加载映射
LPVOID MapViewOfFile (
HANDLE hFileMappingObject, // 映射句柄
DWORD dwDesiredAccess, // 访问方式,FILE_MAP_ALL_ACCESS
DWORD dwFileOffsetHigh, // 偏移量高32位
DWORD dwFileOffsetLow, // 偏移量低32位
SIZE_T dwNumberOfBytesToMap // 映射字节数(不超过映射总字节数)
);
成功返回映射地址,失败返回NULL。
5.使用映射:以内存的方式使用该映射。
6.卸载映射
BOOL UnmapViewOfFile (
LPCVOID lpBaseAddress // 映射地址
);
成功返回TRUE,失败返回FALSE。
7.销毁映射
CloseHandle (hMap);
8.关闭文件
CloseHandle (hFile);
9.打开映射
HANDLE OpenFileMapping (
DWORD dwDesiredAccess, // 访问方式,FILE_MAP_ALL_ACCESS
BOOL bInheritHandle, // 子进程是否继承此函数所返回的句柄
LPCTSTR lpName // 映射名
);
成功返回映射句柄,失败返回NULL。
10.基于内存映射文件的进程间通信
1)写进程:创建文件->创建映射->加载映射->写入映射->卸载映射->销毁映射->关闭文件
2)读进程:打开映射->加载映射->读取映射->卸载映射->关闭映射
八、关于句柄
句柄就是内存对象地址在句柄表中索引。通过句柄不能直接访问内存,但是可以通过APIs函数操作其标识的对象。
BOOL SetFileAttributes (
LPCTSTR lpFileName, // 目录/文件路径
DWORD dwFileAttributes // 目录/文件属性
);
成功返回TRUE,失败返回FALSE。
dwFileAttributes为以下值的位或:
FILE_ATTRIBUTE_ARCHIVE - 归档
FILE_ATTRIBUTE_COMPRESSED - 压缩
FILE_ATTRIBUTE_OFFLINE - 离线
FILE_ATTRIBUTE_DIRECTORY - 目录
FILE_ATTRIBUTE_ENCRYPTED - 加密
FILE_ATTRIBUTE_HIDDEN - 隐藏
FILE_ATTRIBUTE_READONLY - 只读
FILE_ATTRIBUTE_SYSTEM - 系统
FILE_ATTRIBUTE_TEMPORARY - 临时
DWORD GetFileAttributes (
LPCTSTR lpFileName // 目录/文件路径
);
成功返回目录或文件的属性,失败返回-1。
FILE_ATTRIBUTE_ARCHIVE 00000001
FILE_ATTRIBUTE_COMPRESSED 00000010
FILE_ATTRIBUTE_OFFLINE 00000100
00000111
&11111011
------------
00000011
增加文件属性:获取原属性,与需要增加的属性做位或,再设回去。
删除文件属性:获取原属性,与需要删除的属性之反做位与,再设回去。
二、文件遍历APIs
HANDLE FindFirstFile (
LPCTSTR lpFileName, // 查找路径
LPWIN32_FIND_DATA lpFindFileData // 查找信息结构
);
成功返回查找句柄,用于后续函数调用,失败返回INVALID_HANDLE_VALUE。
BOOL FindNextFile (
HANDLE hFindFile, // 由FindFirstFile返回查找句柄
LPWIN32_FIND_DATA lpFindFileData // 查找信息结构
);
成功返回TRUE,失败返回FALSE,GetLastError()返回ERROR_NO_MORE_FILES表示遍历结束。
三、Windows内存空间
1.地址空间:32操作系统地址空间0-2^32(4G)。地址空间越大程序越易于编写。
2.地址空间的划分:
1)用户地址空间:0-2G
A.空指针区:0-64K
B.用于区:64K-0x7FFEFFFF,存放用户程序的代码和数据
C.禁入区:0x7FFEFFFF-2G
2)内核地址空间:2G-4G
3.区域:连续的内存,区域大小一般是64K或64K的整数倍。每个区域都有一个特定的状态。
1)空闲:未被使用
2)私有:已被预订
3)映像:存放代码
4)映射:存放数据
4.物理内存(半导体内存,内存条)
系统实际可以使用的内存,CPU只能访问物理内存。
5.虚拟内存(磁盘交换文件)
将磁盘文件(pagefile.sys)虚拟成内存使用。CPU如果要访问虚拟内存,必须将虚拟内存中的数据放到物理内存中。经常使用的数据存放在物理内存中,不经常使用的数据存放在虚拟内存中。
6.内存页
系统进行内存管理的最小单位,内存页的大小4K字节,每个内存页都有特定的权限。
7.页目表
32位地址:
31 22 21 12 11 0
XXXXXXXXXX XXXXXXXXXX XXXXXXXXXXXX
10位 10位 12位
2^10=1K 2^10=1K 2^12=4K
页目中包含1K 页表中包含1K 页中包含4K个
个页表 个页 字节
页目中包含1K项,每项对应一个页表,页表包含1K项,每项对应一个页,页包含4K字节——1K*1K*4K=4G。
8.内存访问的过程
1)CPU首先根据地址在物理内存中查找相应的位置。如果找到,则访问其中的数据,否则执行2);
2)根据地址在虚拟内存中查找相应的位置,如果没找到(野指针),则返回错误,否则执行3);
3)将该地址所在的内存页换入物理内存,同时将原物理内存页换出到虚拟内存,执行4);
4)访问物理内存中的数据。
9.内存的分配方式
1)虚拟内存分配:专有APIs
2)堆内存分配:malloc/new
3)栈内存分配:自动完成
四、虚拟内存分配
1.特点:速度快,分配大内存效率高,将内存的分配和地址的分配分开进行,可以先分配地址,直到需要时再将地址绑定(提交)到内存。常用于大型电子表格类应用程序的实现。
2.分配虚拟内存
LPVOID VirtualAlloc (
LPVOID lpAddress, // NULL或者绑定(提交)地址
SIZE_T dwSize, // 内存大小
DWORD flAllocationType, // 分配方式
DWORD flProtect // 访问方式,一般PAGE_READWRITE
);
成功返回虚拟内存地址,失败返回NULL。
flAllocationType取值:
MEM_COMMIT - 在分配地址的同时分配内存,如买现房
MEM_RESERVE - 只分配地址不绑定到内存,如买期房
获取内存状态:
void GlobalMemoryStatus (
LPMEMORYSTATUS lpBuffer // 内存状态结构
);
typedef struct _MEMORYSTATUS {
DWORD dwLength; // 结构体字节数
DWORD dwMemroyLoad; // 内存使用率,百分之几
SIZE_T dwTotalPhys; // 物理内存总字节数
SIZE_T dwAvailPhys; // 空闲物理内存字节数
SIZE_T dwTotalPageFile; // 分页文件总字节数
SIZE_T dwAvailPageFile; // 空闲分页文件字节数
SIZE_T dwTotalVirtual; // 虚拟内存总字节数
SIZE_T dwAvailVirtual; // 空闲虚拟内存字节数
} MEMORYSTATUS, *LPMEMORYSTATUS;
3.释放虚拟内存
BOOL VirtualFree (
LPVOID lpAddress, // 虚拟内存地址
SIZE_T dwSize, // 释放字节数,0表示全部释放
DWORD dwFreeType // 释放方式
);
成功返回TRUE,失败返回FALSE。
dwFreeType取值:
MEM_DECOMMIT - 只释放内存不释放地址
MEM_RELEASE - 基释放内存也释放地址
注意:内存绑定(提交)以页(4096字节)为单位。
五、堆内存分配
1.特点:适合小内存(<1M)分配。一般每个程序都有自己的堆,默认为1M,会根据需要动态调整。每个进程都有几个缺省堆空间,malloc/new都是从这些堆中分配内存。
2.获取调用进程的首个堆
HANDLE GetProcessHeap (void);
成功返回调用进程首个堆的句柄,失败返回NULL。
3.获取调用进程的所有堆
DWORD GetProcessHeaps (
DWORD NumberOfHeaps, // 堆句柄数组的容量
PHANDLE ProcessHeaps, // 堆句柄数组
);
成功返回调用进程堆的个数,堆句柄放在ProcessHeaps数组中,失败返回0。
4.创建堆
HANDLE HeapCreate (
DWORD flOptions, // 创建选项
DWORD dwInitialSize, // 初始字节数
DWORD dwMaximumSize // 最大字节数,0表示无穷大
);
成功返回堆句柄,失败返回NULL。
flOptions取值:
HEAP_GENERATE_EXCEPTION - 堆内存分配失败则引发异常
HEAP_NO_SERIALIZE - 支持不连续存取
5.分配堆内存
LPVOID HeapAlloc (
HANDLE hHeap, // 堆句柄
DWORD dwFlags, // 分配方式
DWORD dwBytes, // 内存大小
);
成功返回堆内存地址,失败返回NULL。
dwFlags取值:
HEAP_GENERATE_EXCEPTION - 堆内存分配失败则引发异常
HEAP_NO_SERIALIZE - 支持不连续存取
(若在创建堆时已经指定此参数,则此处忽略之)
HEAP_ZERO_MEMORY - 初始化清零
6.释放堆内存
BOOL HeapFree (
HANDLE hHeap, // 堆句柄
DWORD dwFlags, // 释放方式,只能取HEAP_NO_SERIALIZE
LPVOID lpMem // 堆内存地址
);
成功返回TRUE,失败返回FALSE。
7.销毁堆
BOOL HeapDestroy (
HANDLE hHeap // 堆句柄
);
成功返回TRUE,失败返回FALSE。
当堆被销毁后,其中所有堆内存都会被自动释放。
8.Win32的malloc/new实现实际就是调用了上述堆函数。
六、栈内存
1.每个线程都有自己的栈,默认为1M字节。
2.操作系统负责自动维护栈内存的分配与释放。
3.Windows提供了_alloca函数,用于在栈中分配内存。
七、内存映射文件
1.基本概念
1)将文件映射成内存来使用,即访问内存的方式访问文件。
2)常用于实现进程通信,比直接通过文件I/O效率更高。
2.创建文件
用CreateFile创建,可读可写GENERIC_READ | GENERIC_WRITE。
3.创建映射
HANDLE CreateFileMapping (
HANDLE hFile, // 文件句柄
... // 安全属性,设NULL
DWORD flProtect, // 访问方式,PAGE_READWRITE,可读可写
DWORD dwMaximumSizeHigh, // 映射总字节数高32位
DWORD dwMaximumSizeLow , // 映射总字节数低32位
LPCTSTR lpName // 映射名,NULL表示匿名映射,其它进程无法访问
);
成功返回映射句柄,失败返回NULL。
4.加载映射
LPVOID MapViewOfFile (
HANDLE hFileMappingObject, // 映射句柄
DWORD dwDesiredAccess, // 访问方式,FILE_MAP_ALL_ACCESS
DWORD dwFileOffsetHigh, // 偏移量高32位
DWORD dwFileOffsetLow, // 偏移量低32位
SIZE_T dwNumberOfBytesToMap // 映射字节数(不超过映射总字节数)
);
成功返回映射地址,失败返回NULL。
5.使用映射:以内存的方式使用该映射。
6.卸载映射
BOOL UnmapViewOfFile (
LPCVOID lpBaseAddress // 映射地址
);
成功返回TRUE,失败返回FALSE。
7.销毁映射
CloseHandle (hMap);
8.关闭文件
CloseHandle (hFile);
9.打开映射
HANDLE OpenFileMapping (
DWORD dwDesiredAccess, // 访问方式,FILE_MAP_ALL_ACCESS
BOOL bInheritHandle, // 子进程是否继承此函数所返回的句柄
LPCTSTR lpName // 映射名
);
成功返回映射句柄,失败返回NULL。
10.基于内存映射文件的进程间通信
1)写进程:创建文件->创建映射->加载映射->写入映射->卸载映射->销毁映射->关闭文件
2)读进程:打开映射->加载映射->读取映射->卸载映射->关闭映射
八、关于句柄
句柄就是内存对象地址在句柄表中索引。通过句柄不能直接访问内存,但是可以通过APIs函数操作其标识的对象。