2011年10月31日 星期一

svn+ssh不用輸入密碼

最近碰到一個問題,我在build project時會需要到svn update,因此在build的時候需要一直打密碼,十分的不方便。 還好svn+ssh也可以和ssh一樣建立private key和public key做認證,步驟如下:

1. 在自己的主機上建立private key和public key
sway:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/sway/.ssh/id_rsa):  (按Enter即可)
Enter passphrase (empty for no passphrase): (按Enter即可)
Enter same passphrase again: (按Enter即可)
會在~/.ssh 產生兩個檔案,其中id_rsa.pub是public key,id_rsa是private key

2. 修改.ssh目錄權限
sway:~$ chmod 700 ~/.ssh

3. 複製public key到要登入的server上的~/.ssh/authorized_keys
sway:~$ cd ~/.ssh
sway:~/.ssh$ ls
id_rsa  id_rsa.pub 
sway:~/.ssh$ scp id_rsa.pub sway@server:/home/sway/.ssh/authorized_keys
sway@server's password:
id_rsa.pub 100% 1675 2.5MB/s 00:00
把public key傳上去之後,使用svn+ssh就不需要再輸入密碼了!

2011年10月27日 星期四

瀏覽器cache的問題


有時候修改程式後,產生的結果是之前的資料,執行瀏覽器上的重新整理才能看到更改後的結果。這是因為瀏覽器會將之前瀏覽過的資料存放在瀏覽器的cache中(session 也會被存起來),所以再次執行後瀏覽器會直接從cache中取出,並不一定會再連向web server要一次資料,所以會顯示舊的資料。


解決方法:

【PHP】語法:
<?
header(”Cache-Control: no-store, no-cache, must-revalidate”);
header(”Cache-Control: post-check=0, pre-check=0〃, false);
?>

【JSP】語法:
<%
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
%>

Game programming library

ClanLib
http://clanlib.org/

ClanLib是一個跨平台的C++ toolkit library(Supports Windows, Linux and Mac OS X)
2D繪圖使用OpenGL 和 SDL。我覺得一些比較特殊的地方
sprite and animation class: 這應該很多library都有,不過我以前都用wxWidgets or win32API 自己寫
collision detection: 這還蠻神奇的,連碰撞偵測都幫你寫好了
Layers的管理: 這是指2D貼圖,y座標比較小的要先貼。以前我是自己寫一個
sprite list,把要貼的圖insert到list,然後對y軸做排序
XML: 有內建class可以解讀XML的資料,例如可以根據.xml檔裡頭的數據設定一些參數
缺點的話,網路上的資料非常少...連國外的書都只有一兩本


Allegro
http://alleg.sourceforge.net/
最早聽到這個名字是在C: How to program 第五版裡頭,這本書就是大一的計概課本
不過暑假的時候他改版了,新版本加入了新的一章
Game Programming with the Allegro C Library,這章簡單介紹了如何用Allegro寫game
同樣也是跨平台的library,在Windows上使用DirectX繪圖
功能還蠻完整的,書和網路上的資料也比ClanLib多
Allegro 只要會C就能使用了,而且他還支援3D繪圖
算是除了SDL外另外一個簡單易學的Library

SDL + WxWidgets

SDL
http://zh.wikipedia.org/w/index.php?title=SDL&variant=zh-tw

wxWidgets
http://zh.wikipedia.org/w/index.php?title=WxWidgets&variant=zh-tw

SDL是cross-platform game programming library
wxWidgets是cross-platform widget toolkits

去年暑假我用wxWidgets寫了一些小遊戲sample
雖然wx提供了很多好用的GUI toolkits
不過畢竟他不是專門設計出來寫game的library
在遊戲開發上SDL明顯比wx好用
有沒有什麼方法可以結合兩者的長處呢?

最近我看到這個網站
http://code.technoplaza.net/wx-sdl/
這網站簡單介紹了如何結合這兩個工具

1. 把SDL_Init() and SDL_QUIT() 分別放到SDLApp(繼承wxApp)裡的
   OnRun() and OnExit()這兩個member function裡頭

2. 在SDLPanel::createScreen()裡,建立SDL_SURFACE

3. 繪圖動作都寫在SDLPanel::onIdle()

4. 在SDLPanel::onPaint()裡頭建立wxBitmap 並使用screen(SDL_SURFACE)
   的pixel data當做初值

5. 利用wxBufferedPaintDC 把wxBitmap裡頭存的圖案印出到螢幕
   也就等於把screen的pixel data output to 電腦螢幕
   用wxBufferedPaintDC 是為了避免flicker

jQuery plugin: Validation

在網頁時候常常會碰到表單輸入需要做防呆
這時候要寫一堆JavaScript非常的麻煩
其實網路上已經有別人寫好的表單驗證功能
可以直接拿來用

http://bassistance.de/jquery-plugins/jquery-plugin-validation/

這是一個 jQuery plugin: Validation
用來很快速的撰寫JavaScript進行表單驗證
基本上只要簡單的設定驗證的rule即可

Executable and Linkable Format(ELF)


Object file: Compiler編譯原始碼產生的檔案

目前常見的可執行檔格式 :
      Windows下的PE(Portable Executable)
      Linux的ELF(Executable Linkable Format)

Object file就是原始碼編譯過還沒Link的檔案(Windows的.obj和Linux的.o)
廣義來說,Object file和可執行檔的格式其實幾乎是一樣的

Object file被分成好幾個塊Section

    Code Section:常見的名稱有.code或是.data
                 用來存放source code編譯過後產生的instruction

    Data Section:global variable和local static variable都存放在這邊
                 (注意: 不包含local variable以及dynamic variable
                        前者放在程式被load到memory後配置的stack
                        後者放在程式被load到memory後配置的heap  )

                 Data Section又分成三個部分

                 1. .data: 放置已初始化的global variable
                    以及static local variable

                 2. .bss:  放置未初始化的global variable
                    以及static local variable,因為他們的值都是零
                    不需要花額外空間存放零這個值。bss只是為未初始化
                    的變數預留位置而已

                 3. .rodata: 放置Read-Only data,比如說加const修飾過
                    的變數,或是字串常數。ex: printf("%d", a);的%d
                    char a[] = "abcd"; 中的abcd

 
  ELF layout(http://en.wikipedia.org/wiki/File:Elf-layout--en.svg)

這邊出一個小問題,請問一下code中 x1和x2會被放在什麼區段中
static int x1 = 0;
static int x2 = 1;
x1會放在.bss中,x2會放在.data中。x1可以被當作未初始化,compiler最佳化之後
會把x1放到.bss中,節省空間,因為.bss不佔空間。另一個變數x2因為已經初始化
為1,所以放在.data區段中。

除了上述這些比較常用的區段外,ELF還有很多預設的區段。甚至我們可以自己定義區段
比如說我們可以在ELF檔中加入一個"music"的區段,裡面存放一首MP3音樂
程式執行時候可以直接讀取這個區段的MP3資料,把資料加入object file
可以使用objcopy這個指令

ELF檔一開始會有個ELF Header,Header最開頭是ELF的magic number
當OS load可執行檔的時候,會去檢查magic number是否正確
如果錯誤就拒絕載入。Header檔接下來包含一些檔案的資訊
比如說是Little-Endian還是Big-Endian、程式開始執行的的進入點
在unix下,可使用readelf來詳細查看ELF檔案

ELF Header有個e_shoff會定義Section Header Table在ELF檔中的位置
Section Header Table明確的定義這個ELF檔各個區段的資訊
內容包含區段的名稱、所佔的長度、在檔案中的位置、以及讀寫權限等屬性

同樣的,可以使用"readelf -S"來查看Section Header Table的內容

ELF檔還有一些重要的Table如下:

    Relocation Table: Linker在Link object file檔的時候會需要對
                      object file的某些地方作relocation
                      這時候會參考此表進行relocation

    Symbol Table: 儲存所有用到的variable以及function的名稱
                  可以使用nm列出Object file的Symbol Table
                  其中包含Sysmbol所在區段、以及所在位置等資訊

2011年10月26日 星期三

Strong Symbol and Weak Symbol(強型別和弱型別)


Compiler會把已經初始化的Global varible當作Strong Symbol
未初始化的Global varible為Weak Symbol
我們可以使用GCC提供的 "__attribute__((weak))"
來定義任意一個Strong Symbol為Weak Symbol
以下是一個例子:
extern int ext;

int weak;
int strong = 1;
__attribute__((weak)) weak2 = 2;

int main(){
    return 0;
}

在這個例子中weak和weak2都是Weak Symbol
strong和main是Strong Symbol
ext不是Strong也不是Weak,因為他是一個外部變數

Compiler會按照下列規則處理Strong以及Weak Symbol

Rule1: Strong Symbol不能在不同的Obj檔被多次定義
       這就是我們常常看到的重複定義錯誤

Rule2: 如果一個Symbol在某個檔案是Strong,在其他檔案都是Weak
       Compiler會選擇Strong Symbol

Rule3: 如果一個Symbol在每個檔案都是Weak,會選擇最大的Type
       比如說一個同樣名稱的int和double global varible
       Compiler在Link的時候會選擇double

外部符號的Reference也有分兩種

Strong Reference:  在Link時找不到符號定義會回報錯誤
Weak Reference:   在Link時找不到符號定義不會回報錯誤,通常會預設為0

下面是GCC把foo()宣告成weak reference的擴充keyword
__attribute__((weakref)) void foo();

int main()
{
    foo();
}
上面這段code可以編譯成執行檔且不會產生錯誤

但是我們執行程式的話,因為沒有定義foo(),foo的位置為0
因此會發生不合法的位置存取錯誤

Weak Symbol和 Weak Reference對函式庫的設計非常有用
函式庫可以定義一些Weak Symbol的函式
使用者可以自己定義一些Strong Symbol達到擴充功能
因為Strong Symbol會蓋掉Weak Symobl

我們也可以透過Weak Reference使用一些擴充功能
以後就算我們把擴充功能去掉,程式還是可以正常Link


Static Linking(靜態連結)


Linking : Linker把多個Obj檔加工合併成一個Output檔

方法有下列兩種

1. 依序累加: 把Input的Obj檔依照次序合併起來
             Output檔依序為ObjA的.text .data .bss section
             接下來放ObjB的.text .data .bss section,以此類推

   缺點: 浪費空間,以x86來說最小的page為4096bytes
             有可能ObjA和ObjB的.text section加起來小於一個page
             但是使用此方法卻必須放在兩個不同的page

2. 相似區段合併:
                 把所有Obj檔的.text合併到輸出檔的.text section
                 依序合併.data、.bss這些section
                 目前static linking採用此方法

目前gcc Linker使用一種叫Two-pass Linking的方法,整個Linking過程分兩步驟


1. 分配空間和位置: 先掃描一遍所有的Obj檔,計算出各個section所需要
                   配置的空間大小以及虛擬位置(VMA)

2. 符號解析和重定(relocation) : 對所有使用到的Symbol做調整以及relocation

一般來說,如果我們使用到一些外部變數或是函式。在編譯的時候Compiler由於
還不知道這些變數和函數的位置,因此會先填一個假的位置到產生出來的指令中
當Linker做Linking的時候,就會去對這些假位置做調整並且換成正確的位置
這個動作叫做Relocation


重定表: 紀錄哪些指令需要被調整的一張table,可以重定的ELF檔中都會有一份table


Static Linking有下列缺點

1. 空間浪費: 每個程式內部都會有printf()、scanf()這些函式
2. 更新困難: 假如要更新某個lib檔,必須要對所有使用到這個lib檔
             的程式重新Link。並且重新發佈給使用者。

extern "C"


大家都知道extern "C"這個關鍵字是用來讓C++與C相容

例如:
extern "C" {
       int func(int);
       int var;
   }

C++會把大括號裡面的code當作C來處理
因此不會使用C++的Name Mangling機制
而會使用C原本的機制
目前Visual C++會把符號前面加上"_"
GCC則不會做修飾

因此我們常常會看到C Library中這樣寫
#ifdef __cplusplus
extern "C" {
#endif

void *memset (void *, int, size_t);
char *strcat (char *, const char *);
int   strcmp (const char *, const char *);
char *strcpy (char *, const char *);


#ifdef __cplusplus
}
#endif

就是因為C++會自動把memset修飾成_Z6memsetPvii
造成Linker在C Library中找不到這個符號發生錯誤
這時候可以使用__cpluscplus這個Macro來辨別是否是編譯C++程式
Compiler會在編譯C++程式時自動定義這個Macro

分析大型專案程式碼的方法(How to trace the source code of a big project)


要如何Trace一個數百萬行的程式呢? 這是一個好問題

在幾十萬行甚至數百萬行的程式,是不可能一行一行去Trace code
我們也不需要去瞭解code每一行的意思
這時候工具就很重要了

我們最重要要知道的事就是整個系統的流程,這點可以利用GDB來觀察


整個分析系統的方式分為三個步驟

1.觀察
  一開始利用GDB執行程式,並隨意操作系統
  這時候可以觀察debug訊息以及thread的狀態
  根據印出來的訊息,在適當的地方觀察系統流程和架構

2.適度修改
  適度修改是說在run time的時候利用GDB修改執行的程式
  避免用editor直接修改code
  可以把GDB當成是Interpreter來用,設定中斷點把某一行code換掉
  甚至可以直接修改記憶體內容。修改完之後觀察執行結果是否如我們猜測的一樣
  這邊可以利用GDB提供的資訊,比如說call stack、變數內容、變數type進行分析
  直到瞭解系統整個流程為止

3.擴充
  幫這個系統加入一些新的功能,同樣的我們不需要用editor修改code
  只要透過GDB在run time時候修改code即可
  當你可以為一個系統加入新功能的時候,大概就瞭解整個系統八九成了


4.驗證的macro
  在GDB當中可以加入驗證你程式正確性的macro
  大部分open source專案(比如說Apache)都會提供GDB macro讓你驗證程式
  你也可以自行撰寫macro來自動驗證你修改的程式是否正確


另外盡量不要使用GUI的GDB
原因有兩個

1.GUI的GDB往往都不是最新版本的GDB,沒辦法用一些新的功能
2.在有些情況下X-window會整個爛掉,比如說開發一個新的輸入法

程式設計師的自我修養:連結、載入、程式庫



看完這本書後我有個心得,強烈推薦每位程式設計師都該讀這本書!

本書就如同他副標題所寫得,詳細的帶讀者深入瞭解一個程式的Linking, Loading, Library是如何運作的。書中先從最基本的Compile and Linking開始介紹,之後慢慢介紹Static Linking、剖析Object檔、Loading、Dynamic Linking。作者不只分析了Linux下的ELF檔,連Windows的Dynamic Linking機制都有詳細的介紹。本書是每個想要瞭解系統在做什麼的程式設計師必讀的一本書。我當場在書局翻過之後就決定買一本回家看了。

就我求學的經驗,就算念到碩班,能把這些東西搞清楚的人也沒幾個。很多人念到碩士畢業可能連程式怎麼在OS中執行都沒有概念。雖然說不用懂這些東西也能寫程式,不過我相信要寫一手好程式是不可能不去瞭解Compiler。這本書提供了一個很好的入門點,學C和C++有一段時間的朋友們相信讀了這本書都會有恍然大悟的感覺! 很推薦大家去讀這本好書

最後離題一下,我一直覺得國內中文書很缺少這類偏底層、系統的書籍(這本書也是大陸人寫得)。反而國內很喜歡出一些程式語言的入門書(什麼一個禮拜學會C++......)。說真的有時候很羨慕大陸,他們真的出了很多硬底子技術的書。這點真的值得國內出版社省思。

底下是一些讀書筆記,大家可以參考看看

2011年10月25日 星期二

C++名稱修飾(Name Mangling)


在70年代以前,Compiler產生Object檔時
符號名稱和對應的變數、函式名稱是一樣的
也就是說如果你在code宣告定義一個foo函式
foo在Object檔中對應的符號名稱也叫做foo

這樣的設計有個缺點,如果你使用C Library
你就不能使用C Library已經定義好的變數與函式名稱
否則有重複定義的問題

因此後來UNIX就規定code經過編譯之後
要在對應的符號名稱前加入"_"
比如說"foo"就變成"_foo"
這個方法的確可以減少符號衝突的機率
但是並沒辦法完全解決符號衝突的問題
因此C++加入了Namespace來解決符號衝突的問題

C++有繼承和多載等特性
以多載來說,要讓Compiler能分辨兩個相同名稱的函式
就必須要在產生的符號上動一些手腳
這個動作叫做Name Mangling

以下列這個例子來說
int func(int);
float func(float);

class C {
    int func(int);
    class C2 {
        int func(int);
    };
};

namespace N {
    int func(int);
    class C {
        int func(int);
    };
}

這段code一共有6個func函式,他們不同的地方只有參數以及Namespace不一樣
Compiler會使用這些函式的Function Signature來辨別他們
Function Signature會加入函式所在Class以及Namespace等資訊
用來辨別不同的函式,就好像函式的簽名一樣


例如:
class C {                      Function Signature
    int func(int);        ==>  int C::func(int)
    class C2 {
        int func(int);    ==>  int C::C2::func(int)
    };
};

namespace N {
    int func(int);       ==>  int N::func(int)
    class C {
        int func(int);   ==>  int N::C::func(int)
    };
}

當compiler在把source code編譯成object檔的時候
會使用Name Mangling這個方法,把Function Signature修飾後轉成對應的符號
例如(使用GCC):


                            修飾過後的符號名稱
int C::C2::func(int)   ==>  _ZN1C2C24funcEi
int N::C::func(int)    ==>  _ZN1N1C4funcEi
int func(int)            ==>  _Z4funci
float func(float)      ==>  _Z4funcf

GCC的C++名稱修飾規則如下

1.所有的符號都由"_Z"開頭
2.如果是巢狀的名稱,就在開頭的"_Z"之後加上"N"
3.接下來是Namespace以及Class的名稱,寫法為字串長度 + "N" or "C"
4.如果是巢狀名稱的話,最後以"E"結尾
5.E後面接retrun type,int寫為i,float寫為f

此外binutils當中提供一個工具"c++filt"可以幫我們解析修飾過得名稱
ex:
$ c++filt _ZN1N1C4funcEi
N::C::func(int)
C++的global varible以及static varible同樣也會經過修飾
例如Namespace foo中的global varible bar會被修飾成 _ZN3foo3barE

不同編譯器Name Mangling的作法通常會不一樣
例如Visual C++就使用和GCC完全不同的Name Mangling方法


Reference: http://en.wikipedia.org/wiki/Name_mangling
                 程式設計師的自我修養:連結、載入、程式庫

2011年10月24日 星期一

USB Complete: The Developer's Guide(USB完全開發手冊)


前些日子因為工作要用到USB的關係,我從書局買了這本書。讀完這本書之後再搭配一些網路上的文件。我很快花了一個多禮拜的就搞懂USB的基本原理。

這本書寫得不但詳細而且好懂。因為我是搭配USB spec一起看,有些地方spec沒寫清楚,回頭看一下這本書就瞭解之前不懂得地方了。書中舉了很多Windows USB Driver的sample code(written in C# and VB),雖然我最後沒有用書中的範例,不過sample code還是幫助我瞭解了整個USB的概念。總而言之,這是本很好介紹USB的起步書,推薦給想要瞭解USB的新手!

附註: 這本書同時還有中譯本,翻得也不錯。如果覺得英文書太貴也可以考慮買中文書。

Programming Embedded Systems in C and C++(嵌入式系統–使用 C/C++)



這本書是很多人推薦的嵌入式入門書,內容針對嵌入式系統做了一些基本的簡介。如果你是一個完全沒有嵌入式概念的人,看這本書或許是一個不錯的選擇。因為作者寫得很淺顯,只要你會基本的C和C++就能看懂這本書。

或許這是一本不錯的新手入門書,不過這本書有個問題。現在已經是2011年了,而這本書出版的日期是1999年。書中使用的一些硬體(80186)現在都不是主流的產品。此外一些Embedded Systems重要的元件像是Keypad, LCD, Button, ADC......都沒有介紹。簡單的說,這真的就是一本給初學者簡單認識Embedded Systems的書。如果你是完全沒概念的新手,這本書或許可以給你一些協助。不過我還是建議去看 現代嵌入式系統開發專案實務 這本中文書,這本才是真正可以帶你進入Embedded Systems的入門書。

至於有經驗的開發者完全不建議來看這本書,因為你根本學不到什麼。

附帶一提,這本書有翻譯成中文版。不過我覺得翻譯的沒有很好,有些名詞怪怪的。還是建議新手要看的話看原文書會比較好。

註: 這本書有第二版不過我沒看過,第二版是2006年出得。

Les 4 Vies de Steve Jobs(你所不知道的4個賈伯斯)



這是我在Jobs過世之前去圖書館預約的,不過沒想到還沒拿到書就聽到他去世的消息。

這整本書是由Daniel Ichbiah這位法國記者所撰寫,此作者之前也寫過Bill Gates傳記。這本書主要的資料來源都是作者實際去採訪蘋果的創始員工,像是Wozniak、Kottke......等,因此整個角度有點像是從旁觀著來描述Jobs。附帶一提,Jobs唯一的一本授權的自傳是今天開始販售的《Steve Jobs: A Biography》。

這本書收集了大量的資料,內容非常的豐富,作者和翻譯也寫得非常好,讀起來的感覺就像在讀一本小說而且很流暢。作者一共把Jobs的一生分成4個階段:

  1. 求學生崖到創立蘋果時期
  2. 推出Apple II成為美國最年輕的百萬富翁,之後因為麥金塔的失敗而被自己請來的CEO John Sculley趕出蘋果
  3. 失落的十年,直到把NeXT和Pixar推向高峰,重返蘋果
  4. 推出iMac和iPod從此邁向事業高峰

看完了這本書真的很佩服Jobs,他是一個真正在做創新的人,他早期推出的產品有些雖然失敗。但是事後都證明他當時的眼光是沒有錯得,只是推出的時間太早了或是當時的技術還達不到他的要求。

另一個佩服他的一點就是在他被趕出蘋果之後,這段時間長達了10年,而且這段時間剛好是他的死對頭Bill Gates最輝煌的年代,他還能重生並且重返蘋果。要是一般人承受他當時的壓力可能早就放棄了。

總之,這是一本很值得一看的書。這本書從Jobs的創業夥伴的角度來描寫他,這點或許可以和之後推出的Job傳來一起閱讀。看看兩方不同的觀點也是很有意思的

2011年10月18日 星期二

vim + ctags 看程式碼好用的工具

http://ctags.sourceforge.net/

ctags是用vim寫程式的程式設計師一定要知道的套件
ctags會幫使用者建立一個tag (or index) 檔,方便游標可以在各個符號中快速移動
比如說你在trace一段source code時候發現一個function call
就可以利用快速鍵切換游標到function定義的地方
使用方法非常簡單

首先先在source code的最外層目錄輸入指令:
$ ctags -R *
這時候系統就會建立對應的tag檔
接著進去vim就會自動載入對應tag檔
你也可以在vim中輸入以下指令來載入對應的tag:
:set tags=/home/sway/src/tags
/home/sway/src/tags就是我tag存放的路徑
如果常用的話也可以把這行vim設定存放到~/.vimrc裡頭

之後當你游標移到function call上時,輸入:
【Ctrl】+【]】
就可以跳到function的定義
接著再輸入:
【Ctrl】+【t】
就可回到function call的地方

如果你沒有SourceInsight這類tool的話,vim + ctags也是可以幫你做一些簡單的trace
其他包括macro, class, namespace都可以用這種方法來trace

2011年10月17日 星期一

在vim, vi使用數字鍵盤(pietty, putty)

putty或pietty連到遠端主機使用vi或vim時常常會有這個問題:鍵盤右方的數字鍵無法使用
其實只要更改一下putty, pietty裡頭的設定就可以支援這個功能了
修改方法如下圖

進入設定選單,把Terminal -> Features 中的 Disable application keypad mode打勾即可


2011年10月14日 星期五

Startup code

startup code是程式在正式執行main function前所執行的一段code。在一般的軟體開發中,這段code是由compiler所自動產生並安插在main執行之前。因此平常我們可能不會注意到這段code。一般軟體的startup code最主要的工作就是初始化stack。

在embedded system,startup code是開機上電後第一段會執行的程式,通常是由組合語言所寫成。大部分都叫做startup.asm或是crt0.s。他比一般PC上的軟體會多作一些事情,以下是一個embedded software startup code的流程:

1. 關掉所有的interrupt
2. 配置一塊空間給stack
3. 初始化stack pointer, frame pointer
4. 把資料從ROM搬到RAM上,通常是用DMA來搬
5. 把.bss section清空為0,因為未初始化的變數一開始皆為0
6. 打開interrupt
7. 初始化系統的一些設定
8. jump到main function,開始執行main function中的程式

2011年10月13日 星期四

The C Programming Language


今天很不幸的聽到K & R當中的Dennis Retchie不幸逝世的消息。這本書可能大部分程式設計師都讀過了。講到C的聖經第一本大家會想到的就是這本The C Programming Language。The C Programming Language雖然只有200多頁,但是內容涵蓋了所有C的細節。真的可以說是技術著作的經典之作

The C Programming Language出版於1978年,是第一本C的教學書。並於1988年出第二版。書中的內容一直到現在都還影響這個世界,一個最有名的例子就是"Hello World",這本經典之作的第一個程式就是印出Hello World。一直到現在許多程式語言教學書的第一個程式都還是用印出Hello World來當作範例

2011年10月6日 星期四

設定Register常用的Code

將某bit設為1
C
flag |= (1 << n);
Verilog
flag[n] = 1'b1;
將某bit設為0
C
flag &= ~(1 << n);
Verilog
flag[n] = 1'b0;
將某bit做toggle (0變1,1變0)
C
flag ^= (1 << n);
Verilog
flag[n] = ~flag[n];
判斷某bit是否為1
C
if ((flag & mask) == mask)
Verilog
if (flag[n])
判斷某bit是否為0
C
if ((flag & mask) != mask))
Verilog
if (~flag[n])

System Call

System Call簡介
System call 是 process 與OS之間的介面,由Linux kernel實做出來給user使用,system call提供user
programm和os溝通的界面,當user program需要os的服務時,user program便使用system call


System Call 流程圖




流程:
system call會伴隨一個trap(在Linux下會跳到int 0x80),此時系統將mode bit由user mode改成
monitor mode(1->0)並查尋trap vector找尋相對應trap service routine
(此時可做context switch 0->1)
執行完此routine發出interrupt告訴os已經完成




User program 與 wrapper routine 是 user space 的 code
system call handler 與 service routine 則是屬於 kernel space
從 User space 切換到 kernel space 是透過中斷
底下的用link這個system call當例子,解釋程式碼

syscall_table.S
PATH : /usr/src/linux/arch/i386/kernel/syscall_table.S
在 Linux 中, 每個 system call 都有自己獨有的號碼。
當 user-space 執行一個 system call 時,process是去參考 syscall 的號碼而不是名字。



unistd.h
PATH : /usr/src/linux/include/asm/unistd.h
unistd.h 是一個重要的標頭檔,裡頭是 system call 編號的定義,當 system call 發生時,system call 的號碼將透過 register (EAX) 傳給 kernel。





#define _syscall2(type,name,type1,arg1,type2,arg2) 
type name(type1 arg1,type2 arg2) 
{ 
long __res; 
__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" 
         : "=a" (__res) 
         : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)) 
         : "memory"); 
__syscall_return(type,__res); 
}

unistd.h 也定義了不同參數的 system call handler,上面的程式碼是處理 2 個參數的 handler
這是一個 macro,當遇到系統呼叫的時候,就會被展開。

參數傳遞與傳回值
system call 的編號透過 %eax 暫存器來指定;若要傳遞參數,則是透過其它暫存器來傳遞(最上面的流程圖上有標示)
Linux system call 最多可傳遞6個參數,參數的傳遞是透過以下的暫存器來完成:
%ebx:第1個參數。
%ecx:第2個參數。
%edx:第3個參數。
%esi:第4個參數。
%edi:第5個參數。
%ebp:第6個參數(做臨時用途)。

x86 的 Interrupt
x86 的interrupt(中斷)可分為系統定義與使用者自訂:
中斷向量0~8、10~14、16~18:predefined interrupts and exceptions。
中斷向量19-31:保留。
中斷向量32-255:user-defined interrupts(maskable interrupts)。

當從 shell 執行 link 時,0x80 號中斷向量會指到 system_call 進入點的位址,由於 link 有兩個參數,const char* oldfile 和 const char* newfile,因此 shell 會執行 syscall2(int link, const char* oldname, const char* newname),執行 sys_link(),sys_link()會呼叫linkat()。在 linux 中,目錄和檔案在系統中被視為同樣。

int link(const char* oldname, const char* newname)
{
    long _res;
    _asm_  volatile(“int $0x80”
         :”=a”(_res)
         :”0”(_NR_link),”b”((long)(oldname)),
         “c”((long)(newname)));
    do{
        if((unsigned long)(_res)>=(unsigned long)(-(128+1))){
            errno=-(_res);
            _res=-1;
    }
    return (int)(_res);
    }while(0);
}

下面這段組語是 system call 的進入點,也就是 system call table
PATH : /usr/src/linux/arch/i386/kernel/entry.S
ENTRY(system_call)
        pushl %eax                      # save orig_eax
        SAVE_ALL
        GET_CURRENT(%ebx)
        testb $0x02,tsk_ptrace(%ebx)    # PT_TRACESYS
        jne tracesys
        cmpl $(NR_syscalls),%eax
        jae badsys
        call *SYMBOL_NAME(sys_call_table)(,%eax,4)
        movl %eax,EAX(%esp)             # save the return value
 ENTRY(ret_from_sys_call)
        cli                             # need_resched and signals atomic test
        cmpl $0,need_resched(%ebx)
        jne reschedule
        cmpl $0,sigpending(%ebx)
        jne signal_return

syscalls.h
PATH : /usr/src/linux/include/linux/syscalls.h
這個檔案包含了 system call 的宣告。





asmlinkage 是在i386 system call實做中,gcc 很重要的一個標籤。他是一個macro,會被展開成
#define asmlinkage __attribute__((regparm(0)))
這是/usr/include/asm/linkage.h 裡頭的定義,regparm(0)表示不使用register傳遞參數
如此一來所有的參數就會被強迫放在stack當中

這麼做的原因是因為system call handler是assembly code,但是system call routine是C code
為了要保證當system call handler呼叫相對應的system call routine時,符合C語言參數傳遞的規則
是以 stack 方式傳參數,在C function的 prototype前面就要加上 "asmlinkage"。

System call 結束
當 system call 執行完的時候,最後會執行 ret_from_sys_call() 離開。
最後回到 syscallX() 中去。在 syscallX() 中,檢測是否有錯誤碼,然後返回。

2011年10月5日 星期三

GNU Screen教學

最近發現一個Linux底下好用的軟體叫Screen,他可以在一個實體的終端機中模擬多個虛擬視窗
當你要看一堆code的時候,就會覺得他很方便

安裝好後輸入screen就可以開始執行
Screen使用上都是由一個前置鍵【Ctrl-A】所組成,以下是一些我覺得常用的指令

【Ctrl-A】【c】 : 建立一個新的視窗
【Ctrl-A】【w】: 查看目前已經存在的視窗清單
【Ctrl-A】【0~9】 : 切換不同編號的視窗
【Ctrl-A】【Ctrl-A】: 切換回上一個視窗,就是遙控器來回切換的功能
【Ctrl-A】【K】: 刪除這個視窗
【Ctrl-A】【\】: 刪除所有視窗,並且關閉Screen
【Ctrl-A】【g】: 取消畫面閃動提示
【Ctrl-A】【n】: 切換到下一個視窗
【Ctrl-A】【p】: 切換到上一個視窗
底下是Screen指令大全:

Key Action Notes
Ctrl+a c new window
Ctrl+a n next window
Ctrl+a p previous window
Ctrl+a “ select window from list
Ctrl+a Ctrl+a previous window viewed
Ctrl+a S split terminal horizontally into regions Ctrl+a c to create new window there
Ctrl+a | split terminal vertically into regions Requires debian/ubuntu patched screen 4.0
Ctrl+a :resize resize region
Ctrl+a :fit fit screen size to new terminal size Ctrl+a F is the same. Do after resizing xterm
Ctrl+a :remove remove region Ctrl+a X is the same
Ctrl+a tab Move to next region
Ctrl+a d detach screen from terminal Start screen with -r option to reattach
Ctrl+a A set window title
Ctrl+a x lock session Enter user password to unlock
Ctrl+a [ enter scrollback/copy mode Enter to start and end copy region.
Ctrl+a ] to leave this mod
Ctrl+a ] paste buffer Supports pasting between windows
Ctrl+a > write paste buffer to file useful for copying between screens
Ctrl+a < read paste buffer from file useful for pasting between screens
Ctrl+a ? show key bindings/command names Note unbound commands only in man page
Ctrl+a : goto screen command prompt up shows last command entered