windows I/O 基础
在Windows
中,同步I/O
和异步I/O
是两种不同的数据输入输出处理方式,而I/O
完成端口(IOCP
)是一种特定的高效异步I/O模型。
1、同步I/O
(Synchronous I/O)
同步I/O操作要求发起I/O请求的线程等待操作完成才能继续执行。这意味着线程在等待I/O完成时会被阻塞。
Windows
中打开文件或者设备都可以使用CreateFile
函数,Windows
系统为我们封装了底层设备I/O
的细节让我们可以像操作文件一样操作串口并口等设备。
优点:
- 编程模型简单,容易理解和实现。
- 适合不需要高并发处理的应用。
缺点:
- 线程在等待I/O完成时无法执行其他任务,可能导致资源浪费。
- 在处理多个I/O请求时,可能需要多个线程,增加了上下文切换的开销。
示例代码:同步文件读取
#include <windows.h>
#include <iostream>
void ReadFromFile(const wchar_t* filename) {
HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Error opening file" << std::endl;
return;
}
char buffer[128];
DWORD bytesRead;
BOOL result = ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL);
if (result) {
std::cout << "Read " << bytesRead << " bytes." << std::endl;
} else {
std::cerr << "Error reading file" << std::endl;
}
CloseHandle(hFile);
}
备注:注意,在同步模式下,示例中 CreateFile
中 参数 FILE_ATTRIBUTE_NORMAL
,也可以是NULL
同步I/O
设备
对设配I/O进行读写最方便时ReadFile和WriteFile 函数原型如下:
BOOL ReadFile(
HANDLE hFile, // 设备句柄
LPVOID lpBuffer, //数据缓存
DWORD nNumberOfBytesToRead, // 告诉设备需要读取多少字节
LPDWORD lpNumberOfBytesRead, // 真实读取的字节
LPOVERLAPPED lpOverlapped // 同步I/O 此参数应该为NULL,异步I/O时需要传入LPOVERLAPPED
);
BOOL WriteFile(
HANDLE hFile,// 设备句柄
LPCVOID lpBuffer,//数据缓存
DWORD nNumberOfBytesToWrite,// 告诉设备需要写入多少字节
LPDWORD lpNumberOfBytesWritten,真实写入的字节
LPOVERLAPPED lpOverlapped// 同步I/O 此参数应该为NULL,异步I/O时需要传入LPOVERLAPPED
);
2、异步I/O
(Asynchronous I/O)
异步I/O允许线程发起一个I/O操作后立即继续执行,不需要等待I/O操作完成。操作系统会在I/O操作完成后通知应用程序。
优点:
- 提高了线程的利用率,因为线程可以在等待I/O操作完成时执行其他任务。
- 可以提高应用程序处理多个并发I/O请求的能力。
缺点:
- 编程模型复杂,需要处理回调或检查I/O操作的状态。
- 错误处理更加复杂。
示例代码:异步文件读取
#include <windows.h>
#include <iostream>
void CALLBACK FileIOCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
std::cout << "Read " << dwNumberOfBytesTransfered << " bytes." << std::endl;
}
void ReadFromFileAsync(const wchar_t* filename) {
HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Error opening file" << std::endl;
return;
}
char buffer[128];
OVERLAPPED ol = {0};
BOOL result = ReadFileEx(hFile, buffer, sizeof(buffer), &ol, FileIOCompletionRoutine);
if (!result) {
std::cerr << "Error reading file" << std::endl;
}
SleepEx(INFINITE, TRUE); // Wait for the I/O to complete
CloseHandle(hFile);
}
3、 I/O
完成端口(IOCP
)
IOCP是Windows提供的一种高效的线程池技术,用于处理大量的异步I/O操作。
优点:
- 高效地处理数千个并发异步I/O操作。
- 自动线程管理,根据系统负载动态调整线程数量。
- 减少了线程上下文切换和同步对象的使用,提高了性能。
缺点:
- 实现复杂,需要对Windows内核的工作方式有深入理解。
- 调试困难,错误处理较为复杂。
示例代码:使用IOCP
由于IOCP的实现相对复杂,涉及到创建完成端口、关联文件或套接字句柄、处理完成通知等多个步骤,以下是一个简化的IOCP使用示例,展示了如何创建完成端口、如何将文件句柄与完成端口关联,以及如何在工作线程中处理I/O完成事件。
#include <windows.h>
#include <iostream>
// 工作线程函数
DWORD WINAPI WorkerThread(LPVOID lpParam) {
HANDLE hCompletionPort = (HANDLE)lpParam;
DWORD bytesTransferred;
ULONG_PTR completionKey;
OVERLAPPED* pOverlapped;
while (GetQueuedCompletionStatus(hCompletionPort, &bytesTransferred, &completionKey, &pOverlapped, INFINITE)) {
// 处理完成的I/O请求
// ...
}
return 0;
}
void UseIOCP(const wchar_t* filename) {
// 打开文件
HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Error opening file" << std::endl;
return;
}
// 创建完成端口
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (hCompletionPort == NULL) {
std::cerr << "Error creating IOCP" << std::endl;
CloseHandle(hFile);
return;
}
// 将文件句柄与完成端口关联
if (CreateIoCompletionPort(hFile, hCompletionPort, 0, 0) == NULL) {
std::cerr << "Error associating file handle with IOCP" << std::endl;
CloseHandle(hCompletionPort);
CloseHandle(hFile);
return;
}
// 创建工作线程
HANDLE hThread = CreateThread(NULL, 0, WorkerThread, hCompletionPort, 0, NULL);
if (hThread == NULL) {
std::cerr << "Error creating worker thread" << std::endl;
CloseHandle(hCompletionPort);
CloseHandle(hFile);
return;
}
// 发起异步读取操作
char buffer[1024];
OVERLAPPED ol = {0};
if (!ReadFile(hFile, buffer, sizeof(buffer), NULL, &ol)) {
if (GetLastError() != ERROR_IO_PENDING) {
std::cerr << "Error reading file" << std::endl;
CloseHandle(hThread);
CloseHandle(hCompletionPort);
CloseHandle(hFile);
return;
}
}
// 等待工作线程完成
WaitForSingleObject(hThread, INFINITE);
// 清理资源
CloseHandle(hThread);
CloseHandle(hCompletionPort);
CloseHandle(hFile);
}
在这个示例中,我们首先打开一个文件并创建一个完成端口。然后,我们将文件句柄与完成端口关联,并创建一个工作线程来处理I/O
完成事件。接着,我们发起一个异步读取操作。工作线程将在I/O
操作完成时被唤醒,以处理该事件。
请注意,这个示例是高度简化的。在实际应用中,你需要处理多个并发的I/O
操作,正确处理I/O
操作的完成或失败,并且可能需要管理多个工作线程。此外,你还需要设置OVERLAPPED
结构体,以便在I/O操作完成时能够正确地获取到操作的上下文信息。
参考文献:
1、Windows同步设备I/O与异步设备I/O
2、追求极致:Windows异步IO各种模式性能对比
3、浅谈windows下高性能服务器模型IOCP
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!