全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:1197
推到 Plurk!
推到 Facebook!

卸載你的Windows 2000 -2

 
conundrum
尊榮會員


發表:893
回覆:1272
積分:643
註冊:2004-01-06

發送簡訊給我
#1 引用回覆 回覆 發表時間:2005-08-15 00:49:05 IP:218.175.xxx.xxx 未訂閱
卸載你的Windows 2000  太長ktop無法一篇載入 卸載你的Windows 2000-1 http://delphi.ktop.com.tw/topic.php?TOPIC_ID=76770    

Windows 2000作業系統優化
    大家知道,Windows 2000結合了Windows 98和Windows NT 4.0的很多優良的功能/性能與一身,它是Windows家族的一個新的延伸,超越了Windows NT的原來含義。Windows 2000系列分成四個產品:Windows 2000 Professional, Windows 2000 Server, Windows 2000 Advanced Server, Windows 2000 Datacenter Server。Windows 2000 Professional是Windows 2000家族中的用戶端產品,是Windows NT Workstation 4.0的升級換代產品。Windows 2000 Professional的定位是取代Windows 95/98成為公司標準的商業桌面系統,並且是適合移動用戶使用的作業系統。
  在實際使用的過程中,優化Windows 2000的性能,也是很重要的一項工作,下面是筆者在實際工作的幾點經驗總結以及有關注意事項。
  一、升級檔系統 
src="http://www.it.com.cn/f/edu/0411/30/110566.jpg">
  注意:在Windows 2000下將檔系統升級為NTFS格式的方法是,點擊“開始-程式-附件”選中“命令提示符”,然後在打開的提示符視窗輸入“convert drive_letter/fs:ntfs”,其中的“drive”是我們所要升級的硬碟分區符號,如C區,還需要說明的是,升級檔系統,不會破壞所升級硬碟分區裏的檔、無需要備份。 
    二、清楚不需要的檔和程式 
  1、刪除Windows強加的附件 
  (1)用記事本NOTEPAD修改\winnt\inf\sysoc.inf,用查找/替換功能,在查找框中輸入“,hide”(一個英文逗號緊跟hide),將“替換為”框設為空,並選全部替換,這樣就把所有的,hide都去掉。 
  (2)存檔退出; 
  (3)再運行“添加→刪除程式”,就會看見多出了個“添加/刪除Windows元件”的選項(這樣可以刪除一些附件)。
  2、打開“檔夾選項”,在“查看”標籤裏選中“顯示所有檔和檔夾”,此時在我們安裝Windows 2000下的區盤根目錄下會出現Autoexec.bat和Config.sys兩個檔,事實上這兩個檔裏面根本沒有任何內容,可以將它們安全刪除。 
src="http://www.it.com.cn/f/edu/0411/30/110567.jpg">
  注意:各個載入程式後面都有說明,以及運行狀態;選中了要禁用的程式,右擊它選“屬性”,然後單擊停止,並將“啟動類型”設置為“手動”或者“已禁用”就行了。 
  三、去掉文鼎字體 
  Windows 2000似乎與文鼎字體並不怎麼相容,如果我們在運行2000時總是安裝不了新的軟體,或者運行些程式總是出錯,請把文鼎字體刪除。 
  四、更好的“休眠”功能 
  在“控制面板-電源選項”中的“休眠”標籤中,選中“啟用休眠支援”即可。
    五、優化系統屬性 
  1、右擊“我的電腦”選“屬性”,在“高級”的“性能選項”標籤下,選中“應用程式回應”下的“應用程式”,這樣系統會分配給前臺程式更多的資源,使之運行速度更快。 
  2、選中仍然在此標籤下“虛擬記憶體”下的“更改”選項,將虛擬記憶體的值定為我們實體記憶體的2.5倍且最大值和最小值一樣;例如我們的實體記憶體為32M,那虛擬記憶體值就是80,64M記憶體則為160,並且儘量避免將虛擬記憶體設置在與系統檔同一個分區上,如果Windows 2000安裝在C區,就將虛擬記憶體設置在D區或者E區。 
  六、優化啟動設置 
src="http://www.it.com.cn/f/edu/0411/30/110568.jpg">
  1、右擊“我的電腦”選“屬性”,選中“硬體”下的“設置管理器”標籤,然後在“磁碟機”中找到我們的硬碟,查看它的屬性,在“磁片屬性”標籤中選中“啟用了寫入緩存”。 
  2、在“IDE控制器”中分別查看“Primary IDE Channel”和“Secondary IDE Channel”的屬性,在“高級設置”中將“設備類型”設定為“自動檢測”,“傳輸模式”設定為“DMA”(若可用)。
No Comments »
Linux30 Nov 2004 12:00 am
分析Windows和Linux動態庫
    摘要:動態連結程式庫技術實現和設計程式常用的技術,在Windows和Linux系統中都有動態庫的概念,採用動態庫可以有效的減少程式大小,節省空間,提高效率,增加程式的可擴展性,便於模組化管理。但不同作業系統的動態庫由於格式 不同,在需要不同作業系統調用時需要進行動態庫程式移植。本文分析和比較了兩種作業系統動態庫技術,並給出了將Visual C  編制的動態庫移植到Linux上的方法和經驗。
  1、引言
  動態庫(Dynamic Link Library abbr,DLL)技術是程式設計中經常採用的技術。其目的減少程式的大小,節省空間,提高效率,具有很高的靈活性。採用動態庫技術對於升級軟體版本更加容易。與靜態庫(Static Link Library)不同,動態庫裏面的函數不是執行程式本身的一部分,而是根據執行需要按需載入,其執行代碼可以同時在多個程式中共用。
  在Windows和Linux作業系統中,都可採用這種方式進行軟體設計,但他們的調用方式以及程式編制方式不盡相同。本文首先分析了在這兩種作業系統中通常採用的動態庫調用方法以及程式編制方式,然後分析比較了這兩種方式的不同之處,最後根據實際移植程式經驗,介紹了將VC  編制的Windows動態庫移植到Linux下的方法。
  2、動態庫技術
  2.1 Windows動態庫技術
  動態連結程式庫是實現Windows應用程式共用資源、節省記憶體空間、提高使用效率的一個重要技術手段。常見的動態庫包含外部函數和資源,也有一些動態庫只包含資源,如Windows字體資源檔案,稱之為資源動態連結程式庫。通常動態庫以.dll,.drv、.fon等作為尾碼。相應的windows靜態庫通常以.lib結尾,Windows自己就將一些主要的系統功能以動態庫模組的形式實現。
  Windows動態庫在運行時被系統載入到進程的虛擬空間中,使用從調用進程的虛擬位址空間分配的記憶體,成為調用進程的一部分。DLL也只能被該進程的線程所訪問。DLL的控制碼可以被調用進程使用;調用進程的控制碼可以被DLL使用。DLL模組中包含各種導出函數,用於向外界提供服務。DLL可以有自己的資料段,但沒有自己的堆疊,使用與調用它的應用程式相同的堆疊模式;一個DLL在記憶體中只有一個實例;DLL實現了代碼封裝性;DLL的編制與具體的編程語言及編譯器無關,可以通過DLL來實現混合語言編程。DLL函數中的代碼所創建的任何物件(包括變數)都歸調用它的線程或進程所有。
  根據調用方式的不同,對動態庫的調用可分為靜態調用方式和動態調用方式。
  (1)靜態調用,也稱為隱式調用,由編譯系統完成對DLL的載入和應用程式結束時DLL卸載的編碼(Windows系統負責對DLL調用次數的計數),調用方式簡單,能夠滿足通常的要求。通常採用的調用方式是把產生動態連接庫時產生的.LIB檔加入到應用程式的工程中,想使用DLL中的函數時,只須在原始檔案中聲明一下。 LIB檔包含了每一個DLL導出函數的符號名和可選擇的標識號以及DLL檔案名,不含有實際的代碼。Lib檔包含的資訊進入到生成的應用程式中,被調用的DLL檔會在應用程式載入時同時載入在到記憶體中。
  (2)動態調用,即顯式調用方式,是由編程者用API函數載入和卸載DLL來達到調用DLL的目的,比較複雜,但能更加有效地使用記憶體,是編制大型應用程式時的重要方式。在Windows系統中,與動態庫調用有關的函數包括:
  ?LoadLibrary(或MFC 的AfxLoadLibrary),裝載動態庫。
  ?GetProcAddress,獲取要引入的函數,將符號名或標識號轉換為DLL內部位址。
  ?FreeLibrary(或MFC的AfxFreeLibrary),釋放動態連結程式庫。
  在windows中創建動態庫也非常方便和簡單。在Visual C  中,可以創建不用MFC而直接用C語言寫的DLL程式,也可以創建基於MFC類庫的DLL程式。每一個DLL必須有一個入口點,在VC  中,DllMain是一個缺省的入口函數。DllMain負責初始化(Initialization)和結束(Termination)工作。動態庫輸出函數也有兩種約定,分別是基於調用約定和名字修飾約定。DLL程式定義的函數分為內部函數和導出函數,動態庫導出的函數供其他程式模組調用。通常可以有下面幾種方法導出函數:
  ?採用模組定義檔的EXPORT部分指定要輸入的函數或者變數。
  ?使用MFC提供的修飾符號_declspec(dllexport)。
  ?以命令行方式,採用/EXPORT命令行輸出有關函數。
  在windows動態庫中,有時需要編寫模組定義檔(.DEF),它是用於描述DLL屬性的模組語句組成的文字檔案。
 2.2 Linux共用物件技術 
  在Linux作業系統中,採用了很多共用物件技術(Shared Object),雖然它和Windows裏的動態庫相對應,但它並不稱為動態庫。相應的共用物件文件以.so作為尾碼,為了方便,在本文中,對該概念不進行專門區分。Linux系統的/lib以及標準圖形介面的/usr/X11R6/lib等目錄裏面,就有許多以so結尾的共用物件。同樣,在Linux下,也有靜態函數庫這種調用方式,相應的尾碼以.a結束。Linux採用該共用物件技術以方便程式間共用,節省程式佔有空間,增加程式的可擴展性和靈活性。Linux還可以通過LD-PRELOAD變數讓開發人員可以使用自己的程式庫中的模組來替換系統模組。
  同Windows系統一樣,在Linux中創建和使用動態庫是比較容易的事情,在編譯函數庫根源程式時加上-shared選項即可,這樣所生成的執行程式就是動態連結程式庫。通常這樣的程式以so為尾碼,在Linux動態庫程式設計過程中,通常流程是編寫用戶的介面檔,通常是.h檔,編寫實際的函數檔,以.c或.cpp為尾碼,再編寫makefile檔。對於較小的動態庫程式可以不用如此,但這樣設計使程式更加合理。
  編譯生成動態連接庫後,進而可以在程式中進行調用。在Linux中,可以採用多種調用方式,同Windows的系統目錄(..\system32等)一樣,可以將動態庫檔拷貝到/lib目錄或者在/lib目錄裏面建立符號連接,以便所有用戶使用。下面介紹Linux調用動態庫經常使用的函數,但在使用動態庫時,根源程式必須包含dlfcn.h頭檔,該檔定義調用動態連結程式庫的函數的原型。
  (1)_打開動態連結程式庫:dlopen,函數原型void *dlopen (const char *filename, int flag); 
dlopen用於打開指定名字(filename)的動態連結程式庫,並返回操作控制碼。 
  (2)取函數執行位址:dlsym,函數原型為: void *dlsym(void *handle, char *symbol); 
dlsym根據動態連結程式庫操作控制碼(handle)與符號(symbol),返回符號對應的函數的執行代碼位址。
  (3)關閉動態連結程式庫:dlclose,函數原型為: int dlclose (void *handle); 
dlclose用於關閉指定控制碼的動態連結程式庫,只有當此動態連結程式庫的使用計數為0時,才會真正被系統卸載。
  (4)動態庫錯誤函數:dlerror,函數原型為: const char *dlerror(void); 當動態連結程式庫操作函數執行失敗時,dlerror可以返回出錯資訊,返回值為NULL時表示操作函數執行成功。 
  在取到函數執行位址後,就可以在動態庫的使用程式裏面根據動態庫提供的函數介面聲明調用動態庫裏面的函數。在編寫調用動態庫的程式的makefile檔時,需要加入編譯選項-rdynamic和-ldl。
  除了採用這種方式編寫和調用動態庫之外,Linux作業系統也提供了一種更為方便的動態庫調用方式,也方便了其他程式調用,這種方式與Windows系統的隱式鏈結類似。其動態庫命名方式為“lib*.so.*”。在這個命名方式中,第一個*表示動態連結程式庫的庫名,第二個*通常表示該動態庫的版本號,也可以沒有版本號。在這種調用方式中,需要維護動態連結程式庫的配置檔/etc/ld.so.conf來讓動態連結程式庫為系統所使用,通常將動態連結程式庫所在目錄名追加到動態連結程式庫配置檔中。如具有X window視窗系統發行版該檔中都具有/usr/X11R6/lib,它指向X window視窗系統的動態連結程式庫所在目錄。為了使動態連結程式庫能為系統所共用,還需運行動態連結程式庫的管理命令./sbin/ldconfig。在編譯所引用的動態庫時,可以在gcc採用 ?l或-L選項或直接引用所需的動態連結程式庫方式進行編譯。在Linux裏面,可以採用ldd命令來檢查程式依賴共用庫。
    3、兩種系統動態庫比較分析 
  Windows和Linux採用動態連結程式庫技術目的是基本一致的,但由於作業系統的不同,他們在許多方面還是不盡相同,下面從以下幾個方面進行闡述。
  (1)動態庫程式編寫,在Windows系統下的執行檔格式是PE格式,動態庫需要一個DllMain函數作為初始化的人口,通常在導出函數的聲明時需要有_declspec(dllexport)關鍵字。Linux下的gcc編譯的執行檔默認是ELF格式,不需要初始化入口,亦不需要到函數做特別聲明,編寫比較方便。
  (2)動態庫編譯,在windows系統下面,有方便的調試編譯環境,通常不用自己去編寫makefile檔,但在linux下面,需要自己動手去編寫makefile檔,因此,必須掌握一定的makefile編寫技巧,另外,通常Linux編譯規則相對嚴格。
  (3)動態庫調用方面,Windows和Linux對其下編制的動態庫都可以採用顯式調用或隱式調用,但具體的調用方式也不盡相同。
  (4)動態庫輸出函數查看,在Windows中,有許多工具和軟體可以進行查看DLL中所輸出的函數,例如命令行方式的dumpbin以及VC  工具中的DEPENDS程式。在Linux系統中通常採用nm來查看輸出函數,也可以使用ldd查看程式隱式鏈結的共用物件檔。
  (5)對作業系統的依賴,這兩種動態庫運行依賴於各自的作業系統,不能跨平臺使用。因此,對於實現相同功能的動態庫,必須為兩種不同的作業系統提供不同的動態庫版本。
    4、動態庫移植方法 
  如果要編制在兩個系統中都能使用的動態連結程式庫,通常會先選擇在Windows的VC  提供的調試環境中完成初始的開發,畢竟VC  提供的圖形化編輯和調試介面比vi和gcc方便許多。完成測試之後,再進行動態庫的程式移植。通常gcc默認的編譯規則比VC  默認的編譯規則嚴格,即使在VC  下面沒有任何警告錯誤的程式在gcc調試中也會出現許多警告錯誤,可以在gcc中採用-w選項關閉警告錯誤。
  下面給出程式移植需要遵循的規則以及經驗。
  (1)儘量不要改變原有動態庫頭檔的順序。通常在C/C  語言中,頭檔的順序有相當的關係。另外雖然C/C  語言區分大小寫,但在包含頭檔時,Linux必須與頭檔的大小寫相同,因為ext2檔系統對檔案名是大小寫敏感,否則不能正確編譯,而在Windows下面,頭檔大小寫可以正確編譯。
  (2)不同系統獨有的頭檔。在Windows系統中,通常會包括windows.h頭檔,如果調用底層的通信函數,則會包含winsock..h頭文件。因此在移植到Linux系統時,要注釋掉這些Windows系統獨有的頭檔以及一些windows系統的常量定義說明,增加Linux都底層通信的支援的頭檔等。
  (3)資料類型。VC  具有許多獨有的資料類型,如__int16,__int32,TRUE,SOCKET等,gcc編譯器不支援它們。通常做法是需要將windows.h和basetypes.h中對這些資料進行定義的語句複製到一個頭檔中,再在Linux中包含這個頭檔。例如將套接字的類型為SOCKET改為int。
  (4)關鍵字。VC  中具有許多標準C中所沒有採用的關鍵字,如BOOL,BYTE,DWORD,__asm等,通常在為了移植方便,儘量不使用它們,如果實在無法避免可以採用#ifdef 和#endif為LINUX和WINDOWS編寫兩個版本。
  (5)函數原型的修改。通常如果採用標準的C/C  語言編寫的動態庫,基本上不用再重新編寫函數,但對於系統調用函數,由於兩種系統的區別,需要改變函數的調用方式等,如在Linux編制的網路通信動態庫中,用close()函數代替windows作業系統下的closesocket()函數來關閉套接字。另外在Linux下沒有檔控制碼,要打開檔可用open和fopen函數,具體這兩個函數的用法可參考文獻[2]。
  (6)makefile的編寫。在windows下面通常由VC  編譯器來負責調試,但gcc需要自己動手編寫makefile檔,也可以參照VC  生成的makefile檔。對於動態庫移植,編譯動態庫時需要加入-shared選項。對於採用數學函數,如冪級數的程式,在調用動態庫是,需要加入-lm。
  (7)其他一些需要注意的地方
  ?程式設計結構分析,對於移植它人編寫的動態庫程式,程式結構分析是必不可少的步驟,通常在動態庫程式中,不會包含介面等操作,所以相對容易一些。
  ?在Linux中,對檔或目錄的許可權分為擁有者、群組、其他。所以在存取檔時,要注意對檔是讀還是寫操作,如果是對檔進行寫操作,要注意修改檔或目錄的許可權,否則無法對檔進行寫。
  ?指標的使用,定義一個指標只給它分配四個位元組的記憶體,如果要對指標所指向的變數賦值,必須用malloc函數為它分配記憶體或不把它定義為指標而定義為變數即可,這點在linux下面比windows編譯嚴格。同樣結構不能在函數中傳值,如果要在函數中進行結構傳值,必須把函數中的結構定義為結構指標。
  ?路徑識別字,在Linux下是“/”,在Windows下是“\”,注意windows和Linux的對動態庫搜索路徑的不同。
  ?編程和調試技巧方面。對不同的調試環境有不同的調試技巧,在這裏不多?述。
  5、結束語
  本文系統分析了windows和Linux動態庫實現和使用方式,從程式編寫、編譯、調用以及對作業系統依賴等方面綜合分析比較了這兩種調用方式的不同之處,根據實際程式移植經驗,給出了將VC  編制的Windows動態庫移植到Linux下的方法以及需要注意的問題,同時並給出了程式示例片斷,實際在程式移植過程中,由於系統的設計等方面,可能移植起來需要注意的方面遠比上面複雜,本文通過總結歸納進而為不同作業系統程式移植提供了有意的經驗和技巧。
No Comments »
Linux30 Nov 2004 12:00 am
編寫Linux實用程式的藝術
    Linux 和其他類 UNIX 系統總是附帶了大量的工具,它們執行從顯而易見的到不可思議的廣泛功能。類 UNIX 編程環境的成功很大程度上歸功於工具的高品質和選擇,以及這些工具之間相互銜接的簡易性。 
  作為開發人員,您可能會發現現有實用程式並不總是能夠解決問題。雖然能夠通過結合使用現有實用程式來容易地解決許多問題,然而解決其他問題卻至少需要一些實 際的編程工作。這些後面的任務通常是創建新實用程式的候選任務,結合現有實用程式來創建新實用程式可以通過做最少的工作來解決問題。本文考察優秀實用程式所具有的品質,以及設計這種實用程式所經歷的過程。 
  優秀的實用程式具有哪些品質? 
Kernighan & Pike 所著的 The UNIX Programming Environment 一書中包含了對此問題的精彩討論。優秀的實用程式是把自己的工作做得盡可能好的實用程式。它必須與其他實用程式配合融洽;必須能夠容易地與其他實用程式結合使用。無法與其他實用程式結合使用的程式不是實用程式,而是應用程式。 
  實用程式應該允許您根據手邊的材料廉價而容易地構建一次性的應用程式。許多人認為實用程式就像是工具箱中的工具。設計實用程式的目標不是為了讓單個工具來做所有事情,而是為了擁有一組工具,其中每個工具都盡可能好地做一件事情。 
  有些實用程式自身就是相當有用的,而其他實用程式則必須與一系列實用程式配合使用。前者的例子包括 sort 和 grep。另一方面,xargs 除了與其他實用程式(最常見的是 find)配合使用外,很少單獨使用。 
  使用什麼語言來編寫實用程式? 
  大多數 UNIX 系統實用程式都是用 C 語言來編寫的。本文中的例子使用 Perl 和 sh。應該使用恰當的工具來做恰當的事情。如果您對某個實用程式使用得足夠頻繁,那麼用編譯型語言來編寫它的成本也許能通過性能提升來獲得回報。另一方面,對於程式的工作負荷很輕這種相當普遍的情況,使用腳本語言也許會提供更快的開發速度。 
  如果無法肯定,您應該使用自己最瞭解的語言。至少當您在對某個實用程式進行原型化,或在弄清它是如何有用時,程式師效率將優先於性能調整。大多數 UNIX 系統實用程式都是用 C 編寫的,這只是因為這些實用程式使用得足夠頻繁,以致考慮效率比考慮開發成本更加重要。Perl 和 sh(或 ksh)可能是用於快速原型化的很好語言。對於與其他程式配合實用的實用程式,使用 shell 來編寫它們或許要比使用更傳統的編程語言來編寫它們要容易一些。另一方面,當您希望與原始的位元組交互時,C 或許就是最好的選擇。 
    設計實用程式 
  一個不錯的經驗法則就是當您第二次必須解決某個問題時,首先考慮實用程式的設計。不要對第一次編寫的一次性作品感到遺憾;您可以將它看作是一個原型。第二次,請把您所需的功能與第一次所需的功能作比較。在第三次前後,您應該開始考慮花時間來編寫一個通用實用程式。即使純粹的重複性任務也可能會給實用程式的開發帶來好處;例如,由於人們對嘗試以通用的方式重命名檔感到失望,於是開發了許多通用檔重命名程式。 
  下面是一些實用程式設計目標;每個目標將在下面單獨的小節中介紹。 
  做好一件事情;不要糟糕地做多件事情。關於做好一件事情的最佳例子或許是 sort。除了 sort 外,沒有其他 哪個實用程式具有排序功能。基本的思想很簡單:如果一次僅解決一個問題,您就能花時間把它解決好。 
  設想一下,如果大多數程式都具有排序功能,但是有些僅支持按詞法排序,而其他一些僅支援按數字排序,另外一些甚至支持關鍵字選擇而不是對整行排序,那將是一件多麼令人沮喪的事情。起碼,這也是惱人的。 
  當您發現某個問題需要解決時,應嘗試將問題分解為多個部分,不要重複那些其他實用程式中已經存在的部分。您對允許配合現有工具使用的工具關注得越多,您的實用程式就越有可能保持有用。 
  也許您需要編寫多個程式。完成專門任務的最佳途徑通常是編寫一兩個實用程式,再用一些線索將它們聯繫起來,而不是編寫單個程式來解決整件事情。使用 20 行的 shell 腳本來將新的實用程式與現有工具結合起來是很理想的。如果嘗試一次解決整個問題,隨之而來的第一個變更就可能要求您全盤重新考慮。 
  我偶爾需要從資料庫生成兩列或三列的輸出。編寫一個程式在單個列中生成輸出,然後結合使用一個對輸出進行分列的程式,這樣通常會更有效率。組合這兩個實用程式的 shell 腳本本身是臨時性的,單獨的實用程式比這個腳本的使用壽命更長。 
  有些實用程式服務於非常專一的需要。針對一個包含大量內容的目錄,如果 ls 的輸出非常快地滾出螢幕,這可能是因為其中有一個檔具有非常長的檔案名,從而迫使 ls 僅對輸出使用單個列。使用 more 來對輸出分頁會花一些時間。為什麼不像下面這樣就按長度對行排序,然後通過 tail 來管道輸出結果呢? 
  清單 1. 世間能找到的最小實用程式 sl 
#/usr/bin/perl -w
print sort { length $a <=> length $b } <>;
  清單 1 中的腳本確切地就做一件事情。它不接受任何選項,因為它不需要選項;它僅關心行的長度。歸功於 Perl 便利的 <> 表達方式,這個小實用程式既適用於標準輸入,也適用於命令行指定的檔。 
    成為一個篩檢程式 
  幾乎所有實用程式都最適合想像為篩檢程式,儘管有一些非常有用的實用程式不符合這個模型。(例如,某個程式在執行計數時可能非常有用,儘管它作為篩檢程式工作得並不好。僅接受命令行參數作為輸入並潛在地產生複雜輸出的程式可能非常有用。)然而,大多數實用程式都應該作為篩檢程式來工作。根據慣例,篩檢程式對文本的行起作用。大多數篩檢程式都應該支持多個輸入檔。 
  記住實用程式需要在命令行和腳本中運行。有時,理想的行為會稍有不同。例如,大多數版本的 ls 都會在向終端寫出時自動將輸入排序到多個列中。grep 的默認行為是在指定多個檔的情況下列印從其中找到匹配項的那個檔案名稱。這樣的差別應該與用戶希望的實用程式工作方式有關,而不是與其他事項有關。例如,舊版本的 GNU bc 在啟動時顯示強迫性的版權標記。請不要那樣做。讓您的實用程式僅做它應該做的事情。 
  實用程式喜歡生活在管道中。管道允許實用程式專注於自己的工作,而不是去關注旁枝末節。為了生活在管道中,實用程式需要從標準輸入讀取資料,然後向標準輸出寫出資料。如果您希望處理記錄,那麼您最好能夠使每一行成為一個“記錄”。諸如 sort 和 join 之類的現有程式已經在那樣考慮了。它們將會因為您這樣做而感謝您。 
  我偶爾使用這樣一個實用程式,它針對一個檔樹反復調用其他程式。這充分利用了標準的 UNIX 實用程式篩檢程式模型,但是該模型僅適用於讀取輸入然後寫出輸出的實用程式;不能將它用於就地操作或接受輸入輸出檔案名的實用程式。 
  可以使用標準輸入來運行的大多數程式也完全可以針對單個檔或一組檔運行。注意,可以證明這樣違背了反對重複工作的規則;顯而易見,這可以通過將 cat 的輸出饋送給該系列中的下一個程式來解決。然而這在實踐中似乎是合理的。 
  有些程式可能合法地讀取一種格式的記錄,但是卻產生完全不同的輸出。這樣的一個例子就是將輸入材料劃分為列的實用程式。這樣一個實用程式可能將輸入中的行視為記錄,但是卻在輸出中的每行上產生多個記錄。 
  並非每個實用程式都完全符合這個模型。例如,xargs 不是接受記錄而是接受檔名作為輸入,並且所有的實際處理都是由其他程式完成的。 
  通用化 
  嘗試將任務看作與您實際執行的任務類似;如果您能找出這些任務的通用描述,那麼最好嘗試編寫一個符合該描述的實用程式。例如,如果您發現自己一天在根據詞法對文本排序,而另一天在根據數位對文本排序,那麼考慮編寫一個通用排序實用程式也許是有意義的。 
  對功能進行通用化有時會導致您發現:某個看起來似乎像單個實用程式的程式,實際上卻是配合起來使用的兩個實用程式。這很好。編寫兩個設計良好的實用程式可能要比編寫一個醜陋的或複雜的實用程式更容易。 
  做好一件事情並不意味著 僅僅 做一件事情。它意味著處理一致但有用的問題空間。許多人都使用 grep。然而,它的大量效用在於執行相關任務的能力。grep 的各種選項完成許多小實用程式的工作,如果這些工作都由單獨的小實用程式來完成,最終會造成大量共用的、重複的代碼。 
  這條規則,以及做好一件事情的規則,都是一個根本原理的必然結果:無論何時都要盡可能避免代碼重複。如果您編寫半打程式,其中每個都對行排序,您最終可能必須六次修復六個類似的 bug,而不是去使用一個得到更好維護的 sort 程式。 
  這是編寫實用程式的一部分,即把大多數工作添加到完成該實用程式的過程中。您也許沒有時間在最初就完全通用化一個實用程式,但是當您一直使用該實用程式就會獲得相應的回報。 
  有時,向某個程式添加相關功能是很有用的,即使這個功能並不是用來完成完全相同的任務。例如,當運行在終端設備上時,對原始二進位資料進行完美列印的程式可能更為有用,因為它使終端進入原始模式。這樣使得測試涉及鍵盤映射、新鍵盤等的問題變得容易多了。不確定為什麼當您按 delete 鍵時卻得到代字型大小(~)嗎? 這是弄清實際發送了什麼內容的容易途徑。這並不是完全相同的任務,但它足夠類似,因而可能成為一個附加特性。 
  清單 2 中的 errno 實用程式就是通用化的很好例子,因為它同時支援數位和符號名稱。 
  健壯 
  實用程式的穩定性是很重要的。容易崩潰或無法處理真實資料的實用程式不是有用的實用程式。實用程式應該能夠處理任意長度的行、巨型檔,等等。實用程式無法處理超過其記憶體容量的資料集或許是可以容忍的,但是有些實用程式不是這樣;例如,sort 通過使用暫存檔案,一般能夠對比其記憶體容量大得多的資料集排序。 
  應該儘量確保弄清楚您的實用程式可能要操作哪些資料。不要簡單地忽略無法處理的資料的可能性。應該檢查這種情況並診斷您的實用程式。錯誤消息越明確,您對用戶就越有幫助。儘量給用戶提供足夠的資訊,以便讓他們知道發生了什麼情況以及如何解決。當處理資料檔案時,儘量準確識別出不良的資料。當嘗試解析數字時,不要簡單地放棄;應該告訴用戶您得到了什麼資料,而且如果可能的話,還應該告訴用戶該資料位於輸入流中的哪一行上。 
  作為一個很好的例子,請考慮 dc 的兩種實現之間的區別。如果您運行 dc /home ,其中一種實現會顯示“Cannot use directory as input!”而另一種實現只是無聲地返回,沒有錯誤消息,也沒有不尋常的退出代碼。當您錯誤地鍵入一個 cd 命令時,您更希望當前路徑中有哪一種實現呢?類似地,如果您提供某個目錄中的資料流程(或許是執行 dc < /home),前者會給出詳細的錯誤消息。另一方面,當它在獲得無效資料的早期就選擇放棄可能是理想的。 
  安全漏洞經常植根於在意料之外的資料面前表現得不夠健壯的程式中。務必記住,優秀的實用程式能夠設法在 shell 腳本中作為根(root)用戶身份運行。諸如 find 這樣的程式中的緩衝區溢出可能會給大量的系統帶來風險。 
  程式對意料之外的資料處理得越好,它就更可能適應變化的環境。通常,設法使程式更健壯會導致您更好地理解該程式的作用,從而更好地使之通用化。 
  新穎 
  要編寫的最糟糕的實用程式種類之一就是您已經有了的實用程式。我編寫過一個名為 count 的美妙的實用程式。它允許我執行幾乎任何計數任務。它是一個出色的實用程式,但是已經有一個名為 jot 的標準 BSD 實用程式做同樣的事情。同樣地,我的一個用於將資料轉換為列的靈活的程式重複了一個現有實用程式 rs 的功能,這個實用程式同樣可以在 BSD 系統上找到,只不過 rs 更靈活,設計得更好。請參閱下面的 參考資料 以瞭解關於 jot 和 rs 的更多資訊。 
  如果您即將開始編寫一個實用程式,請花一點時間流覽一下各種系統,以確定那樣的實用程式是否已經存在。不要害怕在 BSD 上借用 Linux 實用程式,或在 Linux 上借用 BSD 實用程式;實用程式碼的樂趣之一在於,幾乎所有實用程式都具有很好的可攜性。 
  不要忘了考察一下組合現有應用程式來形成一個實用程式的可能性。從理論上講,組合現有程式來形成的實用程式運行得不足夠快是可能的,但是編寫一個新的實用程式很少會比等待一個稍慢的管道更快。 
  一個例子實用程式 
  從某種意義上,這個程式是一個可執行檔,因為對於作為篩檢程式來說,它決不會有任何用處。然而,它作為一個命令行實用程式卻工作得非常好。 
  這個程式僅做一件事情。它以近乎完美的輸出格式輸出 /usr/include/sys/errno.h 中的 errno 行。例如: 
  $ errno 22
  EINVAL [22]: Invalid argument 
  清單 2. errno 查找器 
    #!/bin/sh
    usage() {
        echo >&2 ‘usage: errno [numbers or error names]\n’
        exit 1
    }
    for i
    do
        case ‘$i’ in
        [0-9]*)
            awk ‘/^#define/ && $3 == ‘’$i'’ {
                for (i = 5; i < NF;   i) {
                    foo = foo ‘ ‘ $i;
                }
                printf(’%-22s%s\n’, $2 ‘ [’ $3 ‘]:’, foo);
                foo = ‘’
            }’ < /usr/include/sys/errno.h
            ;;
        E*)
            awk ‘/^#define/ && $2 == ‘'’$i”’ {
                for (i = 5; i < NF;   i) {
                    foo = foo ‘ ‘ $i;
                }
                printf(’%-22s%s\n’, $2 ‘ [’ $3 ‘]:’, foo);
                foo = ‘’
            }’ < /usr/include/sys/errno.h
            ;;
        *)
 echo >&2 ‘errno: can’t figure out whether ‘$i’ is a name or a number.’
            usage
            ;;
        esac
    done
  這個程式通用化了嗎?是的,非常理想。它同時支援數位和符號名稱。另一方面,它不知道關於可能具有相同格式的其他檔的資訊,比如 /usr/include/sys/signal.h。可以容易地擴展它來做到這點,但是對於這樣一個便利的實用能夠程式,簡單地創建一個名為“signal”的拷貝來讀取 signal.h,同時使用“SIG*”作為模式來匹配名稱,這樣會更容易。 
  雖然這僅比對系統頭檔使用 grep 方便一小點,但是它更不容易出錯。它不會因為考慮不周的參數而產生無用的結果。另一方面,如果沒有從頭檔中找到給定的名稱或數位,它不會產生診斷資訊。它也不會費心去糾正某些輸入錯誤。而且,由於命令行實用程式從來沒有打算在自動化的環境中使用,因此它的上述特性無可非議。 
  另一個例子可能是取消對輸入排序的程式(請參閱 參考資料 以獲得指向此實用程式的鏈結)。這相當簡單;也就是讀入輸入檔,以某種方式存儲它們,然後生成一個隨機順序來輸出那些行。這是一個幾乎具有無限應用前景的實用程式。編寫這個實用程式也比編寫排序程式容易得多;例如,您不需要指定您沒有對哪些鍵排序,或者是您希望按字母順序、詞法順序還是按數位順序隨機排序。棘手的部分在於讀入可能非常長的行。事實上,上面提供的版本在搞欺騙;它假設所讀入的行中沒有空位元組。糾正這個問題要困難多了,我在編寫它時懶得去理會它。 
  結束語 
  如果您發現自己在重複執行某個任務,可以考慮編寫一個程式來完成這個任務。如果事實證明該程式更通用化一點是合理的,那就通用化它,這樣您就編寫了一個實用程式。 
  不要在您第一次需要某個實用程式的時候設計它。要等到您具有一些經驗之後才著手設計。請隨意地編寫一兩個原型;優秀的實用程式比糟糕的實用程式更能證明所花的時間和研究工作的價值。如果原先設想的出色實用程式最終卻在您編寫它之後成為無用之物,不要感到遺憾。如果您發現自己對新程式的缺點感到沮喪,您只需再執行另外一個原型化階段。如果結果證明它是無用的,不奇怪,有時會發生這樣的事情。 
  您要尋求的是這樣一個程式,它查找您的最初使用模式之外的通用應用。我編寫 unsort 是因為,我希望找到一種從舊的 X11“rgb.txt”檔中獲得隨機顏色序列的容易途徑。從那以後,我將它用於令人難以置信的大量任務中,這些任務都不是為了生成用於調試和基準排序常式的測試資料。 
  優秀的實用程式能夠為您在所有不很理想的作品上所花的時間帶來回報。要做的下一件事情是使它對其他人可用,以便他們能夠試驗它。也要使您失敗的嘗試對其他人可用,也許其他人對某個實用程式具有您所不需要的用途。更重要的是,您的失敗的實用程式也許是其他某個人的原型,從而給每個人帶來一個美妙的實用程式。
No Comments »
Linux30 Nov 2004 12:00 am
如何編寫Linux設備驅動程式
    Linux是Unix作業系統的一種變種,在Linux下編寫驅動程式的原理和思想完全類似於其他的Unix系統,但它dos或window環境下的驅動程式有很大的區別。在Linux環境下設計驅動程式,思想簡潔,操作方便,功能也很強大,但是支援函數少,只能依賴kernel中的函數,有些常用的操作要自己來編寫,而且調試也不方便。本人這幾周來為實驗室自行研製的一塊多媒體卡編制了驅動程式,獲得了一些經驗,願與Linux fans共用,有不當之處,請予指正。
  以下的一些文字主要來源於khg,johnsonm的Write linux device driver,Brennan’s Guide to Inline Assembly,The Linux A-Z,還有清華BBS上的有關device driver的一些資料. 這些資料有的已經過時,有的還有一些錯誤,我依據自己的試驗結果進行了修正. 
  一、Linux device driver 的概念 
  系統調用是作業系統內核和應用程式之間的介面,設備驅動程式是作業系統內核和機器硬體之間的介面.設備驅動程式為應用程式遮罩了硬體的細節,這樣在應用程式看來,硬體設備只是一個設備檔, 應用程式可以象操作普通檔一樣對硬體設備進行操作.設備驅動程式是內核的一部分,它完成以下的功能: 
  1.對設備初始化和釋放. 
  2.把資料從內核傳送到硬體和從硬體讀取資料. 
  3.讀取應用程式傳送給設備檔的資料和回送應用程式請求的資料. 
  4.檢測和處理設備出現的錯誤. 
  在Linux作業系統下有兩類主要的設備檔類型,一種是字元設備,另一種是塊設備.字元設備和塊設備的主要區別是:在對字元設備發出讀/寫請求時,實際的硬體I/O一般就緊接著發生了,塊設備則不然,它利用一塊系統記憶體作緩衝區,當用戶進程對設備請求能滿足用戶的要求,就返回請求的資料,如果不能,就調用請求函數來進行實際的I/O操作.塊設備是主要針對磁片等慢速設備設計的,以免耗費過多的CPU時間來等待. 
  已經提到,用戶進程是通過設備檔來與實際的硬體打交道.每個設備檔都都有其檔屬性(c/b),表示是字元設備還?強檣璞?另外每個檔都有兩個設備號,第一個是主設備號,標識驅動程式,第二個是從設備號,標識使用同一個設備驅動程式的不同的硬體設備,比如有兩個軟碟,就可以用從設備號來區分他們.設備檔的的主設備號必須與設備驅動程式在登記時申請的主設備號一致,否則用戶進程將無法訪問到驅動程式. 
  最後必須提到的是,在用戶進程調用驅動程式時,系統進入核心態,這時不再是搶先式調度.也就是說,系統必須在你的驅動程式的子函數返回後才能進行其他的工作.如果你的驅動程式陷入閉環,不幸的是你只有重新啟動機器了,然後就是漫長的fsck.//hehe 
  讀/寫時,它首先察看緩衝區的內容,如果緩衝區的資料 
  如何編寫Linux作業系統下的設備驅動程式 
  二、實例剖析 
  我們來寫一個最簡單的字元設備驅動程式。雖然它什麼也不做,但是通過它可以瞭解Linux的設備驅動程式的工作原理.把下面的C代碼輸入機器,你就會獲得一個真正的設備驅動程式.不過我的kernel是2.0.34,在低版本的kernel上可能會出現問題,我還沒測試過.//xixi 
  #define __NO_VERSION__ 
  #include  
  #include  
  char kernel_version [] = UTS_RELEASE; 
  這一段定義了一些版本資訊,雖然用處不是很大,但也必不可少.Johnsonm說所有的驅動程式的開頭都要包含,但我看倒是未必. 
  由於用戶進程是通過設備檔同硬體打交道,對設備檔的操作方式不外乎就是一些系統調用,如 open,read,write,close…., 注意,不是fopen, fread,但是如何把系統調用和驅動程式關聯起來呢?這需要瞭解一個非常關鍵的資料結構: 
struct file_operations { 
int (*seek) (struct inode * ,struct file *, off_t ,int); 
int (*read) (struct inode * ,struct file *, char ,int); 
int (*write) (struct inode * ,struct file *, off_t ,int); 
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int); 
int (*select) (struct inode * ,struct file *, int ,select_table *); 
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long); 
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *); 
int (*open) (struct inode * ,struct file *); 
int (*release) (struct inode * ,struct file *); 
int (*fsync) (struct inode * ,struct file *); 
int (*fasync) (struct inode * ,struct file *,int); 
int (*check_media_change) (struct inode * ,struct file *); 
int (*revalidate) (dev_t dev); 
} 
  這個結構的每一個成員的名字都對應著一個系統調用.用戶進程利用系統調用在對設備檔進行諸如read/write操作時,系統調用通過設備檔的主設備號找到相應的設備驅動程式,然後讀取這個資料結構相應的函數指標,接著把控制權交給該函數.這是linux的設備驅動程式工作的基本原理.既然是這樣,則編寫設備驅動程式的主要工作就是編寫子函數,並填充file_operations的各個域. 
  相當簡單,不是嗎? 
  下面就開始寫副程式. 
#include  
#include  
#include  
#include  
#include  
unsigned int test_major = 0; 
static int read_test(struct inode *node,struct file *file, 
char *buf,int count) 
{ 
int left; 
if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT ) 
return -EFAULT; 
for(left = count ; left > 0 ; left–) 
{ 
__put_user(1,buf,1); 
buf  ; 
} 
return count; 
} 
  這個函數是為read調用準備的.當調用read時,read_test()被調用,它把用戶的緩衝區全部寫1.buf 是read調用的一個參數.它是用戶進程空間的一個位址.但是在read_test被調用時,系統進入核心態.所以不能使用buf這個位址,必須用__put_user(),這是kernel提供的一個函數,用於向用戶傳送資料.另外還有很多類似功能的函數.請參考.在向用戶空間拷貝資料之前,必須驗證buf是否可用。
 
 這就用到函數verify_area. 
static int write_tibet(struct inode *inode,struct file *file, 
const char *buf,int count) 
{ 
return count; 
} 
static int open_tibet(struct inode *inode,struct file *file ) 
{ 
MOD_INC_USE_COUNT; 
return 0; 
} 
static 
        
系統時間:2024-05-21 22:50:33
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!