控制代碼

程式設計中,控制代碼handle)是Windows作業系統用來標識被應用程式所建立或使用的對象的整數。其本質相當於帶有參照計數的智慧型指標。當一個應用程式要參照其他系統(如資料庫作業系統)所管理的主記憶體塊或對象時,可以使用控制代碼。

控制代碼與指標

控制代碼與普通指標的區別在於,指標包含的是參照對象主記憶體位址,而控制代碼則是由系統所管理的參照標識,該標識可以被系統重新定位到一個主記憶體位址上。這種間接訪問對象的模式增強了系統對參照對象的控制。(參見封裝)。通俗的說就是我們呼叫控制代碼就是呼叫控制代碼所提供的服務,即控制代碼已經把它能做的操作都設定好了,我們只能在控制代碼所提供的操作範圍內進行操作,但是普通指標的操作卻多種多樣,不受限制。

控制代碼與安全性

客戶獲得控制代碼時,控制代碼不僅是資源的識別碼,也被授予了對資源的特定存取權限。

控制代碼與作業系統

在上世紀80年代的作業系統(如Mac OS[1]Windows)的主記憶體管理中,控制代碼被廣泛應用。Unix系統的檔案描述子基本上也屬於控制代碼。和其它桌面環境一樣,Windows API大量使用控制代碼來標識系統中的對象,並建立作業系統與使用者空間之間的通訊管道。例如,桌面上的一個表單由一個HWND類型的控制代碼來標識。

如今,主記憶體容量的增大和虛擬記憶體演算法使得更簡單的指標愈加受到青睞,而指向另一指標的那類控制代碼受到冷淡。儘管如此,許多作業系統仍然把指向私有對象的指標以及行程傳遞給客戶端的內部陣列下標稱為控制代碼。

譯名

康奈爾大學副教授David Gries英語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()在主記憶體中申請緩衝區,做到主記憶體頁對齊(從而硬碟磁區也對齊)。

參見

參考文獻

  1. ^ Hertzfeld, Andy, The Original Macintosh: Hungarian, January 1982 [2010-05-10], (原始內容存檔於2010-06-19) 
  2. ^ Ruediger Asche: "Give Me a Handle, and I'll Show You an Object", in MSDN. [2017-09-18]. (原始內容存檔於2018-10-29).