顯示具有 筆記 標籤的文章。 顯示所有文章
顯示具有 筆記 標籤的文章。 顯示所有文章

2014年5月3日 星期六

使用 git subtree 切分版本庫

有候我們希望把一個大的 Git Repository 切分成多個 Repositories。這時我們就可以用 git subtree 這個指令。以下我以 Android NDK 為例子:

$ git clone https://android.googlesource.com/platform/ndk
$ cd ndk

分割 sources/cxx-stl/llvm-libc++abi/libcxxabi 下面的 commits:

$ git subtree split -P sources/cxx-stl/llvm-libc++abi/libcxxabi -b libcxxabi

之後只要另外在不同的資料夾執行:

$ cd ..
$ git init libcxxabi
$ cd libcxxabi
$ git pull ../ndk libcxxabi:master

就可以有一個完全獨立的版本庫了!

參考資料

2013年9月6日 星期五

編譯 Android AOSP

先前曾經寫過一篇怎麼下載 Android AOSP 原始碼的文章。不過還沒說怎麼編譯 Android Open Source Project。雖然官方網頁有很詳細的說明,但我自己有遇到一些無法執行的步驟,而且有很多可選的步驟不太好讀。所以我就花一點時間把我自己的步驟寫下來。希望可以拋磚引玉。

準備編譯環境


安裝套件


根據官方的說明文件,最適合編譯 Android 的開發環境為 Ubuntu 12.04 (x86_64)。以下文章我們假設大家的環境都是 Ubuntu 12.04。首先在開始之前,我們必須要安裝一些必要的套件:

$ sudo apt-get install git gnupg flex bison gperf build-essential \
    zip curl libc6-dev libncurses5-dev:i386 x11proto-core-dev \
    libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386 \
    libgl1-mesa-dev g++-multilib gcc-multilib mingw32 tofrodos \
    python-markdown libxml2-utils xsltproc zlib1g-dev:i386


另外為了可以讓 Build System 找得到  Mesa3d 函式庫,我們必須為他建立一個 Soft Link:

$ sudo ln -s /usr/lib/i386-linux-gnu/mesa/libGL.so.1 /usr/lib/i386-linux-gnu/libGL.so

如果你在 apt-get install 時,遇到類似下面的錯誤(沒有遇到可以直接跳到 Java 的段落):

E: Unable to locate package mingw32
E: Package 'python-markdown' has no installation candidate

這是因為你的 APT 沒有開啟 Universe 來源(也就是第三方維護的自由軟體)。我們要先把他加進 Ubuntu 軟體來源,然後再更新套件資訊。

$ sudo vim /etc/apt/sources.list
# 找到 "deb http://archive.ubuntu.com/ubuntu precise main" 在後面加上 universe

$ sudo apt-get update

等到 apt-get update 執行完,再從頭開始執行應該就不會有問題了。

安裝 OpenJDK 7 JDK


如果要編譯最新的 Android,你會需要 Java 7。你可以用以下指令安裝 OpenJDK 7:

$ sudo apt-get install openjdk-7-jdk

之後如果我們要編譯 Android 之前,我們必須記得:

$ export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
$ export PATH=$JAVA_HOME/bin:$PATH


安裝 Java SE 6 JDK


如果你是要編譯較舊的 Android,你會需要 Java SE 6 JDK,如果你是要編譯最新的 Android,可以跳過這一節。我們必需到 Oracle 網站取得 Java SE Development Kit 6u45 (Java SE 6 JDK)。因為我們的環境是 Ubuntu,所以我們要下載的是  jdk-6u45-linux-x64.bin。下載之後,就依下述指令安裝:

$ chmod +x ./jdk-6u45-linux-x64.bin
$ ./jdk-6u45-linux-x64.bin
$ mv jdk1.6.0_45 /opt

之後如果我們要編譯 Android 之前,我們必須記得:

$ export JAVA_HOME=/opt/jdk1.6.0_45
$ export PATH=$JAVA_HOME/bin:$PATH


取得 repo 管理工具


因為 Android 是一個很大的專案,由數百個 Git 版本庫所構成。為了管理這些版本庫,Google 開發了 repo 這個 Git 管理工具。透過 repo 我們可以一次下載整個 Android 原始碼樹。我們必須先取得 repo:

$ mkdir ~/bin
$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
$ chmod +x ~/bin/repo
$ export PATH=~/bin:$PATH

取得原始碼與 Prebuilt Binaries


下載 Android 原始碼


和先前那篇一樣,我們現在可以下載 AOSP 的原始碼了!我們先建立一個資料夾,再使用 repo init 取得 Android Source Tree 的設定檔。最後使用 repo sync 把所有 Git 版本庫抓到本地端。

$ mkdir ~/android-src
$ cd ~/android-src
$ repo init -u https://android.googlesource.com/platform/manifest
$ repo sync -j2

下載 Prebuilt Binaries


如果要刷手機,我們還需要一些來自廠商的驅動程式或 Prebuilt Binaries。我們可以到 Binaries for Nexus Devices 找到 Nexus 系列所需的驅動程式。下載之後(以 broadcom-maguro-jdq39-8edbeae8.tgz 為例),我們先把壓縮檔放在 ~/android-src,然後:

$ cd ~/android-src
$ tar zxvf broadcom-maguro-jdq39-8edbeae8.tgz
$ ./extract-broadcom-maguro.sh
# 按下 Enter,再按下 q,輸入 "I ACCEPT" 之後按下 Enter 即可。

編譯 Android 原始碼


取得所有程式碼之後我們就可以開始編譯整個 Android。首先我們必需先設定好環境變數:

$ cd ~/android-src
$ export JAVA_HOME=/opt/jdk1.6.0_45
$ export PATH=$JAVA_HOME/bin:$PATH
$ source build/envsetup.sh

選取你想要編譯的目標。一般來說 full-eng 就可以滿足所需。如果你要刷手機,可能要從 Building for Devices 頁面找到合適的目標。

$ lunch full-eng

接下來我們就可以開始編譯了!

$ make -j16
(...中略...)
Install system fs image: out/target/product/generic/system.img
out/target/product/generic/system.img+ maxsize=588791808 blocksize=2112 total=576716800 reserve=5947392

經過漫長的等待,如果你可以在最後看到類似上面的訊息,那整個 Android 就成功完成編譯工作,我們可以打開模擬器或刷手機了。

開啟模擬器


開啟模擬器的方法非常容易,只要輸入以下指令:

$ ./out/host/linux-x86/bin/emulator

  • 根據我的測試,有些目標例如 full_maguro-eng 沒辦法使用模擬器。如果要使用模擬器要改用 lunch full-eng。
  • 如果你之後要使用模擬器,但是看到:

    emulator: ERROR: You did not specify a virtual device name, and the system directory could not be found.

    那是因為你忘記 lunch full-eng 了。我們必需 lunch full-eng 模擬器才能找到對應的 System Image。

刷手機


在刷手機之前,最好先上網尋找有沒有成功的案例。因為刷機一定會違反保固條款失去保固。其次,如果指令錯誤更有可能讓你的手機變成磚塊。一般來說,Google 推出的 Nexus 系列都沒有什麼大問題。但是因為不同的手機有不同的狀況,本文僅供參考,不為後果負責。

首先我們先看 adb 能不能抓到你的手機:

$ adb devices

  • 如果看不到你的裝置,有可能是 udev 設定檔沒有設好。可以參考 Configuring USB Access,建立 /etc/udev/rules.d/51-android.rules。
  • 如果你有看到裝置,但是在 offline 的狀態。這有可能是因為你沒有開啟手機的開發者選項。你可以拿起手機,進入「Setting/About Phone/Build Number」,連續點 7 下。之後在「Developer Options」頁面勾選「USB Debugging」。

接下來我們要開機進入刷機模式:

$ adb reboot bootloader

這時我們可以看到手機進入 Bootloader,我們可以用 fastboot devices 尋找手機:

$ fastboot devices

如果你還沒有 Unlock 手機,這時我們就可以 Unlock 他。注意:這一步會讓保固失效!

$ fastboot oem unlock

最後,我們來刷手機。刷手機的過程絕對不能拔手機線。

$ fastboot flashall

除了把新的映象檔刷進手機,我們還要清空所有 User Data 與 Cache。否則 Android 有一定的機會一直停留在開機畫面。

$ fastboot erase userdata
$ fastboot erase cache

最後我們就可以重新開機!

$ fastboot reboot

我們可以用 adb logcat 觀察開機過程。有時候會遇到一直開不了機的情況。這時可以先試著清空 User Data 與 Cache。如果還是有問題,有時候是 AOSP 有問題,可以先刷回官方的映象檔。

結語


本文簡單的介紹怎麼編譯 Android Open Source Project 與刷機的基本流程。 希望大家看完之後有能力自己 Hack Android。Happy hacking!

2010年1月4日 星期一

Ubuntu 9.10、Network Manager、與 ADSL

因為 Ubuntu 9.10 光碟片附的 Network manager 好像有問題。所以我在灌他的時後就必需要使用 pppoeconf 來連接 ADSL。更麻煩的是因為 pppoeconf 好像會和 Network manager 衝突,所以 Network manager 就不會動了(只剩下無線網路的功能)。

之前有在網路上看到有人說 Network manager 的問題已經修復了!只要先到「軟體來源」把 ppa:network-manager/trunk 加上去,重新載入,然後 Network manager 更新的 trunk 即可正常地使用 Network manager。不過經過我的實測,我發現還是不行。Network manager 會顯示「無法管理資源」之類的問題。

再經過一番研究,發現原有的 pppoeconf 的設定檔會雖著安裝的過程複製到硬碟上,所以衝突的問題還是存在。要解決這個問題,我們要先修改 /etc/NetworkManager/nm-system-settings.conf。我們要把 managed=false 改成 managed=true。接著再修改 /etc/network/interfaces,把所有的東西刪到只剩下:
auto lo
iface lo inet loopback
之後重新開機即可。


參考資料:
http://blog.yam.com/lijohnson/article/25953686
http://forum.ubuntu.org.cn/viewtopic.php?p=1569643#p1569643

2009年9月23日 星期三

Design Pattern 與 Double Dispatching

為了寫 Design Pattern 作業,所以上網查了一下。不過我覺得以這個作業而言,Double Dispatch 好像也沒有什麼用,如果不偷用 RTTI(不管是用 instanceof、getClass 或自己加上一個回傳型別的 Virtual Function),好像就沒有辦法確認 object 的真正的 class。

--

在談 Double Dispatch 之前我們必需先從 Polymorphism 說起。Polymorphism 有兩種:
  1. Subtype Polymorphism: 前者就是指繼承類別之間的多型。例如:人和狗同時繼承動物類別,二者都會進食,不過二者的吃飯方式不同。我們會在動物宣告一個 Virtual Method,人和狗類別會各自 Override 自己的吃飯方式,利用 Virtual Method Invocation,在執行期喚起正確的函式。 (Method Overriding)
  2. Type Polymorphism (Ad-hoc Polymorphism): 有一個同名的函式,編譯器會依據傳入參數的不同,選擇不同的函式,例如:同樣是 + operator,你把數字加數字與字串加字串就會有不同的結果。(Function Overloading)
值得一提的是 Virtual Method Invocation 的效率略低於一般的函式呼叫,所以 C++ 要求希望被 Virtual Invoke 的函式應該要額外加上 virtual 關鍵字。而 Function Overloading 因為是在編譯期決定呼叫的函式,所以不會影響效率。

我們先看一下什麼是 Type Polymorphism (Function Overloading):

class A;
class B;

class A {
public:
    void interact(A const &a) { cout << "class A vs A" << endl; }
    void interact(B const &b) { cout << "class A vs B" << endl; }
};

class B : public A {
public:
    void interact(A const &a) { cout << "class B vs A" << endl; }
    void interact(B const &b) { cout << "class B vs B" << endl; }
};

int main() {
    A a;
    B b;

    a.interact(a); // Invoke A::interact(A const&)
    a.interact(b); // Invoke A::interact(B const&)
    b.interact(a); // Invoke B::interact(A const&)
    b.interact(b); // Invoke B::interact(B const&)
    return 0;
}

我們可以注意到,透過 Function Overloading,我們可以在不同的時候喚起不同的函式。例如:a.interact(b) 就會喚起 void A::interact(B const &) 這個函式。

如果因為某些原因我必需使用類別 A 的 Reference 來指稱其子類別的 Instance,我把上面的程式改成這樣呢?

int main() {
    A &aa = a;
    A &ba = b;

    a.interact(aa); // Invoke A::interact(A const&)
    a.interact(ba); // Invoke A::interact(A const&)
}

我們就會發現 Type Polymorphism 完全幫不了我們!為什麼?因為類別 A 的 Reference 所指向的物件的 Type 是可以隨 Runtime 的不同而有所變動,所以一般來說編譯器只能假定這個 Reference 只會是類別 A 的實例。從而只能喚起 A::interact(A const&) 函式。那我們該怎麼做呢?

此時我們會想聯想的 Subtype Polymorphism (Method Overriding) 的 Late Binding。首先我們要為上述的函式加上 virtual 關鍵字,使其可以動態地決定要喚起哪個函式。我分別 class A, class B 的 interact 函式的前面都加上了 virtual 關鍵字。(雖然一日 Virtual Method 終身 Virtual Method,所以加在 class A 裡面的 Method 就可以了,不過為了方便閱讀,我是建議在 class B 裡面的 Method 也加上 virtual)

class A {
public:
    virtual void interact(A const &a) { cout << "class A vs A" << endl; }
    virtual void interact(B const &b) { cout << "class A vs B" << endl; }
};

class B : public A {
public:
    virtual void interact(A const &a) { cout << "class B vs A" << endl; }
    virtual void interact(B const &b) { cout << "class B vs B" << endl; }
};

這下問題解決了嗎?我們看看:

int main() {
    aa.interact(aa); // Invoke A::interact(A const&)
    aa.interact(ba); // Invoke A::interact(A const&)
}

很顯然地,雖然 interact 是 Virtual Function,但是因為只有 this 會透過 Late Binding 而被延遲到執行期,編譯器還是會在編譯期施行 Method Overloading Resolution 從眾多 Overloaded Method 當中挑選特定版本生成程式碼,讓我們的程式於執行期呼叫 Virtual Method(進而依據 this 所屬類別進行 Dynamic Dispatch),所以這裡 Subtype Polymorphism 似乎幫不上忙。

不過,事實上 Subtype Polymorphism 是可以幫上忙的!只是我們需要一個小技巧!這就是所謂的 Double Dispatching,我們可以再寫一個函式叫作 apply,它固定傳入一個類別 A 的 Reference,不過我們會在函式的本體上動手腳!

class A {
public:
    virtual void apply(A &obj) { obj.interact(*this); }
};

class B : public A {
public:
    virtual void apply(A &obj) { obj.interact(*this); }
};

int main() {
    aa.apply(aa); // Finally invoke A::interact(A const&)
    ba.apply(aa); // Finally invoke A::interact(B const&)

    aa.apply(ba); // Finally invoke B::interact(A const&)
    ba.apply(ba); // Finally invoke B::interact(B const&)
}

對,這裡我用了 *this,*this 的型別是什麼呢?對!就是擁有 apply 函式的類別。注意:我們必需要在每個類別加上相同的 apply 函式。因為每個 apply 函式中的 *this 型別是不一樣的。當我們用 object1.apply(object2) 呼叫時,apply 會幫我們找出 object1 的確切類別,再使用 object2.interact((THISCLASS &)object1) 來呼叫 interact 函式,因為 object1 的型別已經確定,所以第二次呼叫 interact 就是找出 object2 的確切類別,喚起正確的函式。

這大概是 Double Dispatch 的內容。

--

不過壞消息是:這個作業中我們不能碰原有的 class 也就不能為原有的 class 加上 virtual method,所以這樣寫似乎沒有什麼用。

2009年9月16日 星期三

C++ 與 instanceof

C++ 本身是沒有提供 instanceof 關鍵字,不過我們可以利用簡單地實作出該語意:

template <class Class, typename T>
inline bool
instanceof(T const &object)
{
    return dynamic_cast<class const *>(&object);
}

不過我發現如果要用 dynamic_cast,我們的 class 至少要有一個 virtual function,因為 C++ 的 RTTI 實作是依賴 vtable 的。

以下是整個範例程式:

#include <iostream>
#include <cstdlib>

using namespace std;



template <class Class, typename T>
inline bool
instanceof(T const &object)
{
    return dynamic_cast<Class const *>(&object);
}



class A
{
public:
    virtual ~A() {}
};

class B : public A { };
class C : public A { };
class D : public B, public C { };

class E
{
public:
    virtual ~E() {}
};



int
main()
{
    A a;
    B b;
    C c;
    D d;
    E e;

    A& aa = a;
    A& ab = b;
    A& ac = c;
    A& ad = *static_cast<B *>(&d);

#define CHECK(CLASS, REF) \
    do \
    { \
        if (instanceof<CLASS>(REF)) \
        { \
            cout << #REF " is an instance of " #CLASS << endl; \
        } \
        else \
        { \
            cout << #REF " is NOT an instance of " #CLASS << endl; \
        } \
    } \
    while (0);

    CHECK(A, aa);
    CHECK(A, ab);
    CHECK(A, ac);
    CHECK(A, ad);
    CHECK(A, e);

    CHECK(B, aa);
    CHECK(B, ab);
    CHECK(B, ac);
    CHECK(B, ad);
    CHECK(B, e);

    CHECK(C, aa);
    CHECK(C, ab);
    CHECK(C, ac);
    CHECK(C, ad);
    CHECK(C, e);

    CHECK(D, aa);
    CHECK(D, ab);
    CHECK(D, ac);
    CHECK(D, ad);
    CHECK(D, e);


    return EXIT_SUCCESS;
}

2009年9月9日 星期三

類別的抽象意涵

到底基於什麼樣的誤會會讓人讓 Point3d 繼承 Point2d?有人會說「空間的點」是一種「平面的點」嗎?

class Point2d {
private:
    int x;
    int y;
public:
    Point2d(): x(0), y(0) {}
    Point2d(int x_, int y_) : x(x_), y(y_) {}

    int get_x() const { return x; }
    int get_y() const { return y; }

    void set_x(int x_) { x = x_; }
    void set_y(int y_) { y = y_; }
};

class Point3d : public Point2d {
private:
    int z;
public:
    Point3d(): Point2d(0, 0), z(0) {}
    Point3d(int x_, int y_, int z_): Point2d(x_, y_), z(z_) {}

    int get_z() const { return z; }
    void set_z(int z_) { z = z_; }
};

上面的程式碼看似可以重復使用 Point2d 的部分,基於「儘可能重複使用原則」,這樣寫好像沒有錯。可是如果今天我有一個函式長這樣呢?

int dist(Point2d const &a, Point2d const &b) {
    return sqrt(pow((double)(a.get_x() - b.get_x()), 2) +
                pow((double)(a.get_y() - b.get_y()), 2));
}

然後假設經過很久的時間,你早已忘記你的 Point3d 還有 dist 是怎麼寫得。有一天我們忽然必需要計算空間中的二點的距離,於是你很自然地寫下下面的程式碼:

Point3d pa(0, 0, 0);
Point3d pb(0, 0, 10);

double d1 = dist(pa, pb); // d1 = 0

這時你會得到一個讓你意外的結果!為什麼是 0?沒有任何的機制阻止你犯下錯誤。當然,當你發現數值不對的時候,也許會認為在多補一個 Point3d 的 dist 函式就可以了(事實上你也應該這麼做)。但是事情有這麼簡單嗎?

Point2d pc(0, 0);
Point3d pd(0, 0, 10);

double d2 = dist(pc, pd);

上面的程式出了什麼問題?對,沒有錯,dist 又把 Point3d 的 instance 當成 Point2d 的 instance。然後就是你的程式怎麼死得都不知道!

這組 class 的設計有個根本的問題就是隨便使用繼承的語意,看到「重複的程式碼」就開槍!結果反而寫出意想不到的程式碼。根本的解決方式就是抄一次 int x, y ... 等程式碼,而且多抄一次錯誤的機會反而會更少。

當然我知道有人會反駁說我應該使用 private inheritance。對,private inheritance 的確有 has-a 的語意,可是你認為這樣的程式碼比較直觀嗎?

class Point3d : private Point2d {
private:
    int z;
public:
    Point3d(): Point2d(0, 0), z(0) {}
    Point3d(int x_, int y_, int z_): Point2d(x_, y_), z(z_) {}

    using Point2d::get_x;
    using Point2d::get_y;
    int get_z() const { return z; }

    using Point2d::set_x;
    using Point2d::set_y;
    void set_z(int z_) { z = z_; }
};

不要自欺欺人了,要是「重複使用」這麼重要,你也應該使用 composition 與 delegation 來完成 has-a 的語意。
class Point3d {
private:
    Point2d xy;
    int z;
public:
    Point3d(): xy(0, 0), z(0) {}
    Point3d(int x_, int y_, int z_): xy(x_, y_), z(z_) {}

    int get_x() const { return xy.get_x(); }
    int get_y() const { return xy.get_y(); }
    int get_z() const { return z; }

    void set_x(int x_) { xy.set_x(x_); }
    void set_y(int y_) { xy.set_y(y_); }
    void set_z(int z_) { z = z_; }
};

我今天看書的時候還看到更扯的例子:橢圓是圓形的子類別,因為橢圓要二個參數來描述,圓只需要一個,同理矩形是正方形的子類別。還有,因為向量表示法很相似,所以三角形是一種矩型。照這種亂七八糟繼承法推演下去,所有的形狀都可以是正方形的一種(is-a-kind-of),你看正方形所需的參數最少嘛!?

這樣為了重複使用而重複使用對嗎?明明是毫不相干的類別我們應該讓他們互相繼承嗎?原本是超集的集合現在要變成別人的子集對嗎?敝人不才,我不懂這些聰明絕頂的人在想什麼。

2009年9月1日 星期二

xrandr 與筆電的 VGA 輸出

之前在 R219 做 C++ 演講的時候,發現 Ubuntu 沒有辦法使用 VGA 輸出,臨時改用 Windows Vista 結果我覺得用起來很不順,而且有些範例沒有辦法展示,覺得相當捥惜。今天我用桌機的螢幕測試看看,結果發現 Ubuntu 是可以自動偵測螢幕,雖然結果不是很令人滿意(自動選用的解析度對二個螢幕而言都不是最佳解析度),但也還算是堪用。

那之前的演講是怎麼一回事呢?我在想有可能是因為投影機的解析度和我的筆電的解析度八字不合,所以 Ubuntu 沒有辦法自動選出最合適的解析度組合,所以當天就沒有辦法正常使用投影機。

早期要更改解析度,一定要修改 xorg.conf 然後重新開啟 X server。不過現在我們可以用 xrandr 來重新設定解析度,而且可以像 Windows 那樣立刻生效,甚至還可以做一些特別的設定。

首先我們要下面的指令來觀察目前的設定:
xrandr --current

在我的電腦會看到有 VGA 與 LVDS 二種輸出方式,前者是 VGA 輸出端子,後者是筆電本身的螢幕;同時 xrandr 也會顯示每種輸出方式可以使用的解析度與更新頻率。

如果我想要調整 LED panel (筆電內建) 的解析度我們可以使用 --mode 來設定:
xrandr --output LVDS --mode 1280x800

當然如果我要調整 VGA output 的解析度我們可以用下面的指令:
xrandr --output VGA --mode 1024x768

如果我們要關閉一種輸出,我們可以用 --off 來關閉。off 很重要,因為二種輸出有時候會互相干擾,我們可以先關閉一個,調整好再開啟不同的輸出。

接下來,我們可以讓不同的螢幕有不同的解析度。之所以會有這樣的需求是因為 LCD 螢幕的解析度是不能動態調整的,所以對於「非出廠內定值」通常只是把輸入訊號用內差法放大,效果都不甚理想,所以我希望可以讓筆電的螢幕是「出廠內定值」。

我的做法是:
xrandr --output LVDS --mode 1280x800 --output VGA --off

先把 LCD panel 的解析度調整好,再開啟 VGA output 的解析度:

xrandr --output VGA --mode 1024x768

此時,我們還可以稍做修改,例如我不想要顯示 GNOME 上層的選單,我就可以用 --pos 來移動我的 VGA output 的顯示區:

xrandr --output VGA --mode 1024x768 --pos 0x25

我想在一般的演講,這些指令就很夠用了。不過我在研究 xrandr 的時候發現了一個有趣的參數:panning。我們可以用 panning 模擬比較大的螢幕。有人可能會很好奇它是如何「模擬」的?事實上使用了 panning 就有點像顯微鏡,我們的「可視區」還是只有螢幕的大小,隨著滑鼠的移動,「可視區」的範圍也會隨之移動。也就是說如果我剛才修改一下 VGA 的設定,VGA output 就可以隨著滑鼠的移動看到不同的部分。

xrandr --output VGA --mode 1024x768 --panning 1280x800

當然 xrandr 的功能不止如此,他還可以把二個螢幕串起來,一左一右,不過我就懶得試了,因為還要修改 xconf 的 Virtual 值以加大 Virtual Screen 的大小。

2009年8月29日 星期六

dm-crypt 與加密磁區容器

因為之前我新買不到一年的硬碟毫無預警地(HDD SMART 都還在良好)就壞掉了,所以我現在對所有的硬碟商都抱持不信任的態度,硬碟只能當作耗材。所以我昨天我又去買了一個隨身硬碟用來備分我的重要資料。不過隨身硬碟有一個問題就是為了要讓 Windows 也能讀取,所以我必需要讓隨身硬碟本身的檔案系統是 NTFS 或 FAT32,可是我又希望我在 Linux 之下的檔案屬性、權限不要亂動,所以我只好使用一個折衷的方案:建立一個容器,再把這一個容器當成 device,mount 到我的系統樹上面。在 Linux 之下,這很容易。

建立步驟:
  1. dd if=/dev/zero of=TARGET bs=1G count=40
  2. sudo mkfs.ext3 TARGET

使用步驟:
  1. sudo mkdir /media/DISKNAME
  2. sudo mount TARGET /media/DISKNAME -o loop
  3. ... (做你想做得事)
  4. sudo umount /media/DISKNAME
  5. sudo rmdir /media/DISKNAME


不過我後來又想到我想要把資料加密,因為我不想要我的資料被偷看。印象中有一個叫 dm-crypt 的 Linux 2.6 的模組可以把一個普通檔案映射成一個加密硬碟,只要 umount 之後,沒有你的密碼,誰也不能讀取。

事前準備:
  1. sudo aptitude install dmsetup cryptsetup
這二個套件一定要先安裝起來,dmsetup 是 device mapper 的工具;cryptsetup 是 dm_crypt 的工具。另外因為我用得 Linux 發行版是 Ubuntu (9.04),Ubuntu 有把 aes 還有 dm_crypt 編譯成模組,如果你的系統沒有把這二個功能編入核心,也沒有編為模組,你可能需要重新編譯你的 Linux Kernel。


掛載系統模組:
  1. sudo modprobe aes
  2. sudo modprobe dm_crypt


建立步驟:
  1. sudo dd if=/dev/zero of=TARGET bs=1G count=40
  2. sudo losetup /dev/loop0 TARGET
    備註: 這個步驟的 /dev/loop0 可以用任何一個 LOOP device 取代。
  3. sudo cryptsetup -y create CRYPTDEVNAME /dev/loop0 -c aes
    備註: 你必須要輸入密碼二次。
  4. sudo mkfs.ext3 /dev/mapper/CRYPTDEVNAME
  5. sudo cryptsetup remove CRYPTDEVNAME
  6. sudo losetup -d /dev/loop0


使用步驟:
  1. sudo losetup /dev/loop0 TARGET
  2. sudo cryptsetup create CRYPTDEVNAME /dev/loop0 -c aes
    備註: 即使你輸入的密碼是錯誤的也不會有任何提示。
  3. sudo mkdir /media/MNTNAME
  4. sudo mount /dev/mapper/CRYPTDEVNAME /media/MNTNAME
    備註: 如果這時有錯誤,不要急著修復檔案系統,如果亂動,資料就一去不復返了。這一個錯誤有可能是因為之前在 cryptsetup 的時候,你輸入的密碼是錯誤的,使用 cryptsetup remove,然後回到 2 重新輸入一次你的密碼。
  5. ... (做你想做得事)
  6. sudo umount /media/MNTNAME
  7. sudo rmdir /media/MNTNAME
  8. sudo cryptsetup remove CRYPTDEVNAME
  9. sudo losetup -d /dev/loop0
我稍微講解一下,losetup 是用來把一個檔案「掛載」成為 LOOP device 的指令,我們一開始要先把檔案「掛載」成一個「裝置」。然後,cryptsetup 是利用 device mapper (裝置對映) 功能來進行加密,所以經過一次對映之後,我們的裝置會出現在 /dev/mapper 之中,我們可以用 sudo dmsetup ls 來觀察。剩下的就是我們要把 /dev/mapper 之中的那一個裝置,mount 到我們的系統樹,也就是 / 的上面,我遵照 Ubuntu 的習慣把它 mount 在 /media 之中的一個子資料夾。

另外,第 8, 9 二步是不能少的,不然有 root 權限的有心人士不用輸入密碼也可以 mount 你的資料。

備註: 用 losetup mount 起來的 device,其根目錄是只有 root 才有寫入權限的,解決方法是在根目錄用 sudo mkdir 建立一個子目錄,然後用 sudo chown 把使用權轉移給其他使用者。


後讀工作(可選):
  1. sudo modprobe -r dm_crypt
  2. sudo modprobe -r aes
這二個步驟是要用來卸載 dm_crypt 還有 aes 這二個系統模組。

2009年8月26日 星期三

C++ concept 與 Haskell type class

今天開始看《泛型程式設計與STL》這一本書,一開始我看到了 concept,他在 STL 之中,只是一個概念,在 C++ 之中沒有真正與之對應的程式語法。concept 代表的是一種概念,例如: 我們說 Iterator(迭代器)是一個 concept,Iterator 可以進行 dereference, increase, 之類的操作,而 pointer 就是一個 Iterator concept 的 model。

而在 C++0x 之中,本來有一個很振奮人心的提案,就叫 concept,就是要用來對映 STL 之中 concept 的語意,並提供編譯器更好的解析工具,使得我們對各種型別可以有更嚴謹的定義。可惜好像不會在 C++0x 推出,而且被 Delay (但還沒有被放棄)。

我在看 C++ concept 的時候,我腦海忽然想到之前在 Haskell 學得type class,我覺得他們的目的還有語意都十分相似,上網搜尋一下,還真得有期刊是在寫 Haskell type class vs C++ concept。

想一想還真有趣!