2011年12月30日 星期五

Clean Code - Meaningful Names

最近看了一本大家推薦的書
Clean Code: A Handbook of Agile Software Craftsmanship

看書名就知道這是一本教你怎麼寫出乾淨又簡潔的code
書中提的一些概念和方法我覺得蠻有用的,很推薦大家去看這本書
以下是我整理的一些筆記


  • Use Intention-Revealing Names
  • 選一個好的變數名稱雖然會花時間,但是未來絕對可以幫你省下更多時間
  • 一個好的命名要能傳達以下資訊
  • Why it exists?
  • What it does?
  • How it is used? 
  • 需要額外的註解,不是好的命名
  •  int d; // elapsed time in days
  • 好的命名,清楚的表達變數的用途
  • int elapsedTimeInDays; 
    int daysSinceCreation; 
    int daysSinceModification; 
    int fileAgeInDays; 
    
  • 不好的命名方式,很難看懂這段code在做什麽
  • public List<int[]> getThem() { 
      List<int[]> list1 = new ArrayList<int[]>(); 
      for (int[] x : theList) 
        if (x[0] == 4) 
          list1.add(x); 
      return list1; 
    }
    
  • 同樣一段code經過重新命名後可以看出這是一段踩地雷遊戲程式
  • public List<int[]> getFlaggedCells() { 
      List<int[]> flaggedCells = new ArrayList<int[]>();
      for (int[] cell : gameBoard) 
        if (cell[STATUS_VALUE] == FLAGGED) 
          flaggedCells.add(cell); 
      return flaggedCells; 
    } 
    
  • 更好的作法,用一個Cell的class來取代array of ints
  • public List<int[]> getFlaggedCells() { 
      List<int[]> flaggedCells = new ArrayList<int[]>();
       for (Cell cell : gameBoard) 
         if (cell.isFlagged()) 
           flaggedCells.add(cell); 
       return flaggedCells; 
    } 
    
  • Avoid Disinformation
  • 不要使用一些特殊縮寫的名字
  • ex: hp, aix , sco  (這些縮寫在Unix有特殊意義)
  • 不要使用accountList ,除非他真的是一個List 
  • 就算真的是List也不要把Type encoding到命名裡 
  • accountGroup, bunchOfAccounts, 或是只用accounts都會比accountList來的好
  • 命名不要太過相似
    • XYZMyTodayBrunchBreadXYZMyTodayBreakfastBread 很難被區別
  • 不要使用小寫L和小寫O來做命名,因為很難跟1和0做區別
  • int a = l; 
    if ( O == l ) 
      a = O1; 
    else 
      l = 01; 
    



  • Make Meaningful Distinctions

    • 命名要有意義上的區別
    • 使用Number-series naming(a1, a2, .. aN)不是一種好的命名方式
      • public static void copyChars(char a1[], char a2[]) { 
           for (int i = 0; i < a1.length; i++) { 
             a2[i] = a1[i]; 
           } 
        } 
        
      • 應該命名成source和destination
    • 不要使用類似的單字來命名
      • ex: Product, ProductInfo, ProductData,你無法分辨這三者有什麽不同
    • 不要加上多餘的字
      • ex: NameString, CustomerObject,難道Name會是一個floating pointer嗎?
    • function名稱也要避免使用多餘的字,誰能告訴我以下三個function有什麽差別?
    • getActiveAccount(); 
      getActiveAccounts(); 
      getActiveAccountInfo(); 
      



  • Use Pronounceable Names

    • 使用可以發音的單字來命名,方便Programmer之間溝通
      • genymdhms(generation date, year, month, day, hour, minute, and second)應改成 generationTimestamp



  • Use Searchable Names

    • 使用好被搜尋的名字
    • MAX_CLASSES_PER_STUDENT會比數字7要來的好被找到(簡單說就是不要用magic number)
    • single-letter命名只能被使用在short methods中的local variable


  • Avoid Encodings

    • 不要使用匈牙利命名法
      • 早期因為Compiler不會幫忙檢查type才需要匈牙利命名法,現在的Compiler都很強大
      • 使用匈牙利命名法未來如果要更動型別會很麻煩
        PhoneNumber phoneString; 
        // name not changed when type changed!
        
    • Member Prefixes
      • 不需要prefix member variable with m_
      • 直接用this pointer來區分會比較好,而且在大多數的編輯器中this會有highlight
      • public class Part { 
          private String m_dsc; // The textual description 
          void setName(String name) { 
            m_dsc = name; 
          } 
        } 
        
        public class Part { 
          String description; 
          void setDescription(String description) { 
            this.description = description; 
          } 
        } 
        


  • Avoid Mental Mapping

    • 不要使用單一小寫字母來做命名,因為會需要人腦去做Mapping
    • 如果在很小並且不會和其他名稱衝突的scope,比如說迴圈,使用i, j, k還可以被接受。因為大家都習慣了,容易聯想
    • 如果是使用單一字母a, b, c的話就很糟糕
    • 清楚命名才是王道(clarity is king)


  • Class Names

    • 使用名詞來命名Class和Object,不要使用動詞
    • ex: Customer, WikiPage, Account, AddressParser
    • 避免使用Manager, Processor, Data, Info


  • Method Names

    • 使用動詞來命名,ex: postPayment, deletePage, save
    • Accessors, mutators和predicates要以 get, set和is開頭
      • string name = employee.getName();
        customer.setName("Mike");
        if (paycheck.isPosted())...
        
    • overload constructor的時候,可以使用Factory method pattern來清楚敘述參數
    • Complex fulcrumPoint = Complex.FromRealNumber(23.0);會比Complex fulcrumPoint = new Complex(23.0);來得好


  • Don't be Cute

    • 不要使用俚語或是隱喻的命名方式,因為別人有可能會看不懂
      • (X)HolyHandGrenade => (O)DeleteItems
      • (X)whack() => (O)kill()
      • (X)eatMyShorts() => (O)abort()


  • Pick One Word per Concept

    • 使用一樣的單字來表達同樣一件事情
    • 沒有人分的出來fetch, retrieve, get有什麽差別,使用其中一個為你的method命名就好
    • DeviceManager和ProtocolController,看到這兩個單字你會疑惑Manager和Controller有什麽差別。因此不是一個好的命名
    • 維持code的一致性是很重要的


  • Don't Pun

    • 不要使用同一個單字去表達兩個不同的概念
    • 如果已經有一個叫做add的method用來把兩數相加,就要避免再用add命名"把single parameter加到一個collection的method"。改用insert和append


  • Use Solution Domain Names

    • 命名盡量使用Computer Science領域的專有名詞,因為讀你code的人都是programmer
    • 好的例子
      • AccountVisitor => VISITOR pattern
      • JobQueue => 每個programmer都知道這是什麽


  • Use Problem Domain Names

    • 如果沒有Solution Domain Names可以使用,那就使用和你所解問題有關的字彙。至少可以讓看你code的人去問相關領域的專家


  • Add Meaningful Context

    • firstName, lastName, street, houseNumber, city, state, zipcode這些變數放在一起很明顯可以組成一個address。但是如果你只看到state,你會不知道這代表什麽意思
    • 使用prefix讓他們組合成一個概念,addrFirstName, addrLastName, addrState......


  • Don't Add Gratuitous Context

    • 不要加上多餘的prefix
    • 如果一個application叫做"Gas Satation Deluxe",不需要把所有的class都加上GSD這個prefix
      • 會很難使用IDE做搜尋(所有Class開頭都是GSD)
      • Class名稱會變的很冗長


  • Final Words

    • 不要害怕重新命名,現在有很多好用的tool可以幫助你

    2011年12月27日 星期二

    使用BitBucket來管理你的程式碼



    BitBucket是一個網路服務,他提供了使用者免費的版本控制系統來管理程式碼。
    並且提供issue tracking和wiki的功能。

    其實這類型的網站挺多的,以Git來說最有名的就是Github。不過Github有個缺點是沒辦法免費開私人的Repository,所有的專案都必須公開,不然就是要花錢購買。

    最近,BitBucket 開始支援Git。BitBucket最大的優點就是沒有限制私人Repository的個數。唯一的限制是使用者不能超過5個人
    (5 users free plan and you can have unlimited public and private repositories.)
    官網對user的定義為Someone with read or write access to one of your private repositories
    簡單來說,如果整個專案少於五人參與開發的話,這個限制對你來說完全沒影響
    於是我決定把一些私人的專案搬到BitBucket上試用看看,註冊的方法很簡單

    連結到首頁https://bitbucket.org/,點選Sign up for free

    輸入帳號密碼和信箱(信箱會收到驗證信)

    輸入基本資料

    成功建立帳號後,可以點選create a repository來建立新的repository
    或是點選Import an existing repository來Import其他地方建立的專案,比如說從Github, Google Code, SourceForge......


    輸入專案名稱和使用語言,當然別忘了設定Repository type為Git

    接下來就可以開始上傳程式碼了,這邊不詳細講解Git的指令,只示範如何上傳到BitBucket
    首先先輸入git clone把剛才建立的repository複製一份下來,這邊假設專案名稱為bbs_parser
    swaywang則是你的BitBucket帳號
    sway:~$ git clone https://swaywang@bitbucket.org/swaywang/bbs_parser.git
    
    建立一個上傳測試用的Python程式
    sway:~$ echo "print 'Hello World'" > hello.py
    
    把hello.py加入到Server上
    sway:~$ git add hello.py
    
    commit,並且夾帶訊息
    sway:~$ git commit -m "Print Hello World"
    
    最後push到Server上
    sway:~$ git push -u origin master
    
    
    
    
    


    最後在個人首頁就可以發現程式已經被傳上去了
    之後要再建立repository只需要點個人首頁中的create a repository連結即可

    最後推廣大家一起來使用Git,Git的優點可以參考下列這個網站
    http://git-scm.com/about

    2011年12月26日 星期一

    人月神話:軟體專案管理之道


    這本書應該不用多介紹了,軟體工程的聖經

    人月神話雖然出版30多年了,但是書中所寫得大部分準則到現今仍然適用,甚至有許多主管或是老闆都還是會犯書中所寫到的一些問題。書中最重要的觀念之一就是人月(man-month),到現在還是很多人會認為軟體開發進度落後時候就是要多加一些人手進來。作者在30年前就告訴大家這個主張是完全錯誤的。因為人力和工時根本無法互換。

    記得這本書最早是我高中時候看得,那時候大部分的章節都沒辦法真正體會。研究所時我又看了一次,這次因為有實際開發過一些大型Project,所以比較能搞懂書中的一些章節。接下來我想等工作5年後再來看一次,相信又會體會更多的東西。

    這本書絕對是每個軟體工程師必讀的一本書,如果你還是學生的話可能很多內容沒辦法體會。不過等你經驗夠多之後再看,絕對能學到很多東西。

    Head First Programming(深入淺出程式設計)



    我一直很喜歡Head First系列的書籍,HF的特色就是用許多圖片和有趣且實際的範例來教導讀者。因此看到這本HF Programming就決定借回家看。這本書就如同書名所寫得,是專門寫給完全沒寫過程式的人看。不過花了幾個小時看完之後,我覺得這本書根本不適合初學者看

    先講本書的優點。書中以Python來教導基本的程式概念,我覺得這是很好的主意。Python簡單的語法會比C來的更適合初學者學習。本書1~6章主要以教導程式流程和基本資料結構為主,這部份我覺得寫得還OK,舉得例子(web parser, twitter貼文程式)也很貼近實際上的應用。

    不過從第7章到結束竟然都在講解GUI,我邊看邊想一個完全沒寫過程式的人看到這邊真的能瞭解怎麼撰寫GUI嗎?何況後面講解GUI的速度又非常的快,新手一定很難消化。我反而覺得應該藉由一些簡單的Project再度複習一些前面的基本觀念,這樣才是一本適合初學者的書。

    因此這本書我覺得比較適合有一點點程式基礎的人來看,而不是給完全沒有程式基礎的人看。完全沒程式基礎的人還是比較適合去看詳細一點的入門書,並搭配大量的練習。這本書只花了大約200頁(而且因為圖片的關係,大概3頁等於傳統書籍的一頁)在介紹基本的語法,對初學者來說是完全不夠的。

    題外話,如果是想學Python的人那更是完全不需要看這本書。因為這本書不是一本Python的教學書,沒有很深入的介紹Python,建議去看Python專門的書會比較好。

    2011年12月13日 星期二

    [Vim]清除搜尋完後的Highlight

    在使用Vim搜尋功能的時候,會把找到的關鍵字自動Highlight起來
    這些反白一直到你離開Vim再進來都還會存在
    我剛開始用Vim時候也不知道怎麼把反白清掉
    於是就用很笨的方法,去搜尋一個不會存在的字串
    比如說:
    /asdfghj
    
    最近總算查到消除搜尋Highlight指令

    只要輸入:
    :noh
    
    就可以消除Highlight

    如果要完全關掉搜尋自動Highlight功能
    可以在~/.vimrc中加入以下指令
    set nohlsearch
    

    2011年11月30日 星期三

    Linux和Windows的換行字元

    Linux和Windows所使用的換行字元是不一樣的
    在Windows底下是使用\r\n (0x0D 0x0A)
    在Linux底下是使用\n (0x0A)

    因此如果你把Windows上建立的文件搬到Linux上用editor看的話
    會在每一行的最後面看到一個 '^M' 的字元
    這是因為Linux的換行字元只有\n,因此會把\r給印出來

    這點可以做個簡單的實驗來觀察
    sway:~/tmp$ ls
    hello_linux  hello_windows.txt
    
    hello_linux是在Linux產生出來的檔案
    hello_windows.txt是在Windows產生出來的檔案
    之後我們用od把ascii dump出來看
    sway:~/tmp$ od -c hello_windows.txt
    0000000   h   e   l   l   o  \r  \n
    0000007
    
    sway:~/tmp$ od -c hello_linux
    0000000   h   e   l   l   o  \n
    0000006
    
    可以發現Linux下是使用\n當換行字元
    而Windows是使用\r\n

    這些多出來的'^M'有時後會造成程式的錯誤
    當我們把檔案從Windows搬到Linux底下有沒有什麽解決方法呢?
    有一些方法,解決方法如下:

    1.
    dos2unix [-kn] filename [new filename]
    sway:~/tmp$ dos2unix -k -n hello_windows.txt new_hello_windows
    
    2. 在Vim下輸入指令,用Regular Expression把\r取代掉
     :%s/\r//g  
    
    3. 用tr把\r給清掉
    cat filename | tr -d '\r' > new filename
    sway:~/tmp$ cat hello_windows.txt | tr -d '\r' > new_hello_windows
    

    2011年11月28日 星期一

    學徒模式:優秀軟體開發者的養成之路

    一開始我以為這本書是在講軟體工程的書,不過實際看過才知道跟我想的完全不一樣。這本書的重點不是那些技術性的內容,而是在講解如何成為一個優秀軟體開發者。這本書教了一些態度以及一些方法。

    本書主要圍繞幾個重要的觀念。第一就是要熱愛你的工作,一個偉大的工匠必定十分熱愛自己的工作,而不是只是為了錢在工作。本書也教了一些方法幫助你找回你當初學程式的熱情。再來你要找到一些志同道合的朋友以及導師和他們討論學習,如果你在一個周圍每個成員都比你厲害的團隊,相信你一定會成長的很快。這部份書中也舉了一些建議,比如說多參加研討會或是參加社群的活動。除了持續學習以外,本書也提到保持謙虛的重要性。當你學得越多越會覺得這條路是永無止境的,只有水還沒裝滿的杯子才有辦法繼續裝更多的水。

    這本書很適合有一些經驗的程式設計師看,也很適合剛學程式的新手。總而言之,保持對軟體開發的熱情非常的重要,一個沒有熱情的程式設計師絕對沒有辦法成為一位軟體大師。

    2011年11月22日 星期二

    C語言之修煉與實踐


    這本書是我看過C語言入門書籍中,少數有講解到一些比較進階議題的書。作者一開始就先介紹了GDB和Makefile,在介紹function call時順便介紹了memory layout,在講static和extern的時候介紹了binding等一些compiler的issue。因此我覺得這本書不但適合新手看,也很適合有寫過一些程式但是還不太熟的人看。如果是新手的話,這本書可以幫助你稍微瞭解一些底層的觀念。比如說call by value,這本書就有簡介了一下stack frame會怎麼長,variable會放在什麽地方。這些部份是我在傳統的C的教科書中比較少見的。至於有些經驗的程式設計師則可以再複習一次這些觀念,書中有些範例我個人覺得還蠻有用的。

    我一直覺得學程式很多盲點就在於這些compiler幫你做的事情,因為這些東西你看不到,感覺起來就會很抽象。大多數的書本也只要你死背而沒跟你說為什麽。但是這些東西我覺得對於一位程式設計師來說是很重要的,這本書做了一些簡單的介紹,有興趣的讀者看完這本書後可以再看一些更進階的書。

    一個整數中有多少個bits被set成1(How Many Bits Set)

    這個問題有一個很好記的解法
    此方法是在C的聖經 The C Programming Language 的一段範例程式
    #include <iostream>
    using namespace std;
    
    int main() {
        int val;
        unsigned int count = 0;
    
        cin >> val;
    
        while (val) {
            val &= (val - 1);  // clear the least significant bit set
            count++;
        }
    
        cout << count << " bits set" << endl;
    
        return 0;
    }
    
    val &= (val - 1)這行code每做一次就會把最右邊的bit清成0
    因此用這個簡單好記的方法就可以算出有多少bit被set
    當然,這個方法並不是最快的方法
    worst case會出現在所有bit都被set的情況
    這個網站提供了很多神奇的演算法
    有興趣的人可以上去看看

    2011年11月17日 星期四

    [MFC]從螢幕擷取畫面並存成bmp檔的範例程式

    這段MFC範例程式會從目前視窗擷取800*600的圖片,並存成.bmp檔
    檔名為目前時間,所以需要#include <ctime>
    存好的檔案會放在C:\
     
     CDC* pDC = GetDC();
    
     //create a memory dc
     CDC memDC;
     memDC.CreateCompatibleDC(pDC);
    
     //Create a memory bitmap
     CBitmap newbmp;
     newbmp.CreateCompatibleBitmap(pDC, 800, 600);
    
     //select the bitmap in the memory dc
     CBitmap *pOldBitmap = memDC.SelectObject(&newbmp);
    
     //blit from screen into memory dc
     memDC.BitBlt(0,0,800,600,pDC,0,0,SRCCOPY);
    
     //select old bitmap back into the memory dc
     memDC.SelectObject(pOldBitmap);
    
     //release the Display DC
     ReleaseDC(pDC);
    
     time_t timestamp;
     timestamp = time (NULL);
     CString loc;
     loc.Format(_T("C:\\%ld.bmp"), timestamp);
     LPCTSTR img_name = (LPCTSTR)loc;
     CImage img;
     img.Attach((HBITMAP)newbmp.Detach());
     img.Save(img_name,Gdiplus::ImageFormatBMP);
    

    2011年11月11日 星期五

    Designing Embedded Hardware(設計嵌入式硬體)


    這是一本給嵌入式初學者看的書,不過這本書和市面上大部分嵌入式的書籍都不太一樣,對大多數資工背景的學生來說,這是一本很硬的書。作者花了很多時間在介紹硬體相關的議題。前幾章在介紹簡單的電子電路學、焊接工具、示波器,中間幾張花了一些時間在介紹嵌入式常見的協定(SPI, I2C, USB, 紅外線, RS232),最後是介紹幾個常見的處理器架構。

    和一般書籍不同的地方,這本書每一章幾乎都會舉一個實際的例子,示範怎麼真的把線路圖兜起來,並講解一些電路是如何實做的。因此如果你之前沒有修過微處理機相關課程的話,這本書對你來說會很難看懂他。

    這本書我一共看了兩遍,第一遍的時候幾乎都看不太懂。因為我當時完全沒有什麽嵌入式的底子,只是一個會寫軟體的程式設計師。之後實際接觸嵌入式系統一兩年後,我回來再看了一遍這本書。有點底子之後才發現這本書寫得真的不錯,書中舉的一些例子也都是實際上會用到的,尤其是最後幾章介紹了許多常用微處理器的架構(這是2005年的書,不過大多數微處理器到現在2011年都還可以在市面上看得到),非常值得一看,其中甚至有講解Address Decoder和MMU電路的實做。

    因此我的建議是,如果你是軟體背景新手的話可以先大概看看就好。幾個重要的協定先看過有概念就好,因為SPI, I2C這些協定沒有實際實做過,只看書是不可能會得(而且書上也只有講最簡單的概念,詳細相關協定還是要去看spec比較好懂)。電路圖如果都看不懂可以先跳過沒關係。等到你真的有實做經驗後再回來看,相信收穫一定會不少。

    2011年11月9日 星期三

    [Python]畫圖表的好工具 - Matplotlib


    碩班要寫論文時常常會需要畫一堆圖表,由於我不太會用Excel於是就選擇Python + Matplotlib來當做我畫圖表的工具。

    http://matplotlib.sourceforge.net/
    Matplotlib是Python一套功能很強大的畫圖表Library

    http://matplotlib.sourceforge.net/gallery.html
    上面連結是畫出來的一些範例,基本上所有圖表都可以畫得出來。就算你要自創一個圖表也很簡單,只要自己加進去就好了,因為是他是open source。


    • 以下提供一些簡單的範例,有興趣的讀者可以去查官網上的文件。



    #!/usr/bin/env python
    """
    
    Demonstrate how to do two plots on the same axes with different left
    right scales.
    
    
    The trick is to use *2 different axes*.  Turn the axes rectangular
    frame off on the 2nd axes to keep it from obscuring the first.
    Manually set the tick locs and labels as desired.  You can use
    separate matplotlib.ticker formatters and locators as desired since
    the two axes are independent.
    
    This is achieved in the following example by calling the Axes.twinx()
    method, which performs this work. See the source of twinx() in
    axes.py for an example of how to do it for different x scales. (Hint:
    use the xaxis instance and call tick_bottom and tick_top in place of
    tick_left and tick_right.)
    
    The twinx and twiny methods are also exposed as pyplot functions.
    
    """
    
    import numpy as np
    import matplotlib.pyplot as plt
    
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    t = np.arange(0.01, 10.0, 0.01)
    s1 = np.exp(t)
    ax1.plot(t, s1, 'b-')
    ax1.set_xlabel('time (s)')
    # Make the y-axis label and tick labels match the line color.
    ax1.set_ylabel('exp', color='b')
    for tl in ax1.get_yticklabels():
        tl.set_color('b')
    
    
    ax2 = ax1.twinx()
    s2 = np.sin(2*np.pi*t)
    ax2.plot(t, s2, 'r.')
    ax2.set_ylabel('sin', color='r')
    for tl in ax2.get_yticklabels():
        tl.set_color('r')
    plt.show()
    

    • 底下是一個長條圖的範例



    #!/usr/bin/env python  
    import numpy.numarray as na
    from pylab import *
    
    #draw plot
    labels = ["sample1", "sample2", "sample3"]
    size =   [659, 2659, 32384]
    
    x1 = na.array(range(3))*0.9+0.5
    x2 = na.array(range(3))*0.9+1.0
    x3 = na.array(range(3))*0.9+2.0
    
    #draw bar
    width = 0.2
    b1 = bar(x1, size, width=width, color = 'c')
    
    #draw tick
    yticks(range(0,33000,5000))
    xticks(x1+0.1, labels)
    
    #label Y axis
    ylabel('Y axis', fontsize=12, fontweight='bold')
    
    xlim(0, x3[1])
    ylim(0,33000)
    title("This is an example to draw the bar chart",fontsize=12, fontweight='bold')
    gca().get_xaxis().tick_bottom()
    gca().get_yaxis().tick_left()
    
    savefig('bar_chart.png')
    savefig('bar_chart.pdf')
    show()
    

    • 複雜一點的圖形也可以輕易畫出來,以下是一個簡單的範例



    #!/usr/bin/env python  
    from pylab import *
    
    tmp = 1048576 * 0.0053
    xary = [i for i in range(8,512,8)]
    yary = [((tmp + 1048576 / i * 0.45)/1000*88) for i in range(8,512,8)]
    
    x2ary = [i for i in range(8,512,8)]
    y2ary = [((1048576 / i * 0.0875)+(1048576 * 0.0003223)) for i in range(8,512,8)]
    figure(num=None, figsize=(8, 5), dpi=80, facecolor='w', edgecolor='k')
    ax1 = subplot(111)
    p1 = ax1.plot(xary,yary,'ro')
    ax1.set_xlabel('Title1',fontsize=16, fontweight='bold')
    ax1.set_ylabel('ylabel1',fontsize=16, fontweight='bold', color='r')
    ax2 = twinx()
    p2 = ax2.plot(x2ary, y2ary, 'bv')
    ax2.set_ylabel('ylabel2',fontsize=16, fontweight='bold', color='g')
    title('Title2',fontsize=16, fontweight='bold')
    lines = [p1, p2]
    legend(lines, ("Test1", "Test2"), shadow=True, numpoints=1)
    grid(True)
    show()
    

    約耳趣談軟體:來自專案管理的現場實錄



    這本書已經是十年前的書,但絕對是一本值得一看再看的好書。約耳用很幽默的方式來告訴大家如何正確的開發軟體,內容不但有軟體開發還包涵了人才招募以及一些對微軟的看法。約耳本身就是一位開發經驗豐富的程式設計師,加上幽默的文筆。如果你是有點開發專案經驗的程式設計師,絕對會讓你看了就停不下來。其中作者對測試人員和人才招募的部份最讓我有感而發。因為這兩點是台灣大多數公司最不重視的幾個項目。 約耳提出了一些他的看法絕對會讓你不由自主的贊同他。

    約耳寫得內容其實都有公佈在網路上,台灣也有人在翻譯
    有興趣的朋友可以連到網站上去看
    這邊我推薦幾個我覺得非常值得看得幾篇文章:

    爪哇學校的危害 The Perils of JavaSchools

    軟體人員面試教戰守則 - The Guerilla Guide to Interviewing

    不用測試人員的五大(錯誤)藉口 - Top Five (Wrong) Reasons You Don't Have Testers

    其他文章當然也很棒,總之推薦每個程式設計師都去看看這本書!

    2011年11月8日 星期二

    [Python]修改Excel中row背景顏色的範例程式


    這個範例會建立一個Excel檔案,並把第一行的背景顏色改成黃色
    此範例需安裝pywin32
    ColorIndex可參考下圖,Sample code設定為36也就是黃色


    #!/usr/bin/env python
    
    from win32com.client import Dispatch
    
    xlApp = Dispatch("Excel.Application") 
    xlApp.Visible = 1
    
    # Check if any workbook exists. 
    if xlApp.Workbooks.Count == 0:
        # If not, create a new one.
        workbook = xlApp.Workbooks.Add()
    else:
        # If yes, use the first one.
        workbook = xlApp.Workbooks[0]
    
    # Check if any sheet exists.
    if workbook.Sheets.Count == 0:
        # If not, add a sheet to current workbook.
        sheet = workbook.Sheets.Add()
    else:
        # If yes, use the first sheet of current workbook.
        sheet = workbook.Sheets[0]
        
    # Generate the multiplication table(1x9). 
    for i in xrange(1,10):
            sheet.Cells(1, i).Value = i
            # Set the font color
            sheet.Cells(1, i).Font.Color = 0xFF0000
            
    # Set the background color of row1        
    sheet.Rows(1).Interior.ColorIndex = 36
    
    sheet.Name = "Table"
    workbook.SaveAs('test.xls')
    xlApp.Quit()
    
    Reference: http://msdn.microsoft.com/en-us/library/cc296089.aspx

    [Python]range() and xrange()

    在Python中,我們在使用迴圈的時候經常會這樣寫

    for i in range(0, 1000):
        print i
    
    for i in xrange(0, 1000):
        print i
    
    到底range()和xrange()有什麽差呢? 其實上面這兩個例子跑出來結果是一模一樣
    最主要差別是range()會產生實際的list,而xrange()則是產生一個number generator
    >>> range(5)
    [0, 1, 2, 3, 4]
    >>> xrange(5)
    xrange(5)
    >>> list(xrange(5))
    [0, 1, 2, 3, 4]
    
    range因為會產生實際list,因此當用在迴圈長度很大的時候,會佔掉很大一塊記憶體

    for x in range(1, 1000000):
    (range會產生一個1~1000000的list)

    for x in xrange(1, 1000000):
    相反地,因為xrange是產生一個1~1000000的number generator並每次回傳一個值給迴圈,在這個例子會比range()來的節省記憶體

    因此建議在大範圍的迴圈盡量使用xrange(),除非你真的要產生一個list

    當然有些例外,range()會比xrange來的好
    比如說,當迴圈每次都固定會執行一個範圍時(不會中途break),使用range()會比xrange()來的有效率,因為不用每次都生成一個number(當然,記憶體一定是range使用的比較多)
    另外當你真的需要list的時候,就只能使用range()


    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