2011年2月13日 星期日

Netlink, NETLINK_FIREWALL


關於NETLINK的介紹請看Netlink introduction,這裡假設您已經了解NETLINK,並且準備使用NETLINK_FIREWALL這個netlink family,這個family必須載入ip_queue.ko這個module。而或者您已經直接將他編進kernel當中。

我們由kernel的觀點來看NETLINK_FIREWALL提供哪些功能,首先看到net/ipv4/netfilter/ip_queue.c
static int __init ip_queue_init(void)
{
    ...
    //註冊NETLINK_FIREWALL的handler,即ipq_rcv_skb
        ipqnl = netlink_kernel_create(&init_net, NETLINK_FIREWALL, 0,
                                      ipq_rcv_skb, NULL, THIS_MODULE);
    ...
}

static void
ipq_rcv_skb(struct sk_buff *skb)
{
        mutex_lock(&ipqnl_mutex);
        __ipq_rcv_skb(skb);
        mutex_unlock(&ipqnl_mutex);
}

static inline void
__ipq_rcv_skb(struct sk_buff *skb)
{
    ...
    status = ipq_receive_peer(NLMSG_DATA(nlh), type,
                              nlmsglen - NLMSG_LENGTH(0));
    if (status < 0)
            RCV_SKB_FAIL(status);

    if (flags & NLM_F_ACK)
            netlink_ack(skb, nlh, 0);
}

// 這裡就是提供NETLINK_FIREWALL control功能的function了
// 包含了設定copy to user-space的packet型態,
// 以及設定packet的verdict(NF_DROP/NF_ACCEPT等)
static int
ipq_receive_peer(struct ipq_peer_msg *pmsg,
                 unsigned char type, unsigned int len)
{
        int status = 0;

        if (len < sizeof(*pmsg))
                return -EINVAL;

        switch (type) {
        case IPQM_MODE:
         // 設定copy到user-space的模式為何?IPQ_COPY_META或是IPQ_COPY_PACKET
                status = ipq_set_mode(pmsg->msg.mode.value,
                                      pmsg->msg.mode.range);
                break;

        case IPQM_VERDICT:
        // packet的verdict
                if (pmsg->msg.verdict.value > NF_MAX_VERDICT)
                        status = -EINVAL;
                else
                        status = ipq_set_verdict(&pmsg->msg.verdict,
                                                 len - sizeof(*pmsg));
                        break;
        default:
                status = -EINVAL;
        }
        return status;
}
上述這段code就能大概了解NETLINK_FIREWALL在kernel的流程與提供的facility為何,透過IPQM_MODE設定copy to user-space的資料模式,當user-space收到資料後,判斷該資料是要DROP還是ACCEPT,決定後再透過IPQM_VERDICT告訴kernel,該封包是要DROP還是ACCEPT。

初步了解kernel提供的功能之後,下面就寫一個當接收到icmp echo封包,且seq為奇數的就DROP,其餘的就ACCEPT的範例。
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4/ip_queue.h>

/**
 * 建立socket
 */
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;
    addr.nl_pid = getpid();

    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;
}


/**
 * 設定IPQM_MODE
 */
static int ipq_set_mode(int sock, uint8_t mode, size_t range)
{
    unsigned char buf[1024];
    struct msghdr msg;
    struct sockaddr_nl dst = { .nl_family = AF_NETLINK };
    struct nlmsghdr *nlh;
    struct ipq_peer_msg *pmsg;
    struct iovec iov = {
        .iov_base = (void *) buf,
        .iov_len = sizeof(buf)
    };

    memset(buf, 0, sizeof(buf));
    msg = (struct msghdr) {
            .msg_name = (void *)&dst,
            .msg_namelen = sizeof(dst),
            .msg_iov = &iov,
            .msg_iovlen = 1,
    };

    nlh = (struct nlmsghdr*) buf;
    *nlh = (struct nlmsghdr) {
        .nlmsg_len = sizeof(buf),
        .nlmsg_flags = NLM_F_REQUEST,
        .nlmsg_type = IPQM_MODE,
        .nlmsg_pid = getpid(),
    };

    pmsg = (struct ipq_peer_msg*) NLMSG_DATA(nlh);
    *pmsg = (struct ipq_peer_msg) {
        .msg.mode.value = mode, // IPQM_META或是IPQM_PACKET
        .msg.mode.range = range, // 封包的大小
    };

    printf("%s(#%d):  nlmsglen:%d, NLMSG_LENGTH(0):%d\n",
            __func__, __LINE__, nlh->nlmsg_len, NLMSG_LENGTH(0));
    return sendmsg(sock, &msg, 0);
}

/**
 * 列印封包內容
 */
static void print_pkt(ipq_packet_msg_t *ipq_pkt)
{
    int i;
    printf("packet_id:0x%lx, mark:0x%lx\n,"
            "hook:%d, idev:%s, odev:%s\n,"
            "hw_proto:%d, hw_type:%d, hw_addrlen:%d\n,"
            "hw_addr:0x%02X%02X%02X%02X%02X%02X%02X%02X\n,"
            "data_len:%ld, payload:\n",
            ipq_pkt->packet_id, ipq_pkt->mark,
            ipq_pkt->hook, ipq_pkt->indev_name, ipq_pkt->outdev_name,
            ipq_pkt->hw_protocol, ipq_pkt->hw_type, ipq_pkt->hw_addrlen,
            ipq_pkt->hw_addr[0], ipq_pkt->hw_addr[1],
            ipq_pkt->hw_addr[2], ipq_pkt->hw_addr[3],
            ipq_pkt->hw_addr[4], ipq_pkt->hw_addr[5],
            ipq_pkt->hw_addr[6], ipq_pkt->hw_addr[7],
            ipq_pkt->data_len);
    for (i = 0; i < ipq_pkt->data_len; i++) {
        printf("%02X ", ipq_pkt->payload[i]);
        if (!((i+1) % 16)) printf("\n");
    }
}


/**
 * 根據封包內容給verdict
 */
static void 
get_verdict(ipq_packet_msg_t *ipq_pkt, int *verdict, unsigned long *id)
{
    struct iphdr *iph;
    struct icmphdr *icmph;

    *id = ipq_pkt->packet_id;
    if (ipq_pkt->data_len < sizeof(struct iphdr)) {
        *verdict = NF_DROP;
        return;
    }
    iph = (struct iphdr *) ipq_pkt->payload;
    if (iph->protocol == IPPROTO_ICMP) {
        icmph = (struct icmphdr *) (ipq_pkt->payload + iph->ihl * 4);
        printf("Type: %d, Id:0x%04x, seq:0x%04x\n",
                icmph->type, ntohs(icmph->un.echo.id),
           ntohs(icmph->un.echo.sequence));
        // 序號為奇數就將之DROP
        if (ntohs(icmph->un.echo.sequence) % 2) {
            *verdict = NF_DROP;
            return;
        }
    }
    // 其餘就是ACCEPT
    *verdict = NF_ACCEPT;
}

/**
 * 設定封包的verdict
 */
static int set_verdict(int sock, int verdict, unsigned long id)
{
    unsigned char buf[1024];
    struct msghdr msg;
    struct sockaddr_nl dst = { .nl_family = AF_NETLINK };
    struct nlmsghdr *nlh;
    struct ipq_peer_msg *pmsg;
    struct iovec iov = { .iov_base = (void *) buf, .iov_len = sizeof(buf) };

    memset(buf, 0, sizeof(buf));
    msg = (struct msghdr) {
            .msg_name = (void *)&dst,
            .msg_namelen = sizeof(dst),
            .msg_iov = &iov,
            .msg_iovlen = 1,
    };

    nlh = (struct nlmsghdr*) buf;
    *nlh = (struct nlmsghdr) {
        .nlmsg_len = sizeof(buf),
        .nlmsg_flags = NLM_F_REQUEST,
        .nlmsg_type = IPQM_VERDICT,
        .nlmsg_pid = getpid(),
    };

    pmsg = (struct ipq_peer_msg*) NLMSG_DATA(nlh);
    *pmsg = (struct ipq_peer_msg) {
        .msg.verdict.value = verdict, // NF_DROP或是NF_ACCEPT
        // packet_id詳細資料請看kernel的ipq_set_verdict()
        .msg.verdict.id = id,
    };

    char *p = "NONE";
    switch (verdict) {
        case NF_DROP:
            p = "DROP";
            break;
        case NF_ACCEPT:
            p = "ACCEPT";
            break;
    }
    printf("%s(#%d): %s packet %ld\n", __func__, __LINE__, p, id);
    return sendmsg(sock, &msg, 0);
}

/**
 * 處理接收到的封包
 */
static int ipq_recv_pkt(int sock, size_t len)
{
    unsigned char buf[NLMSG_SPACE(0) + len];
    struct msghdr msg;
    struct sockaddr_nl dst = { .nl_family = AF_NETLINK };
    struct nlmsghdr *nlh;
    struct iovec iov = {
        .iov_base = (void *) buf,
        .iov_len = len,
    };

    memset(buf, 0, sizeof(buf));
    msg = (struct msghdr) {
            .msg_name = (void *)&dst,
            .msg_namelen = sizeof(dst),
            .msg_iov = &iov,
            .msg_iovlen = 1,
    };

    len = recvmsg(sock, &msg, 0);
    for (nlh = (struct nlmsghdr *) buf; NLMSG_OK (nlh, len);
            nlh = NLMSG_NEXT (nlh, len)) {
        /* The end of multipart message. */
        if (nlh->nlmsg_type == NLMSG_DONE) {
            printf("NLMSG_DONE\n");
            return 0;
        }

        /* Do some error handling. */
        if (nlh->nlmsg_type == NLMSG_ERROR) {
            fprintf(stderr, "NLMSG_ERROR\n");
            return -1;
        }

        if (nlh->nlmsg_type == IPQM_PACKET) {
            int verdict;
            unsigned long id;

            print_pkt(NLMSG_DATA(nlh));
            get_verdict(NLMSG_DATA(nlh), &verdict, &id);
            set_verdict(sock, verdict, id);
        }
    }
    return 0;
}


int main(int argc, char *argv[])
{
    int sock, ret, cnt;

    sock = create_nl_socket(NETLINK_FIREWALL);
    if (sock < 0) {
        fprintf(stderr, "create_nl_socket failed\n");
        return -1;
    }

    ret = ipq_set_mode(sock, IPQ_COPY_PACKET, 2048);
    if (ret < 0) {
        fprintf(stderr, "ipq_set_mode failed\n");
    } else {
        printf("ipq_set_mode success\n");
    }

    for (cnt = 0; cnt < 10; cnt++) {
        ret = ipq_recv_pkt(sock, 2048);
    }
    close(sock);
    return 0;
}


透過iptable將經過OUTPUT chain的packet送到QUEUE去,這樣kernel才會將packet丟到NFTLINK_FIRWALL處理。


您可以看到kernel送出來的packet內容是從IP header開始。


您可以發現ping有一半的packet被DROP了。

Kernel version:2.6.37


2011年2月12日 星期六

安裝iptables到QEMU中


首先到netfilter.org中下載iptables。

接著configure並且make之後執行DESTDIR=/path/to/install make install將iptables安裝到特定目錄去(QEMU的root filesystem,我的路徑是~/initramfs)。

並且將相關的檔案(library)複製到root filesystem中。
brook@vista:~/initramfs$ echo "/usr/local/lib" > etc/ld.so.conf
brook@vista:~/initramfs$ cp /lib64/ld-linux-x86-64.so.2 lib64
brook@vista:~/initramfs$ cp /lib/libdl.so.2 lib
brook@vista:~/initramfs$ cp /lib/libm.so.6 lib
brook@vista:~/initramfs$ cp /lib/libc.so.6 lib
brook@vista:~/initramfs$ cp /sbin/ldconfig sbin
brook@vista:~/initramfs$ cp /sbin/ldconfig.real sbin
brook@vista:~/initramfs$ fakeroot
root@vista:~/initramfs# chown -R root.root .
root@vista:~/initramfs# find . |cpio -H newc -o > ../initrd


您可以發現share library路徑並沒有包含/usr/local/lib,所以要執行ldconfig


雖然順利執行iptables,但是kernel中的module並沒有load進來,所以把相關的ko複製到root filesystem中吧。

root@vista:~/initramfs# cp /usr/src/linux/net/ipv4/netfilter/iptable_filter.ko lib/modules/2.6.37/
root@vista:~/initramfs# cp /usr/src/linux/net/ipv4/netfilter/ip_tables.ko lib/modules/2.6.37/
root@vista:~/initramfs# cp /usr/src/linux/net/netfilter/x_tables.ko lib/modules/2.6.37/


終於順利的執行iptables了。

相關文章:
如何利用kvm/qemu練習linux module


2011年1月16日 星期日

Linux softirq執行分析(轉)


又是一篇精彩的文章,強力轉貼。

Linux softirq執行分析 

Author:  sinister
Email:   sinister@whitecell.org
Homepage:http://www.whitecell.org 
Date:    2007-01-11

本文對 Linux 內核軟中斷的執行流程進行了分析,並盡可能的結合當前運行環境詳細地寫出我的理解,
但這並不表明我的理解一定正確。這本是論壇裏的一篇帖子,發出來是為了抛磚引玉,如果您在閱讀本文
時發現了我的錯誤,還望得到您的指正。


今天無意中看了眼 2.6 內核的軟中斷實現,發現和以前我看到的大不相同(以前也是走馬觀花,不大仔
細),可以說改動很大。連 softirq 的調用點都不一樣了,以前是三個調用點,今天搜索了一下源代
碼,發現在多出了ksoftirqd 後,softirq 在系統中的調用點僅是在 ISR 返回時和使用了 
local_bh_enable() 函數後被調用了。網卡部分的顯示調用,我覺得應該不算是系統中的調用點。
ksoftirqd 返回去調用 do_softirq() 函數應該也只能算是其中的一個分支,因為其本身從源頭上
來講也還是在 ISR 返回時 irq_exit() 調用的。這樣一來就和前些日子寫的那份筆記
(Windows/Linux/Solaris 軟中斷機制)裏介紹的 Linux 內核部分的軟中斷有出處了,看來以後
討論 Linux kernel 代碼一定要以內核版本為前題,要不非亂了不可。得買本 Linux 方面的書了,
每次上來直接看相關代碼也不是回事,時間也不允許。


//
// do_IRQ 函數執行完硬體 ISR 後退出時調用此函數。
//

void irq_exit(void)
{
    account_system_vtime(current);
    trace_hardirq_exit();
    sub_preempt_count(IRQ_EXIT_OFFSET);

        //
        // 判斷當前是否有硬體中斷嵌套,並且是否有軟中斷在
        // pending 狀態,注意:這裏只有兩個條件同時滿足
        // 時,才有可能調用 do_softirq() 進入軟中斷。也就是
        // 說確認當前所有硬體中斷處理完成,且有硬體中斷安裝了
        // 軟中斷處理時理時才會進入。
        // 
    if (!in_interrupt() && local_softirq_pending())
                //
                // 其實這裏就是調用 do_softirq() 執行
                //
        invoke_softirq();
    preempt_enable_no_resched();
}


#ifndef __ARCH_HAS_DO_SOFTIRQ

asmlinkage void do_softirq(void)
{
    __u32 pending;
    unsigned long flags;

    //
    // 這個函數判斷,如果當前有硬體中斷嵌套,或者
    // 有軟中斷正在執行時候,則馬上返回。在這個
    // 入口判斷主要是為了與 ksoftirqd 互斥。
    //
    if (in_interrupt())
        return;

    //
    // 關中斷執行以下代碼
    //
    local_irq_save(flags);

    //
    // 判斷是否有 pending 的軟中斷需要處理。
    //
    pending = local_softirq_pending();

    //
    // 如果有則調用 __do_softirq() 進行實際處理
    //
    if (pending)
        __do_softirq();

    //
    // 開中斷繼續執行
    //
    local_irq_restore(flags);
}


//
// 最大軟中斷調用次數為 10 次。
//

#define MAX_SOFTIRQ_RESTART 10

asmlinkage void __do_softirq(void)
{
    //
    // 軟體中斷處理結構,此結構中包括了 ISR 中
    // 註冊的回調函數。
    //
    struct softirq_action *h;
    __u32 pending;
    int max_restart = MAX_SOFTIRQ_RESTART;
    int cpu;

    //
    // 得到當前所有 pending 的軟中斷。
    // 
    pending = local_softirq_pending();
    account_system_vtime(current);

    //
    // 執行到這裏要遮罩其他軟中斷,這裏也就證明了
    // 每個 CPU 上同時運行的軟中斷只能有一個。
    //
    __local_bh_disable((unsigned long)__builtin_return_address(0));
    trace_softirq_enter();

    //
    // 針對 SMP 得到當前正在處理的 CPU
    //
    cpu = smp_processor_id();
//
// 迴圈標誌
//
restart:
    //
    // 每次迴圈在允許硬體 ISR 強佔前,首先重置軟中斷
    // 的標誌位元。
    //
    /* Reset the pending bitmask before enabling irqs */
    set_softirq_pending(0);

    //
    // 到這裏才開中斷運行,注意:以前運行狀態一直是關中斷
    // 運行,這時當前處理軟中斷才可能被硬體中斷搶佔。也就
    // 是說在進入軟中斷時不是一開始就會被硬體中斷搶佔。只有
    // 在這裏以後的代碼才可能被硬體中斷搶佔。
    //
    local_irq_enable();

    //
    // 這裏要注意,以下代碼運行時可以被硬體中斷搶佔,但
    // 這個硬體 ISR 執行完成後,它的所註冊的軟中斷無法馬上運行,
    // 別忘了,現在雖是開硬體中斷執行,但前面的 __local_bh_disable()
    // 函數遮罩了軟中斷。所以這種環境下只能被硬體中斷搶佔,但這
    // 個硬中斷註冊的軟中斷回調函數無法運行。要問為什麼,那是因為
    // __local_bh_disable() 函數設置了一個標誌當作互斥量,而這個
    // 標誌正是上面的 irq_exit() 和 do_softirq() 函數中的
    // in_interrupt() 函數判斷的條件之一,也就是說 in_interrupt() 
    // 函數不僅檢測硬中斷而且還判斷了軟中斷。所以在這個環境下觸發
    // 硬中斷時註冊的軟中斷,根本無法重新進入到這個函數中來,只能
    // 是做一個標誌,等待下面的重複迴圈(最大 MAX_SOFTIRQ_RESTART)
    // 才可能處理到這個時候觸發的硬體中斷所註冊的軟中斷。
    //


    //
    // 得到軟中斷向量表。
    //
    h = softirq_vec;

    //
    // 迴圈處理所有 softirq 軟中斷註冊函數。
    // 
    do {
        //
        // 如果對應的軟中斷設置 pending 標誌則表明
        // 需要進一步處理它所註冊的函數。
        //
        if (pending & 1) {
            //
            // 在這裏執行了這個軟中斷所註冊的回調函數。
            //
            h->action(h);
            rcu_bh_qsctr_inc(cpu);
        }
        //
        // 繼續找,直到把軟中斷向量表中所有 pending 的軟
        // 中斷處理完成。
        //
        h++;

        //
        // 從代碼裏可以看出按位操作,表明一次迴圈只
        // 處理 32 個軟中斷的回調函數。
        //
        pending >>= 1; 
    } while (pending);

    //
    // 關中斷執行以下代碼。注意:這裏又關中斷了,下面的
    // 代碼執行過程中硬體中斷無法搶佔。
    //
    local_irq_disable();

    //
    // 前面提到過,在剛才開硬體中斷執行環境時只能被硬體中斷
    // 搶佔,在這個時候是無法處理軟中斷的,因為剛才開中
    // 斷執行過程中可能多次被硬體中斷搶佔,每搶佔一次就有可
    // 能註冊一個軟中斷,所以要再重新取一次所有的軟中斷。
    // 以便下面的代碼進行處理後跳回到 restart 處重複執行。
    //
    pending = local_softirq_pending();

    //
    // 如果在上面的開中斷執行環境中觸發了硬體中斷,且每個都
    // 註冊了一個軟中斷的話,這個軟中斷會設置 pending 位,
    // 但在當前一直遮罩軟中斷的環境下無法得到執行,前面提
    // 到過,因為 irq_exit() 和 do_softirq() 根本無法進入到
    // 這個處理過程中來。這個在上面詳細的記錄過了。那麼在
    // 這裏又有了一個執行的機會。注意:雖然當前環境一直是
    // 處於遮罩軟中斷執行的環境中,但在這裏又給出了一個執行
    // 剛才在開中斷環境過程中觸發硬體中斷時所註冊的軟中斷的
    // 機會,其實只要理解了軟中斷機制就會知道,無非是在一些特
    // 定環境下調用 ISR 註冊到軟中斷向量表裏的函數而已。
    //

    //
    // 如果剛才觸發的硬體中斷註冊了軟中斷,並且重複執行次數
    // 沒有到 10 次的話,那麼則跳轉到 restart 標誌處重複以上
    // 所介紹的所有步驟:設置軟中斷標誌位元,重新開中斷執行...
    // 注意:這裏是要兩個條件都滿足的情況下才可能重複以上步驟。 
    //
    if (pending && --max_restart)
        goto restart;

    //
    // 如果以上步驟重複了 10 次後還有 pending 的軟中斷的話,
    // 那麼系統在一定時間內可能達到了一個峰值,為了平衡這點。
    // 系統專門建立了一個 ksoftirqd 線程來處理,這樣避免在一
    // 定時間內負荷太大。這個 ksoftirqd 線程本身是一個大循環,
    // 在某些條件下為了不負載過重,它是可以被其他進程搶佔的,
    // 但注意,它是顯示的調用了 preempt_xxx() 和 schedule()
    // 才會被搶佔和切換的。這麼做的原因是因為在它一旦調用 
    // local_softirq_pending() 函數檢測到有 pending 的軟中斷
    // 需要處理的時候,則會顯示的調用 do_softirq() 來處理軟中
    // 斷。也就是說,下面代碼喚醒的 ksoftirqd 線程有可能會回
    // 到這個函數當中來,尤其是在系統需要回應很多軟中斷的情況
    // 下,它的調用入口是 do_softirq(),這也就是為什麼在 do_softirq()
    // 的入口處也會用 in_interrupt()  函數來判斷是否有軟中斷
    // 正在處理的原因了,目的還是為了防止重入。ksoftirqd 實現
    // 看下面對 ksoftirqd() 函數的分析。
    //
    if (pending)
               //
               // 此函數實際是調用 wake_up_process() 來喚醒 ksoftirqd
               // 
        wakeup_softirqd();

    trace_softirq_exit();
    account_system_vtime(current);

    //
    // 到最後才開軟中斷執行環境,允許軟中斷執行。注意:這裏
    // 使用的不是 local_bh_enable(),不會再次觸發 do_softirq()
    // 的調用。
    // 
    _local_bh_enable();
}


static int ksoftirqd(void * __bind_cpu)
{
    //
    // 顯示調用此函數設置當前進程的靜態優先順序。當然,
    // 這個優先順序會隨調度器策略而變化。
    //
    set_user_nice(current, 19);

    //
    // 設置當前進程不允許被掛啟
    //
    current->flags |= PF_NOFREEZE;

    //
    // 設置當前進程狀態為可中斷的狀態,這種睡眠狀
    // 態可回應信號處理等。
    // 
    set_current_state(TASK_INTERRUPTIBLE);

    //
    // 下面是一個大循環,迴圈判斷當前進程是否會停止,
    // 不會則繼續判斷當前是否有 pending 的軟中斷需
    // 要處理。
    //
    while (!kthread_should_stop()) {
        //
        // 如果可以進行處理,那麼在此處理期間內禁止
        // 當前進程被搶佔。
        //
        preempt_disable();

        //
        // 首先判斷系統當前沒有需要處理的 pending 狀態的軟中斷
        //
        if (!local_softirq_pending()) {
            //
            // 沒有的話在主動放棄 CPU 前先要允許搶佔,因為
            // 一直是在不允許搶佔狀態下執行的代碼。
            //
            preempt_enable_no_resched();

            //
            // 顯示調用此函數主動放棄 CPU 將當前進程放入睡眠佇列,
            // 並切換新的進程執行(調度器相關不記錄在此)
            //
            schedule();

            //
            // 注意:如果當前顯示調用 schedule() 函數主動切換的進
            // 程再次被調度執行的話,那麼將從調用這個函數的下一條
            // 語句開始執行。也就是說,在這裏當前進程再次被執行的
            // 話,將會執行下面的 preempt_disable() 函數。
            //

            //
            // 當進程再度被調度時,在以下處理期間內禁止當前進程被搶佔。
            //
            preempt_disable();
        }

        //
        // 設置當前進程為運行狀態。注意:已經設置了當前進程不可搶佔
        // 在進入迴圈後,以上兩個分支不論走哪個都會執行到這裏。一是
        // 進入迴圈時就有 pending 的軟中斷需要執行時。二是進入迴圈時
        // 沒有 pending 的軟中斷,當前進程再次被調度獲得 CPU 時繼續
        // 執行時。
        //
        __set_current_state(TASK_RUNNING);

        //
        // 迴圈判斷是否有 pending 的軟中斷,如果有則調用 do_softirq()
        // 來做具體處理。注意:這裏又是一個 do_softirq() 的入口點,
        // 那麼在 __do_softirq() 當中迴圈處理 10 次軟中斷的回調函數
        // 後,如果還有 pending 的話,會又調用到這裏。那麼在這裏則
        // 又會有可能去調用 __do_softirq() 來處理軟中斷回調函數。在前
        // 面介紹 __do_softirq() 時已經提到過,處理 10 次還處理不完的
        // 話說明系統正處於繁忙狀態。根據以上分析,我們可以試想如果在
        // 系統非常繁忙時,這個進程將會與 do_softirq() 相互交替執行,
        // 這時此進程佔用 CPU 應該會很高,雖然下面的 cond_resched() 
        // 函數做了一些處理,它在處理完一輪軟中斷後當前處理進程可能會
        // 因被調度而減少 CPU 負荷,但是在非常繁忙時這個進程仍然有可
        // 能大量佔用 CPU。
        //
        while (local_softirq_pending()) {
            /* Preempt disable stops cpu going offline.
               If already offline, we'll be on wrong CPU:
               don't process */
            if (cpu_is_offline((long)__bind_cpu))
                //
                // 如果當前被關聯的 CPU 無法繼續處理則跳轉
                // 到 wait_to_die 標記出,等待結束並退出。
                // 
                goto wait_to_die;

                //
                // 執行 do_softirq() 來處理具體的軟中斷回調函數。注
                // 意:如果此時有一個正在處理的軟中斷的話,則會馬上
                // 返回,還記得前面介紹的 in_interrupt() 函數麼。
                //
                do_softirq();

                //
                // 允許當前進程被搶佔。
                //
                preempt_enable_no_resched();
                        
                //
                // 這個函數有可能間接的調用 schedule() 來切換當前
                // 進程,而且上面已經允許當前進程可被搶佔。也就是
                // 說在處理完一輪軟中斷回調函數時,有可能會切換到
                // 其他進程。我認為這樣做的目的一是為了在某些負載
                // 超標的情況下不至於讓這個進程長時間大量的佔用 CPU,
                // 二是讓在有很多軟中斷需要處理時不至於讓其他進程
                // 得不到回應。
                //
                cond_resched();

                //
                // 禁止當前進程被搶佔。
                //
                preempt_disable();

                //
                // 處理完所有軟中斷了嗎?沒有的話繼續迴圈以上步驟
                //
        }

        //
        // 待一切都處理完成後,允許當前進程被搶佔,並設置
        // 當前進程狀態為可中斷狀態,繼續迴圈以上所有過程。
        //
        preempt_enable();
        set_current_state(TASK_INTERRUPTIBLE);
    }
   
    //
    // 如果將會停止則設置當前進程為運行狀態後直接返回。
    // 調度器會根據優先順序來使當前進程運行。
    //
    __set_current_state(TASK_RUNNING);
    return 0;

//
// 一直等待到當前進程被停止
//
wait_to_die:

    //
    // 允許當前進程被搶佔。
    //
    preempt_enable();
    /* Wait for kthread_stop */

    //
    // 設置當前進程狀態為可中斷的狀態,這種睡眠狀
    // 態可回應信號處理等。
    // 
    set_current_state(TASK_INTERRUPTIBLE);

    //
    // 判斷當前進程是否會被停止,如果不是的話
    // 則設置進程狀態為可中斷狀態並放棄當前 CPU
    // 主動切換。也就是說這裏將一直等待當前進程
    // 將被停止時候才結束。
    //
    while (!kthread_should_stop()) {
        schedule();
        set_current_state(TASK_INTERRUPTIBLE);
    }

    //
    // 如果將會停止則設置當前進程為運行狀態後直接返回。
    // 調度器會根據優先順序來使當前進程運行。
    //
    __set_current_state(TASK_RUNNING);
    return 0;
}


參考:
linux kernel source 2.6.19.1 /kernel/softirq.c
WSS(Whitecell Security Systems),一個非營利性民間技術組織,致力於各種系統安全技術的研究。
堅持傳統的hacker精神,追求技術的精純。
WSS 主頁:http://www.whitecell.org/ 
WSS 論壇:http://www.whitecell.org/forums/



How To Boot And Install Windows 7 From USB Flash Drive(轉)


這個網站寫得不錯"How To Boot And Install Windows 7 From USB Flash Drive",試過之後就很想把他記下來分享給大家。

將USB格式化成NTFS

格式化之後,在用管理員執行Command Prompt。並且輸入
diskpart
list disk // 看看USB是那個disk
select disk X (X是剛剛USB顯示的Disk number)
list partition
select partition Y (Y是你想active的partition number)

選擇USB

active USB上的partition

建立可開機的USB

將win7的image檔解到硬碟某目錄上暫存

進入解開後的boot目錄,並且執行bootsect /nt60 X:(X就是USB的磁碟機代號)

最後在將整個解開後的win7複製到USB上。這樣這個就可以用這個USB當開機碟安裝win7啦。

OS環境:win7
    參考資料:
  1. http://maketecheasier.com/boot-and-install-windows-7-from-usb-flash-drive/2009/01/23



2011年1月9日 星期日

Linux Modules(1.1)module parameters


Linux Module允許使用者在insmod時帶入相關的parameters,這些parameters必須被宣告成golbal,並且使用module_param()宣告資料型態與權限,目前支援的資料型態有byte, short, ushort, int, uint, long, ulong, charp, bool等等。也可以使用module_param_array(name, type, num, perm)宣告成陣列。perm(權限)會決定/sys/module/顯示該參數的權限。

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

static unsigned char b_byte = 1;
module_param(b_byte, byte, S_IRUGO|S_IWUSR);

static short int b_short = 2;
module_param(b_short, short, S_IRUGO|S_IWUSR);

static unsigned short int b_ushort = 3;
module_param(b_ushort, ushort, S_IRUGO|S_IWUSR);

static int b_int = 6;
module_param(b_int, int, S_IRUGO|S_IWUSR);

static unsigned int b_uint = 5;
module_param(b_uint, uint, S_IRUGO|S_IWUSR);

static long b_long = 6;
module_param(b_long, long, S_IRUGO|S_IWUSR);

static unsigned long b_ulong = 7;
module_param(b_ulong, ulong, S_IRUGO|S_IWUSR);

static char *b_charp = "brook";
module_param(b_charp, charp, S_IRUGO|S_IWUSR);

static int b_bool = 1;
module_param(b_bool, bool, S_IRUGO|S_IWUSR);

static int __init init_modules(void)
{
    printk("b_byte: %d\n", b_byte);
    printk("b_short: %d\n", b_short);
    printk("b_ushort: %u\n", b_ushort);
    printk("b_int: %d\n", b_int);
    printk("b_uint: %u\n", b_uint);
    printk("b_long: %ld\n", b_long);
    printk("b_ulong: %lu\n", b_ulong);
    printk("b_charp: %s\n", b_charp);
    printk("b_bool: %d\n", b_bool);

    return 0;
}

static void __exit exit_modules(void)
{
}

module_init(init_modules);
module_exit(exit_modules);



Kernel Version:2.6.35
    參考資料:
  1. Linux Device Drivers, 3e
  2. Document/printk-formats.txt



2011年1月8日 星期六

Internet Explorer 7 on Linux with Wine


brook@vista:~$ wget http://www.kegel.com/wine/winetricks
brook@vista:~$ ./winetricks ie7
brook@vista:~$ ./winetricks fakechinese顯示中文
brook@vista:~$ wine 'c:\program files\internet explorer\iexplore'

裝了IE8常常會出現問題,所以最後就安裝IE7了。


    參考資料:
  1. Community Documentation - Wine
  2. Wine 1.2 IE8
  3. Issue 159: winetricks ie8 verb doesn't yield working browser



2011年1月2日 星期日

step by step to install Vserver


vserver是OS-Level的virtual machine,是一種進階的 chroot 機制,提供 processes 完全獨立的file systems,但系統其它部份並不是獨立的。Kernel必須要加上Patch才能支援VServer。

下載支援的kernel和patch
brook@vista:/usr/src$ wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.37.tar.bz2
brook@vista:/usr/src$ wget http://vserver.13thfloor.at/Experimental/patch-2.6.37-vs2.3.0.37-rc1.diff

解開kernel並且給予patch
brook@vista:/usr/src$ tar jxvf linux-2.6.37.tar.bz2
brook@vista:/usr/src$ cd linux-2.6.37
brook@vista:/usr/src/linux-2.6.37$ patch -p1 < ../patch-2.6.37-vs2.3.0.37-rc1.diff

利用make-kpkg建立kernel的.deb檔
brook@vista:/usr/src/linux-2.6.37$ cp /boot/config-`uname -r` .config
brook@vista:/usr/src/linux-2.6.37$ make oldconfig
brook@vista:/usr/src/linux-2.6.37$ make-kpkg clean
brook@vista:/usr/src/linux-2.6.37$ fakeroot make-kpkg --initrd --append-to-version=-vserver kernel-image kernel-headers
brook@vista:/usr/src/linux-2.6.37$ cd ..
brook@vista:/usr/src$ sudo dpkg -i linux-image-2.6.37-vs2.3.0.37-rc1-vserver_2.6.37-vs2.3.0.37-rc1-vserver-10.00.Custom_amd64.deb
brook@vista:/usr/src$ sudo dpkg -i linux-headers-2.6.37-vs2.3.0.37-rc1-vserver_2.6.37-vs2.3.0.37-rc1-vserver-10.00.Custom_amd64.deb



接著就是重新用新的kernel開機,並且建立vserver。
下載並且compile新的util-vserver
brook@vista:/usr/src$ sudo apt-get install e2fslibs-dev libnss3-dev phthon-dev
brook@vista:/usr/src$ wget http://people.linux-vserver.org/~dhozac/t/uv-testing/util-vserver-0.30.216-pre2926.tar.bz2
brook@vista:/usr/src$ tar jxvf util-vserver-0.30.216-pre2926.tar.bz2
brook@vista:/usr/src$ cd util-vserver-0.30.216-pre2926/
brook@vista:/usr/src/util-vserver-0.30.216-pre2926$ ./configure
brook@vista:/usr/src/util-vserver-0.30.216-pre2926$ make -j3
brook@vista:/usr/src/util-vserver-0.30.216-pre2926$ sudo make install
brook@vista:/usr/src/util-vserver-0.30.216-pre2926# sudo vserver BrookVS build -m debootstrap --hostname BrookVS --interface eth0:192.168.1.2/24 --interface lo:127.0.0.1/8  -- -d maverick -m http://tw.archive.ubuntu.com/ubuntu/
brook@vista:/usr/src/util-vserver-0.30.216-pre2926# sudo vserver BrookVS start
brook@vista:/usr/src/util-vserver-0.30.216-pre2926# sudo vserver BrookVS enter




Kernel Version:2.6.37
參考資料:
  1. How to compile a kernel on Ubuntu 10.04
  2. Linux - Vserver
  3. Virtual machine - Wikipedia, the free encyclopedia
  4. VServer - DebianWiki
  5. Linux - Vserver, Installation on Linux 2.6




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倍」。


熱門文章