句柄
![本頁使用了標題或全文手工轉換](http://images.weserv.nl/?url=//upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Zh_conversion_icon_m.svg/35px-Zh_conversion_icon_m.svg.png)
此條目需要補充更多來源。 (2017年3月10日) |
在程序設計中,句柄(handle)是Windows操作系統用來標識被應用程序所建立或使用的對象的整數。其本質相當於帶有引用計數的智能指針。當一個應用程序要引用其他系統(如數據庫、操作系統)所管理的內存塊或對象時,可以使用句柄。
句柄與指針
句柄與普通指針的區別在於,指針包含的是引用對象的內存地址,而句柄則是由系統所管理的引用標識,該標識可以被系統重新定位到一個內存地址上。這種間接訪問對象的模式增強了系統對引用對象的控制。(參見封裝)。通俗的說就是我們調用句柄就是調用句柄所提供的服務,即句柄已經把它能做的操作都設定好了,我們只能在句柄所提供的操作範圍內進行操作,但是普通指針的操作卻多種多樣,不受限制。
句柄與安全性
客戶獲得句柄時,句柄不僅是資源的標識符,也被授予了對資源的特定訪問權限。
句柄與操作系統
在上世紀80年代的操作系統(如Mac OS[1]和Windows)的內存管理中,句柄被廣泛應用。Unix系統的文件描述符基本上也屬於句柄。和其它桌面環境一樣,Windows API大量使用句柄來標識系統中的對象,並建立操作系統與用戶空間之間的通信渠道。例如,桌面上的一個窗體由一個HWND
類型的句柄來標識。
如今,內存容量的增大和虛擬內存算法使得更簡單的指針愈加受到青睞,而指向另一指針的那類句柄受到冷淡。儘管如此,許多操作系統仍然把指向私有對象的指針以及進程傳遞給客戶端的內部數組下標稱為句柄。
譯名
康奈爾大學副教授David Gries所著的《Compiler Construction for Digital Computer》. John Wiley and Sons, New York, 1971, 491 pages, ISBN 0-471-32776-X,給出如下定義:
(2.3.10) DEFINITION. A handle of any sentential form is a leftmost simple phrase.
在該書的中譯本: D.格里斯著,仲萃豪等譯:《數字計算機的編譯程序構造》,科學出版社, 1976年版,給出了如下翻譯:
(2.3.10)定义.任一句型的句柄就是此句型的最左简单短语。
Windows句柄的本質
內核對象是內存中的數據結構,由操作系統內核分配,並且只能由操作系統內核訪問。內核對象的數據結構使用引用計數,計數變為0後操作系統內核就會銷毀該內核對象。安全描述符(SECURITY_ATTRIBUTES結構)描述內核對象的安全性,指出內核對象的擁有者、哪些組或用戶可以訪問此對象。作為對照,用戶對象或GDI對象(如菜單、窗口、鼠標光標等)在創建時不需要指出安全描述符。[2]
操作系統內核中有一個全局句柄表。每個進程有自己的一個句柄表,是一個數據結構組成的數組,每個數據結構包含一個指向內核對象的指針、訪問掩碼、繼承標識等。句柄是進程句柄表數組的下標。在32位系統中,句柄是一個32位值。64位系統中則是64位值。應用程序調用創建內核對象的API函數後,該API函數會返回一個句柄以標識操作系統內核所創建的內核對象。這個句柄可以由進程的任何線程使用。使用CloseHandle函數或者進程結束時,內核句柄表中相應項的計數值會被減1。
GetCurrentProcess函數返回當前進程的句柄,其值為-1;GetCurrentThread函數返回的句柄其值為-2。二者都是偽句柄,在進程的句柄表中沒有這兩項,僅代表當前進程和當前線程。
進程間共享內核對象有如下途徑:
- 父子進程間的句柄繼承:在調用API函數來創建內核對象時,使用SECURITY_ATTRIBUTES結構來指出可以被繼承。創建子進程時,所有被繼承的句柄在子進程句柄表中的位置與在父進程中完全一樣;內核對象的使用計數也會加1。
- 命名對象:注意所有種類的內核對象使用同一個命名空間。
- 複製對象句柄:使用DuplicateHandle函數
文件/設備句柄的操作系統API
Windows操作系統提供下述API函數以訪問文件或設備:
SetHandleInformation: 设置指定句柄的当前标识
GetHandleInformation: 返回指定句柄的当前标识
HANDLE CreateFile(
LPCTSTR,lpFileName, //指向文件名的指针
DWORD dwDesiredAccess, //访问模式(读/写/读写/0表示不读写仅获取文件信息)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//用于确定如何在子进程中继承这个句柄
DWORD dwCreationDisposition, // 文件存在或不存在时的操作
//CREATE_NEW:创建文件,如果文件存在会出错;
//CREATE_ALWAYS:创建文件,如果该文件已经存在,函数将覆盖已存在的文件并清除已存在的文件属性
//OPEN_EXISTING:打开已存在文件;
//OPEN_ALWAYS:如果不存在就创建;
//TRUNCATE_EXISTING:将现有的文件缩短为零长度。调用进程必须用GENERIC_WRITE访问模式打开文件.如果文件不存在则函数就会失败.
DWORD dwFlagAndAttributes, //指定新创建文件的属性;以及文件的标志位
//FILE_ATTRIBUTE_ARCHIVE:标记为归档属性;
//FILE_ATTRIBUTE_NORMAL:默认属性;文件没有被设置任何属性;仅单独使用生效。
//FILE_ATTRIBUTE_HIDDEN:隐藏文件或目录;
//FILE_ATTRIBUTE_OFFLINE 文件数据不可直接利用。指出文件数据已经在物理上移动到脱机存储。
//FILE_ATTRIBUTE_READONLY:文件为只读;
//FILE_ATTRIBUTE_SYSTEM:文件为系统文件;
//FILE_ATTRIBUTE_COMPRESSED 将文件标记为已压缩,或者标记为文件在目录中的默认压缩方式
//FILE_ATTRIBUTE_TEMPORARY 文件用于临时存储;文件系统尽量把文件数据保存在内存而不是刷回主存储;临时文件不再使用后应用程序应该尽快删除临时文件。
//FILE_FLAG_WRITE_THROUGH 系统写通过任何中间缓存直接写入硬盘;系统仍然可以缓存写操作,但不得懒惰地刷回硬盘
//FILE_FLAG_OVERLAPPED 允许对文件进行[[重叠I/O]]操作
//FILE_FLAG_NO_BUFFERING 禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块
//FILE_FLAG_RANDOM_ACCESS 针对随机访问对文件缓冲进行优化
//FILE_FLAG_SEQUENTIAL_SCAN 针对从头到尾的顺序访问方式对大文件缓冲进行优化
//FILE_FLAG_DELETE_ON_CLOSE 关闭了所有打开的句柄后,文件立即被删除。特别适合临时文件。如果没有使用FILE_SHARE_DELETE,后续的打开文件的请求将会失败.
//FILE_FLAG_BACKUP_SEMANTICS 指示系统文件的打开或创建将用于备份或恢复操作。系统保证调用进程如果有必要的特权(SE_BACKUP_NAME与SE_RESTORE_NAME),将忽略文件安全检查。这个标志也适用于文件夹句柄。
//FILE_FLAG_POSIX_SEMANTICS 指明文件基于POSIX规则被访问。例如多个文件使用只有大小写区别的文件名(如果文件系统支持的话)。在MS-DOS与16位Windows下,由于难以兼容,需谨慎使用。
//FILE_FLAG_OPEN_REPARSE_POINT 指出禁止[[NTFS重解析点]]的行为。标志不能够和CREAT_ALWAYS一起使用.
//FILE_FLAG_OPEN_NO_RECALL 指明需要文件数据,但是将继续从远程存储器中读写,不会将数据存放在本地存储器中。这个标志由远程存储系统或分层存储管理器系统使用.
HANDLE hTemplateFile //如果不为0,则指定一个文件句柄,新的文件将从这个文件中复制扩展属性
);//如果函数失败,返会值会是INVALID_HANDLE_VALUE
SetFilePointer();//设置文件指针位置
BOOL WriteFile(
HANDLE fFile, //文件句柄
LPCVOID lpBuffer, //数据缓存区指针
DWORD nNumberOfBytesToWrite, //所要写的字节数
LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区的指针
LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针
);
BOOL ReadFile(
HANDLE fFile, //文件句柄
LPCVOID lpBuffer, //数据缓存区指针
DWORD nNumberOfBytesToRead, //所要写的字节数
LPDWORD lpNumberOfBytesRead, //用于保存实际写入字节数的存储区的指针
LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针
) ;
CloseHandle(hFILE); //关闭句柄
GetDiskFreeSpace(); //确定扇区的尺寸
文件存取的字節數必須是扇區尺寸的整倍數。進行讀和寫操作的地址必須扇區位置對齊(在內存中地址對齊到扇區容量的整倍數),可用VirtualAlloc()在內存中申請緩衝區,做到內存頁對齊(從而硬盤扇區也對齊)。
參見
參考文獻
- ^ Hertzfeld, Andy, The Original Macintosh: Hungarian, January 1982 [2010-05-10], (原始內容存檔於2010-06-19)
- ^ Ruediger Asche: "Give Me a Handle, and I'll Show You an Object", in MSDN. [2017-09-18]. (原始內容存檔於2018-10-29).