2008年12月30日 星期二

Linux Kernel核心中文手冊

這是一個作者不詳的文件,如果有人知道的話,歡迎通知我,讓我補上,另外這是一份介紹性的文件
==================================================
  Chapter 1

 

Hardware Basic(硬件基礎知識)

 

一個操作系統必須和作為它的基礎的硬件系統緊密配合。操作系統需要使用一些只有硬件才能提供的功能。為了完整的瞭解Linux,你需要瞭解底層硬件的基礎知識。本章對於現代PC的硬件進行了。

1975年1月「Popular Electronics」雜誌封面上印出了Altair 8080的圖片,一場革命開始了。

Altair 8080,跟隨早期的「Star Trek epsode」命名,只需要$397,就可由個人電子愛好者自己組裝。它擁有Intel 8080處理器和256字節內存,但是沒有屏幕和鍵盤。以今天的標準來衡量,它太簡陋了。它的發明者,Ed Roberts,製造了名詞「personal computer「來命名他的發明,但現在,PC這個名詞已經用來命名幾乎所有你可以不依靠幫助就可以自己運行起來的計算機。用這個定義,甚至一些十分強 大的Alpha AXP系統也是PC。

愛好者們看到了Altair的潛力,開始為它寫軟件,製造硬件。對於這些早期的先驅來講,它代表著自由:從被神職人員控制和運行的大型批處理的主機 系統中逃脫出來的自由。你可以在自己家裡甚至廚桌上擁有計算機,這使學院的退學生為此著迷並通宵達旦。與此同時出現大量硬件,在一定程度上各自不同,而軟 件專家則樂於為這些新機器撰寫軟件。有諷刺意味的是,IBM在1981年發佈了IBM PC並於1982年早期供貨,從此定義了現代PC的模型。它擁有Intel 8088處理器,64K內存(可以擴充到256K),兩個軟驅和一個80x25的彩色圖卡(CGA),用今天的標準衡量,它功能不算很強,但是它銷售的不 錯。1983年,緊接著推出的IBM PC-XT,則擁有一個豪華的10M硬盤。不久大批公司如Compaq開始製造IBM PC的複製品,PC的結構成為了事實的標準。這個事實的標準使大批硬件公司可以在這個不斷增長的市場上一起競爭,反過來,可以遏制價格,讓用戶滿意。現代 PC承襲了早期PC的許多系統體系特徵。甚至基於最強大的Intel Pentium Pro的系統也可以運行Intel 8086的尋址模式。當Linus Torvalds開始開發後來的Linux時,他選擇了當時最常見和價格最合理的硬件平台:一台Intel 80386 PC。

從PC的外面看,最明顯的部件就是機箱、鍵盤、鼠標和顯示器。在機箱的前面有一些按鈕,一個小屏幕顯示一些數字,還有一個軟驅。現在的大多數系統還有一個CD-ROM期、驅動器。如果你需要保護你的數據,那麼還會有一個備份用的磁帶機。這些設備一律被看作外設。

雖然CPU管理整個系統,但它並不是唯一的智能設備。所有的外設控制器,例如IDE控制器,也都擁有一定程度的智能。在PC內部(圖1.1),你可 以看到一個主板,包括CPU或微處理器、內存和一些ISA或PCI外設控制卡的槽位。其中一些控制器,如IDE磁盤控制器可能內置在系統主板上。

 


CPU

 

CPU,或者說微處理器,是所有計算機系統的心臟。微處理器進行數學運算,邏輯操作並從內存中讀取指令並執行指令,進而控制數據流向。計算機發展的 早期,微處理器的各種功能模塊是由相互分離(並且尺寸上十分巨大)的單元構成。這也是名詞「中央處理單元」的起源。現代的微處理器將這些功能模塊集中在一 塊非常小的矽晶片製造的集成電路上。在本書,名詞CPU、微處理器和處理器交替使用。 微處理器處理二進制數據:這些數據由1和0組成。這些1和0對應電氣開關的開或關。就好像42代表4個10和2個單元,二進制數字由一系列代表2的冪數的 數字組成。這裡,冪數意味著一個數字用自身相乘的次數。10 的一次冪是10,10的2次冪是10x10,10的3次冪是10x10x10,依此類推。二進制0001是十進制1是十進制2,二進制0011是十進制 3,二進制0100是十進制4,等等。所以,十進制42是二進制101010或者(2+8+32或21+23+25)。在計算機程序除了使用二進製表示數 字之外,另一種基數,16進制,也經常用到。在這種進制中,每一位數字表示16的冪數。因為十進制數字只是從0到9,在十六進制中10到15分別用字母 A,B,C,D,E,F表示。例如,十六進制的E是十進制的14,而十六進制的2A是十進制的42(2個16+10)。用C語言的表示法(本書一直使 用),十六進制數字使用前綴「0x」:十六進制的2A寫做0x2A。

 

微處理器可以執行算術運算如加、乘和除,也可以執行邏輯操作例如「X是否大於Y」。

處理器的執行由外部時鍾控制。這個時鍾,即系統時鍾,對處理器產生穩定的時鍾脈衝,在每一個時鍾脈衝裡,處理器執行一些工作。例如,處理器可以在每 一個時鍾脈衝裡執行一條指令。處理器的速度用系統時鍾的頻率來描述。一個100Mhz的處理器每秒鍾接受到100,000,000次時鍾脈衝。用時鍾頻率 來描述CPU的能力是一種誤解,因為不同的處理器在每一次時鍾脈衝中執行的工作量不同。雖然如此,如果所有的條件同等,越快的時鍾頻率表示處理器的能力越 強。處理器執行的指令非常簡單,例如:「把內存位置X的內容讀到寄存器Y中「。寄存器是微處理器的內部存儲空間,用來存儲數據並進行操作。執行的操作可能 使處理器停止當前操作而轉去執行內存中其他地方的指令。正是這些微小的指令集合在一起,賦予現代的微處理器幾乎無限的能力,因為它每秒可以執行數百萬甚至 數十億的指令。

 

執行指令時必須從內存中提取指令,指令自身也可能引用內存中的數據,這些數據也必須提取到內存中並在需要的時候保存到內存中去。

 

一個微處理器內部寄存器的大小、數量和類型完全決定於它的類型。一個Intel 80486處理器和一個Alpha AXP處理器的寄存器組完全不同。另外,Intel是32位寬而Alpha AXP是64位寬。但是,一般來講,所有特定的處理器都會有一些通用目的的寄存器和少量專用的寄存器。大多數處理器擁有以下特殊用途的專用的寄存器:

 

Program Counter(PC)程序計數器

這個寄存器記錄了下一條要執行的指令的地址。PC的內容在每次取指令的時候自動增加。

Stack Pointer(SP)堆棧指針

處理器必須能夠存取用於臨時存儲數據的大容量的外部讀寫隨機存取內存(RAM)。堆棧是一種用於在外部內存中存放和恢復臨時數據的方法。通常,處理 器提供了特殊的指令用於將數據壓在堆棧中,並在以後需要是取出來。堆棧使用LIFO(後進先出)的方式。換句話說,如果你壓入兩個值x和y到堆棧中,然後 從堆棧中彈出一個值,那麼你會得到y的值。

一些處理器的堆棧向內存頂部增長,而另一些向內存的底部增長。還有一些處理器兩種方式都可以支持,例如:ARM。

 

Processor Status(PS)

指令可能產生結果。例如:「X寄存器的內容是否大於Y寄存器的內容?「可能產生真或假的結果。PS寄存器保留這些結果以及處理器當前狀態的其他信息。多數處理器至少有兩種模式:kernel(核心態)和user(用戶態),PS寄存器會紀錄能夠確定當前模式的那些信息。

 


Memory(內存)

 

所有系統都具有分級的內存結構,由位於不同級別的速度和容量不同的內存組成。

最快的內存是高速緩存存儲器,就像它的名字暗示的一樣-用於臨時存放或緩存主內存的內容。這種內存非常快但是比較昂貴,因此多數處理器芯片上內置有 少量的高速緩衝存儲器,而大多數高速緩存存儲器放在系統主板上。一些處理器用一塊緩存內存同時緩存指令和數據,而另一些處理器有兩塊緩存內存-一個用於指 令,另一個用於數據。Alpha AXP處理器有兩個內置的內存高速緩存存儲器:一個用於數據(D-Cache),另一個用於指令(I-Cache)。它的外部高速緩衝存儲器(或B- Cache)將兩者混在一起。

最後一種內存是主內存。相對於外部高速緩存存儲器而言速度非常慢,對於CPU內置的高速緩存存儲器,主內存簡直是在爬。

高速緩存存儲器和主內存必須保持同步(一致)。換句話說,如果主內存中的一個字保存在高速緩存存儲器的一個或多個位置,那麼系統必須保證高速緩存存 儲器和主內存的內容一樣。使高速緩衝存儲器同步的工作一部分是由硬件完成,另一部分則是由操作系統完成的。對於其它一些系統的主要任務,硬件和軟件也必須 緊密配合。

 


Buses(總線)

系統板的各個組成部分由被稱為總線的連接系統互連在一起。系統總線分為三種邏輯功能:地址總線、數據總線和控制總線。地址總線指定了數據傳輸的內存 位置(地址),數據總線保存了傳輸的數據。數據總線是雙向的,它允許CPU讀取,也允許CPU寫。控制總線包含了各種信號線用於在系統中發送時鍾和控制信 號。有許多種不同的總線類型,ISA和PCI總線是系統用於連接外設的常用方式。

 


Controllers and Peripherals (控制器和外設)

 

外設指實在的設備,如由系統板或系統板插卡上的控制芯片所控制的圖形卡或磁盤。IDE控制芯片控制IDE磁盤,而SCSI控制芯片控制SCSI磁 盤。這些控制器通過不同的總線連接到CPU並相互連接。現在製造的大多數系統都是用PCI或ISA總線將系統的主要部件連接在一起。控制器本身也是象 CPU一樣的處理器,它們可以看作CPU的智能助手,CPU擁有系統的最高控制權。

 

所有的控制器都是不同的,但是通常它們都有用於控制它們的寄存器。CPU上運行的軟件必須能夠讀寫這些控制寄存器。一個寄存器可能包含描述錯誤的狀 態碼,另一個寄存器可能用於控制用途,改變控制器的模式。一個總線上的每一個控制器都可以分別被CPU尋址,這樣軟件設備驅動程序就可以讀寫它的寄存器進 而控制它。IDE電纜是一個好例子,它給了你分別存取總線上每一個驅動器的能力。另一個好例子是PCI總線,允許每一個設備(如圖形卡)被獨立存取。

 


Address Spaces(尋址空間)

 

連接CPU和主內存的系統總線以及連接CPU和系統硬件外設的總線是分離的。硬件外設所擁有的內存空間稱為I/O空間。I/O空間本身可以再進一步 劃分,但是我們現在先不討論。CPU可以訪問系統內存空間和I/O空間,而控制器只能通過CPU間接訪問系統內存。從設備的角度來看,比如軟驅控制器,它 只能看到它的控制寄存器所在的地址空間(ISA),而非系統內存。一個CPU用不同的指令去訪問內存和I/O空間。例如,可能有一條指令是「從I/O地址 0x3f0讀取一個字節到X寄存器「。這也是CPU通過讀寫系統硬件外設處於I/O地址空間的寄存器從而控制外設的方法。在地址空間中,普通外設(如 IDE控制器,串行端口,軟驅控制器等等)的寄存器在PC外設的多年發展中已經成了定例。I/O空間的地址0x3f0正是串行口(COM1)的控制寄存器 的地址。

 

有時控制器需要直接從系統內存讀取大量內存,或直接寫大量數據到系統內存中去。比如將用戶數據寫到硬盤上去。在這種情況下,使用直接內存存取(DMA)控制器,允許硬件設備直接存取系統內存,當然,這種存取必須在CPU的嚴格控制和監管下進行。

 


Timer(時鍾)

所有操作系統需要知道時間,現代PC包括一個特殊的外設,叫做實時時鍾(RTC)。它提供了兩樣東西:可靠的日期和精確的時間間隔。RTC有自己的 電池,所以即使PC沒有加電,它仍在運行。這也是為什麼PC總是「知道」正確的日期和時間。時間間隔計時允許操作系統精確地調度基本工作。

 

 

Chapter 2

 

Software Basic(軟件基礎)

程序是用於執行特定任務的計算機指令組合。程序可以用彙編語言,一種非常低級的計算機語言來編寫,也可以使用和機器無關的高級語言,比如C語言編 寫。操作系統是一個特殊的程序,允許用戶通過它運行應用程序,比如電子錶和文字處理等等。本章介紹了基本的編程原理,並簡介操作系統的目的和功能。

 

2.1 Computer Languages(計算機語言)

 

2.1.1.彙編語言

 

CPU從內存中讀取和執行的指令對於人類來講無法理解。它們是機器代碼,精確的告訴計算機要做什麼。比如十六進制數0x89E5,是Intel 80486的指令,將寄存器ESPEBP中。早期計算機中最初的軟件工具之一是彙編程序,它讀入人類可以閱讀的源文件,將其裝配成機器代碼。彙編語言明確 地處理對寄存器和對數據的操作,而這種操作對於特定的微處理器而言是特殊的。Intel X86微處理器的彙編語言和Alpha AXP微處理器的彙編語言完全不同。以下Alpha AXP彙編代碼演示了程序可以執行的操作類型:

 

Ldr r16, (r15) ; 第一行

Ldr r17, 4(r15) ; 第二行

Beq r16,r17,100; 第三行

Str r17, (r15); 第四行

100: ; 第五行

 

第一條語句(第一行)將寄存器15指定的地址中的內容加載到寄存器16中。第二條指令將緊接著的內存中的內容加載到寄存器17中。第三行比較寄存器 16和寄存器17,如果相等,分支到標號100,否則,繼續執行第四行,將寄存器17的內容存到內存中。如果內存中的數據相同,就不必存儲數據。編寫彙編 級的程序需要技巧而且十分冗長,容易出錯。Linux系統的核心很少的一部分是用彙編語言編寫,而這些部分之所以使用彙編語言只是為了提高效率,並且和具 體的微處理器相關。

 

2.1.2 The C Programming Language and Compiler (C語言和編譯器)

 

使用彙編語言編寫大型程序十分困難,消耗時間,容易出錯而且生成的程序不能移植,只能束縛在特定的處理器家族。更好的選擇是使用和機器無關的語言, 例如C。C允許你用邏輯算法描述程序和要處理的數據。被稱為編譯程序(compiler)的特殊程序讀入C程序,並將它轉換為彙編語言,進而產生機器相關 的代碼。好的編譯器生成的彙編指令可以和好的彙編程序員編寫的程序效率接近。大部分Linux核心是用C語言編寫的。以下的C片斷:

if (x != y)

x = y;

執行了和前面示例中彙編代碼完全一樣的操作。如果變量x的內容和變量y的內容不一樣,變量y的內容被拷貝到變量x。C代碼用例程(routine) 進行組合,每一個例程執行一項任務。例程可以返回C所支持的任意的數值或數據類型。大型程序比如Linux核心分別由許多的C語言模塊組成,每一個模塊有 自己的例程和數據結構。這些C源代碼模塊共同構成了邏輯功能比如文件系統的處理代碼。

 

C支持多種類型的變量。一個變量是內存中的特定位置,可用符號名引用。上述的C片斷中,x和y引用了內存中的位置。程序員不需要關心變量在內存中的具體位置,這是連接程序(下述)必須處理的。一些變量包含不同的數據例如整數、浮點數等和另一些則包含指針。

 

指針是包含其它數據在內存中的地址的變量。假設一個變量x,位於內存地址0x80010000, 你可能有一個指針px,指向x。 Px可能位於地址0x80010030。Px的值則是變量x的地址,0x80010000。

 

C允許你將相關的變量集合成為結構。例如:

Struct {

Int I;

Char b;

} my_struct;

是一個叫做my_struct的數據結構,包括兩個元素:一個整數(32位)I和一個字符(8位數據)b。

 

2.1.3 Linkers(連接程序)

 

連接程序將幾個目標模塊和庫文件連接在一起成為一個單獨的完整程序。目標模塊是彙編程序或編譯程序的機器碼輸出,它包括機器碼、數據和供連接程序使 用的連接信息。比如:一個目標模塊可能包括程序的所有數據庫功能,而另一個目標模塊則包括處理命令行參數的函數。連接程序確定目標模塊之間的引用關係,即 確定一個模塊所引用的例程和數據在另一個模塊中的實際位置。Linux核心是由多個目標模塊連接而成的獨立的大程序。

 

2.2 What is an Operating System(什麼是操作系統?)

 

沒有軟件,計算機只是一堆發熱的電子元件。如果說硬件是計算機的心臟,則軟件就是它的靈魂。操作系統是允許用戶運行應用程序的一組系統程序。操作系 統將系統的硬件抽象,呈現在用戶和應用程序之前的是一個虛擬的機器。是軟件造就了計算機系統的特點。大多數PC可以運行一到多個操作系統,而每一個操作系 統從外觀和感覺上都大不相同。Linux由不同功能的部分構成,這些部分總體組合構成了Linux操作系統。Linux最明顯的部分就是Kernel自 身,但是如果沒有shell或libraries一樣沒有用處。

 

為了瞭解什麼是操作系統,看一看在你輸入最簡單的命令時發生了什麼:

 

$ls

Mail c images perl

Docs tcl

$

這裡的$是登錄的shell輸出的提示符(此例是bash):表示shell在等候你(用戶)輸入命令。輸入ls引發鍵盤驅動程序識別輸入的字符, 鍵盤驅動程序將識別的字符傳遞給shell去處理。shell先查找同名的可執行映像,它找到了/bin/ls, 然後調用核心服務將ls執行程序加載到虛擬內存中並開始執行。ls執行程序通過執行核心的文件子系統的系統調用查找文件。文件系統可能使用緩存的文件系統 信息或通過磁盤設備驅動程序從磁盤上讀取文件信息,也可能是通過網絡設備驅動程序同遠程主機交換信息而讀取本系統所訪問的遠程文件的詳細信息(文件系統可 以通過NFS網絡文件系統遠程安裝)。不管文件信息是如何得到的,ls都將信息輸出,通過顯示驅動程序顯示在屏幕上。

 

以上的過程看起來相當複雜,但是它說明了即使是最簡單的命令也是操作系統各個功能模塊之間共同協作的結果,只有這樣才能提供給你(用戶)一個完整的系統視圖。

 

2.2.1 Memory management(內存管理)

 

如果擁有無限的資源,例如內存,那麼操作系統所必須做的很多事情可能都是多餘的。所有操作系統的一個基本技巧就是讓少量的物理內存工作起來好像有相 當多的內存。這種表面看起來的大內存叫做虛擬內存,就是當軟件運行的時候讓它相信它擁有很多內存。系統將內存分為容易處理的頁,在系統運行時將這些頁交換 到硬盤上。而應用軟件並不知道,因為操作系統還使用了另一項技術:多進程。

 

2.2.2 Processes (進程)

 

進程可以看作一個在執行的程序,每一個進程都是正在運行的特定的程序的獨立實體。如果你觀察一下你的Linux系統,你會發現有很多進程在運行。例如:在我的系統上輸入ps 顯示了以下進程:

$ ps

PID TTY STAT TIME COMMAND

158 pRe 1 0:00 -bash

174 pRe 1 0:00 sh /usr/X11R6/bin/startx

175 pRe 1 0:00 xinit /usr/X11R6/lib/X11/xinit/xinitrc --

178 pRe 1 N 0:00 bowman

182 pRe 1 N 0:01 rxvt -geometry 120x35 -fg white -bg black

184 pRe 1 <>

185 pRe 1 <>

187 pp6 1 9:26 /bin/bash

202 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black

203 ppc 2 0:00 /bin/bash

1796 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black

1797 v06 1 0:00 /bin/bash

3056 pp6 3 <>

3270 pp6 3 0:00 ps

$

 

如果我的系統擁有多個CPU那麼每個進程可能(至少在理論上如此)都在不同的CPU上運行。不幸的是,只有一個,所以操作系統又使用技巧,在短時間 內依次運行每一個進程。這個時間段叫做時間片。這種技巧叫做多進程或調度,它欺騙了每一個進程,好像它們是唯一的進程。進程相互之間受到保護,所以如果一 個進程崩潰或不能工作,不會影響其他進程。操作系統通過給每一個進程一個獨立的地址空間來實現保護,進程只能訪問它自己的地址空間。  

2.2.3 Device Drivers(設備驅動程序)

 

設備驅動程序組成了Linux核心的主要部分。象操作系統的其他部分一樣,它們在一個高優先級的環境下工作,如果發生錯誤,可能會引發嚴重問題。設 備驅動程序控制了操作系統和它控制的硬件設備之間的交互。比如:文件系統向IDE磁盤寫數據塊是使用通用塊設備接口。驅動程序控制細節,並處理和設備相關 的部分。設備驅動程序和它驅動的具體的控制器芯片相關,所以,如果你的系統有一個NCR810的SCSI控制器,那麼你需要NCR810的驅動程序。

 

2.2.4 The Filesystems(文件系統)

 

象Unix一樣,在Linux裡,系統對獨立的文件系統不是用設備標示符來存取(比如驅動器編號或驅動器名稱),而是連接成為一個樹型結構。 Linux在安裝新的文件系統時,把它安裝到指定的安裝目錄,比如/mnt/cdrom,從而合併到這個單一的文件系統樹上。Linux的一個重要特徵是 它支持多種不同的文件系統。這使它非常靈活而且可以和其他操作系統良好共存。Linux最常用的文件系統是EXT2,大多數Linux發佈版都支持。

 

文件系統將存放在系統硬盤上的文件和目錄用可以理解的統一的形式提供給用戶,讓用戶不必考慮文件系統的類型或底層物理設備的特性。Linux透明的 支持多種文件系統(如MS-DOS和EXT2),將所有安裝的文件和文件系統集合成為一個虛擬的文件系統。所以,用戶和進程通常不需要確切知道所使用的文 件所在的文件系統的類型,用就是了。

 

塊設備驅動程序掩蓋了物理塊設備類型的區別(如IDE和SCSI)。對於文件系統來講,物理設備就是線性的數據塊的集合。不同設備的塊大小可能不 同,如軟驅一般是512字節,而IDE設備通常是1024字節,同樣,對於系統的用戶,這些區別又被掩蓋。EXT2文件系統不管它用什麼設備,看起來都是 一樣的。

 

2.3 Kernet Data Structures(核心數據結構)

 

操作系統必須紀錄關於系統當前狀態的許多信息。如果系統中發生了事情,這些數據結構就必須相應改變以反映當前的實際情況。例如:用戶登錄到系統中的時候,需要創建一個新的進程。核心必須相應地創建表示此新進程的數據結構,並和表示系統中其他進程的數據結構聯繫在一起。  

這樣的數據結構多數在物理內存中,而且只能由核心和它的子系統訪問。數據結構包括數據和指針(其他數據結構或例程的地址)。乍一看,Linux核心 所用的數據結構可能非常混亂。其實,每一個數據結構都有其目的,雖然有些數據結構在多個的子系統中都會用到,但是實際上它們比第一次看到時的感覺要簡單的 多。

 

理解Linux核心的關鍵在於理解它的數據結構和核心處理這些數據結構所用到的大量的函數。本書以數據結構為基礎描述Linux核心。論及每一個核心子系統的算法,處理的方式和它們對核心數據結構的使用。

 

2.3.1 Linked Lists(連接表)

 

Linux使用一種軟件工程技術將它的數據結構連接在一起。多數情況下它使用鏈表數據結構。如果每一個數據結構描述一個物體或者發生的事件的單一的 實例,比如一個進程或一個網絡設備,核心必須能夠找出所有的實例。在鏈表中,根指針包括第一個數據結構或單元的地址,列表中的每一個數據結構包含指向列表 下一個元素的指針。最後元素的下一個指針可能使0或NULL,表示這是列表的結尾。在雙向鏈表結構中,每一個元素不僅包括列表中下一個元素的指針,還包括 列表中前一個元素的指針。使用雙向鏈表可以比較容易的在列表中間增加或刪除元素,但是這需要更多的內存存取。這是典型的操作系統的兩難情況:內存存取數還 是CPU的週期數。

 

2.3.2 Hash Tables

 

鏈接表是常用的數據結構,但是遊歷鏈接表的效率可能並不高。如果你要尋找指定的元素, 可能必須查找完整個表才能找到。Linux使用另一種技術:Hashing 來解決這種侷限。Hash table是指針的數組或者說向量表。數組或向量表是在內存中依次存放的對象。書架可以說是書的數組。數組用索引來訪問,索引是數組中的偏移量。再來看書 架的例子,你可以使用在書架上的位置來描述每一本書:比如第5本書。

 

Hash table是一個指向數據結構的指針的數組,它的索引來源於數據結構中的信息。如果你用一個數據結構來描述一個村莊的人口,你可以用年齡作為索引。要找出 一個指定的人的數據,你可以用他的年齡作為索引在人口散列表中查找,通過指針找到包括詳細信息的數據結構。不幸的是,一個村莊中可能很多人年齡相同,所以 散列表的指針指向另一個鏈表數據結構,每一個元素描述同齡人。即使這樣,查找這些較小的鏈表仍然比查找所有的數據結構要快。

 

Hash table可用於加速常用的數據結構的訪問,在Linux裡常用hash table來實現緩衝。緩衝是需要快速存取的信息,是全部可用信息的一個子集。數據結構被放在緩衝區並保留在那裡,因為核心經常訪問這些結構。使用緩衝區 也有副作用,因為使用起來比簡單鏈表或者散列表更加複雜。如果數據結構可以在緩衝區找到(這叫做緩衝命中),那麼一切很完美。但是如果數據結構不在緩衝區 中,那麼必須查找所用的相關的數據結構,如果找到,那麼就加到緩衝區中。增加新的數據結構到緩衝區中可能需要廢棄一個舊的緩衝入口。Linux必須決定廢 棄那一個數據結構,風險在於廢棄的可能使Linux下一個要訪問的數據結構。

 

2.3.3 Abstract Interfaces(抽象接口)

 

Linux核心經常將它的接口抽象化。接口是以特定方式工作的一系列例程和數據結構。比如:所有的網絡設備驅動程序都必須提供特定的例程來處理特定 的數據結構。用抽象接口的方式可以用通用的代碼層來使用底層特殊代碼提供的服務(接口)。例如網絡層是通用的,而它由底層符合標準接口的同設備相關的代碼 提供支持。

通常這些底層在啟動時向高一層登記。這個登記過程常通過在鏈接表中增加一個數據結構來實現。例如,每一個連結到核心的文件系統在核心啟動時進行登記 (或者如果你使用模塊,在文件系統第一次使用時向核心登記)。你可以查看文件/proc/filesystems來檢查那些文件系統進行了登記。登記所用 的數據結構通常包括指向函數的指針。這是執行特定任務的軟件函數的地址。再一次用文件系統登記的例子,每一個文件系統登記時傳遞給Linux核心的數據結 構都包括一個和具體文件系統相關的例程地址,在安裝文件系統時必須調用。

2008年11月2日 星期日

Refactoring跟Unit Test

打開refactoring的書皮,他有手法的列表,我看完之後,天阿,是簡單阿,可是...可是...這不是人做的工作阿~~~~~隨便找個程式開始refactoring,很容易發現,你很難在上百個refactoring步驟之後還保證你的程式無誤,瑣碎的步驟本身就是引入bug的危機阿

接著當然作者提出了一個相當有效且正面的手法Unit Test,有了Unit Test你就會有勇氣(很可惜不是保證程式正確,不過我真的感覺到Unit Test跟張網子一樣,你摔下去有人接著,雖然未必接得住XD),有這機制的監控,也比較容易保持程式的正確性;他帶來另外一個好處是,很容易小步驟的修改程式,而非wafterfall的開發手法

C++比較可悲一點,他的refactoring的工具不如java多跟完整,對於java來說,我覺得目前做得相當好的是eclipse(雖然我覺得這工具在寫GUI程式上面蠻陽春的,不要跟我提起他的plug-in,我都裝不起來:P),有了工具終於可以讓程式設計師好好的做設計,不然光重複打字、搬動、重新命名、刪除,我想就有一堆錯誤了。

給將要進入refactoring跟使用unit test的人,請千萬記得做兩件事,一,找到好的工具,二,使用Unit Test ( P.S. 當然先把refactoring跟unit test怎樣使用弄懂)

2008年11月1日 星期六

JUnit4(一)

JUnit4已經發表有一段時間了,藉由JDK5之後的Annotation的幫助,在撰寫unit上面更加的容易了,先來看一個簡單計算機
public class caculator{
private int result;
public caculator(){
result=0;
}
public void add(int n){
result+=n;
}
public void multiply(int n){
}
public void divide(int n){
result/=n;
}
public void substract(int n){
result-=1;
}
public void clear(){
result=0;
}
public int getResult(){
return result;
}
}


上面有幾點要解說的,其中有幾個method是故意做錯的,好用於測試junit,接著看junit4所寫的程式碼

import static org.junit.Assert.*;
import static java.lang.System.out;
import org.junit.Before;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;

public class caculatorTest{
private caculator c;
@Before public void setUp(){
out.println("set up");
c=new caculator();
}
@After public void tearDown(){
out.println("tear down");
c=null;
}
@Test public void testAdd(){
out.println("test add");
c.add(5);
assertEquals(5,c.getResult());
}
@Ignore
@Test public void testMultiply(){
out.println("test multiply");
}
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(caculatorTest.class);
}
}
裡面可以看到out.println是故意加上去,可以用來觀察程式的運作方式,過去以往在每個測試的method前後會被呼叫的setUp跟tearDown可以直接被@Before跟@After所取代,這兩個方法的功能分別是來執行建構準備測試的物件跟把物件清除,這裡單純的只要建構caculator物件跟把他設定為null(即是"刪除"該物件),這樣做的目的確保在測試過程中不會"污染"物件,導致測試結果可能會依賴於測試方法呼叫的次序

@Test這個annotation表示我們要測試某個功能,裡面所要用到的物件分別在@Before跟@After這兩個annotation的方法被準備好了;@Ignore表示說這個方法尚且未被準備好來測試,可能是因為還沒實做,也可能是其他原因

裡面的static method可以回傳一個TestSuite物件,裡面只要加入要測試的class就可以了
將上面兩個檔案放在同一個目錄,並且從junit的官方網站下載junit4.jar放到相同的目錄,接著執行

java -cp %CLASSPATH%;./junit-4.5.jar;. org.junit.runner.JUnitCore caculatorTest

這裡org.junit.runner.JUnitCore這個類別包含了一個main method,他接受的參數為準備要接受測試的class,如果有多個要測試的class就都加入在後面即可
結果如下
JUnit version 4.5
.set up
test add
tear down

Time: 0.015

OK (1 test)

請注意那個紅色的點,每個點表示通過一項測試,後面可以看到在乎叫testadd之前會先呼叫setUp跟之後呼叫tearDown,其實junit4再引入annotation之後,那兩個方法的名稱已經可以隨意取,可是沿用過去junit3以前的慣例比較顯的有可讀性,這裡也有一個跟junit3不一樣的就是,class已經不用強制繼承TestCase這個class了

2008年10月27日 星期一

雜七雜八的名詞解釋

HTPC(Home Theater Personal Computer)
應該考慮的部分是硬體
主要就是解壓縮、介面跟容量
解壓縮最好能夠硬體解壓縮支援,ATI在低階的卡就有些支援,相對的N家的集中在DirectX支援,比較偏向遊戲
介面的話,就是挑有支援一堆啥Full-HD之類的接頭,方便接各種裝置
最後容量的話就沒啥好說的,影片檔案都不小,容量儘量大

PAM

fabless
wiki:無廠半導體公司是專注於設計與銷售應用半導體晶片的硬體裝置並透過將半導體的生產製造外包予專業晶圓代工半導體製造廠商來取得優勢的公司。智霖公司 (Xilinx)的Bernie Vonderschmitt與Chips and Technologies公司的Gordon A. Campbell是無廠概念的先驅者。

無廠半導體公司可將其資源集中在最終市場研發而不需投放資源於保有當前的半導體技術。由於他們的主要產品由專利許可、商業秘密、光罩著作及其他形式的知識產權構成而被稱為知識產權公司(IP firms)。

launchpad
Launchpad 是一個提供維護、支援或聯絡 Ubuntu 開發者的網站平台,由 Ubuntu 的母公司 Canonical 有限公司所架設。
issue:http://wiki.ubuntu-tw.org/index.php?title=Launchpad

mingw, cygwin, xmingw
MinGW 可以唸做 Min-G-W「明~碁~大不了~」或是MinG-W「名~大不了~」,也有人唸做 ming-wee「名貴(台語發音)」 或是 min gnu 「明~格奴」。可參考 http://en.wikipedia.org/wiki/MinGW

MinGW 原來是 Cygwin (念做「賽葛-wing」)裡 GNU 開發工具的一個分支,沒 Cygwin 複雜,而且目的也與 Cygwin 不同,Cygwin 的目的是希望透過一個 dll (就是 Cygwin1.dll 通常被安裝在 C:\Windows\system32底下) 建立一個系統呼叫轉換層,將原先 unix 世界裡的 system call 呼叫,經由這個 DLL 把它們映對到微軟視窗作業系統的系統呼叫,如此一來就可以很方便在 Windows 底下模擬出一個 unix 平台來,這樣你不需要一個 unix 機器照樣也可以開發 unix 程式。開發好的程式只要拿到 UNIX 平台上就可以使用。同理原來UNIX上諸多 Open source 程式也可以粉方便拿到 Windows 世界來享用,當然啦!UNIX system calls 與 Windows APIs 不盡然可以一對一對應,Cygwin 的目標在於儘可能 Compatible,執行績效倒不是重點考量。

「名~大不了~」可就不同啦!他的目的反而是將 gnu 這個 open source, free 的開發工具移植到微軟視窗平台,他直接產生出來的PC視窗平台機器碼,可以說與微軟 Visual Studio 做出來的一摸一樣,可以彼此互相通用。執行效率反而是重點。MinGW 常與 MSYS(Minimal SYStem)搭配使用,後者是跑在Windows視窗上的一個瘦身型羽量級 UNIX shell 功能,MinGW與他搭檔還可以進一步做一些更彈性的 Config。(按: UNIX 世界Open source 的玩法,大約都是用 cvs 或是 Sub version 去取得 Source code,然後 configure --> build --> install 三部曲, configure 會先蒐集你平台上各個工具元件實際安裝路徑,根據它們來調整軟體 Project檔(通常它是一個Makefile),之後才執行 make 去 Build 出軟體,透過 Install 腳本,最後再把作好的軟體 install (安裝)到適當的資料夾來使用。這 MSYS 就可以讓 MinGW 這編譯工具也具備第一步的 configure 功能。

xminGW 則是有人突發奇想,再進一步把 minGW 移植回去 UNIX平台,在 UNIX/Linux平台上有個 xminGW的好處是,在 UNIX 機器上可以有一個跨平台編譯器(Cross-compiler),在 UNIX平台上直接編譯產生 Windows 平台上可以執行的機器碼。Cross-compiler 在內嵌式系統開發上常用到,要內嵌的那個平台通常稱作 Target platform(標的平台),一般而言檔次較低功能上也比較陽春,並不適合在上面開發系統,大多數都是假手另一個開發程式的Source platform(可以是 Windows 或是 UNIX/Linux),透過上面的 Development toolchain 裡的 Cross-compiler 去產生出標的平台上的程式碼(通常是另一個OS平台),下載到標的平台來執行。

你的 PDA 可能上面是 Samsung 的 ARM7 或是 ARM9 處理器,他上面的應用程式其實是透過在 Linux 的 ARM Toolchain 跨平台開發出來的啦!
Likewise 你手機也是個內嵌式系統,它上面的應用程式都是透過類似的 Cross-compiler 編譯出來的啦!
http://eoffice.im.fju.edu.tw/phpbb/viewtopic.php?t=1895

DVR
Digital Video Recorder數位監控

TCC
Tiny C Compiler
http://bellard.org/tcc/

DOCBOOK
http://www.docbook.org/ 適合用來撰寫一些科學文件,但是對於數學算式的處理比tex薄弱
但是使用上比tex簡單

HIPS
主機入侵防禦系統
http://www.gsg9.tw/showthread.php?p=1350

AGPS
AGPS (Assisted GPS)可以除了用衛星定位外, 還可以其他網路設備來定位, 例如用通訊基地台
http://www.nokia.com.tw/A4805286


G-sensor
http://tw.myblog.yahoo.com/jjyeh-1976/article?mid=433&prev=434&next=417

2008年10月23日 星期四

最近在思考C/C++ & Java

無疑的C/C++是一個成功的語言,符合當時的程式設計需要,但是"最近"(約十年吧)竄起的java卻是非常的快速,我覺得C/C++在lib上的發展也不比他快
為何會這樣?有些人歸功於java的memory management的成功,其實我頗不以為然,尤其最近,記憶體對於一般應用程式來說根本不成問題;再加上OS的進步,一個不回收記憶體的程式,只要很快的結束的話,OS也可以把大部分的記憶體回收回來
後起的Ruby、Python、PHP也也感覺不大會威脅到Java,畢竟他們都還不如Java全方位,可是不能否認的,他們在web application上打下了一片天地

Java成功的法則是?我認為歸功於良好的設計跟隨著時代演化的架構。

良好的設計使得程式設計師不用在面對多種的平台,如果移植過C++到不同的平台上,你就會發現你要花上很長的時間除錯跟維護相容性,更可能只要換個compiler,你就得應付到手忙腳亂,反觀java,幾乎只要拿個jar檔,就可以正確無誤的運作(web上一些設定檔還挺麻煩的,這倒是不得不唸一下)。在意外處理上面也是相當的良好,你很容易可以找到你的錯誤,至於C++,如果不一賴debugger,你就得依賴log跟printf,真的要抓出錯誤要額外花上很多時間。當然還有相當多的地方java設計的比C++好,這就不多說了

一個良好的設計,總有一天會"腐敗",因為人們不斷的進步,java另外成功的一點就是不斷使用一些新發明的觀念、架構,不斷的改進他本身的設計,例如java2引進了swing(重要的是他背後一堆的pattern,老實說有些pattern還真不好用XD),在java 5之後,真的是大幅度的改善,java 6更是有大進步;java 2雖然用了一堆pattern,可是在做國際化(i18n),真的是"吐血身亡",swing的些GUI跟事件也是相當難處理,可是可以看到在JSR-296引進之後,開始解決這個問題,一個全新的framwork可以做更好的開發跟國際化,程式設計師不用為了國際化花費太多的心力。以這些觀念看來,java不斷的在成長,可惜C++就成長的比較緩慢

當然在效能上面C/C++真的還是遠遠把java拋在後頭(不要跟我說啥文字處理多快又多快,沒啥意義),看看那繪圖軟體、科學計算軟體、商用資料庫,哪些骨子是用java寫的,因為他們對於資源的苛求、效能的要求,這些都是讓他們把維護性擺在比較後面的理由;再者C/C++才能接觸到比較硬體方面的開發,比方驅動程式等等,畢竟這是一種極度硬體平台相關的東西。

C/C++並不會完全被java取代,但是java將是可能被推上主流開發程式的語言之一;不過還有M$挾持著OS平台優勢的C#在一旁虎視眈眈,看來java還是要努力不懈才可以

2008年10月16日 星期四

我看STL跟BOOST

這兩者無疑都是非常有力的工具,使得C++的programmer好像短時間內瞬間增加了一甲子的功力,老實說剛剛看到的時候是驚豔,後來小心對待,下一步希望可以擴充這個Lib(雖然說感覺離這目標還很遠)

驚豔:你可以想像的到,以前資料結構寫的要死要活,還得東修補西修補,才能完成你合用的東西,短時間內就可用STL完成,還可以套用多種演算法,愛用哪一種型態就是用哪一種型態,不必開發多個版本,全部人家都幫你用好了;BOOST看似一個補完計畫,以前啥atoi、printf之類的東西,好像一下子都解決的,程式的可讀性大幅提昇,以前一些平台相依性的東西,也都被加入了,好像一下子對齊了不少窗口,程式設計師不用再面對一堆不一樣的狀況

小心使用:STL跟BOOST在不瞭解他的原理的狀況下,可能會讓程式當的莫名其妙,再得到好處的時候,也犧牲了一些東西。如果你不知道在刪除了某些container裡面的內容之後,你的iterator會不能用,你大概也很難trace你的程式是如何運作,如果你不知道map其實是一個tree,你會期待他有O(1)的表現,有太多要注意的,天下沒有白吃的午餐;再者以STL來說,因為有些人很懶惰,直接引用,使得程式可讀性下降,明明就是簡單define可以解決的事情,就會看到程式裡充滿map< int,int>::iterator,這是啥鬼東西阿?是一個怎樣的map?

擴充: 人心不足蛇吞象,大家會有一些特殊的應用,在STL跟BOOST有所跟不上的地方,這時候就需要擴充他,可是說來簡單,在沒有通盤考量過STL的結構下,我們可以確定可以完全套入STL的架構中而不出錯嗎?我想很少人有這個自信,可以在STL裡面添加一個hash而套用所有的演算法跟運算子吧!?開發一個面面俱到的hash可能難度遠超乎專案的範圍

啥是BOOST?

正確的說法應該是~BOOST能做什麼?BOOST有哪些東西?
boost是一個准標準庫,相當於STL的延續和擴充,它的設計理念和STL比較接近,都是利用泛型讓復用達到最大化。不過對比STL,boost更加實用。STL集中在算法部分,而boost包含了不少工具類,可以完成比較具體的工作。

boost主要包含一下幾個大類:字符串及文本處理、容器、迭代子(Iterator)、算法、函數對象和高階編程、泛型編程、模板元編程、預處 理元編程、並發編程、數學相關、糾錯和測試、數據結構、輸入/輸出、跨語言支持、內存相關、語法分析、雜項。 有一些庫是跨類別包含的,就是既屬於這個類別又屬於那個類別。

在文本處理部分,conversion/lexcial_cast類用於「用C++」的方法實現數字類型和字符串之間的轉換。 主要是替代C標準庫中的 atoi、 itoa之類的函數。當然其中一個最大的好處就是支持泛型了。

format庫提供了對流的「printf-like」功能。printf裡使用%d、%s等等的參數做替換的方法在很多情況下還是非常方便 的,STL的iostream則缺乏這樣的功能。format為stream增加了這個功能,並且功能比原始的printf更強。

regex,這個不多說了,正則表達式庫。如果需要做字符串分析的人就會理解正則表達式有多麼有用了。

spirit,這個是做LL分析的框架,可以根據EBNF規則對文件進行分析。(不要告訴我不知道什麼是EBNF)。做編譯器的可能會用到。一般人不太用的到。

tokenizer庫。我以前經常在CSDN上看到有人問怎麼把一個字符串按逗號分割成字符串數組。也許有些人很羨慕VB的 split函數。現在,boost的tokenizer也有相同的功能了,如果我沒記錯的話,這個tokenizer還支持正則表達式,是不是很爽?

array: 提供了常量大小的數組的一個包裝,喜歡用數組但是苦惱數組定位、確定數組大小等功能的人這下開心了。

dynamic_bitset,動態分配大小的bitset,我們知道STL裡有個bitset,為位運算提供了不少方便。可惜它的大小需要在編譯期指定。現在好了,運行期動態分配大小的bitset來了。

graph。提供了圖的容器和相關算法。我還沒有在程序中用到過圖,需要用的人可以看看。

multi_array提供了對多維數組的封裝,應該還是比較有用的。

並發編程裡只有一個庫,thread,提供了一個可移植的線程庫,不過在Windows平台上我感覺用處不大。因為它是基於Posix線程的,在Windows裡對Posix的支持不是很好。

接下來的 數學和數值 類裡,包含了很多數值處理方面的類庫,數學類我也不太熟,不過這裡有幾個類還是很有用的,比如rational分數類,random隨機數類,等等。

static_assert,提供了編譯器的assert功能。

test庫,一個單元測試框架,非常不錯。

concept_check提供了泛型編程時,對泛型量的一點檢查,不是很完善,不過比沒有好。

數據類型類any,一個安全的可以包含不同對象的類。把它作為容器的元素類型,那麼這個容器就可以包含不同類型的元素。比用void *要安全。

compressed_pair,跟STL裡的pair差不多。不過對空元素做了優化。

tuple,呵呵,也許是某些人夢寐以求的東西。可以讓函數返回多個值。

跨語言支持:python,呵呵,好東東啊,可以將C++的類和函數映射給python使用。以下為幾個CSDN上的關於 boost.python的中文資料:http://dev.csdn.net/article/19/19828.shtm,http: //dev.csdn.net/article/19/19829.shtm,http://dev.csdn.net/article/19 /19830.shtm,http://dev.csdn.net/article/19/19831.shtm

pool:內存池,呵呵,不用害怕頻繁分配釋放內存導致內存碎片,也不用自己辛辛苦苦自己實現了。

smart_ptr:智能指針,這下不用擔心內存洩漏的問題了吧。不過,C++裡的智能指針都還不是十全十美的,用的時候小心點了,不要做太技巧性的操作了。

date_time,這個是平台、類庫無關的實現,如果程序需要跨平台,可以考慮用這個。

timer,提供了一個計時器,雖然不是Windows裡那種基於消息的計時器,不過據說可以用來測量語句執行時間。

uitlity裡提供了一個noncopyable類,可以實現「無法複製」的類。很多情況下,我們需要避免一個類被覆制,比如代表文件句柄的類,文件句 柄如果被兩個實例共享,操作上會有很多問題,而且語義上也說不過去。一般的避免實例複製的方法是把拷貝構造和operator=私有化,現在只要繼承一下 這個類就可以了,清晰了很多。

value_initialized:數值初始化,可以保證聲明的對象都被明確的初始化,不過這個真的實用嗎?似乎寫這個比直接寫初始化還累。呵呵,仁者見仁了。

這裡面除了regex、python和test需要編譯出庫才能用,其他的大部分都可以直接源代碼應用,比較方便。其實這些庫使用都不難。最主要的原因是有些庫的使用需要有相關的背景知識,比如元編程、STL、泛型編程等等。

來自這裡

練習boost--tuple

主要是參考IBM的網站上的一篇文章

#include <tuple.hpp>
#include <iostream>
#include <tuple_io.hpp>

using namespace std;
using namespace boost;

int main(void){
tuple<int,char,float> t(2,'a',0.9);
cout << t << endl;
return 0;
}

編譯的指令
g++ tuple_b.C -o tuple_b -I /usr/include/boost/tuple/

網頁提醒要注意兩件事情
  1. tuple裡面的元素不可超過10個
  2. tuple裡面的元素要有公開的copy constructor

對於第一點為何不可以超過10個我不大了然,不過第二個大概是當tuple再回傳或者當作參數的時候,預設大概都是bitwise的拷貝,所以在使用上就要考慮到copy constructor,我個人建議是tuple只用來存取一些value only的元素,不要用到pointer或者reference之類的,雖然威力上會打折扣,可是維護上相對應該輕鬆不少

在STL裡面使用make_pair,在boost裡面使用make_tuple,用法上是差不多了
至於取得裡面的元素,則是t.get<0>()或者boost::get<1>(t),紅色的部分表示tuple變數,藍色是第幾個參數

我個人認為,如果把pair跟tuple當作工具來使用,其實不大合乎概念,以STL來說,pair的出現是為了map之類的container存在的,為了對於container跟iterator所做出的抽象對應,而犧牲了可讀性的產品,如果把pair當作回傳一對數值的工具還不如把他變成struct;至於tuple存在的理由我現在還不大明瞭,有機會在慢慢研究

2008年10月11日 星期六

在Ubuntu 8.04上面安裝SVN Server

先說一下廢話,我腦袋還真是愈來愈棒,很快的就會忘記,所以要熬夜把這東西寫下來,不然過個兩天就忘記光光了Orz
安裝的套件有subversion、apache2、libapache2-svn這三個,後面兩個是為了讓使用者可以從網路checkout檔案才需要的,如果你只要主機使用,那麼就只要前面那個就可以了
首先使用apt安裝那三個套件,這就不用多說了,麻煩一點的是安裝apache2的ssh套件,讓我們可以使用https,避免資料被竊取的危險,如果不需要就跳過底下的步驟(底下的步驟建議使用root來執行會比較方便一點)
安裝openssl套件
mkdir /etc/apache2/ssl
產生金鑰openssl req -new -x509 -nodes -out /etc/apache2/ssl/apache.crt -keyout /etc/apache2/ssl/apache.key
載入模組a2enmod ssl
修改/etc/apache2/ports.conf加上Listen 443
修改/etc/apache2/sites-available/default
加上
NameVirtualHost *:443
<>
DocumentRoot /var/www/https/
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
SSLEngine On
SSLCertificateFile /etc/apache2/ssl/apache.crt
SSLCertificateKeyFile /etc/apache2/ssl/apache.key
<>
DAV svn
SVNPath /svn_reposirty
< /location>
< /virtualhost>
重新啟動apache2


如果你覺得SVN server不是對一般人開放,必須要管理機制的話,就是PAM囉,如果不需要認證管理,底下步驟也可以跳過
groupadd dev
adduser www-data dev
adduser [your account] dev
chown -R root:dev myproject
chmod -R g+rws myproject
a2enmod dav_svn
a2enmod auth_pam
a2enmod auth_sys_group
在剛剛location tag內
AuthType Basic
AuthName "Subversion myproject Realm"
require group dev

我注重於一般command line下如何運作,如果說是gui的client就留給網友自行實驗囉,所以底下都是command line在svn server下執行的動作
接著請挑選一個目錄才建立你的svn repository(假如我是放到/root/projects目錄底下)
svnadmin create /root/projects
如果手邊已經有要放到svn server上的目錄就用import會比較方便,我是在主機上操作
svn import doc_dir file:////roots/projects
然後可以選擇任一個使用者,試著把這個目錄checkout出來看看
svn checkout file:////roots/projects/doc_dir

最後要說一句,在ubuntu上面的apache還真有許多小陷阱,蠻討厭的

2008年10月10日 星期五

Code::Blocks IDE

話說KDevelop幾乎可以說為了寫QT程式打造,我希望可以有個light-weight的IDE,當然找上了Code::Blocks
可是安裝完畢之後遇到一個小麻煩,就是如何加入lib呢,結論是,在你所建立的project
選擇Build options,然後就可以看到相關變數的設定,把所要用的library加入

改天再來研究如何加入已經預設好的library

P.S.我很想知道code::blocks如何下編譯指令的

2008年10月7日 星期二

KDevelop土法大煉鋼

用的平台是Ubuntu 8.04,KDE 4.1.2
安裝build-essential、kdevelop、xorg-dev、libqt4-dev、libavahi-qt4-dev、kdelibs4-dev
這樣才可以編譯一個kde的xwindow

2008年10月3日 星期五

QT的中文化

要加上
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
上面的方式只強迫使用了目前系統的locale,對於國際化沒有幫助

其實我還沒弄得很清楚,以後有空再來仔細研究

2008年10月2日 星期四

第一個QT程式

在編譯完畢之後,我開始要寫我第一個QT程式來試驗一下,我發現不是我想像中得那樣簡單,我直接引用別人論壇上的回覆,如果有心的話,你很容易可以google到這段話本來的出處
Hi, There

Following is the method what I successfully did it and for your reference:

1. download "VC++ 2008 Exp" and "qt-win-opensource-src-4.4.0" from officical websites
2. install VC++ 2008 Exp first
3. unzip "qt-win-opensource-src-4.4.0", i.e. "C:\Qt\4.4.0"
4. add "c:\Qt\4.4.0";"c:\Qt\4.4.0\Bin" to your Env Path, and QMAKESPEC=win32-msvc2008
5. open VC++ 2008 Command Prompt/Console from it's Tool Option.
6. go to Qt Directory: "cd c:\Qt\4.4.0"
7. run "configure.exe", then select "y" for yes. (maybe wait for 30~60mins)
8. run "nmake" (maybe wait for 1~4hrs)
9. open VC++ 2008 IDE, select [Tool]->[Option]->[Projects]->[VC++path]
and add following settings:
--> [Execute]: add "c:\Qt\4.4.0" and "c:\Qt\4.4.0\Bin"
--> [Include]: add "c:\Qt\4.4.0" and "c:\Qt\4.4.0\Include"
--> [Lib]: add "c:\Qt\4.4.0" and "c:\Qt\4.4.0\Lib"
10. now, using an editor to create a code, i.e.
#include
#include
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel *label = new QLabel("Hello Qt!");
label->show();
return app.exec();
}
11. then need to create a project for VC++, in the Command Prompt run:
(A) qmake -project
(B) qmake -t vcapp
now, supposed you should get a *.vcproj
12. using VC++2008 IDE to open the *.vcproj
13. build the project..
14. Run it.. then you will see a window with "Hello Qt!" mesg..

:-)


另外我發現,本來很可愛的1xxMB在經過解壓縮跟編譯之後,變成了3~4G的怪物目錄,其實你可以加上nmake clean刪除不必要的object檔案等等,就可以還原到莫約600MB左右的可接受範圍了

Visual C++ 2008 Express + QT4.33

在M$底下免費的C++ IDE,學個跨平台的GUI Framework應該好過MFC跟.NET(雖然.NET也是跨平台),不過還是比較偏向QT
如何整合這兩個東西ㄋㄟ,其實VC++ 2008 express只要去M$那邊抓下來然後安裝就可以了,QT4.33就比較麻煩一點,我這邊用的是open source的版本(不想付錢,我只是要開發一些實驗性的東西)
抓下來解開之後,找到"開始/start"=>"VC++ studio tool"=>"命令提示字元"(一定要用這個command model,他應該含有一些環境變數)
接著切換到解開QT4.33的目錄下,執行configure.exe,選擇同意(你別無選擇,除非你不想裝),接著他會產生一堆檔案跟編譯一些東西
跟著執行nmake,接下來就是二到四個小時的漫長等待,真的非常久(看你電腦等級啦,我的T2450差不多三個多小時)
結束之後會在QT4.33的bin下面產生一個qtdemo的程式(其實nmake的過程產生了很多的demo程式qtdemo只是個引導),你就可以拿來玩玩看
以上執行完畢只是產生了一些相對應的檔案,要開發的話還是要做一些設定,設定環境變數跟打開visual c++ express加入一些設定
  • 新增環境變數QTDIR=C:\QtSource(你QT4.33的目錄)
  • 增加PATH,要加入%QTDIR%\bin
在VC++內(Tool -> Options , in VC++ Directories)加上include file跟lib file
  • $(QTDIR)\include
  • $(QTDIR)\lib
完成後可以開始實驗的去寫程式囉!!

2008年8月12日 星期二

複習一下linux/unix網路程式設計

講到ethernet這個網路當然不能不講到W. Richard Stevens,兩大著"TCP/IP Illustrated"跟"Unix Network Programming"
堪稱網路程式設計的經典之作,不過往作者竟然年過半百再多一些就西去,不勝令人欷噓,雖然現在網路程式設計工具之多,不過這兩部經典還是有許多可以借鏡的地方

今天就來複習一下吧(如果想學習如何寫C網路程式,我想讀者請放棄,因為我並不打算解釋API),首先是server,只是單純的回傳一個字串給client
值得注意的是在bind的第二個參數型態為
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
但是一般都用這個型態來替代
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
}
我想這真的是因為一些相容性的關係,不然真的是只會造成程式設計師的困擾,因為還要處理一堆轉型的問題
話說網路發展剛開始,網路資源遠比程式設計師"值錢"的多阿Orz

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LISTENQ 10

int main(void){
int listenfd,connectfd;
struct sockaddr_in servaddr;
char string[]="Hello~";
listenfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(8188);
bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
listen(listenfd,LISTENQ);
connectfd=accept(listenfd,NULL,NULL);
write(connectfd,string,strlen(string));
close(listenfd);
return 0;
}

2008年8月2日 星期六

M$ .NET framework

話說目前.NET好像已經進入了3.0的版本,很久之前為了.NET推出的新的語言C#,我一直感覺他有推動上的困難,還有整體.NET架構的難以實踐
看著.NET由1.0的外皮到現在的3.0,看到M$硬推Vista取代XP,不禁讓人想到,M$還真有耐心弄出一個全新的平台阿
.NET面對三個難題,一個是平台的普及性,另外一個就是能夠展現平台特性的語言,最後是有足夠的開發團隊(這是全世界的開發者要接受新平台)

M$用Vista取代XP,從核心開始取代,所以M$一定要用Vista取代XP,所以狂推是一定要的,因為不然建構平台會有相當的麻煩

接著是語言這檔子麻煩事情,不過以M$來說,他是經驗豐富阿,不過要取代C/C++成為開發應用程式的主要語言,也非朝夕之功阿;不過光是展現和C/C++一樣的功能,也不足以取代,C/C++發展至今已經是怪物等級的了
沒關係,我來個Managed Extension或者來個CLR C++,保證用C/C++也可以展現.NET的特色,還可以讓開發者繼續用C/C++,可是也不能太便宜你阿~當然要搞的跟以前VB一樣阿,讓你最後得轉向C#才可以,所以你沒看到那種可讀性很低的特性嗎?雜著兩種語法,你看的習慣?"破壞"規格可是我的專長阿
(沒看到M$另外一個功績嗎?XML ODF,M$的創舉,世界上目前還沒有一個文書處理器可以處理,他規格就完備了,還號稱相容過去的ODF,真是敗給他了自己推的竟然沒做出來,起碼他開始喊的時候可以加到他的M$ Office 2007上去,結果也沒有~搞笑)

最後就是開發團隊,這個容易阿,大家幾乎都是商人阿,我們只要平台不賣、開發工具不賣;請問你們這些開發團隊除了買新的之外,你還有啥選擇?我舊的完全不維護囉~反正你拿我怎樣,對喔~差點忘記跟你說,舊的bug我會繼續找,然後繼續公佈,你看我實在非常負責阿,還幫你找系統bug哩,自己要記得自己寫code去patch喔~免得到時候被hack

最後~最後~最後,筆者還搞不清楚一件事情,為何我需要.NET?為何我需要Vista?沒聽過MPEG4叫我換OS,也沒人說3.5G要換電腦,偏偏M$要換整個平台?why?why?why?

2008年7月17日 星期四

sizeof的陷阱

看到Jserv的blog http://blog.linux.org.tw/~jserv/archives/001876.html
還真的發現程式語言一些細緻的地方,一些我以前注意過,但是不大放在心上
現在竟然連C跟C++都"不同調",讓我想起以前一些錯誤的sizeof用法

一般人習慣用sizeof來求取陣列大小,但是這個也只是適用於靜態陣列,如果是動態配置就不行
比方說

#include 〈iostream〉

using namespace std;

int main(void){
int *a=new int[3];
int b[3];
cout
〈〈 sizeof(a) 〈〈 "," 〈〈 sizeof(b) 〈〈 endl;
delete[] a;
return 0;
}


輸出應該是4,12
因為前面的sizeof取到的是指標大小,所以在32bits的OS裡面是4bytes沒有錯,這是一個常見的忽略

2008年6月14日 星期六

兩個M$底下的工具

在linux/unix底下,automake/autoconfig是一大工具,或者rpm/deb都算是標準的格式,在windows底下呢?當然也有,不過好像要收費
Nullsoft Scriptable Install System是一個可以幫你自動把檔案放到適當路徑的軟體工具,另外有一個好玩的工具叫做AutoIt,本來設計是為了執行GUI批次作業,但是目前有人開始使用他做一些GUI功能的測試

2008年6月5日 星期四

三個好用的C語言工具

老實說,覺得Linux有時候code的documents跟comments實在是很少,真的很佩服那些有空去trace code的人,不過現在發現幾個存在已久的工具(自己孤陋寡聞),可以減少負擔

分別就是ctags, cxref, cflow,簡單的功能介紹如下
  • ctags:列表function的定義
  • cxref:分析各個symbol使用到的地方
  • cflow:分析呼叫次序
這幾個工具真的可以大幅度幫忙減輕trace code的負擔,不過我還是想說,把documents跟comments寫好吧

2008年6月3日 星期二

系統程式設計

話說以前有專門的課程都叫做系統程式設計,可是卻是兩碼子事情,一個教導你如何把程式語言變成系統上的machine code,另外一個卻是類似一般程式設計

後者卻是初學者容易陷入混亂的地方,目前大多是C語言,因為一開始還是排除不開一般標準的IO處理,這又是一般初學者在學習程式語言的時候最先接觸的地方,他們就開始有一個懷疑:系統程式設計跟一般程式設計不是差不多嗎?還不就是那些IO

接個更是低階IO的部分,使用的如read, write之類的函數,然後使用者可能覺得這根本是拖褲子放屁,多此一舉(我用fread跟fwrite不是用的好好的嗎?),對於一些啥select, dup之類的function背景又不大了然(我想寫書的作者背景的關係,他們認為已經解釋的很明白,其實初學者根本不懂來由),結果這方面他們就不會去深究

以M$或者java初學者,看到linux系統程式設計,他們大概為覺得signal跟process很不能適應吧,因為他們用的是event跟timer,M$系統程式設計另外又有一套,所以一整個混亂,我想這是一般初學者在進入時候最大的"心理障礙"(當然跟背景也有些關連)

學習過後可能就會發現一件事情,那就是系統程式非常的"底層",他是跟系統強烈相關的,你可能無法很順利的無痛移植到另外一個OS,比方說,你可能寫了一個RS232的通訊程式,可是你卻發現windows並不包含unistd.h這個標頭(我還沒試驗過,我是說可能會有這樣的狀況),然後整個程式就要從新開發

我想在踏入kernel之前,系統程式設計是一門不錯的熱身操

IPC之pipe

在沒有thread之前process大概是unix底下最重要的執行單位之一,所以很多基於process的溝通方式變的很重要,比方說pipe、share memory跟message box
thread出來之後,就可以單純的使用共用的memory,在thread之間溝通是比較方便(當然有其他問體衍生出來)
由IPC學習,漸漸的感覺到,對於unix like的系統來說,幾乎啥都是被當作file

高階的pipe使用兩個函數

#include 〈stdio.h〉

FILE *popen(const char *command, const char *open_mode)
pclose(FILE *stream_to_close);

比方說popen("uname -a","r"),可以看到popen回傳的是一個FILE指標,所以一個pipe也被當作了檔案來運作
高階pipe的運作方式是先建立一個shell然後把command執行類似execl的程序來取得資料,這樣一個運作方式其實還蠻消耗資源的

當然不是一定要透過類似command的模式執行,unix提供另外一種方式,直接套用低階的descriptor

#include 〈unistd.h〉
int pipe(int file_descriptor[2]);

可以看到上面放入了兩個低階的file descriptor,可以將任何寫入file_descriptor[1]的資料透過file_descriptor[0]讀取,採用fifo的原理
回傳的value有可能是
EMFILE:已經超過單一process可用的file descriptor個數
ENFILE:已經超過系統可用的file descriptor個數
EFAULT:descriptor是錯的
這常用在fork後的process於parent/child process之間的溝通上

那如果跨越process呢?本身並沒有絕對的關連,那麼怎麼辦?我不知道unix是否有提供及他的模式可以讓process查詢其他process提供哪些介面的方式,所以他有一個named pipe,看到named就表示過去已經存在,也就是你程式在compile time已經決定名稱的pipe,這樣就可以知道如何存取pipe(以為已經有名字了),其實是透過

#include 〈sys/types.h〉
#include 〈sys/stat.h〉

int mkfifo(const char *filename,mode_t mode);
int mknod(const char *filename,mode_t mode|S_IFIFO,(dev_t) 0);

其中mknod在有些系統並不提供,其實這個函數就是建立了一個臨時的檔案,這樣一來就可以透過這個檔案然操作,有一點要注意的,可能mode會受到umask的影響,比方說umask為0022在乎叫函數的時候使用0777,ls指令出來的結果會是0755

以上的pipe方式可以看的出來unix like的系統的確把各種實體裝置、虛擬裝置都當成檔案,這樣的設計上當然有好有壞囉,好處是幾乎有統一的介面可以操作;壞處是這樣的特色可能在底層的運作上更麻煩,因為並不是每個裝置的特色都可以符合檔案的模式

2008年5月25日 星期日

整理程式碼

這方面出名的工具有formatX,但是那個是要收$$的,目前有一個跨平台免費的軟體,UniversalIndentGUI,可以提供一些簡單的對齊動作,且支援多種檔案格式

很多網頁上的程式如果copy回去,你會發現,那些程式往往慘不忍睹,或者一些source code抓回去充滿了不一樣的空白跟Tab(如果對方tab跟你設定的空白數一樣,或許還看的清楚)

又是偏離上次寫作時間的一篇blog文章~Orz,看來我真的有希望跟程式說bye bye XD