2010年4月28日 星期三

Android 編譯 Native C module

Android編譯環境本身比較複雜,不像普通的編譯環境:
只有頂層目錄下才有Makefile文件,而其他的每個component都使用統一標準的Android.mk。Android.mk文件本身是比較簡單的,不過它並不是我們熟悉的Makefile,而是經過了Android自身build system的很多處理,因此要真正釐清其中的聯繫還蠻複雜的,不過這種方式的好處在於,編寫一個新的Android.mk來給Android增加一個新的Component會比較簡單。

編譯Java程式可以直接採用Eclipse的IDE來完成,這裡就不重複了。
我們主要針對C/C++來說明,下面通過一個小例子來說明,
如何在Android 中增加一個C程式的Hello World:

1. 在$(YOUR_ANDROID)/external 目錄下創建hello目錄,
其中$(YOUR_ANDROID)指的是Android source code所在的目錄。
# mkdir $(YOUR_ANDROID)/external/hello

2. 在$(YOUR_ANDROID)/external/hello/目錄中編寫hello.c文件,
hello.c的內容當然就是經典的HelloWorld程式:

#include stdio.h

int main()
{
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_SRC_FILES:= \

hello.c

LOCAL_MODULE := helloworld

include $(BUILD_EXECUTABLE)


注意上面LOCAL_SRC_FILES用來指定 source file;
LOCAL_MODULE指定要編譯的module的名字,下一步驟編譯時就要用到;
include $(BUILD_EXECUTABLE)表示要編譯成一個可執行文件,
如果想編譯成動態庫則可用 BUILD_SHARED_LIBRARY,
這些可以在 $(YOUR_ANDROID)/build/core/config.mk 查到。

4. 回到Android source code頂層目錄進行編譯:

# cd $(YOUR_ANDROID) && make helloworld

注意make helloworld中的目標名helloworld就是上面Android.mk文件中
由LOCAL_MODULE指定的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
,通過”adb push”將它傳送到模擬器上,
再通過”adb shell”登錄到模擬器終端,就可以執行了


[Reference]
http://www.j2medev.com/android/ShowArticle.asp?ArticleID=5436

2010年4月27日 星期二

[書評] 嵌入式系統—使用 C 和 GNU 開發工具

花了幾天的時間把這本兩百多頁的薄書看完
個人是覺得內容有一看的價值
不過要花錢入手一本還是緩緩先

這本書有點像是給RD看的小說一樣
技術面其實沒有很深入
可是卻也不會太淺
我覺得有點像雞肋
不太適合初學者與進階者看
好在個人就是在這中間 XD
所以就花了點時間把它看完

可能嵌入式系統的技術真的是太雜太廣
沒辦法有一個專門的介紹
但通盤的講解所帶來的效應就是不深入

這本書在網路上也有原文版電子書
可以google一下,應該不難找
推薦看原文的
O'Reilly 的中文譯者...
我不曉得是翻譯工作太枯燥還是如何
本書章節翻譯品質參庛不齊 (O'Reilly其他本也有這毛病...)
有些地方的語意端詳半天還是不得其解
查看原文才知道其意...
這就是翻譯書的大缺點啊

好險這本書是跟我同事借的
幾天就可以看完的雞肋本
因此沒有浪費錢的感覺 XDrz

順帶一提
今年六月 O'Reilly 要出三本必敗新書
希望不要太貴..

Autotools
A Practioner's Guide to GNU Autoconf, Automake, and Libtool

The Linux Programming Interface
A Linux and UNIX System Programming Handbook

LPI Linux Certification in a Nutshell (3rd)

2010年4月26日 星期一

bitfield 位元欄位

蠻特殊的一種bitwise的宣告方法
可以視方便性與bitmask交換使用

struct
{
     unit8_t bit0 : 1;
     unit8_t bit1 : 1;
     unit8_t bit2 : 1;
     unit8_t bit3 : 1;
     unit8_t nibble : 4;
} foo;

2010年4月23日 星期五

I/O-mapped and Memory-mapped

The first address space is called the memory space and is intended mainly for memory devices;
the second is reserved exclusively for peripherals and is called the I/O space.

However, peripherals can also be located within the memory space,
at the discretion of the hardware designer.
When that happens, we say that those peripherals are memory-mapped and
that system has memory-mapped I/O.
Some processors support only a memory space,
in which case all peripherals are memory-mapped.

2010年4月19日 星期一

Ubuntu 9.10 Koala on ASUS W7S

service pack一包接一包
在不錯的硬體上面跑也是有如老牛龜車的M$ XP
實在是讓人好生失望

話說其實會使用XP也是為了一些PC game
現在開始工作以後
因為沒啥時間玩遊戲
所以必玩的遊戲都要過濾再過濾
想玩的遊戲硬體要求已是W7S不堪應付了
Intel CoreDuo T7300
2.5G RAM
nVIDIA GeForce 8400M G 128MB
以上正是W7S的硬體規格
要拿來跑Diablo III以及StarCraft II
應該會有很大的問題 XD

基於以上廢話的種種原因
決定將W7S的OS換成Ubuntu/Linux

使用的版本是Ubuntu 9.10 Karmic Koala Desktop edition

整個安裝過程就是一個順字
所有的硬體驅動也順利完成
WiFi
Bluetooth
Ethernet
Sound Card
都沒問題啦!

唯一需要手動選擇的只有nVIDIA的官方顯示驅動
因為nVIDIA這片顯卡的Linux驅動程式 source code沒有 open source
所以 Ubuntu community 不推薦使用此種 proprietary driver
也造成此driver不會被自動安裝
但還是可以自行手動選擇安裝此driver

=======
安裝完作業系統後
接下來就是一些使用者程式的選擇

輸入法我選擇使用ibus
只要安裝好語言包
就可以使用其他語言的輸入法
例如
ibus+Chewing (酷音)
ibus+Anthy (日文輸入)

使用到現在也沒有什麼特別問題,穩定。

看影片推薦使用SMPlayer

BT下載推薦 Deluge or 內建的 Transmission

本篇網誌就是在W7S+Ubuntu的組合下完成!

2010年4月15日 星期四

Linux Shell 彩色控制以及色碼簡介

\33[ pattern ; foreground ; background m
YOUR_CONTENT
\33[0m

例如: \33[1;33;40mMYMESSAGE\33[0m
==> 高亮度(1);黃字(33);黑底(40)
  • pattern
0 一般亮度
1 高亮度
4 加底線
5 灰底

[Reference]
Tinux Blog
http://tinux.no-ip.com/wordpress/?p=89

2010年4月14日 星期三

Linux Device Driver note

Linux 驅動程式的類別:
- 字元設備 char device
- 區塊設備 block device
- 網路設備 network device

一些重要的資料結構:
  • Char Device Driver
struct file_operations
struct file
struct inode
  • Block Device Driver
struct block_device
struct block_device_operations
struct gendisk
struct request
  • Network Device Driver
struct net_device
struct sk_buff

2010年4月8日 星期四

archive 程式 ar 簡介 with ranlib

ar ( archive ) 以下節錄自 Linux man page

ar - create, modify, and extract from archives
ar 這支程式可以對 archive(靜態函式庫) 做以下的動作,
create(建造),modify(修改),extract(擷取)

以下列出幾個常用的 options 解釋:

d: delete
=> 將 files 從 archive 中移除
r: replace
=> 將 files 以取代的方式插入 archive 中
u: update
=> 通常r option 是取代所有,但u option 僅會將較 archive中新的 files 插入
v: verbose
=> 顯示執行情況以及額外的資訊

ar uv mylib.a first.o second.o
上述指令就是將兩個obj 檔建立成一個 mylib.a,並且顯示建立過程的執行資訊


ranlib - generate index to archive

ranlib generates an index to the contents of an archive and stores it in the archive. The index lists each symbol defined by a member of an archive that is a relocatable object file.
ranlib這支程式會產生一個index檔,存的是archive的組成內容,並且將這個index也塞入archive中。index 會列出每個 archive 中的 member 所定義的 symbol,這些 member 說穿了就是 relocatable object file (可重定位的目的檔)。

可以使用 nm -s 或者 nm --print-armap 來列出這個 index

跑這支程式的作用是增加 linking 到 library 的速度,而且可以讓在 library 內的 routines 可以互相呼叫,而不必顧慮到這些 routines 在 archive 中的擺置順序。

實際上,GNU ranlib 就是執行 ar -s

2010年3月30日 星期二

IPC facility - Pipes

Process Pipes

#include stdio.h

FILE* popen( const char* command, const char* open_mode );
int pclose( FILE* pipe_stream );

open_mode 必須是 "r" or "w",但不允許同時開放讀寫的權限,
這是 pipe 與一般 file stream 不同的特性

FILE* read_fp;
read_fp = popen( "cat prog.c | wc -l", "r" );
nread = fread( buf, sizeof(char), sizeof(buf), read_fp );
pclose( read_fp );


The Pipe Call

#include unistd.h

int pipe( int file_descriptor[2] );
任何寫到 file_descriptor[1] 的資料,可以從 file_descriptor[0] 讀回來
*資訊的順序是 FIFO

另外由於 file descriptor 是屬於較低階的檔案處理,
所以不可以使用 file streams 相關的函數,
而需要使用較低階的 read 和 write 等等的 system calls來讀寫

一般來說,pipe 都會使用在相關的兩個 process 之間,例如 parent & child
file descriptors 會跨 fork,所以 parent 開啟的 fd,child 會有同樣的值

int pipefd[2]
pipe( pipefd );
pid_t pid = fork();

switch( pid ) {
case 0: /* parent */
close( pipefd[0] ); /* parent 關閉讀的 pipe */
write( pipefd[1], data, sizeof(data) );
break;

case -1: /* error */
exit( 1 );
break;

default: /* children */
close( pipefd[1] ); /* children 關閉寫的 pipe */
read( pipefd[1], data, sizeof(data) );
break;
}


Named Pipe: FIFO

#include sys/types.h
#include sys/stat.h

int mkfifo( const char* filename, mode_t mode );
mode 是設定 RWX 的權限,例如 0666,實際建置結果還要看系統的 mask 設定
mkfifo 執行成功後,就會產生一個 pipe file
例如 prwxr-xr-x ...... /tmp/myfifo
之後就使用 open, close, read, write 等低階函數來操作它

open 一個 pipe file,有以下三個 flag 可以使用
O_RDONLY, O_WRONLY, O_NONBLOCK (會對 open read write 3個函數產生影響 )





[Reference]
Begining Linux Programming 4th edition
Chapter 13 Inter-Process Communication: Pipes

2010年3月26日 星期五

IPC facility - Message Queue

IPC facility - Shared Memory

IPC facility - Semaphore

Semaphore

#include sys/types.h
#include sys/ipc.h
#include sys/sem.h

int semget ( key_t key, int num_sems, int sem_flags );
key在這邊是要做 IPC 的各個 process 之間共有的一個鍵值 (key value)
key需要相同,才能確保 semget 會回傳代表共用 semaphore 的 sem_id
每個 process 都會拿到只供自己使用的 sem_id 但是其指向的是共用的 semaphore
後文所述另外兩種 IPC facilities 也是採用同樣的原理

num_sems 表示需要使用的 semaphore 數量,一般最常見的值是 1
表示只使用一個 semaphore,如有需要也可以使用多個,會以陣列表示
*所以一個 sem_id 可以表示一個 semaphore 也可以表示一組 semaphores

sem_flags 可以設定類似檔案擁有的權限值,如 0666 這種值
另外可與 IPC_CREAT 或 IPC_EXCL 使用 bitwise OR 運算結合
IPC_CREAT: create a semaphore
IPC_EXCL: exclusive action

[example]
key_t key = (key_t) 54321;
int sem_id = semget ( key, 1, 0644 | IPC_CREAT );



int semop ( int sem_id, struct sembuf* sem_ops, size_t num_sem_ops );
sem_id 是由 semget 回傳的 semaphore identifier
在此表示指定要使用的 semaphore

struct sembuf {
short sem_num; /* index */
short sem_op; /* operations on semaphore */
short sem_flg; /* SEM_UNDO */
}

num_sem_ops 表示 sem_ops 陣列的 size

[example]
/* P action [wait] */
struct sembuf sembuf_p;
sembuf_p.sem_num = 0;
sembuf_p.sem_op = -1; /* for P( ) action */
sembuf_p.sem_flg = SEM_UNDO;

/* V action [signal] */
struct sembuf sembuf_v;
sembuf_v.sem_num = 0;
sembuf_v.sem_op = 1; /* for V( ) action */
sembuf_v.sem_flg = SEM_UNDO;

semop( sem_id, &sembuf_p, 1 );



int semctl ( int sem_id, int sem_num, int command, ... );
sem_id 是由 semget 回傳的 semaphore identifier
在此表示指定要使用的 semaphore

sem_num 用來表示 semaphore number,用在 semaphore 陣列
其值通常是 0,表示第一個而且是唯一的 semaphore ( 單一 semaphore 的情況 )

command 表示要採取的 action,主要使用的是 SETVAL 以及 IPC_RMID
SETVAL 是拿來設定值,會用到第四個參數 union semnu 中的 val 成員
IPC_RMID 是用來移除 semaphore

第四個參數,如果有設定的話,是一個 C union,定義如下

union semnu{
int val;
struct semid_ds* buf;
unsigned short array;
}

[example]
union semun sem_union;
sem_union.val = 1;
semctl ( sem_id, 0, SETVAL, sem_union );
------
semctl ( sem_id, 0, IPC_RMID, sem_union );



[Reference]
Begining Linux Programming 4th edition
Chapter 14 Semaphore, Shared Memory, and Message Queues

2010年3月24日 星期三

POSIX Threads

POSIX Threads

#include pthread.h

int pthread_create( pthread_t* thread, pthread_attr_t* attr,
void *(*start_routine)(void*), void* arg );

pthread_attr_t 可以利用其他設定 thread attribute 的函數 customize
arg 是丟入 start_routine 的參數

void pthread_exit( void* retval );

在成為 thread 的 routine 當中,呼叫 pthread_exit以傳回 retval

int pthread_join( pthread_t th, void** thread_return );

thread_return 會存 retval 的 address

跟 process-based fork-exec 組合不同的是
同屬一個 process 的 threads 之間是可以共享 全域變數
thread 是去執行一個 program 裡面的一個 function
而 fork-exec 是去執行一個 program

使用 thread 有一點需要特別注意:
critical timing condition
在使用 thread 共享的資源中,包含 call by address 的資源
要注意 thread 執行順序並沒有固定
所以資料值可能會因為 timing 的關係而產生錯誤

[example]
函數A使用 thread 的方式執行函數B,並且使用 call by address 的方式傳遞參數 x
若之後 A 會繼續改變 x 變數,而 B 在執行開始就會讀取 x 的值 (單純讀取)
Here comes the timing problem of scheduling :
A before B => B 原本要讀的值被 A 改變了
B before A => B 正確執行

[solution]
將參數傳遞的方式改為 call by value
則 A 對 x 所做的改變就不會影響到 B 的執行結果

不過若是 B 也需要改變 x 的值,則需要更進一步的處理

==========================================================

Synchronization with Semaphores

#include semaphore.h

int sem_init ( sem_t* sem, int pshared, unsigned int value );
int sem_destory ( sem_t* sem );
A semaphore is created with sem_init function, and destoryed by sem_destroy

value 的值決定是 binary semaphore 或 counting semaphore

int sem_wait ( sem_t* sem ); /* value-- */
atomically decrease the value of the semaphore by 1
int sem_post ( sem_t* sem ); /* value++ */
atomically increase the value of the semaphore by 1

利用 semaphore 可以防止 polling 所帶來的 計算資源浪費

==========================================================

Synchronization with Mutexes

#include pthread.h

int pthread_mutex_init ( pthread_mutex_t* mutex,
const pthread_mutexattr_t* mutexattr );
int pthread_mutex_destroy ( pthread_mutex_t* mutex );

int pthread_mutex_lock ( pthread_mutex_t* mutex );
int pthread_mutex_unlock ( pthread_mutex_t* mutex );

To control access to a critical section of code you lock a mutex
before entering the code section and then unlock it when you have finished.

[example]
pthread_mutex_lock( &mutex );
/***************************
***** critical section ******
***************************/
pthread_mutex_unlock( &mutex );


[Reference]
Begining Linux Programming 4th edition
Chapter 12 POSIX Threads

Processes and Signals

Starting New Processes

#include stdlib.h
int system (const char* string);
/* not recommened for use */

==========================================================

Replacing a Process Image
#include unistd.h
exec family

[example]
/* argument list vector version */
char* const ps_argv[] = { "ps", "ax", 0 };
/* environment variable vector */
char* const ps_envp[] = { "PATH=/bin:/usr/bin", "TERM=console", 0 };

-------End with null pointer-----------
execl ( "/bin/ps", "ps", "ax", 0 );
execlp ( "ps", "ps", "ax", 0 ); /* assumes /bin is in PATH */
execle ( "/bin/ps", "ps", "ax", 0, ps_envp ); /* passes own environment */
-----------vector version--------------
execv ( "/bin/ps", ps_argv );
execvp ( "ps", ps_argv );
execve ( "/bin/ps", ps_argv, ps_envp );
---------------------------------------

l 代表 list [ 表示在exec內的命令列參數直接以字串 list寫入 ]
v 代表 vector [ 表示以陣列方式將命令列參數傳入exec函數 ]
p 代表 path [ 表示 command 可以不需要標明絕對路徑 ]
e 代表 environment [ 表示以陣列方式將環境變數傳入exec函數 ]

=========================================================

Duplicating a Process Image
#include sys/types.h
#include unistd.h
pid_t fork( void );

open file descriptors are preserved across calls to fork and exec

Waiting for a Process
#include sys/types.h
#include sys/wait.h
pid_t wait( int* stat_val );
pid_t waitpid( pid_t pid, int* stat_val, int options );

WIFEXITED( stat_val )
WEXITSTATUS( stat_val )

for parent process to release zombie child processes

=========================================================

Sinals
#include signal.h
void (*signal (int sig, void (*func)(int) ) ) (int);
/* not recommened for use */

#include sys/types.h
#include signal.h
int kill ( pid_t pid, int sig );

#include unistd.h
unsigned int alarm ( unsigned int seconds );

#include signal.h
int sigaction( int sig, const struct sigaction* act, struct sigaction* oldact );


Functions that are safe to call inside a signal handler,
either to be re-entrant or not to raise signals themselves.


[Reference]
Begining Linux Programming 4th edition
Chapter 11 Processes and Signals

2010年3月23日 星期二

Linux 程式設計 --使用C語言--

Linux開發工具介紹
--GNU make
--GNU gcc

Linux檔案處理
--低階 file descriptor [ int fd = open( ) ]
open, close, read, write, creat, link, unlink, symlink, ...

--高階 file stream [ FILE* fp = fopen( ) ]
fopen, fclose, fread, fwrite, ...

Linux環境
command line argument
environment variable
date and time
temporary file creation
user information
host information
logging
resource limits

struct file 簡介

struct file 定義在 /linux/include/linux/fs.h(Linux 2.6.11 核心)中,其原型是:




struct file 代表一個open的 file,
系統中的每個open的file在kernel space都有一個關連的struct file,
此資料結構由kernel在open file時創建,
並傳遞給在file上進行操作的任何函數。
在file的所有instance都close後,kernel會釋放這個資料結構,
在kernel或者driver source code中,
struct file的pointer通常被命名為file或filp。


[Reference]
http://linux.chinaunix.net/techdoc/system/2008/08/19/1026178.shtml

2010年3月17日 星期三

GDB 使用簡介

*編譯原始程式碼時,gcc需加-g選項,以在程式中加入除錯資訊
  • $ gcc -g -o hello hello.c
*執行GDB有兩種方式
  • 先單獨鍵入gdb,等進入gdb環境之後,再鍵入file hello
  • 直接鍵入 gdb hello
*退出GDB有兩種模式
  • (gdb) quit 完全退出GDB程式,回到 linux console
  • (gdb) kill 終止當前正在除錯的程式,仍在gdb環境下
*原始程式碼的檢視,指令是 list,預設顯示10行
  • (gdb) list [行號|函數名稱]
  • (gdb) list 9 或者是 list main
*設定程式的命令列參數,指令是 set args [arg1 ... argn]
  • (gdb) set args add 2 3
  • 如同執行 ./hello add 2 3
*在GDB中執行程式
  • run
  • step [單步執行,若有函數呼叫,會進入函數內部]
  • next [單步執行,不會進入函數內部]
  • continue
*中斷點 (break points) 的操作
  • 設定:break [行號|函數名稱|條件運算式]
  • 撤銷:delete [中斷點編號]
  • 查看:info break
*監看點 (watch points) 的操作
  • 設定:watch [變數名稱]
  • 查看:info watchpoints



[Reference]
Linux系統程式設計 使用C

2010年3月5日 星期五

GNU make manual 閱讀整理

Chpater 4 Writing Rules

Chapter 5 Writing the Commands in Rules

@command: suppress the echoing
-command: suppress the error, keep going
+command: ignore -n -t -q options
=======================
-n: --just-print
-t: --touch
-q: --question
=======================

2010年3月3日 星期三

C語言的Ternary Operator

C Variants

A GNU extension to C allows the second operand to be omitted,

and the first operand is implicitly used as the second as well:

a = x ? : y;

The expression is equivalent to

a = x ? x : y;

except that if x is an expression, it is evaluated only once.

The difference is significant if evaluating the expression has side effects.

2010年2月24日 星期三

Shell Script 進階變數功能

變數擴展 ${varname}
命令替換 $(command)
算術擴展 $((expression))

${var:-default}
測試var,若未定義或為null值,則傳回default,否則傳回var
${var:=default}
測試var,若未定義或為null值,則將var設為default
${var:?message}
測試var,若未定義或為null值,則印出message,並且中斷script執行
${var:+value}
測試var,若未定義或為null值,則傳回value,否則傳回null

上述各式,若移去 :,則只測有無定義,不測是否為空值

-----------------------------------------------------------
取字串切片
${var:pos}
${var:pos:len}

計算字串長度
${#var}

取得陣列元素個數
${#array[@]}
${#array[*]}

比對樣式,做刪除字串的動作
${var#pattern}
${var##pattern}
${var%pattern}
${var%%pattern}

#: 從最左邊開始
%: 從最右邊開始

比對樣式,做取代的動作
${var/pattern/sub_str}
${var//pattern/sub_str}
${var/#pattern/sub_str}
${var/%pattern/sub_str}


[Reference]
Linux Shell 程式設計實務 Chapter 7