Overlapped I/O 在你的身后变戏法

Overlapped I/O是win32的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。事实上,操作系统内部正是以线程来完成overlapped I/O,你可以获得线程的所有利益不需要付出什么代价。

Win32文件操作函数 

win32中有3个基本的函数来执行I/O,分别是CreateFile, ReadFile, WriteFile. 关闭文件调用CloseHandle即可。

CreateFile可以用来打开各种资源,包括(不限于) 

  • 文件(硬盘,光盘,软盘或其他) 
  • 串行口和并行口
  • Named Pipes
  •  Console

HANDLE CreateFile(
  LPCTSTR lpFileName, 
// 文件名称
  DWORD dwDesiredAccess,  // 存取模式,读或写
  DWORD dwShareMode,     // 共享模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,  // 安全属性
  DWORD dwCreationDisposition,    // 如何产生
  DWORD dwFlagsAndAttributes,      // 文件属性
  HANDLE hTemplateFile           // 临时文件,将拥有全部的属性拷贝
); 

第6个参数dwFlagsAndAttributes是使用overlapped I
/ O的关键。可以是多个数值组合在一起。对于overlapped I / O关键的是FILE_FLAG_OVERLAPPED。通过这个参数可以指定是同步或者异步访问,不能2个都指定。

overlapped I/O可以同时读或写文件的许多部分,这些操作都使用相同的文件handle。因此,当你使用overlapped I/O时,每个读写操作都要包含文件位置。如果有多个overlapped请求,那么执行的次序无法保证。不能使用标准的C库来实现overlapped 操作,必须要以ReadFile和WriteFile来完成。

 

BOOL ReadFile( 
  HANDLE hFile,      
// 欲读的文件
  LPVOID lpBuffer,    // 接受数据的缓冲区
  DWORD nNumberOfBytesToRead,    //  欲读取的字节个数
  LPDWORD lpNumberOfBytesRead,   //  实际读取的字节个数的地址
  LPOVERLAPPED lpOverlapped     //  指针,指向overlapped info结构
); 

BOOL WriteFile( 
  HANDLE hFile, 
  LPCVOID lpBuffer, 
  DWORD nNumberOfBytesToWrite, 
  LPDWORD lpNumberOfBytesWritten, 
  LPOVERLAPPED lpOverlapped
); 

typedef 
struct  _OVERLAPPED {  
    ULONG_PTR Internal;  
// 通常保留,当GetOverLappedResult返回False时,有用
    ULONG_PTR InternalHigh;   / 通常保留,当GetOverlappedResult返回TRue时,有用
    union {    
      
struct  {      
        DWORD Offset;      
// 读写的偏移地址
        DWORD OffsetHigh;     // 64位地址中较高的32位
      };    
      PVOID Pointer;  
    };  
    HANDLE hEvent;  
// 一个手动重置的eventuixiang,当overlapped 完成时激发
} OVERLAPPED,   * OVERLAPPEDLPOVERLAPPED;

OVERLAPPED的2个功能,识别每一个正在进行的overlapped操作,你和系统之间的共享区域,参数在这个区域中双向传递。
由于OVERLAPPED结构的生命周期超越ReadFile和writeFile,所以要将其放在一个安全的地方,就是heap。

 被激发的File Handles

 最简单的overlapped I/O类型,是使用它自己的文件handle作为同步机制。首先以FILE_FLAG_OVERLAPPED告诉win32说你不要使用默认的同步IO。然后设立一个OVERLAPPED结构,其中包含“IO请求”的所有必要参数,并以此识别这个IO请求,直到完成为止。接下来,调用ReadFile并以OVERLAPPED结构的地址作为最后一个参数。这时,就可以等待win32为你处理io,你可以做其他的事情了。 如果你需要等待overlapped IO的执行结果,作为WaitForMultipleObjects的一部分,请在handle数组中加上这个文件handle。 当完成操作后,可调用GetOverlappdedResult以确定结果如何。

 

代码
BOOL WINAPI GetOverlappedResult(
   HANDLE hFile,       
// 文件或者设备的handle
   LPOVERLAPPED lpOverlapped,   // 一个指向OVERLAPPED结构的指针
   LPDWORD lpNumberOfBytesTransferred,    // 指针,获得真正传送的字节个数
   BOOL bWait       // 是否要等待操作完成
);

返回值:如果overlapped操作成功,返回TRUE,失败返回FALSE。如果bWait为False而overlapped还没有完成,GetLastError会传回ERROR_IO_INCOMPLETE.

我们要注意,虽然我们要求进行overlapped操作,但如果数据可以很快的传送,那么文件操作就可能会在REadfile返回之前完成,而ReadFile返回TRUE。

如果请求进行overlapped操作,那么ReadFile和WriteFile常规情况下会返回FALSE, 我们必须调用GetLastError并确定它传回ERROR_IO_PENDING,以为着IO操作被放进队列中等待执行。如果是其他返回值,就代表一个真正的错误了。 

被激发的Event对象 

 以文件handle作为激发机制,如果在一个文件上有多个overlapped操作时,无法得知那个overlapped操作完成了。这时,我们可以使用OVERLAPPED结构中的最后一个event handle位,当这个栏位设为一个event对象时,系统核心会在overlapped操作完成时,自动将这个event 对象激发。每个操作都有独一无二的event对象,所使用的event对象必须是手动重置的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解了你的问题。对于使用C++ Windows API监视串口中的数据,可以使用异步I/O功能。下面是一个示例代码: ``` #include <windows.h> #include <iostream> #define MAX_DATA_LENGTH 255 void ReadDataFromSerialPort(HANDLE hSerialPort); int main() { HANDLE hSerialPort; DCB dcbSerialParams = { 0 }; // 打开串口 hSerialPort = CreateFile(L"COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, nullptr); if (hSerialPort == INVALID_HANDLE_VALUE) { std::cerr << "Error: Unable to open serial port!" << std::endl; return 1; } // 配置串口 dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerialPort, &dcbSerialParams)) { std::cerr << "Error: Unable to get serial port state!" << std::endl; CloseHandle(hSerialPort); return 1; } dcbSerialParams.BaudRate = CBR_9600; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = ONESTOPBIT; dcbSerialParams.Parity = NOPARITY; if (!SetCommState(hSerialPort, &dcbSerialParams)) { std::cerr << "Error: Unable to set serial port state!" << std::endl; CloseHandle(hSerialPort); return 1; } // 监视串口中的数据 ReadDataFromSerialPort(hSerialPort); // 关闭串口 CloseHandle(hSerialPort); return 0; } void ReadDataFromSerialPort(HANDLE hSerialPort) { OVERLAPPED ov; DWORD bytesRead; char data[MAX_DATA_LENGTH]; ZeroMemory(&ov, sizeof(ov)); ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); while (true) { if (!ReadFile(hSerialPort, data, MAX_DATA_LENGTH, &bytesRead, &ov)) { if (GetLastError() == ERROR_IO_PENDING) { // 等待I/O操作完成 WaitForSingleObject(ov.hEvent, INFINITE); // 获取I/O操作结果 if (!GetOverlappedResult(hSerialPort, &ov, &bytesRead, FALSE)) { std::cerr << "Error: Unable to get overlapped result!" << std::endl; break; } // 处理串口中的数据 std::cout << "Data received: " << data << std::endl; } else { std::cerr << "Error: Unable to read data from serial port!" << std::endl; break; } } else { // 处理串口中的数据 std::cout << "Data received: " << data << std::endl; } } CloseHandle(ov.hEvent); } ``` 这段代码会打开COM1串口,并监视串口中的数据,如果有数据到达,就会将数据输出到控制台。你可以根据需要修改COM口号和串口参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值