课前复习
内核对象
内核对象的特性:
- 全局性 - 可以跨进程使用
引用计数 -
- 使用CreateXXX 或 OpenXXX 得到内核对象句柄时,引用计数会增加
- 使用CloseHandle 递减引用计数
- 引用计数等于0的时候, 才会被销毁
安全描述符
- 使用进程默认的安全描述符
句柄表
- 内核对象句柄值一般是句柄表的下标.
- 每个进程都自身独立的句柄表.
- 内核对象可以跨进程使用,但是句柄的值不能跨进程使用
进程
- 提供一个虚拟内存地址空间
- 由主模块和其它模块组成
- 进程必须有一个主线程
进程通讯
- WM_COPYDATA
邮槽
- 创建邮槽对象:
CreateMailslot
, 邮槽名字特征是:\\.\mailslot\
- 打开邮槽对象:
CreateFile()
- 读写邮槽:
ReadFile/WriteFile
- 获取邮槽信息:
GetMailslotInfo
- 创建邮槽对象:
快照
- 进程快照(得到是系统上所有的进程)
- 模块快照(以进程为单位)
- 线程快照(得到的是系统所有线程)
- 堆快照(以进程为单位)
线程
- 创建线程的方式
退出线程的方式
- 通过返回的方式正常退出(最理想)
- 通过
ExitThread
强制结束本线程 - 通过
TernamiteThread
来结束指定线程 - 通过结束进程来结束所有的线程.
线程的切换
- 每个线程都有CPU时间片, 用完了时间片就被切换
异步IO
- 将同步IO模式改成异步IO的模式
在打开文件时,加上重叠IO的标志:FILE_FLAG_OVERLAPPED
HANDLE hFile = INVALID_HANDLE_VALUE;
hFile = CreateFile(
L"002_异步IO.cpp",
GENERIC_READ,/*读写方式:只读*/
FILE_SHARE_READ,/*共享方式: 共享读*/
NULL,/*安全描述符*/
OPEN_EXISTING,/*创建标志:存在时才打开*/
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,/*文件属性和标志:正常属性|重叠IO标志 */
NULL);
以重叠方式打开的句柄拥有一下特性:
句柄变成了可等待的状态
- 有信号 : 当有一个IO任务被完成,就变成有信号.
- 无信号 : 默认无信号.
句柄不能使用文件读写位置了.
- 不能使用SetFilePointer来设置文件的读写位置.
- 调用ReadFile或WriteFile的时候, 也不会使用文件读写位置来定义要读写的文件内容.
- 只能使用OVERLAPPED的结构体来指定文件的读写位置.
投递IO任务
- 通过ReadFile来投递一个读取的IO任务
- 通过WriteFile来投递一个写入的IO任务.
typedef struct _OVERLAPPED {
ULONG_PTR Internal;// [输出]保存IO任务的错误码
ULONG_PTR InternalHigh;//[输出]保存IO任务的完成的字节数
union {
struct {
DWORD Offset;//[输入]用于指定文件读写位置的低32位
DWORD OffsetHigh;//[输入]用于指定文件读写位置的高32位
} DUMMYSTRUCTNAME;
PVOID Pointer;
} DUMMYUNIONNAME;
HANDLE hEvent;//[输入]用于提供任务完成通知事件对象
} OVERLAPPED, *LPOVERLAPPED;
- 通过结构体的offset来设置从文件中哪个位置开始读/写
- 当系统完成了这个IO任务之后, 就会将处理结果(错误码)填写到结构体的Internal, 将具体读写了多少字节的内容写入到结构体的InternalHigh字段.
- 系统完成IO任务之后, 如果hEvent字段值不为NULL, 就说明这个字段是有一个事件对象的, 那么系统会将这个事件对象设置为有信号状态.
获取IO完成通知
根据文件句柄的信号状态来判断
- 缺点 : 如果一个文件同时存在多个IO任务, 只要其中一个任务被完成了, 文件句柄就变成有信号状态,无法判断是哪个IO被完成了.
使用事件对象信号状态来判断
- 每个IO任务都有一个重叠IO结构体来配置信息
- 每个IO任务都会配有一个事件对象. 哪个事件对象被设置成有信号了, 就说明哪个IO任务被完成了.
- 要等待事件对象的信号时, 需要创建线程等待. 缺点: 如果IO任务很多,就说明要等待的事件也很多, 就需要创建大量线程线程等待, 造成效率低下.
使用扩展版的API:
- ReadFileEx
- WriteFileEx
- 扩展版的函数能够接收一个完成函数的回调函数.
- 当IO任务被系统底层处理完毕之后, 这个回调函数就会被系统插入到线程的APC队列中.
- 当线程被挂起(睡眠)的时候, 挂起之前,根据可警醒状态是否为真, 来决定调用APC队列中的函数.
- 缺点: 如果在程序中没有调用Sleep或其它等待函数, 或者调用了,但是没有设置可警醒状态, 那么即使IO任务被完成了,完成回调函数也不会被调用.
通过完成端口来等待IO任务
- 创建异步IO方式的文件对象
- 创建一个完成端口对象
- 将完成端口和文件对象进行绑定
创建指定个数线程, 在线程监视完成端口 完成列表的状态
- 通过API
GetQueuedCompletionStatus
来获取完成列表的状态
- 通过API
- 发起异步IO任务.
事件对象
- 内核对象
通过CreateEvent来创建
- 可以配置手动使用还是自动使用
- 可以配置初始化是否有信号
- 通过
SetEvent
将事件设置为有信号 - 通过
ResetEvent
重置事件对象的信号(设置为没有信号)
通过
WaitForSingleObject
来等待它的信号- WaitForSingleObject还会进一步修改被等待的事件对象的信号 : 从无信号开始阻塞等待,知道事件对象有信号之后就从阻塞状态中返回. 返回前,会顺手将事件对象设置为无信号.