2014年11月13日 星期四
2014年11月11日 星期二
2014年11月10日 星期一
[轉] 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)
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)
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;
}
}
讀一下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;
}
}
訂閱:
文章 (Atom)