2014/09/22

FreeRTOS task management

1. Task function


在 FreeRTOS 裡, 每條thread都稱作 task, 實作成C function, 回傳值是void, 並且parameter為void pointer, 像這樣
void ATaskFunction( void *pvParameters );

每個task通常會一直執行不會停 (所以應該有個loop在裡面), FreeRTOS 不准有"return", 也不准執行到最後一行

如果我們不需要某個task, 我們應該刪除這個task

底下是 task function可能的實作

void ATaskFunction( void *pvParameters )
{
    /* 如同一般的function來宣告變數 */
    int iVariableExample = 0;

    /* 實作一個 infinite loop */
    for( ;; )
    {
        /* task實際上要做的事 */
    }
    /* 如果task在loop中break出來, 那麼task必需在到function end之前刪除,
       比如說用 vTaskDelete( NULL ), 裡面的NULL表示是刪目前的task */
    vTaskDelete( NULL );
}

task function 可以拿來建多個task, 每個task都是獨立的instance, 有自己的stack

2. Task state


我們先簡單的把task分成Running / Not Running
當 task 從 Not Running 變成 Running, 稱作 task in
相反地稱作 task out

3. Create Task


建task用到 xTaskCreate() API, 它的prototype如下

portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
                           const signed portCHAR * const pcName,
                           unsigned portSHORT usStackDepth,
                           void *pvParameters,
                           unsigned portBASE_TYPE uxPriority,
                           xTaskHandle *pxCreatedTask
);
( task.h )

底下是參數的說明:

  • pvTaskCode : 放task function的function pointer
  • pcName : task的名字, 在FreeRTOS不會用到它, 這只是用於debug用途, 名字長度限制為configMAX_TASK_NAME_LEN
  • usStackDepth : 每個 task 被放進 stack 的時候, usStackDepth表示它需要的最大stack space, usStackDepth以byte為單位, 比如說, stack size = 32 bit, usStackDepth = 100, 則會用到100*4=400 bytes stack space, 如果task只是設計成idle task, 那麼可以用這個值 configMINIMAL_STACK_SIZE
  • pvParameters : task function的參數, 型態為 (void *)
  • uxPriority : task 的優先權, 0最低, (configMAX_PRIORITIES – 1)最高
  • pxCreatedTask : 這個task的reference, 用於動態調整這個task的priority, 或是刪掉這個task

可能的回傳值 :

  • pdTRUE : 表示成功
  • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY : 表示RAM不夠了
底下是sample code:

void vTask1( void *pvParameters )
{
    const char *pcTaskName = "Task 1 is running\r\n";
    volatile unsigned long ul;

    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay */
        }
    }
}

void vTask2( void *pvParameters )
{
    const char *pcTaskName = "Task 2 is running\r\n";
    volatile unsigned long ul;

    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay */
        }
    }
}

int main( void )
{
    /* 建tasks1, 通常需要檢查回傳值, 這裡先略過 */
    xTaskCreate( vTask1,   /* task function pointer, 通常用function name就可以 */
                 "Task 1", /* Task name */
                 1000,     /* Stack depth - 一般的例子不會用到這麼多 */
                 NULL,     /* 不帶parameter */
                 1,        /* priority */
                 NULL );   /* 不需要task handle */

    /* task2跟task1是差不多的 */
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );

    /* 啟動scheduler, 讓tasks開始執行 */
    vTaskStartScheduler();

    /* 正常來說不會執行到這邊, 因為scheduler會執行tasks,
       執行到這邊可能是 heap memory 不夠 */
    for( ;; );
}

實際執行結果會看到task1 & task2兩個不停地task in/out

前個例子是把兩個task在main裡面create, 我們也可以把task放在task裡面create

void vTask1( void *pvParameters )
{
    const char *pcTaskName = "Task 1 is running\r\n";
    volatile unsigned long ul;

    /* 執行到這邊表示Scheduler已經開始執行task1 */
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );

    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay */
        }
    }
}

因為task1 & task2很像, 所以改寫成一個就好

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    volatile unsigned long ul;

    /* 把要印出來的字串經由parameter帶進來, 再轉成 (char *) */
    pcTaskName = ( char * ) pvParameters;

    for( ;; )
    {
        /* 把perameter帶進來的字串印出來 */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay*/
        }
    }
}

/* 要帶給task的字串 */
static const char *pcTextForTask1 = “Task 1 is running\r\n”;
static const char *pcTextForTask2 = “Task 2 is running\t\n”;

int main( void )
{
    /* Create task 1 */
    xTaskCreate( vTaskFunction,         /* function pointer */
                 "Task 1",              /* task name */
                 1000,                  /* stack depth */
                 (void*)pcTextForTask1, /* 參數, 把要印的字串轉成(void *) */
                 1,                     /* priority */
                 NULL );                /* task handle */

    /* Create task2, 參數不一樣 */
    xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 1, NULL );

    /* Start scheduler */
    vTaskStartScheduler();

    for( ;; );
}

4. Task priority


priority可以動態的改, 最高不會超過configMAX_PRIORITIES (defined in FreeRTOSConfig.h)
我們可以改configMAX_PRIORITIES, 但改的愈高也就耗掉更多的RAM, 所以通常會保留default值

為了選出下一個要執行的task, scheduler應該要在task執行的片段最後面執行
而 tick interrupt 就是讓scheduler達到這個目的
time slice的常度定義在 configTICK_RATE_HZ (FreeRTOSConfig.h)
舉例來說, 如果 configTICK_RATE_HZ =100Hz, 那麼time slice = 10ms

要注意的是, FreeRTOS的時間是以tick當單位, 就連 portTICK_RATE_MS 也是以tick當單位


每次選task, 都會選priority最高的task, 如果有一樣的priority則會輪流

如果我們把剛剛的例子改一下priority的部份

int main( void )
{
    /* task1 的 priority 是 1 */
    xTaskCreate( vTaskFunction, "Task 1", 1000, (void*)pcTextForTask1, 1, NULL );

    /* task2 的 priority 是 2 */
    xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL );

    vTaskStartScheduler();
    return 0;
}

執行結果會看到:
Task 2 is running
Task 2 is running
Task 2 is running
Task 2 is running
Task 2 is running
.....
結果Task 1因為priority比Task 2, 所以每次選都選不到它造成starvation


5. Not running state


為了避免starvation造成某些task永遠跑不到, 可以使用event driven
這意味著在選擇task的時候, scheduler沒有選priority最高的task

5.1 Blocked State


當task在等某個event的時候, 稱它在 Blocked State
task等的event分兩種 :

  • Temporal event : 在等delay或是確切的時間
  • Synchronization event : 這個 event 由其它 task 或 interrupt 產生

task 也可以同時等這兩種, 比如說, 在10s內等待data

5.2 Suspended State


當 task 在 suspended state 的時候, 就不會被scheduler選上
進suspend : vTaskSuspend()
離開suspend : vTaskResume() or xTaskResumeFromISR()
但大多時候不太會用到suspend

5.3 Ready State


當 task為 Not running, 並且不是Blocked/Suspended State, 就稱作 Ready State

5.4 State Transition Diagram



5.4 vTaskDelay()


之前的例子, 用的是busy delay, 所以Task 2實際上並沒有suspend
我們可以用 vTaskDelay() 來改寫這個 delay 讓 Task 2 進 suspend

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;

    pcTaskName = ( char * ) pvParameters;
    for( ;; )
    {
        vPrintString( pcTaskName );

        /* 等250 ms, 參數是ticks, 可以利用portTICK_RATE_MS把milliseconds轉成ticks */
        vTaskDelay( 250 / portTICK_RATE_MS );
    }
}

實際結果是:
Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running
.....

因為有足夠的時間, 所以雖然Task2的priority比較高, 但它執行完就suspend, 讓Task 1在Task 2 suspend的期間成為唯一的task可以執行, 然後suspend

5.5 vTaskDelayUntil()


vTaskDelayUntil() 跟vTaskDelay()很像, 差別是vTaskDelayUntil()決定多少個tick之後要被離開blocked state, 適合用在需要定時執行的task

這是它的prototype

void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );

其中它的參數:

  • pxPreviousWakeTime : 這個pointer會更新上一次離開block state的時間
  • xTimeIncrement : 週期, 單位為ticks, 一樣可以用 portTICK_RATE_MS 轉換成ms
我們再改寫剛剛的例子

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    portTickType xLastWakeTime;

    pcTaskName = ( char * ) pvParameters;

    /* xLastWakeTime 需要初始化成目前的tick count */
    xLastWakeTime = xTaskGetTickCount();

    for( ;; )
    {
        vPrintString( pcTaskName );

        /* 自 xLastWakeTime 的時間等 250ms 離開 block state */
        vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );
    }
}

6. Idle task


Processor通常都需要執行些什麼, 如果task都進suspend, 那裡它會執行idle task
idle task 是在 scheduler 一開始的時候產生, 擁有priority 0, 做的事情就跟busy loop差不多

6.1 Idle Task Hook Function


我們也可以加 callback function 在 idle task 裡
通常用於:

  • 執行低優先權的工作, 或只需要背景處理的工作
  • 計算剩下的processing capacity
  • 進low power mode

6.2 Limitation of Idle Task


不可以block / suspend
如果 callback function 呼叫 vTaskDelete() 來刪除某個task, 那麼會馬上跳回這個task, 讓這個task能夠釋放已用的資源

6.3 Usage of callback of Idle Task


callback function的prototype如下

void vApplicationIdleHook( void );

使用的時候要注意 USE_IDLE_HOOK 的config要設成1 (FreeRTOSConfig.h)

底下是sample code:

/* 儲存 ilde task 裡的 loop 次數 */
unsigned long ulIdleCycleCount = 0UL;

/* Idle hook function只能叫做 vApplicationIdleHook() */
void vApplicationIdleHook( void )
{
    ulIdleCycleCount++;
}

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;

    pcTaskName = ( char * ) pvParameters;
    for( ;; )
    {
        /* 印出 task name 以及 ulIdleCycleCount */
        vPrintStringAndNumber( pcTaskName, ulIdleCycleCount );

        /* Delay 250 ms*/
        vTaskDelay( 250 / portTICK_RATE_MS );
    }
}

執行結果像這樣:
Task 2 is running
uIdleCycleCOunt = 0
Task 1 is running
uIdleCycleCOunt = 0
Task 2 is running
uIdleCycleCOunt = 3869504
Task 1 is running
uIdleCycleCOunt = 3869504
Task 2 is running
uIdleCycleCOunt = 8564623
Task 1 is running
uIdleCycleCOunt = 8564623
......

7. Changing task priority


更改priority :

void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );

pxTask 是 task handle, uxNewPriority 是新的 priority

取得priority :

unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );

底下是 sample code

/* 拿來存task2 handle */
xTaskHandle xTask2Handle;

void vTask1( void *pvParameters )
{
    unsigned portBASE_TYPE uxPriority;

    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        vPrintString( "Task1 is running\r\n" );

  /* 把 Task 2 的 priority 設成 Task 1 多 1 */
        vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
    }
}

void vTask2( void *pvParameters )
{
    unsigned portBASE_TYPE uxPriority;

    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        vPrintString( "Task2 is running\r\n" );

        /* 把 Task 2 的 priority 降 2 */
        vTaskPrioritySet( NULL, ( uxPriority - 2 ) );
    }
}

int main( void )
{
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );

    vTaskStartScheduler();
    for( ;; );
}

因為Task 2的priority在一開始比Task 1少1,
所以這個sample裡, Scheduler會讓Task 1執行 (並把Task 2的priority設成比Task 1大1)
在下一輪, Scheduler發現Task 2的priority比Task 1大, 就執行Task 2 (並降priority比Task 1小1)
執行結果就是Task 1/2輪流地執行

8. Deleting Task


用 vTaskDelete() API 來刪掉task, 被刪的task就再也不會進到 Running state
而 idle task 負責釋放被刪掉的task的資源, 注意不可大量用 vTaskDelete() 造成 idle task starve
並且要注意只有 kernel 分配給 task 的資源會自動釋放, 但如果是 task 主動要的資源則要在刪除前釋放

底下是 prototype

void vTaskDelete( xTaskHandle pxTaskToDelete );

底下是sample code

/* 存 Task 2 handle */
xTaskHandle xTask2Handle;

void vTask2( void *pvParameters )
{
    vPrintString( "Task2 is running and about to delete itself\r\n" );
    vTaskDelete( xTask2Handle );
}

void vTask1( void *pvParameters )
{
    const portTickType xDelay100ms = 100 / portTICK_RATE_MS;
    for( ;; )
    {
        vPrintString( "Task1 is running\r\n" );

        xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );

        vTaskDelay( xDelay100ms );
    }
}

int main( void )
{
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
    vTaskStartScheduler();
    for( ;; );
}

Task 1會create Task 2, Task 2在執行完後就會自刪, 所以執行結果就是Task 1/2輪流執行



2014/09/19

svn

Show unversioned files

svn在commit的時候, 預設不會出現新加的files

如果是TortoiseSVN, 在commit視窗左下角有個"Show unversioned files", 將它勾選, 就會出現新增的files

如果是command line, 用這個command就會顯示所有新增的files

svn status | grep ^?








2014/09/16

Netlink

1. Introduction

Netlink 提供 userspace & kernel 之間的溝通, 它定義在 RFC 3549 "Linux Netlink as an IP Services Protocol", 相較於其它溝通方式, 它有這些優點:

  • 不用 polling
  • 可以從 kernel 當作訊息發起端
  • 提供 multicast
kernel 裡相關的檔案:

  • Netlink core files
    • net/netlink/af_netlink.c
    • include/linux/netlink.h
    • net/netlink/genetlink.c
  • Route netlink files
    • net/core/rtnetlink.c
    • include/linux/rtnetlink.h
  • Generic netlink files
    • net/netlink/genetlink.c
    • include/linux/genetlink.h

2. Create netlink socket

userspace & kernel 在create socket 時, 最後都會 call __netlink_create(), 唯一的差別是, kernel會加上屬性 NETLINK_KERNEL_SOCKET


2.1 create netlink socket in userspace


底下是sample code

int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

struct sockaddr_nl addr;
bzero (&addr, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_ROUTE;

bind(sockfd,(struct sockaddr *)&addr,sizeof(addr);

其中 sockaddr_nl 的定義是

struct sockaddr_nl {
    __kernel_sa_family_t  nl_family;  /* AF_NETLINK */
    unsigned short        nl_pad;     /* zero */
    __u32                 nl_pid;     /* port ID */
    __u32                 nl_groups;  /* multicast groups mask */
};
include/linux/netlink.h)

其中
nl_family 填 AF_NETLINK
nl_pad 通常得填 0
nl_pid 代表 netlink 的 unicast address, 如果是 kernel space 就填 0, 如果是 userspace, 在bind的時候就會被填上 user process 的 pid, 但如果 user process 跳過 bind, 那麼就得自行管理這個欄位的值
nl_groups 填這個 socket 所屬的 multicast group


2.2 create netlink socket in kernel

kernel 的 netlink 是在 init 的時候就會呼叫, 不同的group它的init的點也不一樣, 底下是ROUTE的init的點

static int __net_init rtnetlink_net_init(struct net *net)
{
    struct sock *sk;
    sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX,
                               rtnetlink_rcv, &rtnl_mutex, THIS_MODULE);
    if (!sk)
        return -ENOMEM;
    net->rtnl = sk;
    return 0;
}
( net/core/rtnetlink.c )

其中 rtnetlink_rcv 是callback function, 負責處理從userspace來的data
NETLINK_ROUTE 是  netlink protocol, 定義在 include/linux/netlink.h
RTNLGRP_MAX 是 route multicast group, 定義在 include/linux/rtnetlink.h

netlink_kernel_create 實作在這裡

netlink_kernel_create(struct net *net, int unit, unsigned int groups,
                      void (*input)(struct sk_buff *skb),
                      struct mutex *cb_mutex, struct module *module)
( net/netlink/af_netlink.c )

netlink protocol 除了 route 之外還有很多, 比如說 kobject 也有自己 init 的點

static int uevent_net_init(struct net *net) {
        struct uevent_sock *ue_sk;

        ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
        if (!ue_sk)
                return -ENOMEM;

        ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
                                          1, NULL, NULL, THIS_MODULE);
        if (!ue_sk->sk) {
                printk(KERN_ERR
                       "kobject_uevent: unable to create netlink socket!\n");
                kfree(ue_sk);
                return -ENODEV;
        }
        mutex_lock(&uevent_sock_mutex);
        list_add_tail(&ue_sk->list, &uevent_sock_list);
        mutex_unlock(&uevent_sock_mutex);
        return 0;
}
( lib/kobject_uevent.c )

可以看到它的 netlink protocol 是 NETLINK_KOBJECT_UEVENT, 因為 kobject 沒有 multicast group, 所以 group 的欄位填 1, callback填null因為它不需要處理從 userspace 來的 data, mutex 填null 表示使用 default 的 mutex

2.3 maintain netlink socket


許多不同 netlink protocol 的 kernel socket 用 netlink_table 來maintain, 它是個link list
struct netlink_table {
    struct nl_pid_hash hash;
    struct hlist_head mc_list;
    struct listeners __rcu *listeners;
    unsigned int nl_nonroot;
    unsigned int groups;
    struct mutex *cb_mutex;
    struct module *module;
    int registered;
};

要操作這張 table, 有底下幾個 operation

  • nl_table_lock()
  • netlink_lookup()
  • netlink_insert()
比如說, 當 kobject netlink socket 開起來之後, 就會被放進這張 table 裡

2.4 register maintain netlink socket

在開完 socket 之後, 就要註冊在不同的message要什麼callback

rtnetlink_init() {
// ......
    rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo, rtnl_calcit);
    rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL);
// ......

rtnl_register 定義如下

rtnl_register(int protocol, int msgtype, // protocol = protocol family, ref include/linux/socket.h
    rtnl_doit_func doit,     // for addition/deletion/modification
    rtnl_dumpit_func dumpit, // for retrieving information
    rtnl_calcit_func calcit) // for calculation of buffer size

從上面例子可以看到 RTM_GETLINK 有填對應的 callback, RTM_SETLINK 有些則填 null

而實際上, rtnl_register 用 array 存對應的callback

static struct rtnl_link *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
struct rtnl_link {
    rtnl_doit_func    doit;
    rtnl_dumpit_func  dumpit;
    rtnl_calcit_func  calcit;
};

3. sending rtnetlink message

舉個例子, 當netwrok dev oepn之後, 要通知userspace


3.1 netlink message format

netlink message 前面有 nlmsghdr 可供判斷這段message用的 netlink protocol 是什麼, 接著它的payload 則會依照不同的 netlink protocl 而有不同的format, 但通常都使用TLV的格式


通常一段 netlink message, 可以包含多份 message, 每份 message 前面都有 nlmsghdr, 裡面有一些資訊像是 len 表示這段 message 的長度, type 表示netlink protocol, 也可以利用nlmsghdr和macro幫忙指到對的位置

裡面的 payload, 如果是 route netlink protocol, 它的 format 長這樣

可以看到它的 payload 前面有個 rtmsg 的標頭, 進一步提供 route 相關的 message, 其後接著 TLV 格式的資訊

底下有個例子, 假如收到一包 route 相關 message, 以 buffer 表示其中每段資料的內容


4. Generic Netlink protocol

因為 netlink protocol 只用1 byte來儲存, 所以只有32種 netlink protocol  family 可以用, 如果加新的 netlink protocol family, 很快就不夠用, 再加上每次加上新的, header檔就得重新定義, 所以提出 generic netlink protocol

它的作法就是定義 generic netlink protocol 在 netlink protocol family, 再利用mux來區分

5. libnl.so

在實作上, 拆開每包 netlink message 還是很麻煩, 可以使用 libnl.so
它提供了
  • Connecting/disconnectng of sockets
  • Sending/receiving of data
  • Construction/parsing of messages
  • provides a customizeable receiving state machine
  • provides a abstract data type framework
這些功能對應到這些 library
  • generic netlink family (libnl-genl)
  • routing family (libnl-route)
  • netfilter family (libnl-nf)
底下是libnl的官網架構圖:


底下是它的實作範例



cygwin

notepad++ editor


在 cygwin 底下的vim如果在編輯時, 按上下左右箭頭會出現ASDF, 並且Backspace鍵沒有作用, 那是因為它偵測到雙位元輸入, 這時可以改用

$ vim -N test.txt

在我的環境底下, 這樣做只修好上下左右箭頭, 但Backspace還是壞的

可以考慮用外部的editor像是notepad++, 把下面這段script加到~/.bashrc裡面

#allows you to execute the command npp filename.ext
npp () {
/cygdrive/c/Program\ Files\ \(x86\)/Notepad++/notepad++.exe -multiInst -nosession -noPlugin $(cygpath -w -- "$@")
}

因為我用的是 64bit 電腦, 所以 notepad++.exe 路徑在 c:\Program Files (x86\Notepad++
後面有一些參數像 multiInst表示會額外開新的notepad++視窗而不是開在新的分頁裡
nosession表示不會紀錄開檔紀錄在"open"底下
noPlugin表示把plugin關掉
我是覺得開在新的分頁就好, 所以我的設定是這樣

#allows you to execute the command npp filename.ext
npp () {
/cygdrive/c/Program\ Files\ \(x86\)/Notepad++/notepad++.exe $(cygpath -w -- "$@")
}

最後, 就可以在cygwin底下開notepad了

$ npp textfile1.txt textfile2.txt

但是如果用分頁開有個bug, 就是如果一開始notepad++沒有開著, 那麼command line就會停在那邊等notepad關掉, 所以得先開著notepad++才會讓command line往下走

clear screen

在 linux 可以用 clear 來清光螢幕, cygwin則可以比照上面的方式加上這個功能
在 ~/.bashrc 裡面加上這個

clear() {
printf "\033c"
}

這樣在command line打上clear就可以清光螢幕了

english shell

有時候cygwin安裝的時候, 會變成中聞介面, 常常有亂碼, 要改成英文介面, 在~/.bashrc下面加上這個

export LANG='en_US'



2014/09/11

在blogger顯示程式碼

1. 在"版面配置" -> "新增小工具" -> "HTML/Javascript工具", 標題隨便打

內容填這樣:


<style>
.post .codeblock {
display: block; /* fixes a strange ie margin bug */
font-family: Courier New;
font-size: 10pt;
overflow:auto;
color: #000000
background: #f0f0f0 url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAASwCAYAAAAt7rCDAAAABHNCSVQICAgIfAhkiAAAAQJJREFUeJzt0kEKhDAMBdA4zFmbM+W0upqFOhXrDILwsimFR5pfMrXW5jhZr7PwRlxVX8//jNHrGhExjXzdu9c5IiIz+7iqVmB7Hwp4OMa2nhhwN/PRGEMBh3Zjt6KfpzPztxW9MSAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzB8HS+J9kUTvzEDMwAAAABJRU5ErkJggg==) left top repeat-y;
border: 1px solid #ccc;
padding: 10px 10px 10px 21px;
max-height:1000px;
line-height: 1.2em;
}
</style>

其中color以及background的值可以更改, 請參考RGB三色表

2. 在要顯示成code的html裡, 標籤加上 class='codeblack'
比如說

<pre class="codeblock">I am code block</pre>

3. 如果要印的code有HTML碼, 請先做encode的動作:

http://www.opinionatedgeek.com/DotNet/Tools/HTMLEncode/Encode.aspx

4. 如果要將code上色, 將標籤的class加上prettyprint:

<pre class="codeblock prettyprint"> int main () {
    printf("hello world");
}
</pre>

 顯示效果像這樣
 
int main () {
    printf("hello world");
}

 這樣就大功告成了