2012年3月12日 星期一

KONY 2012


這張臉孔是我最近常常在facebook上看到的臉,但我直到今天才搞清楚他是哪位,

在觀看我的嘴嘴文前,請您先撥空看一下這段影片。


make Joseph Kony be famous

在觀看這部影片前,我壓根不知道 Joseph Kony 是何方妖魔鬼怪,

或者應該說,我只活在我自己所能觸及所能觀看到的世界之中,對於看不見摸不到的世界一無所知,

但 facebook 是個神奇的地方,就如同影片所言,facebook 上的用戶已經比兩百年前的世界人口還要多,

這是一個沒有國界阻隔的世界,世界各地的訊息在其中恣意流動,每個分秒都可能接收到來自世界上某個小角落傳達而來的訊息,
(但還是有某些刻意箝制人民思想的國家...)

任何一個人有意義的舉動,都可能形成一股洪流席捲全世界,聚焦全世界的目光,

在這之中,我們與世界上七億五千萬人分享理念、思想,進而相互聲援、發起行動,

當數以千以萬計的國民在要求政府做某一件事情時,便形成了一種國家利益,

回應它就成了政府不得不面對的問題,這是一條新的人類行為規則,

過去由上而下的訊息傳播方式將被顛覆,facebook 帶來了新的社會溝通方式,

想當然而,它絕對是一種革命,我相信它絕對足以改變這個世界的流動,

觀看這段影片的同時,讓我們不單單在見證歷史,也同時在創造歷史。

2012年3月9日 星期五

Introduction of GCC Compiler

對軟體工程師來說,使用 GCC 編譯程式碼是一項基本的技能,GCC 可說與我們的工作內容密不可分,以下就 GCC 的大致功能做一些簡單的整理:
(主要內容參考自 MAXSOLAR'S LINUX BLOG - Makefile 範例教學)
  • 傳統的編譯方式
  • gcc foo.c -o foo
    可在拆解成如下的動作:
    gcc foo.c -c
    gcc foo.o -o foo
    • 編譯的過程是將原始碼(foo.c)先使用 -c 參數編譯成 Object file(foo.o),然後鏈結一個函式庫成為二進位檔(binary)如下。[-c : Compile]
    • gcc foo1.c $SACLIB/sacio.a -O3 -g -Wall -ansi -o foo1
    • 接下來有更多 Makefile 的參數出現了,一一介紹如下:
      • -c:編譯但不進行鏈結,會產生一個跟原始碼相同名稱但副檔名為.o的目的檔。
      • -O:表示最佳化的程度,-O預設就是-O1,可以指定成-O2或-O3,數字越大表示最佳化程度越好,但是會增加編譯時間。
      • -g:把偵錯資訊也編譯進去,當你有需要使用GDB軟體進行偵錯,必須加入-g使GDB能夠讀取。一般情況下可以不用-g,因為它也會增加binary大小。
      • -Wall:顯示警告訊息,使用這個參數會在編譯時顯示更多的警告訊息。這個參數相當有用,特別是找不到 libs/headers 之類的問題。
      • -ansi:使用相容ANSI標準的編譯方式,ANSI 是 American National Standards Institute,即美國國家標準學會的簡稱。-ansi 可以增加程式的可移植性。
      • $SACLIB:是一個路徑變數名稱,必須被指定正確的值。執行這個命令前必須先確定這個變數是有被指派到正確路徑才行。.a 檔是一個靜態函式(static library),關於靜態跟共享的觀念稍候解釋。
    • 再來更多吧!假設今天要編譯 main 這隻程式,source codes 中有 main.c, foo.c, target.h,並且需要 /usr/local/moreFoo/lib/libpthread.so 這個共享函式,以及 /usr/local/moreFoo/include 裡面的 headers;這麼複雜的情況又該怎麼作呢?
    • gcc main.c foo.c -I /usr/local/moreFoo/include -L /usr/local/moreFoo/lib -lpthread -O3 -ansi -o main
      以上新出現的參數定義如下:
      • -I:需要 include 某些 headers 所在的目錄,通常 include 目錄都放置headers,利用-I使編譯器知道去哪裡找原始碼裡宣告的 header。gcc預設會去尋找headers的目錄大致有:
        • /usr/include
        • /usr/local/include
        • /usr/src/linux-headers-`uname -r`/include
        • /usr/lib/gcc/i486-linux-gnu/UR_GCC_VERSION/include
        • Current Dictionary
        因此,當原始碼內有宣告 #include <fakeFoo.h>,但 fakeFoo.h 並不在上述的資料夾內,就需要利用 -I 引導 gcc 找到它。至於 target.h 因為在當前目錄,因此不必額外宣告。當然,也可以利用多個 -I 來指定多個 headers 的路徑。
      • -l:表示編譯過程需要一個 library。-lpthread 代表需要一個名為 libpthread.so的函式。
      • -L:需要額外鏈結函式庫所在的目錄,有時候程式碼經常會呼叫一些函數(methods, functions 或是 subroutines),而這些函數是使用其他人預先寫好的、已經編譯成函式(例如 libpthread.so )供人使用的話,我們就不必自己從頭寫過。gcc 預設會去找函式的目錄大致有:
        • /lib
        • /usr/lib
        • /lib/modules/`uname -r`/kernel/lib
        • /usr/src/linux-headers-`uname -r`/lib
        • /usr/local/lib
        • Current Dictionary
        因此編譯時,利用-L指定目錄告訴編譯器可以該路徑下尋找 libpthread.so。因此,若使用了-l,則必須確定所使用的 library 有在預設尋找的目錄中,否則就必須利用-L來指定路徑給編譯器。當然,可以利用多個-L來指定多個 libraries路徑。
  • 靜態、共享與動態鏈結函式庫
  • 輪子不必重複發明 -- 人家寫好的方法我們可以直接拿來用。不過很多時候,這些方法可能因為某些因素,希望提供給別人使用卻又不希望公佈原始碼,這時候編譯成 libraries 是最好的選擇。
    • 靜態函式(static libraries)
    • 靜態函式其實就是將一系列 .o 檔打包起來,因此它可以直接視為一個巨大的 .o 檔。打造出一個靜態函式的方法很簡單:
      gcc operator.c -c
      ar crsv liboperator.a operator.o
      或者
      gcc -static operator.c -loperator
      兩種方法皆能產生 liboperator.a。假設這個靜態函式在 /usr/local/foo/lib/ 裡,編譯時要與靜態函式作鏈結也很容易:
      gcc main.c /usr/local/foo/lib/liboperator.a -o main
      把靜態函式當成一般的 .o 檔一起納入 binary,也可以像這樣:
      gcc main.c -L /usr/local/foo/lib -loperator -o main
      靜態函式將所有的功能全部打包在一起,因此 binary 會變得很巨大,但是執行這個程式的所有功能都已滿足,不會再有 libraries 相依性的問題。但是缺點在於當某些libraries的功能有所更新時,這個程式就必須重新編譯,無法享受到即時更新的優點。通常商業軟體以及嵌入式系統等功能異動較少的程式,會傾向使用靜態函式。
    • 共享函式(shared libraries)
    • 共享函式跟靜態函式的觀念剛好相反,程式在執行時必須能夠找到相依的函式,否則執行時會出現錯誤訊息。製作一個共享函式的方法也很簡單:
      gcc -shared operator.c -o liboperator.so
      或是先編譯出目的檔再進行鏈結:
      gcc -c operator.c
      gcc -shared operator.o -o liboperator.so
      產生出 liboperator.so。假設這個共享函式在 /usr/local/foo/lib/ 裡,使用共享函式進行鏈結也很容易:
      gcc main.c /usr/local/foo/lib/liboperator.so -o main
      也可以像這樣:
      gcc main.c -L /usr/local/foo/lib -loperator -o main
      共享函式在程式啟動時期會檢查是否存在。以一個分別鏈結了靜態函式與共享函式的 binary 而言,執行的結果大有差別。以靜態函式鏈結的 main 程式可以順利執行,但是假設系統預設尋找函式庫的路徑裡找不到 liboperator.so,以共享函式鏈結的main程式則會出現錯誤訊息:
      ./main: error while loading shared libraries: liboperator.so: cannot open shared object file: No such file or directory
      這時解決的方法有四種:
      1. 把 liboperator.so 複製或是作一個連結到 /usr/lib 裡。
      2. 修改 /etc/ld.so.conf,把 /usr/local/foo/lib 加進系統 libraries 的搜尋範圍內。
      3. 設定 LD_LIBRARY_PATH 變數,累加該路徑進來:如果你不是系統管理員,前兩個方法根本沒辦法執行。我們只好自己加到 ~/.profile 裡:
      4. export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/foo/lib
      5. 改用靜態函式進行鏈結。
      共享函式經常出現在開放原始碼的 linux 世界裡,由於使用所有函式皆是共享的,因此許多程式都可以重複利用既有的功能;有新功能或是 bug 也能簡單的替換掉該函式,所有程式都可以即時享受到這樣的改變,也是最為常見的函式型態。
    • 動態函式(dynamic libraries)
    • 動態函式跟共享函式非常類似,唯一的差別在於程式執行時期並不會去檢查該函式是否存在,而是程式執行到某功能時才進行檢查。這種動態載入的技術最常用在瀏覽器或是大型程式的外掛程式,當有需要用到這個功能時才載入進來。而製作一個動態函式較為麻煩。
      gcc -c -fPIC operator.c
      gcc -shared operator.o -o liboperator.so
      其中的 -fPIC 是產生 position-independent code,也可以用 -fpic,撰寫呼叫動態函式的程式碼也需要傳入相關參數。

2012年3月1日 星期四

CGI Overview (Based on C++)

CGI (Common Gateway Interface) Overview
  • CGI 的角色定位
  • C++ Output
    • 如果我們想要利用 C++ 在畫面上呈現出"Hello World!"的字串,我們通常會用下列的程式碼:
    • 但是如果是想要在瀏覽器上呈現出"Hello World!"的頁面,我們還有更多動作需要做,這時就需要 CGI 的支援。
  • Content-Type Header
    • 每一個 CGI Script 都必須優先輸出以 MIME 形式表示的 Content-Type Header,舉例來說:text/plain 表示純文字內容輸出;text/html 表示一個 HTML 的網頁。實際上的程式碼可能是如下所示:
  • First Simple CGI Script
    • 如上所言,我們可以簡單的寫出一個 CGI Script 如下:
    • 為使它更符合標準的網頁結構,並善用 HTML 所提供的 Tag 來美化它,可將其改為如此。
    • 如此一來,瀏覽器便能正確的解析它的內容。
  • Using HTTPd Environment Variables
    • 當我們在伺服器上利用 HTTPd Server 執行 CGI Script ,HTTPd Server 會利用許多環境變數來存放資料,我們可以用 getenv() 來取用這些環境變數,以下就以取得 Client IP address 的 CGI Script 做示範。
    • 你也可以秀出當前時間或是顯示使用者的瀏覽器類型(利用 HTTP_USER_AGENT 參數)
  • Receiving Data From Form
    • 接下來介紹使用 Http "POST" 的方式進行資料傳遞,CGI Script 可透過 STDIN 取得Data,當 HTTPd 收到資料後會先放在一塊輸入緩衝區,並將資料大小紀錄在CONTENT_LENGTH (環境變數),再以 CGI 程式的 STDIN 指向此塊緩衝區,在 C++ 下最簡單的方法是透過 fread() function 取得資料。
    • 下面用一個 HTML <FORM> 來說明 POST 資料的運作過程:
    • 當使用者送出這個表單,CGI Script 將透過 STDIN 收到以下的資料。
    • value1=test1&value2=test2&value3=test3
    • CGI Script 接收資料並回覆的程式碼內容可能如下:
  • Parsing The Form Data
    • 這裡有兩個關於CGI Library的檔案,接下來我們會使用到它們內部的函式跟功能,它們提供兩個 Classes: CCGI 跟 CCGItem,可以更方便的處理資料。
    • 以下是它們的運作方式:建立一個 CCGI 跟 CCGItem,呼叫 CCGI::Load() 來讀取資料,接著呼叫 CCGI::GetItemCount() 來取得被送出的數量,然後利用 CCGI::GetItem 來取得資料,以一個指標指向已填入相對應 item name & value 的 CCGItem ,呼叫 GetName() 以及 GetValue() 進行資料取用。請參考如下的程式碼:
現在你應該對於 CGI based on C++ 有了基本的認識,接下來你可以嘗試寫更有用且具有互動性的 CGI 程式。