课前复习

内核对象

  1. 内核对象的特性:

    1. 全局性 - 可以跨进程使用
    2. 引用计数 -

      1. 使用CreateXXX 或 OpenXXX 得到内核对象句柄时,引用计数会增加
      2. 使用CloseHandle 递减引用计数
      3. 引用计数等于0的时候, 才会被销毁
    3. 安全描述符

      1. 使用进程默认的安全描述符
    4. 句柄表

      1. 内核对象句柄值一般是句柄表的下标.
      2. 每个进程都自身独立的句柄表.
      3. 内核对象可以跨进程使用,但是句柄的值不能跨进程使用

进程

  1. 提供一个虚拟内存地址空间
  2. 由主模块和其它模块组成
  3. 进程必须有一个主线程

进程通讯

  • WM_COPYDATA
  • 邮槽

    • 创建邮槽对象: CreateMailslot, 邮槽名字特征是:\\.\mailslot\
    • 打开邮槽对象: CreateFile()
    • 读写邮槽: ReadFile/WriteFile
    • 获取邮槽信息: GetMailslotInfo

快照

  • 进程快照(得到是系统上所有的进程)
  • 模块快照(以进程为单位)
  • 线程快照(得到的是系统所有线程)
  • 堆快照(以进程为单位)

线程

  1. 创建线程的方式
  2. 退出线程的方式

    1. 通过返回的方式正常退出(最理想)
    2. 通过ExitThread强制结束本线程
    3. 通过TernamiteThread来结束指定线程
    4. 通过结束进程来结束所有的线程.
  3. 线程的切换

    1. 每个线程都有CPU时间片, 用完了时间片就被切换

异步IO

  1. 将同步IO模式改成异步IO的模式
    在打开文件时,加上重叠IO的标志:FILE_FLAG_OVERLAPPED
  1. HANDLE hFile = INVALID_HANDLE_VALUE;
  2. hFile = CreateFile(
  3. L"002_异步IO.cpp",
  4. GENERIC_READ,/*读写方式:只读*/
  5. FILE_SHARE_READ,/*共享方式: 共享读*/
  6. NULL,/*安全描述符*/
  7. OPEN_EXISTING,/*创建标志:存在时才打开*/
  8. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,/*文件属性和标志:正常属性|重叠IO标志 */
  9. NULL);

以重叠方式打开的句柄拥有一下特性:

  1. 句柄变成了可等待的状态

    1. 有信号 : 当有一个IO任务被完成,就变成有信号.
    2. 无信号 : 默认无信号.
  2. 句柄不能使用文件读写位置了.

    1. 不能使用SetFilePointer来设置文件的读写位置.
    2. 调用ReadFile或WriteFile的时候, 也不会使用文件读写位置来定义要读写的文件内容.
    3. 只能使用OVERLAPPED的结构体来指定文件的读写位置.
  3. 投递IO任务

    1. 通过ReadFile来投递一个读取的IO任务
    2. 通过WriteFile来投递一个写入的IO任务.
  1. typedef struct _OVERLAPPED {
  2. ULONG_PTR Internal;// [输出]保存IO任务的错误码
  3. ULONG_PTR InternalHigh;//[输出]保存IO任务的完成的字节数
  4. union {
  5. struct {
  6. DWORD Offset;//[输入]用于指定文件读写位置的低32位
  7. DWORD OffsetHigh;//[输入]用于指定文件读写位置的高32位
  8. } DUMMYSTRUCTNAME;
  9. PVOID Pointer;
  10. } DUMMYUNIONNAME;
  11. HANDLE hEvent;//[输入]用于提供任务完成通知事件对象
  12. } OVERLAPPED, *LPOVERLAPPED;
  1. 通过结构体的offset来设置从文件中哪个位置开始读/写
  2. 当系统完成了这个IO任务之后, 就会将处理结果(错误码)填写到结构体的Internal, 将具体读写了多少字节的内容写入到结构体的InternalHigh字段.
  3. 系统完成IO任务之后, 如果hEvent字段值不为NULL, 就说明这个字段是有一个事件对象的, 那么系统会将这个事件对象设置为有信号状态.

获取IO完成通知

  1. 根据文件句柄的信号状态来判断

    1. 缺点 : 如果一个文件同时存在多个IO任务, 只要其中一个任务被完成了, 文件句柄就变成有信号状态,无法判断是哪个IO被完成了.
  2. 使用事件对象信号状态来判断

    1. 每个IO任务都有一个重叠IO结构体来配置信息
    2. 每个IO任务都会配有一个事件对象. 哪个事件对象被设置成有信号了, 就说明哪个IO任务被完成了.
    3. 要等待事件对象的信号时, 需要创建线程等待. 缺点: 如果IO任务很多,就说明要等待的事件也很多, 就需要创建大量线程线程等待, 造成效率低下.
  3. 使用扩展版的API:

    1. ReadFileEx
    2. WriteFileEx
    3. 扩展版的函数能够接收一个完成函数的回调函数.
    4. 当IO任务被系统底层处理完毕之后, 这个回调函数就会被系统插入到线程的APC队列中.
    5. 当线程被挂起(睡眠)的时候, 挂起之前,根据可警醒状态是否为真, 来决定调用APC队列中的函数.
    6. 缺点: 如果在程序中没有调用Sleep或其它等待函数, 或者调用了,但是没有设置可警醒状态, 那么即使IO任务被完成了,完成回调函数也不会被调用.
  4. 通过完成端口来等待IO任务

    1. 创建异步IO方式的文件对象
    2. 创建一个完成端口对象
    3. 将完成端口和文件对象进行绑定
    4. 创建指定个数线程, 在线程监视完成端口 完成列表的状态

      1. 通过APIGetQueuedCompletionStatus来获取完成列表的状态
    5. 发起异步IO任务.

事件对象

  1. 内核对象
  2. 通过CreateEvent来创建

    1. 可以配置手动使用还是自动使用
    2. 可以配置初始化是否有信号
    3. 通过SetEvent 将事件设置为有信号
    4. 通过ResetEvent重置事件对象的信号(设置为没有信号)
  3. 通过WaitForSingleObject来等待它的信号

    1. WaitForSingleObject还会进一步修改被等待的事件对象的信号 : 从无信号开始阻塞等待,知道事件对象有信号之后就从阻塞状态中返回. 返回前,会顺手将事件对象设置为无信号.