2014年11月10日 星期一

[轉] 使用 /proc 文件系統來訪問 Linux 內核的內容

http://www.ibm.com/developerworks/cn/linux/l-proc.html

[轉] android 編譯C程序 在android下可執行

Android編譯環境本身比較複雜,且不像普通的編譯環境:只有頂層目錄下才有Makefile文件,而其他的每個component都使用統一標準Android.mk. Android.mk文件本身是比較簡單的,不過它並不是我們熟悉的Makefile,而是經過了Android自身編譯系統的很多處理,因此要真正理清楚其中的聯繫還比較複雜,不過這種方式的好處在於,編寫一個新的Android.mk來給Android增加一個新的Component會比較簡單。 編譯Java程序可以直接採用Eclipse的集成環境來完成,這裡就不重複了。我們主要針對C/C++來說明,下面通過一個小例子來說明,如何在Android 中增加一個C程序的Hello World:
1. 在$(YOUR_ANDROID)/ development 目錄下創建hello目錄,其中$(YOUR_ANDROID)指Android源代碼所在的目錄。
- # mkdir $(YOUR_ANDROID)/development/hello
2. 在$(YOUR_ANDROID)/external/hello/目錄編寫hello.c文件,hello.c的內容當然就是經典的HelloWorld程序:
#include <stdio.h>
int main(int argc, char **argv)
{
    printf("Hello World!\n");
return 0;
}


3. 在$(YOUR_ANDROID)/external/hello/目錄編寫Android.mk文件。這是Android Makefile的標準命名,不要更改。Android.mk文件的格式和內容可以參考其他已有的Android.mk文件的寫法,針對helloworld程序的Android.mk文件內容如下
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:= \
    hello.c
LOCAL_MODULE := helloworld
include $(BUILD_EXECUTABLE)

 Android.mk的例外一種寫法也可以
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_STATIC_LIBRARIES := libcutils libc
LOCAL_MODULE := helloworld
LOCAL_MODULE_TAGS := eng

LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_SRC_FILES:= \
        hello.c

LOCAL_C_INCLUDES := bionic/libc/bionic

ifeq ($(HAVE_SELINUX),true)
LOCAL_CFLAGS += -DHAVE_SELINUX
LOCAL_SHARED_LIBRARIES += libselinux
LOCAL_C_INCLUDES += external/libselinux/include
endif

include $(BUILD_EXECUTABLE)


注意上面LOCAL_SRC_FILES用來指定源文件;,LOCAL_MODULE指定要編譯的模塊的名字,下一步驟編譯時就要用到;include $(BUILD_EXECUTABLE)表示要編譯成一個可執行文件,如果想編譯成動態庫則可用BUILD_SHARED_LIBRARY,這些可以在$(YOUR_ANDROID)/build/core/config.mk查到。
4. 回到Android源代碼頂層目錄進行編譯:
# cd $(YOUR_ANDROID) && make helloworld
注意make helloworld中的目標名helloworld就是上面Android.mk文件中由LOCAL_MODULE指定的模塊名。編譯結果如下:
target thumb C: helloworld <= development/hello/hello.c
target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld)
target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/helloworld)
target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld)
Install: out/target/product/generic/system/bin/helloworld


5.如上面的編譯結果所示,編譯後的可執行文件存放在out/target/product/generic/system/bin/helloworld目錄
啟動Android模擬器,用如下命令將文件push到Android模擬器上:
    注意:  要把SDK目錄裡的tools目錄放到PATH環境變量中.
adb shell mkdir /dev/sample
adb push hello /dev/sample/hello
adb shell chmod 777 /dev/sample/hello
先創建 /dev/sample目錄,再將編譯好的hello上傳上去,最後將hello改成可執行的。
再進入命令行模式,進入Android的shell環境:
adb shell
#cd /dev/sample
#./hello

2014年11月6日 星期四

[轉] 淺析Linux等待隊列

在Linux驅動程序中,可以使用等待隊列(wait queue)來實現阻塞進程的喚醒。等待很早就作為一個基本的功能單位出現在Linux內核中,它以隊列為基礎數據結構,與進程調度機制緊密結合,能夠用於實現內核中的異步事件通知機制。
我們從它的使用範例著手,看看等待隊列是如何實現異步信號功能的。以下代碼節選自kernel/printk.c。
DECLARE_WAIT_QUEUE_HEAD(log_wait); // 初始化等待隊列頭log_wait
static DEFINE_SPINLOCK(logbuf_lock); // 定義自旋鎖logbuf_lock
int do_syslog(int type, char __user *buf, int len)
{
        ... ... ...
        err = wait_event_interruptible(log_wait, (log_start - log_end)); // 等待
        /*
        // 我們先來看看linux/wait.h中定義的wait_event_interruptible()的原型
        #define wait_event_interruptible(wq, condition)
        ({
               int __ret = 0;
                if ( (!condition) )
                        __wait_event_interruptible(wq, condition, __ret);
                __ret;
        })
        // wait_event_interruptible()將等待隊列加入第一個參數wq為等待隊列頭的等待隊列鏈表中,
        // 當condition滿足時,wait_event_interruptible()會立即返回,否則,阻塞等待condition滿足。
        // 與之類似的還有wait_event(),不同點在於wait_event_interruptible()可以被信號喚醒。
        // __wait_event_interruptible()原型如下:
        #define __wait_event_interruptible(wq, condition, __ret)
        do {
                DEFINE_WAIT(__wait); // 定義等待隊列__wait
                for(;;) {
                        prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);
                        // prepare_to_wait()的作用是:將等待隊列__wait加入以wq為等待隊列的等待隊列
                         // 鏈表中,並且將進程狀態設置為TASK_INTERRUPTIBLE
                        if (condition)
                                break; // 如果condition滿足則跳出
                       if (!signal_pending(current)) { // 如果不是被信號喚醒
                                ret = schedule_timeout(ret);
                                if (!ret)
                                        break;
                                continue;
                        }
                          ret = - ERESTARTSYS;
                          break;
                          schedule(); // 放棄CPU,調度其它進程執行
                }
                finish_wait(&wq, &__wait);
                // finish_wait()的作用是,將等待隊列__wait從等待隊列頭wq指向的等待隊列鏈表中移除,
                // 並且將進程狀態設置為TASK_RUNNING
        }while(0)
        */
        ... ... ...
        spin_lock_irq(&lock_wait); // 鎖定
        ... ... ...
        log_start++; // log_start自加,使得(log_start - log_end)這個等待隊列的condition滿足
        ... ... ...
         spin_unlock_irq(&lock_wait); // 解鎖
        ... ... ...
}
void wake_up_klogd(void)
{
        ... ... ...
        wake_up_interruptible(&lock_wait); //喚醒等待隊列
}
void release_console_sem(void)
{
        ... ... ...
        spin_lock_irqsave(&lock_wait); // 鎖定
        wake_klogd = log_start - log_end;
         ... ... ...
        spin_unlock_irqrestore(&lock_wait); // 解鎖
        ... ... ...
        if (wake_klogd)
                wake_up_klogd(); // 如果wake_klogd非負,則調用wake_up_klogd()來喚醒等待隊列
        ... ... ...
}
我們最後再總結一下等待隊列wait_event_interruptible():


在wait_event_interruptible()中首先判斷conditon是不是已經滿足,如果是則直接返回0,否則調用__wait_event_interruptible(),並用__ret來存放返回值。__wait_event_interruptible()首先將定義並初始化一個wait_queue_t變量__wait,其中數據為當前進程狀態current(struct task_struct),並把__wait加入等待隊列。在無限循環中,__wait_event_interruptible()將本進程置為可中斷的掛起狀態,反覆檢查condition是否成立,如果成立則退出,如果不成立則繼續休眠;條件滿足後,即把本進程運行狀態置為運行態,並把__wait從等待隊列中清除,從而進程能夠調度運行。

掛起的進程並不會自動轉入運行的,因此,還需要一個喚醒動作,這個動作由wake_up_interruptible()完成,它將遍歷作為參數傳入的lock_wait等待隊列,將其中所有的元素(通常都是task_struct)置為運行態,從而可被調度。

[轉] wait_event_interruptible 使用方法

1. 關於 wait_event_interruptible() 和 wake_up()的使用

讀一下wait_event_interruptible()的源碼,不難發現這個函數先將
當前進程的狀態設置成TASK_INTERRUPTIBLE,然後調用schedule(),
而schedule()會將位於TASK_INTERRUPTIBLE狀態的當前進程從runqueue
隊列中刪除。從runqueue隊列中刪除的結果是,當前這個進程將不再參
與調度,除非通過其他函數將這個進程重新放入這個runqueue隊列中,
這就是wake_up()的作用了。

由於這一段代碼位於一個由condition控制的for(;;)循環中,所以當由
shedule()返回時(當然是被wake_up之後,通過其他進程的schedule()而
再次調度本進程),如果條件condition不滿足,本進程將自動再次被設
置為TASK_INTERRUPTIBLE狀態,接下來執行schedule()的結果是再次被
從runqueue隊列中刪除。這時候就需要再次通過wake_up重新添加到
runqueue隊列中。

如此反覆,直到condition為真的時候被wake_up.
  
可見,成功地喚醒一個被wait_event_interruptible()的進程,需要滿足:

   在 1)condition為真的前提下,2) 調用wake_up()。 

所以,如果你僅僅修改condition,那麼只是滿足其中一個條件,這個時候,
被wait_event_interruptible()起來的進程尚未位於runqueue隊列中,因
此不會被 schedule。這個時候只要wake_up一下就立刻會重新進入運行調度。
  
2. 關於wait_event_interruptible的返回值

根據 wait_event_interruptible 的宏定義知:

   1) 條件condition為真時調用這個函數將直接返回0,而當前進程不會
      被 wait_event_interruptible和從runqueue隊列中刪除。

   2) 如果要被wait_event_interruptible的當前進程有nonblocked pending
      signals, 那麼會直接返回-ERESTARTSYS(i.e. -512),當前進程不會
      被wait_event_interruptible 和從runqueue隊列中刪除。

   3) 其他情況下,當前進程會被正常的wait_event_interruptible,並從
      runqueue隊列中刪除,進入TASK_INTERRUPTIBLE狀態退出運行調度,
      直到再次被喚醒加入runqueue隊列中後而參與調度,將正常返回0。

附1:wait_event_interruptible  宏

#define wait_event_interruptible(wq, condition)    \
({                                                 \
     int __ret = 0;                                  \
     if (!(condition))                               \
      __wait_event_interruptible(wq, condition, __ret); \
      __ret;                                         \
})

註: C語言中{a,b, ..., x}的的值等於最後一項,即x,因此上述
宏的值是 __ret。


附2:wait_event_interruptible()和 wake_up的等效代碼

wait_event_interruptible(wq, condition) /*等效沒有考慮返回值*/
{
     if (!(condition))
     {
         wait_queue_t _ _wait;
         init_waitqueue_entry(&_ _wait, current);
         add_wait_queue(&wq, &_ _wait);
         for (;;)
         {
            set_current_state(TASK_INTERRUPTIBLE);
            if (condition)
            break;
            schedule();  /* implicit call: del_from_runqueue(current)*/
         }
         current->state = TASK_RUNNING;
         remove_wait_queue(&wq, &_ _wait);
      }
}


void wake_up(wait_queue_head_t *q)
{
      struct list_head *tmp;
      wait_queue_t *curr;
      list_for_each(tmp, &q->task_list)
      {
        curr = list_entry(tmp, wait_queue_t, task_list);
        wake_up_process(curr->task);
        /* implicit call: add_to_runqueue(curr->task);*/
        if (curr->flags)
          break;
      }