2010年12月25日 星期六

Linux Modules(7.3)- work queue


Work queue提供一個interface,讓使用者輕易的建立kernel thread並且將work綁在這個kernel thread上面,如下圖[1]所示。

由於work queue是建立一個kernel thread來執行,所以是在process context,不同於tasklet的interrupt context,因此,work queue可以sleep(設定semaphore或者執行block I/O等等)。

Creating Work
透過 DECLARE_WORK(name, void (work_func_t)(struct work_struct *work)); // statically
或者
INIT_WORK(struct work_struct*, void (work_func_t)(struct work_struct *work)); //dynamically
建立work,就是要執行的工作。
有了work還需要將它和work thread結合,您可以透過create_singlethread_workqueue("name")建立一個名為name的single thread(執行work的thread就稱為work thread),或者create_workqueue("name")建立per cpu的thread。接著就是要將work和work thread做關聯了,透過queue_work(work_thread, work)就可以將work送給work thread執行了。

queue_delayed_work(work_thread, delayed_work, delay)為queue_work()的delay版本。
flush_workqueue(work_thread)會wait直到這個work_thread的work都做完。flush_workqueue()並不會取消任何被delay執行的work,如果要取消delayed的work則需要呼叫cancel_delayed_work(delayed_work)將delayed_work自某個work thread中移除。

最後,要將work_thread摧毀要呼叫destroy_workqueue(work_thread)。


event/n
除了自己建立work thread以外,kernel還建立一個公用的work thread稱為event

kernel/workqueue.c
void __init init_workqueues(void)
{
    …
    keventd_wq = create_workqueue("events");
    …
}

您可以透過schedule_work(&work)將,work送給"events"執行,flush_scheduled_work(void)等待"events"中所有的work執行完畢。


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/slab.h>

MODULE_LICENSE("GPL");

static void brook_1_routine(struct work_struct *);
static void brook_2_routine(struct work_struct *);
static void brook_3_routine(struct work_struct *);

static struct work_struct *brook_1_work; // for event
static DECLARE_WORK(brook_2_work, brook_2_routine);
static DECLARE_DELAYED_WORK(brook_3_work, brook_3_routine);
static struct workqueue_struct *brook_workqueue;
static int stop_wq;
module_param(stop_wq, int, S_IRUGO | S_IWUGO);

static int __init init_modules(void)
{
    // for event
    brook_1_work = kzalloc(sizeof(typeof(*brook_1_work)), GFP_KERNEL);
    INIT_WORK(brook_1_work, brook_1_routine);
    schedule_work(brook_1_work);

    // for brook_wq
    brook_workqueue = create_workqueue("brook_wq");
    queue_work(brook_workqueue, &brook_2_work);
    queue_delayed_work(brook_workqueue, &brook_3_work, 0);
    stop_wq = 0;
    return 0;
}

static void __exit exit_modules(void)
{
    cancel_delayed_work(&brook_3_work);
    flush_workqueue(brook_workqueue);
    stop_wq = 1;
    destroy_workqueue(brook_workqueue);
}

static void brook_1_routine(struct work_struct *ws)
{
    printk("%s(): on cpu:%d, pname:%s\n",
            __func__, smp_processor_id(), current->comm);
}

static void brook_2_routine(struct work_struct *ws)
{
    printk("%s(): on cpu:%d, pname:%s\n",
            __func__, smp_processor_id(), current->comm);
    // do something to block/sleep
    // the work in the same workqueue is also deferred.
    msleep(5000);
    if (!stop_wq) {
        queue_work(brook_workqueue, &brook_2_work);
    }
}

static void brook_3_routine(struct work_struct *ws)
{
    printk("%s(): on cpu:%d, pname:%s\n",
            __func__, smp_processor_id(), current->comm);
    queue_delayed_work(brook_workqueue, &brook_3_work, 50);
}

module_init(init_modules);
module_exit(exit_modules);


Kernel Version:2.6.35
參考資料:
  1. http://www.embexperts.com/viewthread.php?tid=12&highlight=work%2Bqueue
  2. Linux Kernel Development 2nd, Novell Press



2010年12月18日 星期六

Inline Assembly


最近在看kernel的code,裡面有些組語的語法,所以就花點時間把它看了一下,基本上,這邊幾乎都是參考Brennan's Guide to Inline Assembly[1]。

GCC使用的asm是AT&T/UNIX的語法而不是Intel的語法,所以有些不同必須先弄清楚。

GCC 的 inline assembly 基本格式是:
 asm ( assembler template
     : output operands     (optional)
     : input operands     (optional)
     : list of clobbered registers     (optional)
     );
沒用的欄位可以空下來。最後一個欄位是用來告訴GCC在asm code中,我們已經用了哪些register。

Register name:
Register的名稱前面必須加上”%”
  • AT&T:%eax
  • Intel:eax
為了讓GCC的asm能跨平台,所以可以用%0、%1...%n代表後面依序出現的register。


Source/Destination ordering:
AT&T的source永遠在左邊而destination永遠在右邊
  • AT&T:movl %eax, %ebx
  • Intel:mov ebx, eax
  • 您可以在Instruction後面會被加上b、w和l,用以區分operand的size,分別代表byte、word和long,在不加的情況下,gcc會自動判斷,但有可能誤判。



Constant value/immediate value format:
Constant value/immediate value前面必須加上”$”
  • AT&T:movl $boo, %eax
  • Intel:mov eax, boo
  • 將boo的address載到eax中,boo必須是static變數。
#include <stdio.h>

int main(int argc, char *argv[])
{
    static int x __asm__ ("x") = 10;
    int y;

    x = atoi(argv[1]);
    __asm__("movl $x, %0"  // 這邊的%0代表後面出現的第一個register,即y。
            : "=r" (y));
    // output operand前一定要有個"="表示這個constraint是write-only,
    //  "="叫constraint modifier。
    // input output operands後一定要跟著相對應的C 變數的參數,
    // 這是給asm的參數。
    printf("x=%p, y=0x%x\n", &x, y);
    return 0;
}

  • AT&T:movl $0xabcd, %eax
  • Intel:mov eax, abcd
  • 將eax設為0xabcd。
#include <stdio.h>

int main(int argc, char *argv[])
{
    int y;

    __asm__("movl $0xabcd, %0"
            : "=r" (y));

    printf("y=0x%x\n", y);
    return 0;
}


Referencing memory:
  • AT&T:immed32(basepointer,indexpointer,indexscale)
  • Intel:[basepointer + indexpointer*indexscale + immed32]
  • 沒用的欄位可以空下來。
Addressing a particular C variable:
  • AT&T:_booga
  • Intel:[_booga]
#include <stdio.h>

int main(int argc, char *argv[])
{
    static int i __asm__ ("i");
    int io;

    i = atoi(argv[1]);

    __asm__("movl i, %0;\n"
            : "=r"(io));
    printf("i=%d, io=%d\n", i, io);

    return 0;
}

Addressing a variable offset by a value in a register:
  • AT&T:_variable(%eax)
  • Intel:[eax + _variable]
#include <stdio.h>

int main(int argc, char *argv[])
{
    struct ic {
        int i;
        char c;
    };

    int i = 0;
    char c = 0;
    static struct ic ic __asm__ ("ic");

    ic.i = 11;
    ic.c = 'b';

    __asm__("movl $ic, %%eax;\n"
            "mov (%%eax), %0;\n"
            "mov 4(%%eax), %1;\n"
            : "=r"(i), "=r"(c)
            : "m"(ic)
            : "%eax");
    printf("i=%d, c=%c\n", i, c);

    return 0;
}

Addressing a value in an array of integers (scaling up by 4):
  • AT&T:_array(,%eax,4)
  • Intel:[eax*4 + array]
#include <stdio.h>

int main(int argc, char *argv[])
{   
    int i; 
    static int ary[2][3] __asm__("ary") = {
        {0, 1, 2},
        {10, 11, 12}
    };

    // i = ary[1][1]
    __asm__("movl $12, %%eax;\n" // cal how many size of one raw
            "movl $4, %%ebx;\n"  // which column
            "movl ary(%%ebx, %%eax, 1), %0;\n" 
            : "=r" (i)
            :
            : "%eax", "%ebx");
    printf("i=%d\n", i);

    return 0;
}


參考資料
  1. http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html
  2. http://www.study-area.org/cyril/opentools/opentools/x969.html



2010年12月12日 星期日

用一張圖來為 Git 快速入門(Git Cheat Sheet)


用一張圖來為 Git 快速入門(Git Cheat Sheet)












參考資料:
網路



2010年12月11日 星期六

Linux Kernel(13)- syscall


System Call在HW和user space提供一層抽象層,主要目的有:
  • 為user space提供硬體抽象層。比如,讀取檔案時,不用管檔案所在的媒體類型與檔案儲存類型。
  • System call能確保系統的安全與穩定。避免user space的無意或惡意的破壞。

除了exception和trap以外,System call是user space進入kernel space的唯一管道。
User space的programming主要是base on API(Application Programming Interface)並非system call,從programmer的觀點來看,關注的是API(如C library)而非system call。


System call的return type為long,主要是要相容64bit,return value通常代表失敗或成功,失敗時,error code當常寫入global variable “errno”。

典型的system call都以sys_開頭,如getpid()的system call為:
asmlinkage long sys_getpid(void)
{
    return current->tgid;
}

在Linux中(x86),將所有的system call存放在一個system call table中,透過system call number來所引(index)要執行的system call,儘管每個platform所implement的system call table和system call number都不同,但是原理都是相同的,首先會將system call number存放在某個特定的CPU register(X86放在eax),並將system call的參數也存放置其他的register(最多5個參數,x86依序為ebx、ecx、edx、esi和edi),接著透過int 0x80進入system call處理程序,透過system call number(eax)在system call table中找到相對應的system call,並且執行該system call,因為參數存放是先就定義好了,所以就可以在registers(x86依序為ebx、ecx、edx、esi和edi)中依序讀出要處理的參數,超過五個參數就會用structure來傳遞,而ioctl這個不定參數的system call是傳遞pointer的方式來存取,ioctl是一個不好的例子,因為定義不是很明確,system call最好是能定義明確。


新增system call “brook()”到kernel 2.6.32的步驟(x86)

  1. 新增一筆system call entry到sys_call_table中arch/x86/kernel/syscall_table_32.s。
  2. 直接在最後面加入”.long sys_brook”

  3. 定義brook的system call number,arch/x86/include/asm/unistd_32.h,並將NR_syscalls做遞增。
  4. #define __NR_brook              337
    #define __NR_syscalls           338
    

  5. 定義system call的原型,include/linux/syscalls.h。
  6. asmlinkage long sys_brook(int n, unsigned long arg);
    

  7. 加入至system call table中,arch/x86/kernel/syscall_table_32.S。
  8. .long sys_brook;
    

  9. 撰寫system call的內容。
  10. 建立一個新的資料夾名為”brook_syscall”,並將這個資料夾加入Makefile的core-y+=中。 brook_syscall/Makefile
    obj-y := brook.o
    

    brook_syscall/brook.c
    #include <linux/kernel.h>
    #include <linux/syscalls.h>
    #include <linux/uaccess.h>
    
    SYSCALL_DEFINE2(brook, int, n, unsigned long, arg)
    {
        int __user *p = (int __user *) arg;
        int i, x, sum = 0, err = 0;
        printk("n=%d, ", n);
        for (i = 0; i < n; i++) {
            err = get_user(x, p + i);
            sum += x;
            if (err) {
                return err;
            }
            printk("[%d]=%d, ", i, x);
        }
    
        return sum;
    }
    

    頂層的Makefile要將brook_syscall加入core-y中
    ifeq ($(KBUILD_EXTMOD),)
    core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ brook_syscall/
    

  11. 撰寫Application測試system call
  12. 這邊我們撰寫一個類似C library這層的功能"brook_app/brook.h"
    #include <linux/unistd.h>
    #include 
    #define __NR_brook 337
    int brook(int n, ...)
    {
        int ret;
        va_list ap;
    
        va_start(ap, n);
        ret = syscall(__NR_brook, n, ap);
        va_end(ap);
        return ret;
    }
    

    application呼叫brook(),再由brook()轉call我們的system call "sys_brook()"
    #include <stdio.h>
    #include "brook.h"
    int main(int argc, char *argv[])
    {
        return printf("%d\n", brook(3, 3, 2, 1));
    }
    


Kernel Version:2.6.32
參考資料:
  • Linux Kernel Development 2nd, Novell Press
  • http://pradeepkumar.org/2010/01/implementing-a-new-system-call-in-kernel-version-2-6-32.html
  • Professional Linux Kernel Architecture, Wiley Publishing


config automatically switches from 32-bit to 64-bit for x86


今天我用我的NB去make config,卻發現config會自動的切成64bit的,如果想要編成32bit,就執行linux32 make menuconfig即可。

參考資料:
http://kerneltrap.org/mailarchive/linux-kernel/2010/6/6/4579953/thread



2010年12月6日 星期一

人生三態(轉貼)


人生有三態,悲觀、樂觀與達觀。
悲觀的人在山腳看世界,看到幽冥小徑;
樂觀的人在山腰看世界,看到柳暗花明;
達觀的人在山頂看世界,看到天廣地清。
悲觀的人說:人生像一杯苦酒,清濁均苦澀。
樂觀的人說:人生像一杯美酒,點滴皆芬芳。
達觀的人說:人生像一杯清泉,冷暖都清涼。
 
悲觀的人看到花謝的悲傷;
樂觀的人看到花開的燦爛;
達觀的人看到花果的希望。
悲觀的人見到人生的生老病死;
樂觀的人見到人生的甘甜喜樂;
達觀的人見到人生的春夏秋冬。
 
悲觀的人嘆人生步步走向死亡;
樂觀的人讚人生步步邁上尖端;
達觀的人悟人生步步回歸自然。
悲觀的人趨向陰暗一角;
樂觀的人迎向光明一面;
達觀的人橫跨陰陽二界。
 
悲觀的人埋怨風向;
樂觀的人等待風向;
達觀的人調整風帆。
 
悲觀的人用加法生活,平添勞苦;
樂觀的人用減法生活,減少憂傷;
達觀的人用除法生活,分享喜樂。



2010年12月5日 星期日

git筆記


先把用過的指令List出來
git init
Create an empty git repository or reinitialize an existing one

git add
Add file contents to the index

git commit
Record changes to the repository

git log
Show commit logs.

git config
Get and set repository or global options.

git branch
List, create, or delete branches.

git checkout
Checkout a branch or paths to the working tree.

git clone
Clone a repository into a new directory.


建立Local Repository並且加入新檔
brook@vista:~/git_test$ git init
Initialized empty Git repository in /home/brook/git_test/.git/
brook@vista:~/git_test$ echo 1 > a.txt
brook@vista:~/git_test$ git add .
brook@vista:~/git_test$ git commit -a -m "init version"
[master (root-commit) 3f4bf46] init version
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 a.txt
brook@vista:~/git_test$ git log --stat
commit 3f4bf46a188e676104bd8bb929a8ba85e85bb536
Author: Brook <rene3210@>
Date:   Sat Dec 4 22:21:16 2010 +0800

    init version

 a.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)


透過ssh複製遠端的Repository
brook@vista:~/git_test2$ git clone ssh://brook@127.0.0.1/home/brook/git_test/ .
Initialized empty Git repository in /home/brook/git_test2/git_test/.git/
brook@127.0.0.1's password: 
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
brook@vista:~/git_test2$ ls
a.txt


建立branch/複製遠端的branch
brook@vista:~/git_test$ git branch 顯示目前的branch
* master
brook@vista:~/git_test$ git branch new_branch建立一個名為new_branch的branch
brook@vista:~/git_test$ git branch 
* master
  new_branch
brook@vista:~/git_test$ git checkout new_branch 切換到new_branch
Switched to branch 'new_branch'
brook@vista:~/git_test$ git branch 
  master
* new_branch

複製遠端的branch
brook@vista:~/git_test2$ git checkout --track origin/new_branch 
Branch new_branch set up to track remote branch new_branch from origin.
Switched to a new branch 'new_branch'
brook@vista:~/git_test2$ git branch 
  master
* new_branch

利用pull(下載)/push(上傳)更新資料
利用pull更新資料
brook@vista:~/test$ mkdir git1
brook@vista:~/test$ mkdir git2
brook@vista:~/test$ cd git1/
brook@vista:~/test/git1$ git init
Initialized empty Git repository in /home/brook/test/git1/.git/
brook@vista:~/test/git1$ echo "01" > 01.txt
brook@vista:~/test/git1$ git add 01.txt
brook@vista:~/test/git1$ git commit -a -m "v1"
[master (root-commit) 6d3302a] v1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 01.txt
brook@vista:~/test/git1$ cd ../git2
brook@vista:~/test/git2$ git clone ../git1 .
Initialized empty Git repository in /home/brook/test/git2/.git/
brook@vista:~/test/git2$ ls
01.txt
brook@vista:~/test/git2$ cd ../git1
brook@vista:~/test/git1$ echo "012" > 01.txt 
brook@vista:~/test/git1$ git commit -a -m "v2"
[master 9824999] v2
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/test/git1$ cd ../git2/
brook@vista:~/test/git2$ git pull
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/brook/test/git2/../git1
   6d3302a..9824999  master     -> origin/master
Updating 6d3302a..9824999
Fast-forward
 01.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/test/git2$ cat 01.txt 
012

利用push(上傳)更新資料
brook@vista:~/test/git2$ echo "0123" > 01.txt 
brook@vista:~/test/git2$ git commit -a -m "v3"
[master 3dd46af] v3
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/test/git2$ git push
Counting objects: 5, done.
Writing objects: 100% (3/3), 231 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsist
ent
remote: error: with what you pushed, and will require 'git reset --hard' to matc
h
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable t
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing int
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in som
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, se
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To /home/brook/test/git2/../git1
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/home/brook/test/git2/../git1'
brook@vista:~/test/git2$ vim ../git1/.git/config 
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[receive]
    denyCurrentBranch = false
brook@vista:~/test/git2$ git push
Counting objects: 5, done.
Writing objects: 100% (3/3), 231 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/brook/test/git2/../git1
   9824999..3dd46af  master -> master
brook@vista:~/test/git2$ cd ../git1
brook@vista:~/test/git1$ git log -1
commit 3dd46af43524c4e81597f392f58899c78faf087b
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:37:39 2010 +0800

    v3

merge預設會將每一個change重作一次
rook@vista:~/git$ git init
Initialized empty Git repository in /home/brook/git/.git/
brook@vista:~/git$ echo "01" > 01.txt
brook@vista:~/git$ git add 01.txt
brook@vista:~/git$ git commit -a -m "v1"
[master (root-commit) 0b7b5ea] v1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 01.txt
brook@vista:~/git$ git branch my_branch
brook@vista:~/git$ git checkout my_branch 
Switched to branch 'my_branch'
brook@vista:~/git$ git branch 
  master
* my_branch
brook@vista:~/git$ echo "012" > 01.txt 
brook@vista:~/git$ git commit -a -m "v2"
[my_branch de5e153] v2
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ echo "0123" > 01.txt 
brook@vista:~/git$ git commit -a -m "v3"
[my_branch a8d702d] v3
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ echo "01234" > 01.txt 
brook@vista:~/git$ git commit -a -m "v4"
[my_branch 8bae340] v4
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ git log
commit 8bae34004943242020b4e1b54726ae1bb77bb991
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:50:43 2010 +0800

    v4

commit a8d702da300bbf643c5b7a7f3be60d7133658b5f
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:50:26 2010 +0800

    v3

commit de5e153e8f3414ede60891c21686811b2cf704a6
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:50:16 2010 +0800

    v2

commit 0b7b5ead8e486ba7dc3f1dc75498f27ebd008805
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:49:11 2010 +0800

    v1
brook@vista:~/git$ git checkout master 
Switched to branch 'master'
brook@vista:~/git$ git merge my_branch 
Updating 0b7b5ea..8bae340
Fast-forward
 01.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ cat 01.txt 
01234
brook@vista:~/git$ git log 
commit 8bae34004943242020b4e1b54726ae1bb77bb991
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:50:43 2010 +0800

    v4

commit a8d702da300bbf643c5b7a7f3be60d7133658b5f
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:50:26 2010 +0800

    v3

commit de5e153e8f3414ede60891c21686811b2cf704a6
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:50:16 2010 +0800

    v2

commit 0b7b5ead8e486ba7dc3f1dc75498f27ebd008805
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:49:11 2010 +0800

    v1

subversion方式的merge(squash)
brook@vista:~/git$ git init
Initialized empty Git repository in /home/brook/git/.git/
brook@vista:~/git$ echo "01" > 01.txt
brook@vista:~/git$ git add .
brook@vista:~/git$ git commit -a -m "v1"
[master (root-commit) fbf643a] v1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 01.txt
brook@vista:~/git$ git branch my_branch
brook@vista:~/git$ git checkout my_branch 
Switched to branch 'my_branch'
brook@vista:~/git$ echo "012" > 01.txt
brook@vista:~/git$ git commit -a -m "v2"
[my_branch 51c6575] v2
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ echo "0123" > 01.txt
brook@vista:~/git$ git commit -a -m "v3"
[my_branch 67873cb] v3
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ git log
commit 67873cb645636a0ab70309cfa65b678ed49eb2b9
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:58:42 2010 +0800

    v3

commit 51c6575ba88dcfb8a3cfd19b2b4d36ae85fd5ac1
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:58:37 2010 +0800

    v2

commit fbf643a72076d43c16a089c2fe330c357bba004e
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:58:12 2010 +0800

    v1
brook@vista:~/git$ git checkout master 
Switched to branch 'master'
brook@vista:~/git$ git merge --squash my_branch 
Updating fbf643a..67873cb
Fast-forward
Squash commit -- not updating HEAD
 01.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ git commit -a -m "merge with squash"
[master eefb013] merge with squash
 1 files changed, 1 insertions(+), 1 deletions(-)
brook@vista:~/git$ git log
commit eefb0132ae0c1eaa40b746bca7b5440c54e63cb2
Author: Brook <rene3210@>
Date:   Wed Dec 15 23:00:49 2010 +0800

    merge with squash

commit fbf643a72076d43c16a089c2fe330c357bba004e
Author: Brook <rene3210@>
Date:   Wed Dec 15 22:58:12 2010 +0800

    v1



2010年11月27日 星期六

Linux Kernel(12.1)- netfilter機制之初探


延續Linux Modules(12)- netfilter我們由nf_register_hooks()來看看netfilter這個framework是如何運作的。

struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly;

int nf_register_hook(struct nf_hook_ops *reg)
{
    struct nf_hook_ops *elem;
    int err;

    err = mutex_lock_interruptible(&nf_hook_mutex);
    if (err < 0)
        return err;
    list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
        if (reg->priority < elem->priority)
            break;
    }
    list_add_rcu(®->list, elem->list.prev);
    mutex_unlock(&nf_hook_mutex);
    return 0;
}

void nf_unregister_hook(struct nf_hook_ops *reg)
{
    mutex_lock(&nf_hook_mutex);
    list_del_rcu(®->list);
    mutex_unlock(&nf_hook_mutex);
    synchronize_net();
}
nf_hook_register_hook()其實就是在將要註冊的callback function依照所屬的protocol family以及hooknum插入struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS],並且會依照priority由小到大,而nf_unregister_hook()就是很簡單的reg由nf_hooks中移除。

接著我們再來看看nf_iterate(),程式碼中以//為註解方式,且為粗體字型就是我的註解。
unsigned int
nf_iterate(struct list_head *head, struct sk_buff *skb,
          unsigned int hook, const struct net_device *indev,
          const struct net_device *outdev, struct list_head **i,
          int (*okfn)(struct sk_buff *), int hook_thresh)
{
    unsigned int verdict;

    /*
     * The caller must not block between calls to this
     * function because of risk of continuing from deleted element.
     */
    list_for_each_continue_rcu(*i, head) {
        struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;

        // 註冊的priority必須小於等於hook_thresh才會被執行
        if (hook_thresh > elem->priority)
            continue;

        /* Optimization: we don't need to hold module
           reference here, since function can't sleep. --RR */
        //丟進註冊的hook function執行
        verdict = elem->hook(hook, skb, indev, outdev, okfn);
        if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
            if (unlikely((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT)) {
                NFDEBUG("Evil return from %p(%u).\n", elem->hook, hook);
                continue;
            }
#endif
            //如果不是NF_ACCEPT而且也不是NF_REPEAT就回傳verdict
            // (NF_DROP/NF_STOLEN/NF_QUEUE)
            if (verdict != NF_REPEAT)
                return verdict;
            //會執行到這邊就是NF_REPEAT啦
            *i = (*i)->prev;
        }
        // 如果verdict是NF_ACCEPT就會繼續往下一個hook function執行
    }
    //如果沒有任何的hook function或者所有的hook function都是NF_ACCEPT
    return NF_ACCEPT;
}

/* Returns 1 if okfn() needs to be executed by the caller,
 * -EPERM for NF_DROP, 0 otherwise. */
int
nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
             struct net_device *indev, struct net_device *outdev,
             int (*okfn)(struct sk_buff *), int hook_thresh)
{
    struct list_head *elem;
    unsigned int verdict;
    int ret = 0;

    /* We may already have this, but read-locks nest anyway */
    rcu_read_lock();

    elem = &nf_hooks[pf][hook];
next_hook:
    // 將nf_hooks[pf][hook]這個linked list丟進nf_iterate()中執行
    verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
                         outdev, &elem, okfn, hook_thresh);
    if (verdict == NF_ACCEPT || verdict == NF_STOP) {
        // 如果是NF_ACCEPT或NF_STOP就回傳1, 到時候NF_HOOK()/NF_HOOK_COND()
        // 等macro就會執行okfn, 前面的註解也有說明
        ret = 1;
    } else if (verdict == NF_DROP) {
        // 如果是NF_DROP就會free resource並且回傳!1, 就是不會呼叫okfn()了
        kfree_skb(skb);
        ret = -EPERM;
    } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
        // 如果是QUEUE就會將他nf_queue()將資訊暫時存起來, 等候處理
        if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
                      verdict >> NF_VERDICT_BITS))
            goto next_hook;
    }
    rcu_read_unlock();
    // 執行到這邊有可能是NF_STOLEN, 但ret = 0, 所以不會執行okfn,
    // NF_STOLEN會改變packet原本要走的路徑
    return ret;
}


#iddef CONFIG_NETFILTER
int
nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
             struct net_device *indev, struct net_device *outdev,
             int (*okfn)(struct sk_buff *), int thresh);

/**
 * nf_hook_thresh - call a netfilter hook
 * 
 * Returns 1 if the hook has allowed the packet to pass.  The function
 * okfn must be invoked by the caller in this case.  Any other return
 * value indicates the packet has been consumed by the hook.
 */
static inline int 
nf_hook_thresh(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
               struct net_device *indev, struct net_device *outdev,
               int (*okfn)(struct sk_buff *), int thresh)
{
#ifndef CONFIG_NETFILTER_DEBUG
    if (list_empty(&nf_hooks[pf][hook]))
        return 1;
#endif
    return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
}

static inline int
nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
        struct net_device *indev, struct net_device *outdev,
        int (*okfn)(struct sk_buff *))
{
    return nf_hook_thresh(pf, hook, skb, indev, outdev, okfn, INT_MIN);
}
                   
/* Activate hook; either okfn or kfree_skb called, unless a hook
   returns NF_STOLEN (in which case, it's up to the hook to deal with
   the consequences).

   Returns -ERRNO if packet dropped.  Zero means queued, stolen or
   accepted.
*/

/* RR:
   > I don't want nf_hook to return anything because people might forget
   > about async and trust the return value to mean "packet was ok".

   AK:
   Just document it clearly, then you can expect some sense from kernel
   coders :)
*/

static inline int
NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sk_buff *skb,
               struct net_device *in, struct net_device *out,
               int (*okfn)(struct sk_buff *), int thresh)
{
    int ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, thresh);
    if (ret == 1)
        ret = okfn(skb);
    return ret;
}

static inline int
NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sk_buff *skb,
             struct net_device *in, struct net_device *out,
             int (*okfn)(struct sk_buff *), bool cond)
{
    int ret;

    if (!cond ||
            (ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, INT_MIN) == 1))
        ret = okfn(skb);
    return ret;
}

static inline int
NF_HOOK(uint8_t pf, unsigned int hook, struct sk_buff *skb,
        struct net_device *in, struct net_device *out,
        int (*okfn)(struct sk_buff *))
{
    return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN);
}

#else /* !CONFIG_NETFILTER */

#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) (okfn)(skb)
static inline int 
nf_hook_thresh(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
               struct net_device *indev, struct net_device *outdev,
               int (*okfn)(struct sk_buff *), int thresh)
{
    return okfn(skb);
}

static inline int 
nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
        struct net_device *indev, struct net_device *outdev,
        int (*okfn)(struct sk_buff *))
{
    return 1;
}
#endif /*CONFIG_NETFILTER*/
如果沒有defined CONFIG_NETFILTER,NF_HOOK()其實就是直接呼叫okfn了。到這邊對於netfilter的運作就有基本的認識了,有機會hack其他關於netfilter的心得再和大家分享。

Kernel version:2.6.36



2010年11月6日 星期六

gnu screen


gnu screen是我非常喜歡的tool之一,在這邊寫個筆記紀錄一下免得離開Linux太久忘了。
更多資訊您可以參考http://www.gnu.org/software/screen/


"screen"提供一個window的管理機制,讓您的terminal可以在多個processes中切換。"screen"的指令預設都以"ctrl + a"為開頭。
ctrl + ? Show key bindings.

Window控制

C-a + c 建立一個新的window。
C-a 0...9 在window0~9之間切換。
C-a C-a 在目前和之前的window之間切換。
C-a A 輸入title到目前的window。
C-a n 切換到下一個window。
C-a p 切換到上一個window。
C-a " 列出所有的window供選擇切換。
C-a w 列出所有的window。

Session Management

C-a d Detach the screen session
C-a x lock screen

Copy

C-a [ copy,當您按下"C-a ["之後,您就會進入"Copy Mode",您可以移動滑鼠到您想複製的起始位置,接著按下空白鍵,然後在移動到您想複製結束位置按下空白鍵,接著切換到另外一個window按下"C-a ]"就可以貼上了。

Regions

C-a S Split the current region into two new ones.
C-a Tab Move the input focus to the next region.
C-a Q Kill all regions but the current one.
C-a X Kill the current region.

Logging

C-a H Begins/ends logging of the current window to the file ‘screenlog.n’ in the window’s default directory, where n is the number of the current window.

others

C-a : 進入command line模式。


我最愛的screenrc檔
hardstatus alwayslastline "%{= wk} %{by} %H %{wk} | %-Lw%{kw}.%{= g}%n%f* %t%{wk}.%{wk}%+Lw%< %= %{kw}.%{= G} Load: %l %{= R} [%m/%d %c] %{-}"
defscrollback 8192

2010年10月30日 星期六

nm-applet 在 ubuntu 10.10 無法顯示


我將ubuntu升級成10.10之後,nm-applet就不能用了,找了好久,終於找到修復的方法了。

1. 修改/etc/dbus-1/system.d/NetworkManager.conf和/etc/dbus-1/system.d/nm-applet.conf將deny,改allow
2. 刪除 /etc/network/interfaces
3. 重新開機


參考資料:
https://bugs.launchpad.net/ubuntu/+source/network-manager-applet/+bug/249404
http://louis3c.blogspot.com/2008/11/ubuntu-810-networkmanagernm-applet.html



2010年10月9日 星期六

GCC (4.4.1)之C Preprocessor part III


Conditionals
CPPP的Conditionals和C中的if很像,不過C的if是在run-time決定是否要被執行,而CPP則是在compiler time就決定code是否被編譯。

Conditionals主要有三個指令#if、#ifdef和#ifndef。

#ifdef MACRO
#endif
當MACRO被定義這個block才會被編譯,而#ifndef剛好相反。


#if expression
#endif
當expression為非0這個block才會被編譯。而#if defined(MACRO)就等同於#ifdef MACRO,#if也有巢狀寫法。
#if X == 1
#elif X >= 10
#else
#endif
個人偏愛"#if"勝於"#ifdef",主要因為"#if"可以取代"#ifdef",而且"#if"還支援數學運算,如+,-*,/還有bitwise operations, shifts, comparisons, and logical operations (&& and ||)等等,不過僅限於整數型態的運算
#if (1<<2) > 5
#warning "(1<<2) > 5"
#else
#warning "!((1<<2) > 5)"
#endif


Diagnostics
CPP有兩個Diagnostics指令#error和#warning,差異在於一旦執行到#error就會終止處理。



Management Data Input/Output


Management Data Input/Output簡稱MDIO,MDIO提供MAC(Media Access Control)如何去存取PHY的標準,制定於802.3裡面。

這張圖我想表達MAC/PHY之間的關係,MAC屬於data link層,PHY屬於Physcial層。


MDIO有兩組訊號線,MDC(Management Data Clock),由圖就可以理解是由STA(Station Management)提供的,而且和TX_CLK/RX_CLK無關。MDIO是一個bidirectional signal,用於PHY和STA之間傳送控制訊號。


frame format

PRE (preamble)STA的每一個指令都會先送出連續32bit的1和PHY取得同步。
ST (start of frame)算是一個pattern,告知PHY這個後面就是MDIO frame了。
OP (operation code)10為read,01為write。
PHYAD (PHY Address)這個MDIO bus上面每個PHY都要有獨一無二的address,讓STA辨識,5個bit,所以最多可有32個PHY在這一組MDIO bus上面。
REGAD (Register Address)5個bit,允許每個PHY可以有32組register。
TA (turnaround)當PHY執行wtite時,TA就是單純的送出10,而當PHY是要read時,STA和PHY會在第一個bit會輸出高阻抗(high-impedance),PHY接著會在第二個bit送出0,後面PHY就會吐出data給STA了,看下面的時序圖就會清楚了


圖檔來源:http://www.tech-regal.com/post/Ethernet-MII.html


參考資料:
  1. IEEE 802.3, http://standards.ieee.org/getieee802/download/802.3-2008_section2.pdf
  2. http://www.tech-regal.com/post/Ethernet-MII.html
  3. http://en.wikipedia.org/wiki/Management_Data_Input/Output



2010年6月19日 星期六

cygwin之home directory


今天因為發現自己的Home directory目錄變了,於是很直覺的修改/etc/passwd,發現還是不行,後來在http://www.cygwin.com/faq/faq.setup.html的#16發現到,原來如果local端的cygwin如果有設定HOME,就會以HOME的目錄當成home directory,所以,我在/etc/passwd怎麼修改都沒用。



洛克菲勒的初識


在張忠謀董事長讚許郭台銘總裁為台灣的洛克斐勒之後,就對"洛克斐勒"這個人非常的好奇,於是在網路上找到了他的一些資料,平凡的出身卻能創造非凡的成就,而且在功成名就之後極力的行善更是令人讚賞,個人對於他自己對自己的一生下的總結有一些感受:
I was early taught to work as well as play, 
My life has been one long, happy holiday;
Full of work and full of play-
I dropped the worry on the way-
And God was good to me everyday.
生活是一種美學,把人生當成是一個假期,全心的工作,全力的體驗人生,一步一步的往自己的目標前進,自然就會達到"勇者無懼,仁者無敵,智者無惑"的境界,說白了,煩惱也沒用,對事情不會有所改變,猜想未來,對未來也不會有所幫助,謹慎的思考方向,勇敢的前進,努力的達成目標,人生自然就會關關難過關關過,而且,人生不是只有單調的"成功",而是多元的成功,事業的成功,家庭的成功,健康的成功,生活的成功等等,我想洛克斐勒都達到了,這是讓我極為崇拜的人物。



2010年6月6日 星期日

(轉貼)巧妙移除PDF文件的浮水印


PDF電子文件為了標示文章的來源出處,有時候會在頁面加入浮水印,但是浮水印若是插在文中很容易造成閱讀上的不便,你可以利用Adobe Acrobat 6的「TouchUp」文字工具,來移除PDF上文字的浮水印。

STEP:
1.在Adobe Acrobat中開啟要移除浮水印的PDF檔,然後在功能表按下【工具】→【進階編輯工具】→【TouchUp文字工具】。

2.接著在浮水印文字上按住滑鼠左鍵拖曳選取,然後按一下滑鼠右鍵,在快速選單選擇【內容】。

3.開啟「TouchUp內容」對話盒後,按下「填充」色塊,然後在色盤上選擇「無色彩」,再按下〔關閉〕按鈕。

4.回到Acrobat視窗後,可以看到每個頁面中的浮水印全部都消失了哦,這時候就可以將它另存新檔囉!

http://blog.xuite.net/wcwu1/vicky/9523182


GCC (4.4.1)之C Preprocessor part II


這一篇主要是CPP manual第三章macro的心得,所以都是在講述macro。

object-like macro是最簡單的macro,就是直接將macro的name取代成code。
如:
#define NUMBER 1, \
               2, \
               3,
int x[] = {NUMBER}
 -> int x[] = { 1, 2 3 };
NUMBER會被展開誠1, 2, 3

當macro被展開之後,會再度的被檢驗是否還有macro需要被展開。
如:
#define TABLESIZE BUFSIZE
#define BUFSIZE 1024
TABLESIZE
 -> BUFSIZE
 -> 1024
TABLESIZE會先被展開成BUFSIZE,接著BUFSIZE再被展成1024。


Function-like macro用起來就像在用function,所以被稱為function-like macro。function-like macro是在macro name後面"緊"接括號。
如:
#define lang_init() c_init()
lang_init()
 -> c_init()

當macro name後面緊接著(),這個macro才會被當成function-like macro,否則會當成一般macro展開。
如:
extern void foo(void);
#define foo() boo()
....
 foo(); // macro
 funcptr = foo; // function

#define lang_init () c_init() // 多了空白在name和()之間
lang_init()
 -> () c_init()() // 結果就會造成錯誤


macro arguments,function macro可以像一般function一帶參數,這些參數會先被展開,接著再展開macro,參數要以","區隔開來(和一般的function一樣)。
如:
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
x = min(a + 28, *p)
 -> x = ((a + 28), (*p) ? (a + 28) : (*p));
y = min(min(a, b), c)
 -> min(((a) < (b) ? (a) : (b)), c)
 -> .... // 產生的結果可能會和你想的不一樣


stringification(中文不知道怎麼稱呼,暫稱他字串化吧),當macro所帶的參數前面加上"#"之後,這個參數就會被展成字串,這就是stringification。
如:
#define WARN_IF(EXP) \
     do { if (EXP) \
             fprintf (stderr, "Warning: " #EXP "\n"); } \
     while (0)

WARN_IF (x == 0);
 -> do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);

目前只有stringification,並沒有辦法轉成character。另外,stringification的優先權會高於argument expand。
如:
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
 -> "foo"
xstr (foo)
 -> xstr (4)
 -> str (4)
 -> "4"


token concatenation或稱token pasting,是利用"##"將兩個token組成一個token,'##'被置於兩個token之間,如:
#define COMMAND(name) {#name, name ## _command}
struct command {
    char *name;
    void (*f)(void);
} cmds[] = {
    COMMAND(quit),
    COMMAND(help),
};
->
 struct command {
     char *name;
     void (*f)(void);
 } cmds[] = {
     {"quit", quit_command},
     {"help", help_command},
 };


variadic其實就是不定參數的function-like macro,__VA_ARGS__代表"..."(即後面所有的參數),如:
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
eprintf("%s(#%d)\n", __FUNCTION__, __LINE__)
 -> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)

cpp也可以使用name取代__VA_ARGS__,用法上只是在"..."前面加上name,"_VA_ARGS__"則用name取代即可,如:
#define eprintf(args...) fprintf(stderr, args)
eprintf("%s(#%d)\n", __FUNCTION__, __LINE__)
 -> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)

來看另外一個例子
#define eprintf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
eprintf("hello\n");
 -> fprintf(stderr, "hello\n", ); // error!!
這裡的問題就是如何將__VA_ARGS__前面的","在沒有參數帶入的時候刪除,答案就是"##"。
#define eprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
eprintf("hello\n");
 -> fprintf(stderr, "hello\n"); // correct!!



2010年6月5日 星期六

gmake之4.8 Special Built-in Target Names - .PRECIOUS


4.8 Special Built-in Target Names - .PRECIOUS
The targets which .PRECIOUS depends on are given the following special treatment: if make is killed or interrupted during the execution of their commands, the target is not deleted. See Section 5.6 [Interrupting or Killing make], page 49. Also, if the target is an intermediate file, it will not be deleted after it is no longer needed, as is normally done. See Section 10.4 [Chains of Implicit Rules], page 107. In this latter respect it overlaps with the .SECONDARY special target. You can also list the target pattern of an implicit rule (such as ‘%.o’) as a prerequisite file of the special target .PRECIOUS to preserve intermediate files created by rules whose target patterns match that file’s name.

簡單的說就是當make在執行某個target的時候,如果make被kill或者被interrupted(ctrl+c),那麼target file就會被刪除,如果加到.PRECIOUS就不會刪除。

brook@ubuntu:~$ more Makefile
#.PRECIOUS: obj/x.o
obj/x.o:
        gcc -c x.c -o obj/x.o
        sleep 5;
brook@ubuntu:~$ make -f Makefile
gcc -c x.c -o obj/x.o
sleep 5;
^Cmake: *** Deleting file `obj/x.o'
make: *** [obj/x.o] Interrupt

brook@ubuntu1:~$ more Makefile
.PRECIOUS: obj/x.o
obj/x.o:
        gcc -c x.c -o obj/x.o
        sleep 5;
brook@ubuntu1:~$ make -f Makefile
gcc -c x.c -o obj/x.o
sleep 5;
^Cmake: *** [obj/x.o] Interrupt

brook@ubuntu1:~/excerise/Make$



2010年5月29日 星期六

TCL Basic Syntax


這一篇是研究所時候寫的,幸好還找的到,所以把它貼出來。

TCL (Tool Command Language),TCL念做"tickle",TCL本身不支援物件導向,但是可以和其他物件導向語言結合。

顯示字串到console用puts

variable

設定變數用set:

set varName? value?

set a 10
puts $a

事實上在Tcl中,沒有字串與數字的區分。也就是說:

        set Lab good

        set Lab “good”

這兩行指令會是一樣的結果。

 

comment

Tcl的注解必須以#開頭,而Tcl以換行(\n)和分號 (;)為命令的結束,所以如果想在命令後面做註解,就必須寫成 ;#。

puts "test only" ;# test only

 

data type

Tcl和shell script一樣並不需要是先宣告任何資料型態,其資料型態會隨其給的值變動。

 

Quotes and Curly braces

雙引號把引號內的文字串成字串,變數還是會被解譯(substitutions)。

set a "Hello World!"

 

"{", "}" curly braces,和雙引號一樣,只是變數不會被解釋。

set a "Hello World!"
puts "$a"
puts {$a}

 

"[", "]"就像shell中的`會把括號內的文字當成指令執行,而執行結果則會取代整個[]。

set x 5
set y 10
set z "$x + $y is [expr $x + $y]"

 

另外backslash "\",會讓substitutions失效,如同其他language一樣,tcl也有提供Backslash Sequence。

String Output
\b   Backspace
\f   clear screen
\n   New Line
\r   Carriage Return
\t   Tab
\v   Vertical Tab

 

Control

if  {expr1} {body1} elseif {expr2} {body2} else {body}

set x 1
 
if {$x == 2} {
       puts "$x is 2"
} else {
       puts "$x is not 2"
}
 
if {$x != 1} {
       puts "$x is != 1"
} else {
       puts "$x is 1"
}

 

switch

Evaluate one of several scripts, depending on a given value

switch ?option? string {body}

% set x 1
1
% switch $x {
1 {puts "match 1"}
2 {puts "match 2"}
default {puts "no match"}
}
;# match 1  --> result

 

% set x 10
10
% switch $x {
1 {puts "match 1"}
2 {puts "match 2"}
default {puts "no match"}
}
# no match ---> result
% switch -regexp $x {
1 {puts "match 1"}
2 {puts "match 2"}
default {puts "no match"}
}
# match 1 ---> result

 

loop

while {test} {body}

set x 0
while {$x < 5} {
     set x [expr $x + 1]
     if {$x > 7} break
     if "$x > 3" continue
     puts "x is $x"
}

 

for {start} {test} {next} {body}

for {set x 1} {$x < 10} {incr x 2} {
     puts "x = $x"
}

 

新增命令 -- procedure

TCL可以使用proc建立一個新的命令,其格式如下。

proc name { args }  {
     body
}

proc會建立一個名為name的命令,並且攜帶參數args,以及程式碼body。如果最後一個參數名稱為args,那就類似C的不定參數"..."。

範例:

proc sum {arg1 arg2} {
    set x [expr {$arg1 + $arg2}];
    return $x
}

proc printall {args} {
    foreach j $args {
        puts "$j"
    }
}

proc x {a {b 2}} { #give default vaule for b
    puts $a
    puts $b
}

 

Associative Array

associative array其實就是hash table,給一個Key(Tcl稱為element)會傳回一個value。其範例如下:

set A(1) "one"
set A(2) "two"
set A(Three) "three"
array set B { 1 "one" 2 "two" 3 "three" }
 
array names A
foreach thing [array names A] {
   puts "$A($thing)"
}

 

array set arrayName list

建立陣列,list 必須以 key value形式組成。

 

array size arrayName

傳回陣列大小。

 

array names arrayName ?mode? ?pattern?

 如果只有array names arrayName將會回傳所有的key,否則您也可以用正規表示式搜尋key是否存在。

 

array unset arrayName ?pattern?

如果沒有指明pattern,會將整個array註銷,否則會比對pattern和key是否相同,如果有會註銷該元素 (key),否則會回傳空字串(empty string)。

 

array exists arrayName

如果陣列存在會回傳1,否則會回傳0。

 

array get arrayName ?pattern?

如果沒有指明pattern會將array整個以key value的形式傳回。如果有指明pattern則會搜尋是否有符合的key,有的話會一樣以key value傳回,否則傳回空字串。

 

array statistics arrayName

會將array的資訊顯示出來(distribution of data within the hash table)。

 

array startsearch arrayName

這是用來初始化搜尋的指令,會回傳search-ID,可用於array nextelementarray donesearch指令。

 

array nextelement arrayName searchId

傳回array的key。如果array被新增或刪除,則Tcl會自動呼叫array donesearch終止目前的搜尋。

 

array donesearch arrayName searchId

終止array的搜尋,並且註銷array的search-ID。

 

array anymore arrayName searchId

 

 

更多的tcl tutorial(tcl8.5)

 


學貴專精


今天看到一篇文章寫的不錯把他轉錄過來。

很久以前,有一個年輕人,立志要成為一個武林高手。於是就到了少林寺拜師,學了一年後,他覺得已經學好了少林武功,便離開了少林,急著要下山找人比武。在山下遇到了一個武當的弟子,打了幾回合後,他輸了。於是心裡面想著,好像是武當派比較厲害,便又到了武當學武。

過了一年,武當武功他又懂了點門道,心想我不會輸了,下山跟峨嵋派打,還是輸了……。就這樣過了好幾年,他覺得他什麼門派的武功都會了,但為什麼還是無法成為武林高手?

於是他對於人生絕望,走到河邊,要跳下去時,被過去教他少林武功的老和尚抓了一把。年輕人心酸的把他這幾年學武所受到的挫折一五一十的告訴老和尚。老和尚抓了抓他的鬍子,問年輕人說:「孩子,你這幾年學了這麼多武功,有沒有一種是你覺得綀起來最順手的呢?」 年輕人說:「有啊,每當我一使出少林棍法的時侯,就覺得特別有力道。
可是我心裡面想像的武林高手,應該是要精通各 門派的武功才能算是高手吧!」老和尚告訴他,既然你對於少林棍法特別有把握,那麼你就應該要忘了其它門派的武功,因為它並不適合你。

年輕人說:「可是我看別人在使他們自已門派的武功時,都好厲害。」只見老和尚笑了笑說:「如果真的有一個高手能將所有門派的武功都發揮到極致,那麼幾百年來,就不會有那麼多個門派立足了。
如果你想成為高手,就要把適合你的武功綀好,而不是天下的武功都想綀。」於是年輕人改變了他的想法,開始專注於綀他所熟悉的少林棍法。直到有一天,他成 為了少林的老和尚……

http://sites.google.com/site/tradersland/turtle-farm/trading-phycology

我的心得是:以前我常常覺得要學會很多,才叫做厲害,後來其實發現我們要學的是態度以及方法,執著的態度和做事情的方法,把一件事情最到最好,自然就會成為這個領域中的佼佼者。說到這個,又讓我想到樂天董事長說過一句話「每天改善1%,一年後強大37倍」。


install ns2


NS2是研究所時候玩的一個Network Simulator,今天忽然心血來潮的回味一下,下載網址

我下載了ns-allinone-2.35-RC4,解開後執行裡面的install即可。安裝過程中,我只有遇到can't find X includes
checking for X11 header files
can't find X includes
otcl-1.14 configuration failed! Exiting ...
Please check http://www.isi.edu/nsnam/ns/ns-problems.html
for common problems and bug fixes.
透過安裝libsx-dev即可解決
sudo apt-get install libsx-dev


安裝成功後應該會出現一些重要的訊息,包含將執行目錄加到您的PATH中:
Please put ns-allinone-2.35/bin:ns-allinone-2.35/tcl8.5.8/unix:ns-allinone-2.35/tk8.5.8/unix
into your PATH environment; so that you'll be able to run itm/tclsh/wish/xgraph.

IMPORTANT NOTICES:

(1) You MUST put ns-allinone-2.35/otcl-1.14, ns-allinone-2.35/lib, 
    into your LD_LIBRARY_PATH environment variable.
    If it complains about X libraries, add path to your X libraries 
    into LD_LIBRARY_PATH.
    If you are using csh, you can set it like:
                setenv LD_LIBRARY_PATH <paths>
    If you are using sh, you can set it like:
                export LD_LIBRARY_PATH=<paths>

(2) You MUST put ns-allinone-2.35/tcl8.5.8/library into your TCL_LIBRARY environmental
    variable. Otherwise ns/nam will complain during startup.

After these steps, you can now run the ns validation suite with
cd ns-2.35; ./validate

For trouble shooting, please first read ns problems page 
http://www.isi.edu/nsnam/ns/ns-problems.html. Also search the ns mailing list archive
for related posts.

接下來就是執行一下ns2內建的一些tcl檔:
brook@ubuntu:~/src/ns2$ PATH=/home/brook/src/ns2/ns-allinone-2.35/bin:/home/brook/src/ns2/ns-allinone-2.35/tcl8.5.8/unix:/home/brook/src/ns2/ns-allinone-2.35/tk8.5.8/unix:$PATH
brook@ubuntu:~/src/ns2$ ns ns-allinone-2.35/ns-2.35/tcl/ex/simple.tcl 
210
0.0037499999999999999
running nam...




2010年5月22日 星期六

Netlink introduction


Netlink被用來當作kernel和user space之間溝通資訊的方式之一,使用標準的socket介面來作為Netlink的API,其address family必須填AF_NETLINK,而socket type為SOCK_RAW或SOCK_DGRAM,protocol則根據不同的netlink group不同而有所不同,如NETLINK_ROUTE或NETLINK_GENERIC等等(詳細資訊可以man 7 netlink)。

socket = socket(AF_NETLINK, SOCK_RAW, netlink_family);

Netlink Socket Address Structure

如同一般的socket,Netlink也需要socket address,其socket address為:
struct sockaddr_nl {
    sa_family_t     nl_family;  /* AF_NETLINK */
    unsigned short  nl_pad;     /* Zero. */
    pid_t           nl_pid;     /* Process ID. */
    __u32           nl_groups;  /* Multicast groups mask. */
};
如果傳送的對象是kernel或者以multicast傳送,則nl_pid設為0。如果是kernel傳送給user-space上面的application時(multicast),就會填入正確的pid,然而nl_pid實際的意義並不是指PID,而只是用於識別一個netlink socket而已,對於application在建立一個netlink socket時,可以將nl_pid設為0,然後bind(),kernel會自動將PID填入。而nl_groups是用multicast,採用bit mask方式,所以每個netlink family有32個multicast group。

Netlink Format

Netlink message包含一個或多個struct nlmsghdr,主要用於辨識後面的payload內容為何。
struct nlmsghdr {
    __u32 nlmsg_len;    /* Length of message including header. */
    __u16 nlmsg_type;   /* Type of message content. */
    __u16 nlmsg_flags;  /* Additional flags. */
    __u32 nlmsg_seq;    /* Sequence number. */
    __u32 nlmsg_pid;    /* PID of the sending process. */
};

所以常見的讀取netlink message形式如下:
  int len;
  char buf[4096];
  struct iovec iov = { buf, sizeof(buf) };
  struct sockaddr_nl sa;
  struct msghdr msg;
  struct nlmsghdr *nh;

  msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
  len = recvmsg(fd, &msg, 0);
  for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, len);
    nh = NLMSG_NEXT (nh, len)) {
    /* The end of multipart message. */
    if (nh->nlmsg_type == NLMSG_DONE)
      return;
    if (nh->nlmsg_type == NLMSG_ERROR)
      /* Do some error handling. */
      ...
    /* Continue with parsing payload. */
    ...
  }


Macro

Netlink也提供一些macro來操作這些netlink datagram,因為kernel和user-space溝通會有align的問題,所以不論是在組packet或者parse packet,都請用這些macro。
#define NLMSG_ALIGNTO 4
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
#define NLMSG_HDRLEN  ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
#define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
      (struct nlmsghdr*)(((char*)(nlh)) + \
                                 NLMSG_ALIGN((nlh)->nlmsg_len)))
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
      (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
      (nlh)->nlmsg_len <= (len))
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
NLMSG_ALIGN()很明顯的就是在做align。
NLMSG_HDRLEN()回傳struct nlmsghdr所占的大小。
NLMSG_LENGTH()回傳len + struct nlmsghdr所占的大小,通常用於填struct nlmsghdr的nlmsg_len用。
NLMSG_SPACE()回傳len + struct nlmsghdr再取align的大小,也就是netlink message的大小。
NLMSG_DATA()回傳nlh往後一個struct nlmsghdr所占的大小,就是取struct nlmsghdr後面跟著的資料。
NLMSG_NEXT()取得下一個struct nlmsghdr,並且將len減去原本的nlmsg_len,就等同在算剩餘大小。
NLMSG_OK()先算剩餘長度是否大於等於一個struct nlmsghdr,再算這個nlmsg_len是否大於等於一個struct nlmsghdr,而且nlmsg_len還要小於剩餘長度(len),如果都符合,就算是一個OK的struct nlmsghdr。

各macro與netlink message之間的關係。

Common API

基於netlink family基本上都是大同小異,所以參考Document/accounting/getdelays.c寫了一些API來用。
static int create_nl_socket(int proto)
{
    int sock;
    struct sockaddr_nl addr;

    if ((sock = socket(AF_NETLINK, SOCK_RAW, proto)) < 0) {
        fprintf(stderr, "open sock failed.(%s)\n", strerror(errno));
        return -1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;

    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        fprintf(stderr, "bind failed.(%s)\n", strerror(errno));
        goto bind_err;
    }

    return sock;

bind_err:
    close(sock);
    return -1;
}


這個章節單純的只對netlink提供一些overview,後面會針對各個netlink family有更詳細的描述。



2010年5月19日 星期三

word不能存檔問題


這兩天電腦的word忽然都不能存檔,即便是另存新檔也不行,最後都只能放棄修改檔案,即便重新安裝過或者更新成2007也都不能存檔,最後就乖乖的google一下,原來是暫存的資料夾不存在,按照以下方式就可以解決了。
資料來源: word 2003不能存檔問題

編輯機碼的(regedit)
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\Cache改成存在的暫存目錄就好了。


2010年4月16日 星期五

Linux Kernel(3.2)- procfs之symlink與mkdir


在procfs底下無法直接使用mkdir/ln等指令建立目錄和建立link,不過有提供兩個API讓user達成這兩件事情。
static struct proc_dir_entry *proc_symlink(const char *src,
  struct proc_dir_entry *parent,const char *dest);

static struct proc_dir_entry *proc_mkdir(const char *name,
 struct proc_dir_entry *parent);
看名字就知道proc_symlink()是用來建立link的,src是檔名(basename),parent是src所在的目錄,dest是要link的對象。
proc_mkdir()就更容易了,要在那個目錄(parent)下建立新的目錄(name)。
下面是範例:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>

MODULE_LICENSE("GPL");

static char *bdir = "brook_dir";
module_param(bdir, charp, 0644);
MODULE_PARM_DESC(dir, "brook's dir");

static char *bfile = "brook_file";
module_param(bfile, charp, 0644);
MODULE_PARM_DESC(bfile, "brook's file");

static struct proc_dir_entry *ent = NULL;

static int __init init_modules(void)
{
    if (!(ent = proc_mkdir(bdir, NULL))) {
        printk("create dir \"%s\" failed\n", bdir);
        return -1;
    }

    if (!proc_symlink(bfile, ent, "../uptime")) {
        printk("create symlink \"%s\" failed\n", bfile);
        return -1;
    }

    return 0;
}

static void __exit exit_modules(void)
{
    remove_proc_entry(bfile, ent);
    if (ent) {
        remove_proc_entry(bdir, NULL);
    }
}

module_init(init_modules);
module_exit(exit_modules);




2010年4月3日 星期六

simple TCP


這是最簡單的tcp的架構,server只有接受(accept)一個client,然後收送資料,接著就結束。
Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h> // inet_xx()
#include <unistd.h> // close()
#include <netinet/in.h>

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)


int main(int argc, char *argv[])
{
    int sd, af, asd; // socket descriptor, address family, accepted sd
    struct sockaddr_in sa, peer; // socket address
    uint16_t port = 0;
    char *ip = NULL, buf[128];
    socklen_t addrlen;
    ssize_t len;

    if (argc > 1) {
        ip = argv[1];
    }
    af = AF_INET;
    if ((sd = socket(af, SOCK_STREAM, 0)) < 0) {
        handle_error("socket");
    }

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = af;
    sa.sin_port = htons(port);
    sa.sin_addr.s_addr = INADDR_ANY;
    if (ip) {
        if (inet_pton(af, ip, &sa.sin_addr) != 1) {
            handle_error("inet_pton");
        }
    }
    if (bind(sd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
        handle_error("bind");
    }
    addrlen = sizeof(sa);
    if (getsockname(sd, (struct sockaddr*)&sa, &addrlen) < 0) {
        handle_error("getsockname");
    }

    printf("bind on %s/%d\n",
            inet_ntop(sa.sin_family, &sa.sin_addr, buf, sizeof(buf)),
            ntohs(sa.sin_port));

    if (listen(sd, 1) < -1) { // waiting to be accepted
        handle_error("listen");
    }

    addrlen = sizeof(peer);
    if ((asd = accept(sd, (struct sockaddr*) &peer, &addrlen)) < 0) {
        handle_error("accept");
    }

    if ((len = recv(asd, buf, sizeof(buf), 0)) < 0) {
        handle_error("recv");
    }
    buf[len] = 0;
    printf("Server recv: %s\n", buf);

    strcpy(buf, "bye bye, peer!");
    if (send(asd, buf, strlen(buf), 0) < 0) {
        handle_error("send");
    }

    close(asd);
    close(sd);
    return 0;
}


Client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h> // inet_xx()
#include <unistd.h> // close()
#include <netinet/in.h>

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)


int main(int argc, char *argv[])
{
    int sd, af = AF_INET; // socket descriptor, address family
    struct sockaddr_in serv; // socket address
    char *serv_ip = NULL, buf[128];
    socklen_t addrlen;
    ssize_t len;
    uint16_t port;

    if (argc <= 2) {
        fprintf(stderr, "Usage: %s <serv-ip> <port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    serv_ip = argv[1];
    port = atoi(argv[2]);

    if ((sd = socket(af, SOCK_STREAM, 0)) < 0) {
        handle_error("socket");
    }

    memset(&serv, 0, sizeof(serv));
    serv.sin_family = af;
    serv.sin_port = htons(port);
    if (inet_pton(af, serv_ip, &serv.sin_addr) != 1) {
        handle_error("inet_pton");
    }

    if (connect(sd, (struct sockaddr*) &serv, sizeof(serv)) < 0) {
        handle_error("connect");
    }

    strcpy(buf, "hello server!");
    if (send(sd, buf, strlen(buf), 0) < 0) {
        handle_error("send");
    }

    if ((len = recv(sd, buf, sizeof(buf), 0)) < 0) {
        handle_error("recv");
    }
    buf[len] = 0;
    printf("Client recv: %s", buf);

    close(sd);
    return 0;
}



invest - ubuntu pannel tool


在ubuntu 9.04上面有一個名為invest的pannel tool,這是一個投資理財的小工具,這個小工具利用抓取http://finance.yahoo.com/的資料,幫您做即時的股票和匯率等等的報導,所有在finance.yahoo.com上面查的到的資訊都可以利用這個工具來顯示。



將invest加入pannel中。


將想要觀察的代碼輸入invest中。


invest的畫面。



2010年3月29日 星期一

vconfig


802.1Q VLAN很早就被納入kernel中,而MAC-VLAN在2.6.29版時納入。在編譯kernel時,需要將VLAN編譯進去,接著就可以透過vconfig/ip進行操作,不過建議使用ip。

ip
Usage: ip link add link DEV [ name ] NAME
                   [ txqueuelen PACKETS ]
                   [ address LLADDR ]
                   [ broadcast LLADDR ]
                   [ mtu MTU ]
                   type TYPE [ ARGS ]
       ip link delete DEV type TYPE [ ARGS ]

       ip link set DEVICE [ { up | down } ]
                   [ arp { on | off } ]
                   [ dynamic { on | off } ]
                   [ multicast { on | off } ]
                   [ allmulticast { on | off } ]
                   [ promisc { on | off } ]
                   [ trailers { on | off } ]
                   [ txqueuelen PACKETS ]
                   [ name NEWNAME ]
                   [ address LLADDR ]
                   [ broadcast LLADDR ]
                   [ mtu MTU ]
                   [ netns PID ]
     [ alias NAME ]
       ip link show [ DEVICE ]

TYPE := { vlan | veth | dummy | ifb | macvlan }

vconfig
Usage: add             [interface-name] [vlan_id]
       rem             [vlan-name]
       set_dflt        [interface-name] [vlan_id]
       add_port        [port-name]      [vlan_id]
       rem_port        [port-name]      [vlan_id]
       set_egress_map  [vlan-name]      [skb_priority]   [vlan_qos]
       set_ingress_map [vlan-name]      [skb_priority]   [vlan_qos]
       set_name_type   [name-type]
       set_bind_mode   [bind-type]

對於VLAN Net Device的命名規則則有四種:
  • VLAN_PLUS_VID (vlan0005)
  • VLAN_PLUS_VID_NO_PAD (vlan5)
  • DEV_PLUS_VID (eth0.0005)
  • DEV_PLUS_VID_NO_PAD (eth0.5)


double-tag
所謂的double tag,就是在vlan上,再切vlan,所以在linux的指令也就是vconfig add <vlan_netdev> <vid>。






參考資料:
http://www.candelatech.com/~greear/vlan.html