Windows核心编程学习笔记(20)--同步设备I/O与异步设备I/O1

Drecik学习经验分享

转载请注明出处:http://blog.csdn.net/drecik__/article/details/8181338


1. 打开和关闭设备

首先看下Windows常用的设备和打开他们的函数:

Windows核心编程学习笔记(20)--同步设备I/O与异步设备I/O1_第1张图片

根据上述表格打开一个设备后获得一个用来标志设备的句柄,可以将该句柄传给其他函数来与设备进行通信。

例如:调用SetCommConfig设置串口的波特率,调用SetMailslotInfo设置一个超时值。

完成操作后要使用CloseHandle关闭句柄,特殊的套接字使用closesocket关闭。

如果有一个设备句柄可以使用GetFileType传入设备句柄来获得设备的类型,返回值为:

  • FILE_TYPE_UNKNOW:位置类型
  • FILE_TYPE_DISK:磁盘文件
  • FILE_TYPE_CHAR:字符文件,一般说是一个并口设备或控制台
  • FILE_TYPE_PIPE:命名管道或匿名管道

2. 细看CreateFile函数

就如上面表格中所示,CreateFile函数可以创建和打开磁盘文件还可以打开许多其他设备,打开失败返回INVALID_HANDLE_VALUE。

HANDLE
CreateFileW(
	LPCWSTR lpFileName,		// 设备的类型或该设备的某个实例;
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
	);
  • dwDesiredAccess:指定何种方式和设备进行传输,常用的有:

0:不允许读取设备或向设备写入,只允许改变设备的配置

GENERIC_READ:允许只读访问

GENERIC_WRITE:允许只写访问

GENERIC_READ | GENERIC_WRITE:读写访问

  • dwShareMode:指定设备的共享特权,当我们打开设备之后并在关闭该句柄之前,该参数可以控制其他CreateFIle对该设备的打开,常用参数有:

0:独占对设备的访问

FILE_SHARE_READ:允许只读打开该设备

FILE_SHARE_WRITE:允许只写打开该设备

FILE_SHARE_READ |  FILE_SHARE_WRITE:允许读写打开该设备

FILE_SHARE_DELETE:对文件而言,允许删除,当有操作将文件删除,则先标记,当该文件全部的句柄关闭后,将删除该文件

  • lpSecurityAttributes:安全属性,设置是否可以被继承
  • dwCreationDisposition:通常对文件而言最用大点,打开设备只能指定OPEN_EXISTING参数,常用参数有:

CREATE_NEW:创建一个新文件,如果存在则失败

CREATE_ALWAYS:无论是否存在,创建一个新文件

OPEN_EXISTING:打开以后文件或设备,不存在则失败

OPEN_ALWAYS:打开一个以后文件,不存在则创建新文件

TRUNCATE_EXISTING:打开已有文件,并将文件大小截断为0,不存在则失败

  • dwFlagsAndAttributes:设置一些标志来微调设备之间的通信,如果是文件,还能设置文件属性。当最后一个参数hTemplateFile为NULL时才可用,常用的参数有,还有其他属性请参考MSDN:

1. 高速缓存标志

FILE_FLAG_NO_BUFFER:标志在访问文件的时候不要使用任何数据缓存,读大文件的时候为了防止读取失败,需要设置该标志

FILE_FLAG_SEQUENTIAL_SCAN:标志将要顺序读取文件,该种情况下,系统从文件中读取的数据流会超过我们需要的数量,减少访问次数

FILE_FLAG_RANDOM_ACCESS:标志不要提前读取文件数据

FILE_FLAG_THROUGH:标志禁止对文件写入操作进行缓存以减少数据丢失的可能性,每次写入文件都直接写入到磁盘

2. 其他标志

FILE_FLAG_DELETE_ON_CLOSE:标志所有句柄关闭后将删除文件,通常和文件属性标志中的FILE_ATTRUBUTE_TEMPORARY一起使用

FILE_FLAG_BACKUP_SEMANTICS:一般用于备份和恢复软件,也可以使用该标志来打开一个目录句柄

FILE_FLAG_POSIX_SEMANTICS:标志打开或创建文件时以徐芬大小写的方式查找文件名

FILE_FLAG_OPEN_REPARSE_POINT:标志忽略文件的冲解析属性,不推荐使用

FILE_FLAG_OPEN_NO_RECALL:标志系统不要将文件内容从脱机存储器回复到联机存储器

FILE_FLAG_OVERLAPPED:标志想要以异步方式来访问设备

3. 文件属性标志

FILE_ATTRIBUTE_ARCHIVE:标志文件是一个存档文件,即待备份或待删除,默认为该标志

FILE_ATTRIBUTE_ENCRYPTED:标志文件是经过加密的

FILE_ATTRIBUTE_HIDDEN:标志文件是隐藏的

FILE_ATTRIBUTE_NORMAL:没有其他属性,只有单独使用的时候,这个标志才有效

FILE_ATTRIBUTE_NOT_CONTENT_INDEXED:标志内容索引服务不会对文件进行索引

FILE_ATTRIBUTE_OFFLINE:标志文件虽然存在,但文件已经被转移到脱机存储器中

FILE_ATTRIBUTE_READONLY:标志文件是只读的,不能写入或删除文件

FILE_ATTRIBUTE_SYSTEM:标志文件是操作系统的一部分或专供操作系统使用

FILE_ATTRIBUTE_TEMPORARY:标志文件数据只会使用一小段时间,文件系统会尽量将文件数据保存在内存中而不是硬盘

  • hTemplateFile:既可以标识一个已经打开的文件的句柄,也可以为NULL,当不为NULL时,会忽略dwFlagsAndAttributes所有属性,使用hTemplateFile所标识的文件属性,hTemplateFile必须是使用GENERIC_READ打开的文件,如果CreateFile要打开已有的文件(而不是创建文件),那么它会忽略hTemplateFile参数

3. 使用文件设备

Windows使用64位的值来管理文件,所以理论上能处理的文件可以达到16EB
  • 取得文件大小
最简单是使用GetFileEx来获取:
BOOL
GetFileSizeEx(
	HANDLE hFile,				// 文件句柄;
	PLARGE_INTEGER lpFileSize	// 用来返回文件大小的64位值;
	);

第二种是使用GetCompressedFileSize,返回的是文件的物理大小,而GetFileSizeEx返回的是逻辑大小,例如假设一个文件100KB,压缩后85KB,则逻辑大小为100KB,而物理大小为85KB:

DWORD	// 返回文件大小的低位;
GetCompressedFileSizeW(
	LPCWSTR lpFileName,		// 文件名;
	LPDWORD  lpFileSizeHigh	// 返回文件大小的高位;
	);
  • 设置文件指针的位置

文件内核对象有一个文件指针,是一个64位偏移量,表示下次在哪里读取或者写入。

每个文件内核对象都有自己的文件指针,通过DuplicateHandle复制的文件句柄也共用一个文件指针

如果需要随机访问文件,则我们可以调用SetFilePointerEx来改变文件指针:

BOOL
	SetFilePointerEx(
	HANDLE hFile,						// 文件句柄;
	LARGE_INTEGER liDistanceToMove,		// 想要把指针移动多少字节;
	PLARGE_INTEGER lpNewFilePointer,	// 返回新的指针位置,可以为NULL;
	DWORD dwMoveMethod					// 指定第二个参数相对移动的起始位置;
	);

最后一个参数可以有如下值:

FILE_BEGIN:文件对象的文件指针将被设为liDistanceToMove参数指定的值,此时该参数是一个无符号64位值

FILE_CURRENT:文件对象的文件指针将与liDistanceToMove参数相加,此时该参数是一个有符号64位值

FILE_END:文件对象的指针被设置为文件的逻辑大小加上liDistanceToMove参数,此时该参数是一个有符号64位值

最后文件指针的操作需要注意下面几项:

  • 将文件指针的值设为超过大小是正当操作,除非是在该位置写入或者调用SetEndOfFile,否则这样做不会增加文件实际大小
  • 如果SetFilePointerEx操作的文件是使用FILE_FLAG_NO_BUFFER打开,则文件指针只能被设置为扇区大小的整数倍
  • Windows没有提供GetFilePointerEx函数,但是可以将liDistanceToMove参数设置为0,并设置FILE_CURRENT来获得当前指针
  • 设置文件尾

通常在文件关闭的时候,系统会负责设置文件尾,当我们需要将文件强制变的更大或更小,我们可以使用SetEndOfFile自己设置文件尾,例如下面代码:
	HANDLE hFile = CreateFile( TEXT("Test.txt"), GENERIC_READ | GENERIC_WRITE, 0, NULL,
		CREATE_ALWAYS, 0, NULL );

	// 设置文件指针;
	LARGE_INTEGER li = {0};
	li.LowPart = 1024*1024;
	SetFilePointerEx( hFile, li, NULL, FILE_BEGIN );

	// 强制将文件设置为1MB;
	SetEndOfFile(hFile);

4. 执行同步设备I/O

最常用对设备数据进行的操作就是读和写,使用ReadFile和WriteFile来实现(不单单指对文件):
// 只能用于GENERIC_READ标志打开的设备;
BOOL
ReadFile(
	HANDLE hFile,					// 设备句柄;
	LPVOID lpBuffer,				// 读取缓冲区;
	DWORD nNumberOfBytesToRead,		// 读取的字节数;
	LPDWORD lpNumberOfBytesRead,	// 返回实际读取的字节数;
	LPOVERLAPPED lpOverlapped		// 用来设置异步访问,同步访问必须为NULL;
	);

// 只能用于GENERIC_WRITE标志打开的设备;
BOOL
WriteFile(
	HANDLE hFile,					// 设备句柄;
	LPCVOID lpBuffer,				// 写入缓冲区;
	DWORD nNumberOfBytesToWrite,	// 写入的字节数;
	LPDWORD lpNumberOfBytesWritten,	// 返回实际写入的字节数;
	LPOVERLAPPED lpOverlapped		// 用来设置异步访问,同步访问必须为NULL;
	);

另外一个常用函数是FlushFileBuffers,该函数强制将与hFile参数表示的设备相关联的所有缓存数据写入设备。

Windows Visita及以上提供了同步I/O的取消操作:

当进行同步I/O时,会阻塞当前调用的线程,直到操作返回,当线程正在因为进行同步I/O而挂起时,我们可以使用CancelSynchronousIo来取消同步I/O操作:

BOOL
CancelSynchronousIo(
	HANDLE hThread		// 线程句柄;
	);
hThread是OpenThread打开的时候必须包括THREAD_TERMINATE访问权限

如果指定的线程是因为同步I/O而挂起,调用该函数后可以将线程唤醒,此时同步I/O就会调用失败,该函数返回TRUE

如果调用线程不是因为同步I/O而挂起时,则该函数返回FLASE


你可能感兴趣的:(Windows核心编程学习笔记(20)--同步设备I/O与异步设备I/O1)