Windows核心编程(九)同步设备I/O

1、同步IO指线程在发起IO请求后会被挂起,IO完成后继续执行。

异步IO指线程发起IO请求后并不会挂起而是继续执行。IO完毕后会得到设备的通知。而IO完成端口就是实现这种通知的很好的一种方式。

线程是我们开发高性能、响应性好的一个必不可少的工具。这样在多处理器上就可以同时执行多个操作,从而提高吞吐量。当线程发出一个同步设备IO请求的时候,它会被临时挂起,直到设备完成IO请求为止。但线程阻塞会损害性能,这里有个问题是我们如何让线程不被挂起。

让线程始终进行有用的工作就需要它们相互通信。Windows开发出了一种被称为IO完成端口的机制的技术。它可以帮助我们创建高性能而且伸缩性好的应用程序。通过使用完成端口我们可以让线程在读取设备和写入设备而不必等待设备的响应,从而显著的提高吞吐量。

作为Windows程序员都必须要完全理解IO完成端口的工作原理。

 Windows支持多种不同种类的设备。在此,我们把设备定义为能够与之进行通信的任何东西。如文件、目录、串口、并口、套接字、控制台等。接下来讨论是与这些设备进行通信,此种方式下与线程通信时,线程需要挂起等待设备响应——这种方式被称为同步IO。

Windows向开发人员隐藏了各种设备的差异,许多WindowsAPI允许我们以相同的方式来从设备读取数据和向设备写入数据,而不必关心何种类型的设备。


2、可以调用SetCommConfig来设置串口的波特率。调用SetMailslotInfo设置一个超时值。当完成对设备的操作后,我们必须将其关闭,即调用CloseHandle函数。

BOOL CloseHandle(HANDLE hObject);

如果设备是套接字,必须调用closesocket。

int closesocket(SOCKET s);

如果有一个设备句柄,那么可以调用GetFileType来查出设备的类型。

DWORD GetFileType(HANDLE hDevice);


3、CreateFile函数可以创建和打开磁盘文件。它同样可以打开其他设备。传入参数的不同可以让CreateFile打开不同的设备。

HANDLE CreateFile(  

         PCTSTR pszName,  
         DWORD dwDesiredAccess,  
         DWORD dwShareMode,  
         PSECURITY_ATTRIBUTES psa,  
         DWORD dwCreationDisposition,  
         DWORD dwFlagsAndAttributes,  

         HANDLE hFileTemplate);  

---psaName既表示设备类型也表示该类设备一个实例。
---dwDesiredAccess用来指定我们以何种方式和设备通信。可以传入以下值:
     0                                      不允许读写,但可以改变设备属性。
    GENERIC_READ             只读访问
    GENERIC_WRITE           只写访问
    GENERIC_READ|GENERIC_WRITE  读写访问。

---dwSharedMode用来指定共享权限:

0                   独占对设备的访问。如果设备已经打开,我们的CreateFile会失败。

FILE_SHARE_READ         只读共享,不允许修改内容。如果设备已经以写入或独占方式打开,我们的CreateFile会失败。

FILE_SHARE_WRITE        写共享,不允许读取内容。如果设备已经以读取或独占方式打开,我们的CreateFile会失败。

FILE_SHARE_READ|FILE_SHARE_WRITE  不关心向设备读还是写数据。如果设备已经以独占方式打开,我们的CreateFile会失败。

FIEL_SHARE_DELETE       先将文件标记待删除,所有对该文件引用的句柄都关闭之后,才将其真正的删除。

---psa指向一个PSECURITY_ATTRIBUTES结构,用来指定安全属性。只有当我们在具备安全性的文件系统中,如NTFS中创建文件时才会用到此结构。在其他情况下都只需要传入NULL就可以了,此时会用默认的安全属性来创建文件,并且返回的句柄是不可继承的。

---dwCreationDisposition参数对文件的含义更重大。它可以是以下值:

    CREATE_NEW        创建一个新文件。如果同名文件存在则失败。
    CREATE_ALWAYS     文件同名文件存在与否都创建文件。存在时会覆盖。
    OPEN_EXISTING     打开一个已存在文件。如不存在,则失败。
    OPEN_ALWAYS        打开一个已存在文件。如不存在,则创建。

    TRUNCATE_EXISTING 打开一个已存在文件,将文件大小截断为0,如果不存在则调用失败。

---dwFlagsAndAttributes有两个用途:一允许我们设置一些标志微调与设备的通信。二如果设备是文件,还可以设置文件属性。这些标志大多数是一些信号,用来告诉系统我们打算以何种方式来访问设备,这样系统就可以对缓存算法进行优化。

---hFileTemplate既可以标识一个已经打开的文件句柄,也可以是NULL。如果是一个文件句柄,那么CreateFile会完全忽略dwFlagsAndAttributes参数,转而使用hFileTemplate标识的文件属性。此时,hFileTemplate标识的文件句柄必须是一个用GENERIC_READ标志打开的文件。

CreateFile成功的创建或打开设备那会返回设备句柄。否则返回INVALID_HANDLE_VALUE。注意返回值不是NULL。


4、Windows在设计时使用了64位来表示文件大小。但是64位需要分两个32位值来传入。实际上在日常工作中还有使用大于4G的文件。高32位在大多数情况下都会是0。

GetFileSizeEx用于得到文件大小。
BOOL  GetFileSizeEx(  
          HANDLE hFile,  

          PLARGE_INTEGER pliFileSize);  

---hFile表示一个已打开文件的句柄。

---pliFileSize表示文件大小。定义如下:

typedef union _LARGE_INTEGER  

{  

  struct  
  {  
      DWORD LowPart;  

      LONG HighPart;  

  };  

LONGLONG QuadPart;  

}LARGE_INTEGER,*PLARGE_INTEGER;  

它允许我们以一个64位有符号数或者是两个32位值来表示一个64位数。

另外一个很重要的函数是GetCompressedFileSize:

DWORD GetCompressedFileSize(  

           PCTSTR pszFileName,  
           PDWORD pdwFileSizeHigh);  

此函数返回文件物理大小,而GetFileSizeEx是返回文件逻辑大小。此函数要求将文件名通过一个字符串的形式传入,而不是将文件在第一个参数中以句柄的形式传入,其通过一种特殊的方式返回64位的文件大小:文件大小的低32位是函数的返回值,文件大小的高32位值被放在pdwFileSizeHigh参数指向的DWORD中。


5、CreateFile会创建一个文件内核对象来管理文件。返回的句柄就是对该文件内核对象的引用。在这个内核对象中有一个文件指针,它是一个64位偏移量,它表示应该在哪里执行下一次读取或写入操作。开始时它的值是0。

SetFilePointerEx可以通过操作文件指针实现随机访问文件。

BOOL SetFilePointerEx(  
         HANDLE hFile,  
         LARGE_INTEGER liDistanceToMove,  
         PLARGE_INTEGER pliNewFilePointer,  
         DWORD dwMoveMethod);  

---hFile表示我们要操作的哪个文件内核对象的文件指针。

---liDistanceToMove指定我们要移动文件指针的字节数。系统会把我们指定的值与文件指针的当前值相加。传入负值是合法的。

---dwMoveMethod指定移动文件指针的起始位置。

FILE_BEGIN      从文件开头开始。

FILE_CURRENT   从当前位置开始。
FILE_END       从文件末尾。

---pliNewFilePointer返回文件指针的新值。

设置文件尾。

BOOL SetEndOfFile(HANDLE hFile);  

此函数会根据文件对象当前的文件指针当前所在的位置,截断文件或增大文件的大小。如果想要将文件设置为2k,可以这样:

LARGE_INTEGER li;  

li.QuadPart=2048;  

SetFilePointerEx(hFile,li,NULL,FILE_BEGIN);  

SetEndOfFile(hFile);  

CloseHandle(hFile);  

ReadFile和WriteFile

BOOL ReadFile(  
       HANDLE hFile,  
       PVOID pvBuffer,  
       DWORD nNumBytesToRead,  
       PDWORD pdwNumBytes,  
       OVERLAPPED*pOverlapped);  


BOOL  WriteFile(  

       HANDLE hFile,  
       CONST VOID *pvBuffer,  
       DWORD nNumBytesToWrite,  
       PDWORD pdwNumbytes,  
       OVERLAPPED*pOverlapped);  

---hFile表示我们要访问的设备。调用CreateFile打开设备时一定不能指定FILE_FLAG_OVERLAPPED标志,否则系统认为我们想要与该设备执行异步IO。

---pvBuffer指向一个缓存,函数会把设备数据读取到该缓存中或者把该缓存的数据写入设备。

---nNumbytesToRead和nNumBytesToWrite分别告诉ReadFile和WriteFile要从设备读取或写入多少字节。

---pdwNumBytes返回读取的字节或向设备写入的字节。

在执行同步IO时,最后一个参数pOverlapped应该被设为NULL。

ReadFile和WriteFile执行成功后都返回true。

ReadFile只能用于那些用GENERIC_READ标志打开的设备,WriteFile同样。


6、将数据刷新至设备。

BOOL FlushFileBuffers(HANDLE hFile);  

该函数会强制将hFile参数所标识的设备相关联的所有缓存写入设备。设备必须是通过GENERIC_WRITE标志打开的。函数调用成功返回TRUE。


7、同步IO很容易使用,但是它会阻塞来自同一个线程(即发出I/O请求的线程)的任何其他操作。比如:如果由于CreateFile正在执行同步IO操作而导致线程被阻塞,那么该线程的其他操作都会得不到处理。更严重的情况是会导致应用程序停止响应。Windows允许我们取消指定线程尚未完成的同步IO请求。

BOOL  CancelSynchronousIo(HANDLE hThread);  

---hThread标识由于等待同步IO完成而被挂起的线程句柄。这个句柄必须是使用THREAD_TERMINATE访问权限创建的。否则,函数会失败。GetLastError会返回ERROR_ACCESSS_DENIED。

我们自己创建的线程的安全属性是THREAD_ACCESS的,其中包括THREAD_TERMINATE。如果我们利用线程池,那么我们必须调用OpenThread来得到当前线程标识符对应的线程句柄,同时传入THREAD_TERMINATE。

CanelSynchronousIo会将等待IO完成而被挂起的线程唤醒。如果线程并不是因为要等待设备响应而被挂起,函数返回false。GetLastError返回ERROR_NOT_FOUND。

即便如此,为了创建响应性好的应用程序我们应该尽可能的执行异步IO操作。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值