2008年9月26日 星期五

Android 1.0 SDK Released

更新速度真快@@
Android 1.0 SDK已經在2008/09/24釋放出來可以下載囉!
有興趣的人快去載來玩看看吧!

http://android-developers.blogspot.com/2008/09/announcing-android-10-sdk-release-1.html

0.9->1.0改變了些什麼呢?
http://code.google.com/android/migrating/0.9-1.0/changes-overview.html

  1. Content Provider

  2. Settings.System加入了許多新的Wi-Fi設定,用來提供儲存Wi-Fi的狀態。

  3. Device Hardware

  4. Camera增加了新的介面,名為Camera.ShutterCallback,用來在拍照後快門關閉後的事件回呼函式。
    SensorManager增加了一些新的fields。

  5. Maps

  6. ItemizedOverlay增加新的方法(methods)到overlays。
    MyLocationOverlay類別增加一個新的方法去偵測是否有指南針被顯示在MapView以及接收sensor位置改變時的事件。

  7. Media

  8. 在AudioManager裏加入了新的載入/卸載音效的方法。
    MediaMetadataRetriver類別在此版尚不支援。
    android.media.MediaPlayer.getFrameAt()在此版本也不支援。

  9. NetWork

  10. android.net.wifi新增了一些類別用來保持wi-fi無線喚醒及識別配對加密的WPA功能,這也改變了其它相關類別的fields及methods。
    org.apache.http.impl已被加入到這個平台

  11. Package manager
  12. 刪除使用者資料及快取(cache)的方法已經從PackageManager中移除。

  13. Text input
  14. TextKeyListener增加了release()的方法。

  15. Test

  16. 在android.test裏增加了一些新的類別及介面,如建置包含測試裝置的測試環境,測試單元及測試包。
    新增junit.runner套件。
    新增android.test.mock套件含工具類別,用來提供stub或mock來進行Android框架區塊單元的各種測試。
    新增android.test.suitebuilder套件含工具類別,用來提供test runner classes。

  17. Views

  18. 新增數個XML屬性給View物件。
    新增支援音效功能。
    你已可以監聽sensors(感知器)的改變狀態。
    WebView加入新的透明物件用來給移動的WebView跨越process的範圍。
    WebView加入新方法給managing scrollbar overlays.

  19. XML

  20. org.xmlpull.v1在這平台已可以使用

  21. Widgets

  22. verifyDrawable(Drawable)這個方法的屬性已從公開(public)改變成保護(protected).還有一些小widget有做一些變動。



1.0 Source Code下載網址
http://code.google.com/p/android/downloads/list
主要項目如下四個:
Linux 2.6.25 for Android 1.0 SDK,Release 1
Source for WebKit as included in Android 1.0 SDK, Release 1
Emulator for Android 1.0 SDK, Release 1
Android Development Tools: Plugins 0.8.0 for Eclipse 3.3/3.4, for Android SDK 1.0_r1(source only)

2008年9月25日 星期四

[轉載]斷點續傳技術

北京理工大學 20981 陳罡

要做手機斷點續傳了,網上的代碼有很多,但是多數要麼是過於復雜,要麼是用java,pascal之類的語言編寫的。都不適合直接用在手機上,無奈之下我這個懶人開始動手自己寫了。
(1)手機斷點續傳的未來?
手機上開發應用程序的時候,或多或少都要用到gprs連接互聯網(Internet),從互聯網上的服務器中把數據取出來,然後存儲到手機上,利用專門的客戶端來查看。這就可以美其名曰“在線更新”了。隨着智能手機的處理能力越來越強以及gprs升級在即(也就是傳說中的2.5G或3G了),手機的網絡應用更加惹人注意,尤其是在RSS手機新聞組、手機mail下載大附件、手機電視實時緩沖視頻流、在或者在線聽mp3、下載圖鈴之類的3G手機網絡應用上,是否具有斷點續傳的功能尤其重要。這項技術還將發展相當長的一段時間(除非移動把什麼cmwap,cmnet都統一了,目前還沒有看到有統一的跡象)。

(2)手機斷點續傳的實質?
手機上的應用越花俏就必然對應着需要下載的數據文件就會越大。目前絕大多數的手機瀏覽器,都支持gprs下載功能,所不同的是,絕大多數都沒有斷點續傳的功能。比如你要下載一首几百K的mp3,下載到2/3的時候,突然進了地鐵或者信號不好,斷開了,那就意味着剛剛的那2/3已經浪費了。再次下載的時候,就需要重新下載了。斷點續傳這個技術就是用來解決這個問題的,它的實質就是如果程序開始准備重新下載的時候,先檢查一下,已經下載了多少了,然後再接着剛剛下載過的地方繼續,接着下載。

(3)傳統斷點續傳的原理?
首先,斷點續傳不是什麼高深的技術,它是標准的http協議中已經定義了很久的東西;其次,需要服務器支持,我這邊使用的是apache的服務器,對斷點續傳支持得很好。

其實斷點續傳的原理很簡單,就是在http的請求上和一般的下載有所不同而已。
假設服務器域名為www.5mbox.com,文件名為/bbs/mp1.mp3
當web瀏覽器請求從服務器上的一個文時,所發出的請求如下:
GET /bbs/mp1.mp3 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive


服務器收到請求以後,會回應如下內容:
200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon, 30 Apr 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT

...後面跟着就是數據了。

斷點續傳,也就是要從文件已經下載的地方開始繼續下載。所以在客戶端瀏覽器傳給
web服務器的時候要多加一條信息--從哪里開始。
下面是用自己編的一個"瀏覽器"來傳遞請求信息給web服務器,要求從2000070字節開始。
GET /bbs/mp1.mp3 HTTP/1.0
User-Agent: NetFox
RANGE: bytes=2000070-
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

仔細看一下就會發現多了一行RANGE: bytes=2000070-
這一行的意思就是告訴服務器mp1.mp3這個文件從2000070字節開始傳,前面的字節不用傳了。
服務器收到這個請求以后,返回的信息如下:
206
Content-Length=106786028
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT

。。。二進制數據

和前面服務器返回的信息比較一下,就會發現增加了一行:
Content-Range=bytes 2000070-106786027/106786028
返回的代碼也改為206了,而不再是200了。

知道了以上原理,就可以進行斷點續傳的編程了。

(4)斷點續傳的實現
懶得一個個解釋,直接上代碼了(請注意,下面的代碼是在win32上做驗證用的,不是給手機用的),採用c++編寫,dev-c++ 4.9.9.2編譯的,我相信g++都行vs系列自然不在話下:

M5HttpDown.h文件的內容:
#ifndef _M5_HTTP_DOWN_H_
#include <process.h>
#include <wininet.h>
#include <winsock2.h>
#include <stdio.h>
#define HTTP_DEBUG_MODE 1

#define HTTP_WEB_PORT 80
#define HTTP_TEMP_BUF_LEN 120
#define HTTP_SEND_BUF_LEN 256
#define HTTP_RECV_BUF_LEN 4096
#define HTTP_HDR_OK "200 OK"
#define HTTP_HDR_FILE_LEN "Content-Length: "
#define HTTP_HDR_DIV "\r\n"
#define HTTP_HDR_END "\r\n\r\n"
#define HTTP_PREFIX "http://"
#define HTTPS_PREFIX "https://"

// 這里分成了兩個get字符串的定義,主要是為了兼容普通的http下載
// 以及支持斷點續傳的http下載
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #999;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">#define HTTP_COMMON_GET &quot;GET /%s HTTP/1.1\r\n\
User-Agent: Opera 8.0\r\n\
Host: %s:%d\r\nAccept: */\
*\r\nConnection: Keep-Alive\r\n\r\n&quot;

#define HTTP_RESUME_GET &quot;GET /%s HTTP/1.1\r\n\
User-Agent: Opera 8.0\r\n\
Host: %s:%d\r\nAccept: */\
*\r\nRANGE: bytes=%d-\r\n\
Connection: Keep-Alive\r\n\r\n&quot;
</pre>

// 這里為了方便起見,就沒有用什麼notifier或者虛擬函數之類的東西了,直接回調
// recv_buf:里面裝着二進制數據,就是要下載的文件中的數據部分
// recv_len:數據的長度
// data:既然是回調函數,就需要允許caller把相關的數據結搆也帶進來。
typedef void (*RECV_CALLBACK)(char * recv_buf, int recv_len, void * data) ;

class CM5HttpDown {
protected:
// socket data
SOCKET m_sock ;
bool m_running ; // 標志是否運行
bool m_is_first_resp ; // 第一次收到數據的標志,用于跳過服務器的http頭
char * m_web_addr ; // 存放從uri中解析出來的網址
char * m_web_fname ; // 存放uri中的文件名
int m_web_port ; // uri中的服務器端口好,缺省值是80
char * m_recv_buf ; // 接收緩沖區
int m_total_bytes ; // uri中文件的總大小,單位字節
int m_recv_bytes; // 已經接收了多少字節,用于斷點續傳中接着傳

// custom defined receive handler
RECV_CALLBACK m_custom_callback ; // 回調函數指針
void * m_custom_data ; // call的自定義數據結搆指針
public:
// common receive thread func
static void recv_thread(void * data) ; // 線程函數,必須是靜態的
void recv_thread_handler() ; // 在線程函數中調用,是實際上的接收函數

protected:
// uri解析函數,能夠把諸如http://www.5mbox.com/bbs/mp1.mp3的uri分解為
// web_addr : www.5mbox.com
// web_fname : bbs/mp1.mp3
bool parse_uri(char * uri,
char * web_addr, char * web_fname, int * web_port) ;

// 第一次收到http回應的時候,解析出來文件的大小(toal_length),以及需要跳過的長度
// (jumplen),這樣就可以只把有用數據給call傳過去了,而無用的http頭就丟棄了。
bool parse_webfile_info(char * recv_buf,
int * total_length, int * jump_len) ;

// 用于把指定的field的值字符串從http頭中讀取出來,例如回應http的頭為:
// 206
// Content-Length: 106786028
// Content-Range: bytes 2000070-106786027/106786028
// Date=Mon, 30 Apr 2001 12:55:20 GMT
// ETag=W/"02ca57e173c11:95b"
// Content-Type: application/octet-stream
// Server=Microsoft-IIS/5.0
// Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
// 該函數就可以把比方說"Content-Length: "的數據"106786028"給取出來
bool get_resp_field(char * recv_buf,
char * field_name, char * end_flag, char * res) ;

// socket的常規操作函數
bool init_sock(char * server_name, int server_port) ;
bool send_req(char * req_str, int req_len) ;
bool close_sock() ;
public:
CM5HttpDown() ;
~CM5HttpDown() ;

bool is_running() {return m_running ; }
int http_total_size() { return m_total_bytes ; }
int http_recv_size() { return m_recv_bytes ; }

// 這個就是主要的http下載函數了,使用的時候直接調用這個函數就行了。
// uri : 要下載的web文件地址,例如http://www.5mbox.com/bbs/mp1.mp3
// custom_func : 回調函數,收到二進制數據的時候,會自動調用該函數
// custom_data : call的自定義數據類型
// recv_bytes : 已經接收了多少個字節的數據,用于斷點續傳。如果該數值為0
// 則釆用普通的get方法下載;如果不為0,則釆用斷點續傳接着下載
bool http_down(char * uri,
RECV_CALLBACK custom_func, void * custom_data,
int recv_bytes = 0) ;

// 下載過程中強制關閉下載用的
bool http_stop() ;
} ;

#endif



M5HttpDown.cpp的文件內容:(作家侯杰曾經說過,源碼之下了無秘密,不需要我解釋什麼了吧。。。)
#include "M5HttpDown.h"
#include <string.h>
CM5HttpDown::CM5HttpDown()
{
m_sock = (SOCKET)(NULL) ;
m_running = false ;
m_is_first_resp = true ;
m_web_addr = NULL ;
m_web_fname = NULL ;
m_custom_callback = NULL ;
m_custom_data = NULL ;
m_total_bytes = 0 ;
m_recv_bytes = 0 ;
m_recv_buf = new char [HTTP_RECV_BUF_LEN] ;
memset(m_recv_buf, 0, HTTP_RECV_BUF_LEN) ;
}
CM5HttpDown::~CM5HttpDown()
{
if(m_recv_buf) delete [] m_recv_buf ;
if(m_web_addr) delete [] m_web_addr ;
if(m_web_fname) delete [] m_web_fname ;
}
void CM5HttpDown::recv_thread(void * data)
{
CM5HttpDown * http_down_ptr = static_cast<CM5HttpDown *>(data) ;
http_down_ptr->recv_thread_handler() ;
_endthread() ;
}
void CM5HttpDown::recv_thread_handler()
{
fd_set recv_fd ;
struct timeval tmv ;
int recv_bytes ;
int jump_length ;

while(m_running) {
FD_ZERO(&recv_fd) ;
FD_CLR(m_sock, &recv_fd) ;
FD_SET(m_sock, &recv_fd) ;
tmv.tv_sec = 1 ;
tmv.tv_usec = 0 ;
if(select(m_sock+1, &recv_fd, NULL, NULL, &tmv) < 0) {
#ifdef HTTP_DEBUG_MODE
printf("select recv failed !\n") ;
fflush(stdout) ;
#endif
return ;
}

if(FD_ISSET(m_sock, &recv_fd)) {
// time to read
recv_bytes = 0 ;
jump_length = 0 ;
memset(m_recv_buf, 0, HTTP_RECV_BUF_LEN) ;
recv_bytes = recv(m_sock, m_recv_buf, HTTP_RECV_BUF_LEN, 0) ;
if(recv_bytes > 0) {
if(m_is_first_resp) {
if(parse_webfile_info(m_recv_buf, &m_total_bytes, &jump_length)) {
// 這里比較亂,意思是:如果是斷點續傳的話,第一次收到response
// 的時候,m_recv_bytes就有數據,此時整個文件的大小應該是
// 服務器返回的content-length長度加上已經接收過了的數據長度
if(m_recv_bytes > 0) m_total_bytes += m_recv_bytes ;
#ifdef HTTP_DEBUG_MODE
printf("file length : %d\n", m_total_bytes) ;
#endif
m_recv_bytes += (recv_bytes - jump_length) ;
(*m_custom_callback)(m_recv_buf + jump_length,
recv_bytes - jump_length,
m_custom_data) ;
}
m_is_first_resp = false ;
continue ;
} else {
// common receive procdure
if((m_recv_bytes + recv_bytes) > m_total_bytes) {
recv_bytes = m_total_bytes - m_recv_bytes ;
m_recv_bytes = m_total_bytes ;
} else {
m_recv_bytes += recv_bytes ;
}
(*m_custom_callback)(m_recv_buf, recv_bytes, m_custom_data) ;
}
} else if(recv_bytes == 0) {
// conn down
#ifdef HTTP_DEBUG_MODE
printf("disconn...\n") ;
#endif
m_running = false ;
}
}
}
}
bool CM5HttpDown::send_req(char * req_str, int req_len)
{
fd_set send_fd ;
struct timeval tmv ;
int send_bytes ;
if(!m_sock || req_len <= 0 || req_str == NULL) return false ;

FD_ZERO(&send_fd) ;
FD_CLR(m_sock, &send_fd) ;
FD_SET(m_sock, &send_fd) ;
tmv.tv_sec = 1 ;
tmv.tv_usec = 0 ;
if(select(m_sock+1, NULL, &send_fd, NULL, &tmv) < 0) {
#ifdef HTTP_DEBUG_MODE
printf("select send failed !\n") ;
fflush(stdout) ;
#endif
return false ;
}

if(FD_ISSET(m_sock, &send_fd)) {
send_bytes = send(m_sock, req_str, req_len, 0) ;
if(req_len != send_bytes) return false ;
return true ;
}
return false ;
}
bool CM5HttpDown::parse_uri(char * uri, char * web_addr,
char * web_fname, int * web_port)
{
char * ptr_a = NULL ;
char * ptr_b = NULL ;

*web_port = HTTP_WEB_PORT ;
if(!uri) return false ;
// search for http or https prefix
ptr_a = uri ;
if(!strncmp(ptr_a, HTTP_PREFIX, strlen(HTTP_PREFIX)))
ptr_a = uri + strlen(HTTP_PREFIX) ;
else if(!strncmp(ptr_a, HTTPS_PREFIX, strlen(HTTPS_PREFIX)))
ptr_a = uri + strlen(HTTPS_PREFIX) ;
// get web_addr without "http://" or "https://" prefix
ptr_b = strchr(ptr_a, '/');
if(ptr_b) {
memcpy(web_addr, ptr_a, strlen(ptr_a) - strlen(ptr_b));
if(ptr_b + 1) {
// get web file name
memcpy(web_fname, ptr_b + 1, strlen(ptr_b) - 1);
web_fname[strlen(ptr_b) - 1] = '\0' ;
}
} else memcpy(web_addr, ptr_a, strlen(ptr_a)) ;
if(ptr_b) web_addr[strlen(ptr_a) - strlen(ptr_b)] = '\0' ;
else web_addr[strlen(ptr_a)] = '\0' ;
// search for uri port number
ptr_a = strchr(web_addr, ':') ;
if(ptr_a) *web_port = atoi(ptr_a + 1);
else *web_port = HTTP_WEB_PORT ;
return true ;
}
bool CM5HttpDown::get_resp_field(char * recv_buf, char * field_name, char * end_flag, char * res)
{
char * start_ptr = NULL ;
char * end_ptr = NULL ;

start_ptr = strstr(recv_buf, field_name) ;
if(start_ptr == NULL) return false ;

start_ptr += strlen(field_name) ;
end_ptr = strstr(start_ptr, end_flag) ;

if(end_ptr == NULL) return false ;
memcpy(res, start_ptr, end_ptr - start_ptr) ;
res[end_ptr - start_ptr] = '\0' ;
return true ;
}
bool CM5HttpDown::parse_webfile_info(char * recv_buf, int * file_length, int * jump_len)
{
char tmp_str[50] ;
char * offset_str = NULL ;

#ifdef HTTP_DEBUG_MODE
printf("%s\n", recv_buf) ;
#endif
// get file length
if(!get_resp_field(recv_buf, HTTP_HDR_FILE_LEN, HTTP_HDR_DIV, tmp_str))
return false ;

*file_length = atoi(tmp_str) ;

// get current offset
offset_str = strstr(recv_buf, HTTP_HDR_END) ;
if(offset_str == NULL) return false ;
*jump_len = (int)(offset_str + strlen(HTTP_HDR_END) - recv_buf) ;
return true ;
}
bool CM5HttpDown::init_sock(char * server_name, int server_port)
{
struct sockaddr_in sock_in ;
struct hostent * he ;
{
// only worked in dos
WSADATA wsadata ;
if (WSAStartup(0x0202, &wsadata) != 0) return false ;
}

// get server ip address
he = gethostbyname(server_name) ;
if (!he) sock_in.sin_addr.s_addr = inet_addr(server_name) ;
else {
sock_in.sin_addr.s_addr = *(unsigned long *)(he->h_addr_list[0]) ;
#ifdef HTTP_DEBUG_MODE
printf("ip : %s\n", inet_ntoa(sock_in.sin_addr)) ;
#endif
}

sock_in.sin_family = AF_INET;
sock_in.sin_port = htons(server_port);
m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ;

if(!connect(m_sock,(struct sockaddr *)(&sock_in), sizeof(sock_in))) {
HANDLE thread_handle ;
m_running = true ;
thread_handle = (HANDLE)(_beginthread(CM5HttpDown::recv_thread, 0,(void *)(this))) ;
return true ;
}
return false ;
}
bool CM5HttpDown::close_sock()
{
if(m_running) {
m_running = false ;
Sleep(1000) ;
if(m_sock) closesocket(m_sock) ;
}
{
// only worked in dos
WSACleanup() ;
}
return true ;
}
bool CM5HttpDown::http_down(char * uri,
RECV_CALLBACK custom_func, void * custom_data,
int recv_bytes)
{
char buffer[HTTP_SEND_BUF_LEN] ;
memset(buffer, 0, HTTP_TEMP_BUF_LEN) ;

if(uri == NULL) return false ;
m_recv_bytes = recv_bytes ;
m_custom_callback = custom_func ;
m_custom_data = custom_data ;

m_web_addr = new char [HTTP_TEMP_BUF_LEN] ;
m_web_fname = new char [HTTP_TEMP_BUF_LEN] ;

memset(m_web_addr, 0, HTTP_TEMP_BUF_LEN) ;
memset(m_web_fname, 0, HTTP_TEMP_BUF_LEN) ;

parse_uri(uri, m_web_addr, m_web_fname, &m_web_port) ;
if(m_recv_bytes == 0) {
snprintf(buffer, HTTP_SEND_BUF_LEN, HTTP_COMMON_GET,
m_web_fname, m_web_addr, m_web_port) ;
} else {
snprintf(buffer, HTTP_SEND_BUF_LEN, HTTP_RESUME_GET,
m_web_fname, m_web_addr, m_web_port, m_recv_bytes) ;
}
#ifdef HTTP_DEBUG_MODE
printf("%s\n", buffer) ;
#endif
m_running = true ;
if(!init_sock(m_web_addr, m_web_port)) return false ;

// send the request
return send_req(buffer, strlen(buffer)) ;
}
bool CM5HttpDown::http_stop()
{
return close_sock() ;
}


貼上整個dev-c++的工程,感興趣的朋友直接下了玩玩。
nettest.rar
運行nettest.exe,就會在其當前目錄生成一個叫test.mp3的文件,螢幕上還顯示下載進度。
可以隨時關閉,然後再次打開,看看斷點續傳的效果。
下面的這個是將上述代碼,通過RSocket移植到symbian s60 2nd平台上的測試程序(我做了一些修改使之可以同時支持cmwap和cmnet的gprs環境,再此鄙視一下移動的行為),
出于公司的利益考慮,就不開放代碼了。
NetTestSIS.rar
當調用cmwap conn的時候,接入點要選擇"移動夢網"或者"nokia.com";
當調用cmnet conn的時候,接入點要選擇"gprs連接互聯網";
看到螢幕上顯示"connected"的時候,選擇"resume",就會開始斷點續傳過程。
對於cmwap由於移動有推送頁面,在程序里面加入了效驗,如果有推送頁面,
程序會顯示"check failed",這時,再按一次"resume"即可。
更換連網方式之前,需要選擇"stop",斷開gprs連接,然後再連。
還有一個小小的意外是對于移動的網關來說,Content-Length這是標准http服務器返回的;
但是移動的代理返回的結果是Content-length這個"l"是小寫的,這個細節一定要注意才行喔!!

呵呵,期待5mbox網路版早日成功。

資料來源:
http://blog.chinaunix.net/u/26691/showart_433628.html
http://blog.chinaunix.net/u/26691/showart_433631.html

[轉貼]EFI/UEFI BIOS 入門 : All For Beginners – 另附深入學習指南以及有用的URL連接

我們已經使用BIOS超過了二十年.可是直到今天還友許多朋友不知道BIOS到底是什麼,以及它主要做些什麼事情,它在整個個人計算機之中所處的地位如何.事實上,BIOS是整個計算機系統中最重要的底層系統軟體.二十多年來,中國的程序員們紛紛忽略了BIOS,或者由BIOS衍生出的開發技術,相反,我們對如何調整一兩個BIOS設置津津樂道.今天,BIOS業界開始悄悄的變革,EFI或者UEFI的到來即將改變世界,從而徹底改變我們對過去的計算機啟動過程的認識.但是我們中國的開發者們仍然在談論JAVA或者.NET,我想,是到了清晰的研究BIOS的時候了.

小生不才,但也願意就我所學,貢獻成一篇簡短的入門文章,帶領大家進入BIOS這個有趣而又充滿了神秘的地域,我們一起來探究BIOS,尤其是探究下一代個人計算機的基礎系統軟體,或者說基礎固件:UEFI bios的方方面面.由于類似的文章網上也比較多,所以我就重點談些別人一般忽略的部分吧.

BIOS Definitions

BIOS -- Basic Input and Output System,is used for initializing,testing and putting the PC into the ready state so that an OS may be started.Part of the BIOS remains in the system main memory after POST,or Power On Self Test.BIOS provides a consistent software interface to varying types of the hardware devices.It also provides the basic system level services to OS.The BIOS is also used for helping IHV to fix their hardware design bugs by using the SMM mode of the IA architecture.

上面這句話是我在初學BIOS的時候,我的老師,一位受到整個業界尊敬的杰出BIOS Engineer對我說的,這段話雖然短,但是卻清楚的道出了BIOS的基本功能,那就是:
  1. 檢測硬體,又叫POST.

  2. 初始化硬體,設置其基本狀態,使得整個計算機達到所謂的"可用狀態"(Ready State).

  3. 啟動OS Loader加載操作系統.

  4. 在操作系統啟動起來以後,一部分繼續駐留內存(記憶體),向操作系統以及其他軟體提供基本的系統級的服務.如磁碟讀寫等.

  5. 修復硬體缺陷.
下面我們一個一個的來看這些功能.

(第一個)檢測硬體可能比較好理解一點,就是看看你的硬體是否還正常的工作,但是從軟體的角度看.其中最重要的就是對內存的檢測的.大家都還對剛開機的時候內存的大小一直在跳的螢幕有記憶吧,那就是在做Memory Test,或者說Memory Sizing.

(第二個)功能是初始化硬體,可能有不少朋友問:為什麼我的硬體還需要初始化?問的好,硬體的設計廠家往往為了通用市場的考慮,不願意將硬體設計成定制的狀態,可能一個網卡,可以安裝在PC,同樣也可以安裝在嵌入式系統上.所以為了使得硬體能夠按照PC的架構工作,BIOS必須要按照由IHV(Independent Hardware Vendor)提供的手冊將硬體設置好,比如寫几個必須的寄存器之類的,做一些enable的工作.這點非常重要,如果一個硬體沒有enable,那麼在OS下將不可見.

(第三個)功能是啟動操作系統,這也是BIOS必須要做的事情之一.啟動的方式是由BIOS規定,操作系統必須按照BIOS的要求來設計.這也是為什麼操作系統從DOS一直到Vista,都只能把自己的loader放在MBR,因為BIOS只讀MBR.強大的微軟都必須要按照這個不成標准的標准來:)當然,在EFI時代,這一點有所改變,EFI支持的Boot From File不在需要MBR.

(第四個)功能可能之前作過DOS開發的朋友比較熟悉吧,還記得INT 10基本螢幕服務,INT 13磁槃服務嗎?多少病毒正是靠INT 13來傳播.又有朋友曾經試圖繞過INT 10來直接寫螢幕?Windows時代,這些東西事實上仍然存在,並且繼續發揮着基本的核心作用,只是他們被Windows包裝起來了,一般的程序無法接觸到,但這並不能說明他們就沒有用處了.MS的開發人員不久前還表示,事實上甚至就是開發中的Longhorn的安裝程序,目前仍然有許多code是基於INT 10來寫螢幕的.

(第五個)功能估計一般的朋友可能就不知道了,就是之前稍微接觸過BIOS的朋友們可能也是第一次聽說吧!Intel在它的CPU裏專門留了個模式叫System Management Mode,擁有最高的權限.SMM中斷的時候,就連號稱無所不能的Windows的也不知道,這樣就可以給CPU補bug了,舉個例子,比如某天Intel的一個CPU對ADD指令給出錯誤操作結果,那麼就可以利用SMM在每次執行這個指令的時候,中斷一下,由BIOS軟體給出正確的執行結果.這就達到了給硬體修復缺陷的目的.這樣Intel也不用招回它的CPU了,呵呵.此外,每次BIOS開機的時候,事實上都會更新CPU Microcode,同樣是用來給CPU補bug的.所以很多時候,刷BIOS刷出問題,事實上某個CPU的bug沒有補上導致出了問題出現.

BIOS在哪裏
上面囉嗦了一大堆的BIOS Basics,那麼BIOS到底在哪里呢?答案是在你的計算機裏:) 的確有些無聊,事實上BIOS有三種狀態,分別是:
  1. Before Build

  2. BIOS Image

  3. BIOS Runtime
(第一種)呢,這個時候BIOS表現為BIOS開發者硬碟上的一堆源始程式. 處於(第二種)的時候,BIOS則是沉睡在Flash里的一段image.BIOS真正發揮作用是在(第三種)模式下,哪個時候BIOS執行,控制系統,與操作系統交互.

EFI BIOS
EFI是由Intel提出的,目的在於為下一代的BIOS開發樹立全新的框架。EFI是英文Extensible Firmware Interfaces的縮寫。正如它的名字一樣,EFI不是一個具體的軟體,而是在操作系統與平台固件(platform firmware)之間的一套完整的接口(interface)規範。EFI定義了許多重要的數據結構以及系統服務,如果完全實現了這些數據結構與系統服務,也就相當於實現了一個真正的BIOS核心。

EFI最早是在Spring 2000 IDF(Intel Developer’s Forum)上提出的,當時Intel認為,隨着IBM在80年代初推出了第一台個人計算機開始,直到今天為止,個人計算機硬體平台已經發生了翻天覆地的變化,相關的系統軟體如操作系統等也從最早的MS DOS1.0到今天的Windows XP,而作為整個系統的最底層也最為關鍵的系統軟體之一的BIOS卻基本上保持了架構二十年不變。這在整個軟體史上都是一件不可思議的事情。如今,BIOS已經變成了嚴重阻礙IT產業前進的絆腳石,必須通過對BIOS的革新來為下一代的操作系統(如Windows Server Longhorn)提供更加強大的支持。

上面是我很早之前寫的一段對EFI的介紹,現在看來,難免有些錯誤,不過大致意思非常明確,EFI就是用來替換傳統BIOS.作為更好的BIOS,EFI可以提供過去無法在BIOS中作到的許多事情.后面的文章我會逐步展現給大家.

一些常見的關于BIOS/EFI的問題以及我的簡短回答:
1) BIOS一般有多大?
傳統bios(以後說legacy bios)一般都是512KB,而早期的EFI bios也是512KB.現在EFI基本上是1MB了.
2) BIOS用什麼工具開發?
legacy bios一般用MASM 6.11開發,同時還會配上一些廠商自己寫的build tools. EFI則使用Visual Studio.NET 2003以及MASM 6.11開發(沒想到吧~)
3) EFI boot是怎麼一回事?
EFI有自己獨特的boot方式,完全拋棄掉了傳統的0磁道0扇區的MBR概念.EFI的boot方式與文件系統息息相關.過去的legacy bios由於不帶文件系統,不得已選擇從硬碟上特定空間裝載程序的辦法,而EFI則附帶了完整的文件系統支持,所以不再對硬碟有特定的要求,EFI下的操作系統加載程序事實上存儲在boot\ia32\bootia32.efi文件里.(假定是IA32架搆).這是一個EFI應用程序.


下面是一些深入學習bios的資源匯總:
1. BIOS Boot Specification
業內一般叫BBS,詳細描述bios啟動時必須要做的所有事情,如何區分啟動設備,如何選擇啟動設備等等.
http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf

2. UEFI Specification
UEFI規範,詳細描述了UEFI bios必須支持的接口.以及UEFI bios的模型,提供的服務等等. 開發UEFI必備的.
http://www.uefi.org

3. Ralf Brown's Interrupt List
這個人似乎就一輩子都都在收集中斷的東西,對legacy bios學習很有用.
http://www.ctyme.com/rbrown.htm

4. El Torito CD-ROM Boot 描述了bios如何從光碟上boot的細節.
http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf

5. USB Specification USB設備規範
http://www.usb.org

6. Plug-and-Play Specifications MS的PnP規範
http://www.microsoft.com/hwdev/tech/pnp/default.ASP

7. BIOS Writer's Guide
bios開發的聖經,由cpu廠商給出.Intel的絕對看不到,Intel的是絕密級的文檔.AMD的倒是可以看到,不同的cpu有不同的BWG.這里給出一個amd比較新的cpu的BWG:
http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/31116.pdf

還有很多很多相關的文檔.其實編寫bios最難的在於同時支援業界幾乎所有的通用規範.

歡迎大家回帖提出相關問題. 我會在空閑時間一一作答.

轉載自:
http://www.huarw.com/program/assembler/assembler01/200804/1561026.html

2008年9月17日 星期三

Anatomy of an Android Application

Anatomy of an Android Application - 剖析Android應用程式的四大區塊
原則上Android的應用程式會由下面四個區塊所組合而成

  1. Activity
  2. Broadcast Intent Receiver
  3. Service
  4. Content Provider
並不是所有的Android程式都需要有這四個部份才行,而是由上面那四項去挑選出來組合而成。
當你決定要在你的程式裏放這些元件時,你必需將它們明確的列在AndroidManifest.xml這個檔案裏。 所以在這個xml檔案,你必需宣告你要使用的元件並且明白的指出它們所擁有的能力及需求。 manifest檔案更詳細的內容請點我

底下列出一個簡單的manifest.xml範例,這個範例程式就只有放一個Activity元件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.my_domain.app.helloactivity">
<application android:label="@string/app_name">
<activity android:name=".HelloActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

(1)Activity:
Activities是四個元件當中最常被使用的。一個Activity在你的程式裏通常都會有一個畫面(single screen)可呈現出來。每個Activity都會實作出一個類別,而且這個類別是繼承自Activity這個基礎類別。你的這個類別要顯示使用者介面的話就必需再組合可視元件(Views)及回應事件功能。大部份的程式都會由多畫面(multiple screens)組成。比如:一個文字訊息傳送程式會有一個畫面是用來顯示可傳送訊息的聯絡人清單,一個畫面用來輸入要傳送的訊息內容,另一個畫面用來查閱舊的訊息內容,或是改變程式的設定畫面。每個畫面都會實作在一個Activity裏。換一個畫面就是去啟動另一個Activity起來,在某些情況下Activity可以回傳值給上一個Activity:比如說有個Activity可以讓使用者選一張照片,然後告知上一個Activity使用者選到了那張圖片。當一個新的畫面顯示出來後,上一個畫面就會進入pause的狀態,系統會將它放進歷史堆疊去。有了歷史堆疊,使用者可以很方便的返回上一個畫面。如果畫面不再需要使用的話,也可以選擇從堆疊中移除。Android會幫你管理這個歷史堆疊的資料,從家(Home)畫面開始執行的所有應用程式都會被管理到。
Intent and Intent Filters:
Android使用了一個很特別的類別叫Intent,用來從一個畫面跳到另一個畫面。Intent是用來描述一個程式想要作些什麼事情。在Intent的資料結構裏有兩個很重要的部份,一個是動作(action)及對資料產生反應(data to act upon)。action主要的內容有MAIN(程式的進入點),VIEW,PICK,EDIT等等。data則是用URI的形示來表示。比如:想要查看一個人的聯絡資料時,你需要建立一個Intent,它包含了VIEW的動作(action)及指向該人資料的URI描述句。

還有另一個相關的類別是IntentFilter。當Intent要求做某些事情時,IntentFilter被用來描述這個activity能夠做些什麼事情。比如一個activity要能夠顯示個人連絡資料,你就必需要在IntentFilter說明你要如何處理個人連絡資料並用ACTION_VIEW呈現出來。IntentFilter都會宣告在AndroidManifest.xml檔案裏。

而畫面的切來切去則是由resolving intent來實現的。當你想產生新的畫面時,現行的activity就使用startActivity(myIntent)。然後系統會根據所有已安裝的程式所定義的intent filter來看那個程式是最適合myIntent。當startActivity被呼叫時,resolving Intents的處理過程是伴隨而來的。
resolving intent提供我們兩個好處:
讓Activities可以很容易的利用Intent的方式去使用別的程式的功能。
讓Activities可以很容易的在任何情況下由新Activity所取代。

先來看看底下的範例:它的目的是希望在onClick事件引發時,使用Intent去打開http://stenlyho.blogspot.com/ 這個網頁,所以你會發現要做兩件事情,1.設定URI資料2.把uri及action傳給新的Intent,這裏的action是view,最後startActivity(intent)把intent執行起來。但按寫程式的常理來推斷,應該是把網址傳給瀏覽器啊,為什麼都沒看到執行瀏覽器的動作,只有Intent呢?這就是resolving Intent的功能囉!其實就像你在Windows裏點.doc檔時,自動會叫起word,點.txt檔時,自動會用記事本一樣的功能啦,resolving intent會根據uri內容是http開頭的,action又是VIEW時,就直接執行browser去把你設定的網址view出來給你看。

public void onClick(){
Uri uri = Uri.parse("http://stenlyho.blogspot.com/");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}

(2)Broadcast Intent Receiver :
當你想要寫一個程式來對外部的事件做些處理時,可以使用Broadcast Intent Receiver。比如:當電話響時,網路資料可以使用時,或半夜來臨時。Broadcast Intent Receiver它並不能拿來顯示UI畫面,它必需利用NotificationManager來通知使用者他們感興趣的事件發生了。
Broadcast Intent Receiver同樣的可以被宣告定義在AndroidManifest.xml檔案中,但你也可以用寫Context.registerReceiver()程式的方式來註冊你自己的Broadcast Intent Receiver。你自己的程式並不會因為BroadcastReceivers被呼叫而被它執行起來。而是當BroadcastReceiver被觸發時系統會依需求來執行相對應的程式。
程式可以利用Context.sendBroadcast()來發出他們自己的intent broadcast給其它的程式。

(3)Service
Service就是一段程式,它並沒有UI呈現畫面,但它可以長期的存在系統內執行。舉個多媒體播放器的例子來說:一個媒體播放器可能會有一個以上的activities存在,允許使用者選歌及播放歌曲。但是你可能不能一直用activity來做播放的行為,因為使用者可能會希望可以持續的播放歌曲的同時,他要去看其它的畫面,比如一邊聽歌,一邊逛網頁。
在這種情況下你就必需使用Context.startService()來使程式在背景執行,以保持音樂的持續播放。系統將會一直讓音樂保持播放的狀態,一直到它結束為止。
當它進入背景狀態時,你可以利用Context.bindService()來跟Service進行連結。當你正確的連結後,你就可以透過這個Service開放出來的界面跟它進行溝通。以剛剛放音樂的例子來看,Service應該就會開放出讓你暫停播放,倒轉,快轉等功能囉!

(4)Content Provider
應用程式可以將它們的資料儲存起來到一個檔案,存到SQLite資料庫,或其它的機制儲存起來。當你希望你的程式資料可以被其它程式使用時,Content Provider就非常有用了,Content Provider實現了讓應用程式之間互相分享資料。更多的結節請看Accessing Content Providers

資料來源:
http://code.google.com/android/intro/anatomy.html

2008年9月16日 星期二

DroidDraw

什麼是DroidDraw?
User Interface (UI) designer/editor for programming the Android Cell Phone Platform

如果你已經使用eclipse去玩過了Android的一些程式後,應該會發現,要做UI時,超級的難用,一開始時,我還以為所有要用的元件都要自己手動寫程式寫在res/layout/main.xml裏面,後來才發現原來eclipse有支援,增加元件的功能。如下圖:
上面那個增加元件的功能,最少可以,讓你在需要Button時,不用自己手動打,幫你自動產生底下的程式碼。
<Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
但,還是超麻煩的,因為如果我想要去改變Button的顯示文字,或位置及大小,又要去翻文件,把元件屬性找出來,然後自己手動修改。(至少我找不到這種功能,如果有人知道麻煩告知一下,感謝!)

但如果用DroidDraw的話,非常的方便,它提供你,用托拉的方式把元件拉到畫面上去,還可以編輯元件的屬性,等你都排好,設定好後,再用"Generate"產生xml檔,你再把這個程式Copy到你自己的xml去,就完成囉!

DroidDraw網址

它提供2個版本,一個可以直接在網頁上編輯,另一個是提供standalone executable版本,讓你也可以在本機端使用^^

2008年9月15日 星期一

霹靂神州II之蒼玄泣 43戰神、44造化弄神更弄人

霹靂神州II之蒼玄泣 43戰神、44造化弄神更弄人
  1. 精采武學:

  2. 曌雲裳:漫天飄雪雪飄紅,藍閣雪擁千層起,便看冰魂白上紗。
    柳生劍影:萬神劫,萬神劫‧三。
    黑羽恨長風:不問歲月任風歌,納真神訣‧一任天風蔽月明。
    赭杉軍:赤霞照天東,四聖諸魔陣。
    斷風塵:斷神掌,神魔斷。
    華顏無道:摩羅法擊,惡露天返。
    墨磨人:率爾操觚,文風可長。
    造化弄神:鍛白手,闢黑掌。
  3. 法術陣法:

  4. 赭杉軍:四聖諸魔陣
  5. 武器:


  6. 藥草醫術:


  7. 物品:

  8. 玄貘精心打造之盾牌
    倍元丹
  9. 異獸:


  10. 文學詩句:


---------------------------------------------------------------------------
萬血邪籙,開啟神州之柱,識界玄貘,變化天地之序
紅樓劍閣,論定劍鋒之最,棄天魔帝,造就毀滅之端

霹靂神州II之蒼玄泣 41紅樓遺恨、42情‧義‧爭

  1. 精采武學:

  2. 任劍誰:切齒怒目橫向天,衝冠豎髮亂開雲,今生唯恨一劍敗。
    曌雲裳:傲凌霜峰寒勝雪,漫天飄雪雪飄紅,藍閣雪擁千層起。
    邪亦正:殘刀合劍斷血獄。
    不二做:劍旋十字斬。
    赭杉軍:翎羽飄風,天霞流影,雪亂春秋,一劍動神威,天地玄黃‧陰陽妙法‧真極烈燄。
    筆少允:墨染千秋。
    章少輔:傾天之章。
    笏少丞、章少輔、筆少允、典少監:揆天理地,明聖尊賢,存心察理,學海無邊。
    白忘機:風雨殘雷。
    帝鵬:帝源擊,帝風擊,帝鵬擊。
    黑羽恨長風:劍無二,一斬風月,怒馬凌關,烈灩風歌,氣雙流‧納真神訣。
  3. 法術陣法:

  4. 赭杉軍:伏天王‧降天一,靈識出體,傳定心一。
  5. 武器:


  6. 藥草醫術:


  7. 物品:


  8. 異獸:


  9. 文學詩句:

  10. 一敗塗地任劍誰臨終含恨所吟:雪不得,眾靈沉冤;恨只能,血訴青天。
    造化弄神嘲諷夜愁雨:無情的人,走多情的路,注定得淋一場多情的雨。
    柳生劍影突破自我劍道所吟:劍亦道、情亦道,不棄劍、不棄情,道之真意,唯有自然,一任自然。
    學海無涯執令代表詩:文章耀千古,道德宣乾坤,天下滔滔論,為我儒者尊。

---------------------------------------------------------------------------
萬血邪籙,開啟神州之柱,識界玄貘,變化天地之序
紅樓劍閣,論定劍鋒之最,棄天魔帝,造就毀滅之端

2008年9月14日 星期日

20080907 舞林大道 Dance Flow Fall in Love

Dance Flow跳的超棒的啦,介紹給大家看一下囉^^

底下第一個影片是20080807 Dance Flow(HQ版) 表演Fall in Love,超讚的,真的讓人有戀愛的感覺唷!



2008年9月12日 星期五

Google Developer Day 2008 Taiwan : Android: Dalvik VM Internals

Google Developer Day 2008 Taiwan : Android: Dalvik VM Internals
想要更了解Android: Dalvik Virtual Machine有什麼特異功能嗎?看這裏就對了^^

中文版:程本中介紹Android: Dalvik VM Internals

http://sites.google.com/site/developerdaytaiwan/google-developer-day-2008-taiwan/android-dalvik-vm-internals

Google文件:程本中介紹Android: Dalvik VM Internals
http://docs.google.com/Present?docid=dfqs8fqs_4379243wxzk

英文版:Dan Bornstein介紹Android: Dalvik VM Internals

http://sites.google.com/site/io/dalvik-vm-internals

2008年9月11日 星期四

Android Process Lifecycle(行程生命週期)

Process Lifecycle(行程生命週期)

除了Activity Lifecycle之外,Android還有一段Process Lifecycle的說明如下:

Android系統基本上都會盡量的維持應用程式的運作,但如果記憶體真的不夠用時,就會需要把舊的或不需要用的應用程式移除。 就如同之前的Activity生命週期所介紹,這個移除的決定是由應用程式所處的狀態來判斷。一般來說,一個應用程式有四個狀態,底下由最重要到不重要的順序列出這四個狀態。 當需要移除應用程式時系統將會自已做排序,然後從最不重要的開始移除。

  1. Foreground Activity(這個activity就是在螢幕最頂端,是目前跟使用者保持互動)所以它的優先權最高,原則上會是一個最後被移除的程式,除非,這個Activity所需要的記憶體大小已經超出系統所能給與的了。

  2. Visible Activity(這個activity是可以被使用者看見,但並不是最頂端的一個程式,比如說:最頂端的程式並不是全螢幕的,或者是有個對話盒跳出來時),所以它的權重也很高,非到必要的情況,也不會去移除這種activity。

  3. Background Activity(這個activity是無法被使用者看到的的情況,並且已處於pause的狀態)這種情況下的應用程式就沒上面兩種情況處於那麼重要的位置,所以當系統需要把更多的記憶體空間分配給foreground或visible process時,系統就可以考慮把background activity給安全的移除掉。如果說這個background activity就移除了,使用者又操作了返回這個已被移除的activity時,這個activity就會把savedInstanceState並執行onCreate(Bundle),savedInstanceState是來自於當activity被移除時所會執行的onSaveInstanceState(Bundle)所儲存起來的。

  4. Empty Process這個所謂的空行程指的是那些沒有跟Activity綁定在一起的行程,也沒有跟任何的應用程式元件(比如Service或BroadcastReceiver)綁定在一起,這些空行程一定是最最最快被系統優先考慮移除的,所以如果你真的必需要用到Service/BroadcastReceiver這些功能時,你一定要把他們給你的activity綁在一起,否則系統會認為它不重要而去移除它。
有時候有些Activity可能會需要執行較長時間的操作,而且希望這個操作可以是一個較特別的activity生命週期。比如說一個攝影機的應用程式,它允許使用者上傳相片到網站去,這個上傳的時間可能需要蠻長的,這種情況下這個應用程式應要可以讓使用者離開這個應用程式,但上傳程式還必需繼續運作,要實現這種功能,你的應用程式就必需啟動一個Service用它來負責上傳的功能。 在這種情況下呢,你可以把這個上傳功能的service看成是比non-visible activity再重要一點的地位。

資料來源:
http://code.google.com/android/reference/android/app/Activity.html

違反GPL授權的新聞網站

GPL Violations
這是一個專門違反GPL授權的新聞網站,有許多的案例可供研究唷!

參考資料:
Jserv 檢視GPL3.0草案
資訊人的角度看自由軟體法律問題
GPL 1.0 (1989): http://www.gnu.org/copyleft/copying-1.0.html
GPL 2.0 (1991): http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
GPL 3.0 (2007): http://www.gnu.org/licenses/gpl.html

LGPL 1.0 (應該與GPL 1.0一樣,也是1989年吧!)
LGPL 2.0 (1991): http://www.gnu.org/copyleft/library.html
LGPL 2.1 (1999): http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
LGPL 3.0 (2007): http://www.gnu.org/licenses/lgpl.html

一般稱的LGPL2就是指LGPL 2.1。

2008年9月10日 星期三

Android Architecture 2

一‧Android的來源:
Google 併購 Android 公司後所推出的手機平台,採用 Linux 為作業系統。所以系統程式包含了 C 語言的標準函式庫,但應用程式主要以Java語言在Dalvik VM虛擬機器執行,應用程式開發環境以Eclipse為主,加上 Android Development Tools (ADT) Plugin。

二‧Android的目的:
Android平台的主旨是提供一個應用程式架構(application framework),好讓各家OEM業者可不須從頭進行軟體開發,就能推出一系列的Android平台手機。

三‧Android的特色:
Application framework:提供可重覆使用及改寫的元件。
Dalvik virtual machine:提供給行動裝置一個最佳化過的Java虛擬機器。
Integrated browser:基於一個WebKit引擎開放源碼的瀏灠器 ,像Mac上的Safari也是從WebKit衍生出來的。
Optimized graphics:最佳化過的繪圖函式庫,包含了SGL的2D繪圖函式庫及基於OpenGL ES 1.0規範的3D繪圖函式庫。
SQLite:一個結構化的資料庫系統 。
Media support:多媒體影音格式支援包含了MPEG4,H.264,MP3,AAC,AMR,JPG,PNG,GIF 。
GSM:GSM手機的通話能力其實必須在硬體電路層次實現,軟體層次無法實現,只是Android平台要求必須要有GSM通話能力,此項功效特點與其說是特點,不如說是訂立出最低的硬體要求規範。
Bluetooth,EDGE,3G,and WiFi,Camera,GPS,compass,and accelerometer:同GSM它必需由實際硬體決定是否支援 。
Rich development environment:包含了裝置模擬器,除錯工具,記憶體及效能評估分析工具及讓Eclipse IDE支援Android專案的外掛程式。

四‧Android的系統架構圖如下:

上圖呈現出整個系統架構分成(1)Applications,(2)Application Framework,(3)Libraries,(4)Android Runtime,(5)Linux Kernel五大架構
Android本身是一套軟體堆疊(Software Stack),或稱為「軟體疊層架構」,疊層主要分成三層:作業系統、中介軟體(Middleware,大陸方面稱為:中間件)、應用程式。想開發可在Android平台執行的應用程式,必須用Java程式語言撰寫才行。
中介軟體,凡是介於作業系統與應用程式間的,多概稱為中介軟體,Android的中介軟體可再細分出兩層,底層為函式庫(Library)及虛擬機器(Virtual Machine;VM),上層為應用程式框架(Application Framework)。 上面的架構圖裏全部的藍色框框全部都是用Java語言撰寫的綠色框框由C/C++組成紅色的Kernel層當然就是C囉

底下分別簡介一下各層的功用

應用程式層(Applications)
Android出貨時整套的應用程式將會包含了有電子郵件程式,短訊程式,日曆,地圖,瀏覽器,通訊程式...等等。所有的應用程式皆必需以Java的程式語言撰寫。

應用程式框架層(Application Framework)
程式設計師在寫應用程式時可以完整的存取使用統一的應用程式標準介面,這個應用程式框架的架構是可以很容易的去重覆使用各個原件, 所有的應用程式都可以提供出它自己本身的特殊功能,所有的應用程式也可以較容易的去使用其它程式的特殊功能。這樣子的統一機制,有助於開發者去改寫自己新的軟體原件。

基本上所有的應用程式是由services及systems所組成,包含有:
(1)Views System:用來建構一個應用程式的基本原件,包含了有lists,grids,text boxes,buttons,甚至是一個嵌入式的瀏覽器。這裏有Android整理好的view外觀圖
(2)Content Providers(內容提供者):它可以用來讓程式跟程式之間互相存取/分享資料。例如某一個應用程式可以去存取通訊錄應用程式內的聯絡人資料。或者呼用Resource Manager可存取的非程式碼資源,例如:當地性設定(該地貨幣、語言、時間格式)、圖像...等。
(3)Resource Manager(資源管理員):提供各種資源讓程式去使用,比如:區域性的字串,圖片,排版檔(layout files)。
(4)Notification Manager(訊息管理員):提供程式在狀態列(status bar)的地方顯示應用程式自有的警示(alert)訊息。 狀態列Android設定在手機的頂部,像短訊(short message),voice mail皆會出現在此。
(5)Activity Manager(活動管理員):管理所有的應用程式生命週期及一般性的回溯導航(navigation backstack )。
(6)Window Manager(視窗管理員):管理所有的視窗程式
(7)Location Manager:應該是用來做地圖服務的功能。
(8)Telephony Manager:用來管理所有的移動設備(電話)的功能,就是提供使用者撥打與接收電話的功能啦。
(9)Package Manager:Android系統內所有安裝的程式管理。
(10)XMMP Service
ineXtensible Messaging and Presence Protocol透過此服務得知手機的位置,在0.9 SDK已暫時移除此功能的樣子。

函式庫層(Libraries)
Android所有豐富多樣的元件皆是由C/C++函式庫所組成,但Android所有的應用程式皆必需經由API來使用這些功能。
主要的核心函式庫列表如下:

(1)System C library - 一個由BSD衍生出來的標準C系統函式庫(libc),目前這個已針對Linux嵌入式裝置進行過最佳化。
(2)Media Libraries - 基於PacketVideo's OpenCORE的多媒體函式庫;這個多媒體函式庫支援播放/錄製許多現在很流行的聲音及影像格式。比如:MPEG4,H.264,MP3,AAC,AMR,JPG,及PNG。
(3)Surface Manager - 這個部份讓開發者可以去存取顯示系統,以及實現無接縫的2D/3D多應用程式之間的整合。
(4)LibWebCore - 一個先進的網頁瀏覽器引擎。
(5)SGL - 2D繪圖引擎。
(6)3D Libraries - 一個基於OpenGL ES 1.0規範的3D繪圖引擎。它可以選擇要硬體的3D加速或高度最佳化的3D軟體程式去呈現結果。
(7)Free Type - 點陣圖及向量字形。
(8)SQLite - SQLite是一套開放原碼的關連式資料庫,提供給android上所有的應用程式使用。其特點在於輕量性(僅500KB左右的容量),適合手持式裝置運用,此外桌上型運算的應用程式也有使用,如OpenOffice.org 2.0版之後就有內建SQLite。
(9)SSL - Secure Socket Layer專門用於保護網頁通訊的協定。


執行層(Android Runtime)
Android 雖然用Java程式語言來開發、撰寫應用程式,但卻不使用一般大家目前在用的Java Runtime(J2ME)版本來執行Java程式,而是用Android自有的Android Runtime來執行。

Android Runtime包含下面兩個核心
(1)Core Libraries
核心函式庫裡頭已經包含了絕大多數Java程式語言所需要呼用的功效函式,接著每一個Android應用程式都會以自屬的process而且Android不是用一個Dalvik虛擬機器來同時執行多個Android應用程式,而是每個Android應用程式都用一個自屬的Dalvik虛擬機器來執行。
(2)Dalvik Virtual Machine
Dalvik虛擬機器是一種暫存器型態的虛擬機器。在撰寫開發時就已經設想用最少的記憶體資源來執行,以及前述的「同時可執行多個VM個體」。
Dalvik虛擬機器有許多地方是參考Java虛擬機器設計,Dalvik虛擬機器所執行的中介碼並非是Java虛擬機器所執行的Java Bytecode,同時也不直接執行Java的類別檔(Java Class File),而是依靠轉換工具將Java bytecode轉為Dalvik VM執行時特有的dex(Dalvik EXcutable)格式,稱為.dex。Dalvik VM相較於Java VM最大的不同在於Java VM為Stack-based,而Dalvik是register-based。 以技術層面考量Register-based VM的特性有個很大的好處,那就是對於現有主流的硬體架構,如此很容易與現有系統整合且最好化,而所需要的資源也相對較少。 甚至在硬體實作VM上會比較容易實現。 最重要的是Dalvik 並非是Java ME的實作,因此沒有Java ME授權相關的議題。
正常的Java比較慢不單單只是因為Virtual Machine的關係,Java的程式編釋成bytecode的也是關鍵因素之一,因Java VM採用了Stack Based的方式產生指令,所以所有的變數皆需push,pop到stack裏去,所以就會多出不少指令,而Delvik VM則改用register based的方式,變數皆存放於暫存器中,相較之下Delvik VM的指令就會少一點,速度也就會再加快一點。

作業系統層(Linux Kernel)
Android平台的作業系統用的是Linux,其核心版本為2.6版,Android所用的Linux核心,其包含的功效包括:安全(Security)、記憶體管理(Memory Management)、行程管理(Process Management)、網路堆疊(Network Stack,大陸方面稱為:網絡堆棧)、驅動程式模型(Driver Model)等,另外也在前述的軟體堆疊與硬體間建立起一個抽象層(Abstraction Layer)。

資料來源:
Google Android裡頭有什麼?
http://tech.digitimes.com.tw/ShowNews.aspx?zCatId=416&zNotesDocId=0000075729_A6M61Q6E423GAM68B95IS
What is Android?
http://code.google.com/android/what-is-android.html
Dalvik VM Internals
Android's Developers Blog

2008年9月9日 星期二

Android Activity生命週期簡介

何謂Activity:最簡單的就是你可以把Activity看成一個User Interface Program,原則上它會提供使用者一個互動式的介面功能,那一個activity只有一個UI嗎?當然不是囉,舉例來說:一個EMail程式,就可能包含三個activity(1)郵件列表的activity(2)顯示郵件內容的activity(3)寫新郵件或回覆郵件的activity。

所有的Activity在系統裏由Activity堆疊所管理,當一個新的Activity被執行後,它將會被放置到堆疊的最頂端,並且變成"running activity", 而先前的Activity原則上還是會存在於堆疊中,但它此時不會是在前景的情況,除非剛剛那個新的Activity離開。

一個Activity基本上有四個生命狀態

  • 當一個Activity在螢幕的最上層時(堆疊的最頂端),它就是屬於activerunning的狀態


  • 如果一個Activity失去焦點(focus)但還看得到它的畫面(比如:一個新的Activity畫面並不是全螢幕或者它是一個半透明的情況),那失去焦點的這個Activity則處在paused的狀態。像這個失去焦點的Activity它還是完全活著的喔,並沒有消失。(活著的意思是指,Activity自已本身所有的狀態及資料都還是存在的,也跟視窗管理程式window manager保持連繫著),像這種paused的activity,會在一種情況下消失,那就是當系統的記憶體不夠用之時,系統會自動判斷,把不重要的activity移除。


  • 如果一個Activity被其它的Activity完全的遮住時,這個被遮的Activity處於stop的狀態,它仍然保有全部的狀態及資料,但因為它已不再被使用者看見,所以它的畫面是被隱藏起來的(畫面不需要更新),當系統記憶體不足時,這種stop狀態的activity是最先被系統考慮拿來釋放記憶體的。


  • 當一個Activity處於pause或stop的狀態時,系統可以要求Activity結束(finish)或直接移除(kill)它。當它需要再度呈現在使用者面前時,它必需要能完整的重新啟動及回復先前的狀態。


  • 底下的流程圖呈現出一個Activity的狀態運行的情況,長方形的方塊代表著回呼函式(callback methods)你可以自已實作出在每個狀態你想要處理的事情,有顏色的部份就是實際Activity會處於的狀態。


    上圖裏有三個主要迴路
    (1)entire lifetime
    一個Activity的entire lifetime是由onCreate(Bundle)開始,一直到onDestroy()結束。一個Activity可以把所有的資源設定寫在onCreate裏,一直到onDestroy()時,再釋放出來。比如說,你需要一個執行緒在背景執行網路下載資料,這個thread就可以在onCreate()裏建立起來,一直到onDestroy()再把thread釋放不要用。
    (2)visible lifetime
    一個Activity的visible lifetime則是指在onStart()到onStop()之間,算是"可視生命時期",在這段時間內,使用者可以在螢幕上看見Activity,要注意這個"看見"算是抽象形容詞@@這個Activity不見得一定在前景直接跟使用者可直接互動。比如說:你可以在onStart()註冊一個BroadcastReceiver用來監控並改變你的UI,當使用者不想再看你所呈現的結果時,在onStop()移除註冊的BroadcastReceiver。onStart()跟onStop()可以在activity在visible及hidden兩個狀態切換時多次被呼叫執行。
    (3)foreground lifetime
    一個foreground lifetime則是指onResume()到onPause()之間,在這個時期的Activity是在所有的Activity的前面,並且直接跟使用者進行互動,所以這段時期指的就是圖裏的Activity is running囉。 一個Activity能很頻繁的在resume及pause這兩個狀態切換,比如:當一個裝置進行睡眠時,當一個Activity的結果被傳送,當一個新的intent被傳送時。所以在onResume()及onPause()裏實作的程式應盡量精簡。

    看到這裏先做個小結:onCreate()用來做程式的初使化動作,onDestory()通常都拿來把onCreate()時所要來的資料做釋放的動作,onPause()時把需要保存的資料在此時保存,onResume()把保存的資料拿回來使用。


    整個Activity的生命週期所用到的方法(methods)定義如下表,這些methods都可以依據你實際上的需求去繼承出來改寫它的行為。所有的Activity都會實作onCreate(Bundle)來實現他們的初使化設定;或許你也需要去實作出onPause()來保留一些資料;但請記得當你要使用這些methods時必需要做一件事情,那就是在你改寫的methods裏必需呼叫superclass相對應的methods.如:super.onCreate(Bundle)。

    public class Activity extends ApplicationContext {
    protected void onCreate(Bundle savedInstanceState);
    protected void onStart();
    protected void onRestart();
    protected void onResume();
    protected void onPause();
    protected void onStop();
    protected void onDestroy();
    }


    底下這段影片是在說明Activity Lifecycle的影片,內容有個lifecycle的demo,可以讓你對onCreate/onStart/onResume/onPause/onStop...更有感覺。



    lifecycle demo的程式碼: svn://svn.sleepydog/net/guestarea/android/LifeCycleDemo
    user=android_ro
    password=welcome

    更詳細的內容請參照
    http://code.google.com/android/reference/android/app/Activity.html

    http://sleepydroid.blogspot.com/
    http://blip.tv/file/958450/

    2008年9月5日 星期五

    李白<月下獨酌>霹靂文不通的的解釋

    花間一壺酒,獨酌無相親。
    舉杯邀明月,對影成三人。
    月既不解飲,影徒隨我身。
    暫伴月將影,行樂須及春。
    我歌月徘徊,我舞影零亂。
    醒時同交歡,醉後各分散。
    永結無情遊,相期藐雲漢。

    為尋異人三口組掉入一本書中,只見裡面有個眼鏡仔在看書

    秦假仙:請問先生就是文先生是嗎?在下有事要請妳幫忙

    文不通:花間一壺酒,獨酌無相親,舉杯邀明月,對飲成三人

    秦假仙:看這個古古怪怪,這邊遍地是書,說不定是一個書呆,我來給他試看看就知道。

    秦假仙:好詩!果然好詩

    文不通:你懂得欣賞這首詩?

    秦假仙:好說了,舉凡詩詞歌賦ˋ四六駢文ˋ唐詩千首ˋ宋詞萬闕,樣樣精ˋ件件靈,天下第一ˋ舉世無雙

    文不通:看你形容猥褻~不像啊

    蔭屍人:大仔~他說話很直接

    秦假仙:閉嘴啦

    秦假仙:剛才你所吟的那首詩是青蓮居士四首月下獨酌當中最為流傳的一首,是說他一個人喝酒寂寞....

    文不通:等一下!你說幾個人?

    秦假生:一個人啊!

    文不通:放屁!

    秦假仙:啥?你說我放屁!?

    文不通:明明是三個人~其中一個還是青蓮居士的徒弟

    秦假生:李白的徒弟~叫啥名?

    文不通:叫白影

    秦假生:我長這麼大~還沒聽過李白有一個徒弟叫白影!也沒聽過這首詩裡面有三個人!你跟我裝瘋子!?

    葉小靈:大仔息怒~我們是來求人的

    秦假生:對吼~差點忘記了~麻忙這位大文豪解釋一下這首詩怎會有3個人?李白又哪裡來的徒弟叫白影

    文不通:還說自己多厲害~讀書最怕不求甚解~你把這首詩唸一遍看看

    秦假生:花間一壺酒 獨酌無相親 舉杯邀明月 對影成三人

    文不通:停著~花間一壺酒 獨酌無相親,意思是說:有一天~青蓮居士在花園散步,突然發現有一壺酒放在花園內,他覺得很爽,但是一個人喝又很無聊,所以他拿著杯子去邀請一位叫明月的姑娘跟他的徒弟白影三個人一起喝

    秦假生:是這樣解釋的嘛?

    文不通:當然

    秦假生:那你怎知白影是他的徒弟?

    文不通:因為接下來~月既不解飲 影徒隨我身~這句話的意思是明月這位姑娘不會喝酒,這名叫白影的徒弟只會跟在我的身邊~影徒影徒就是指白影這名徒弟

    秦假生:你怎麼知道他姓白?

    文不通:你真是腦筋迂腐到不知變通~我問你-你看過論語沒有?

    秦假生:當然看過

    文不通:孔子的徒弟就叫子路子思同理可證~李白的徒弟當然就叫白影~可惜白先生學藝不精~無法在文史上留名~可歎啊...

    秦假生:天啊...原來我是遇到瘋子了....

    蔭屍人:瘋子...

    秦假生:連你沒讀書的都知道它是瘋子...

    葉小靈:大仔~怎麼辦?

    秦假生:沒關係~我對應付瘋子很有把握

    秦假仙:呃,聽你一席話,勝讀三天書,可否將這首詩作一個完整的解釋給我?

    文不通:既然你誠心向我求教~我就收你為徒弟

    秦假仙:等一下,我又沒向你拜師

    文不通:你不想拜師,我就不能為你傳道授業解惑了!!!

    業小靈:大仔~二島主之事來不及了

    秦假仙:呃~拜師就拜師-師父在上請受徒兒一拜

    文不通:等一下,拜師要三牲四禮,沐浴齋戒七天,你現在禮規不齊,我不能收你為徒

    秦假仙:我勒,真是有夠盧...師父阿。

    文不通:叫爹!!

    秦假仙:為甚麼又要叫爹?

    文不通:你沒聽過一日為師終身為父嗎?

    秦假仙:我....

    蔭屍人:大仔時間來不及了

    秦假生:我認輸了....義父,叫義父可以吧?

    文不通:恩,可以!接下來我為你解釋這首詩之意,下兩句我歌月徘徊,我舞影零亂,意思就是我唱歌的時候,明月就在旁邊走來走去,為什麼會走來走去,因為她不會喝酒,所以覺得很無聊,這跟前文相呼應,李白被稱為詩仙,就是因為他的詩前後連貫,一氣喝成!

    秦假仙:是....是....

    文不通:我舞影零亂,意思就是因為李白跳舞跳得很差,一跳舞東西踢的東倒西歪,所以他的徒弟白影就要負責收拾,但是李白酒喝太多,喝醉了,他覺得它的徒弟跟明月實在不是好酒伴,所以翻面無情,將兩人趕走,並且自我警惕,以後千萬別找他們一起出來玩,這就是醒時同交歡,醉後各分散,永結無情遊,相期邈雲漢,這最後四句的意思

    秦假仙:有理...實在太有理了....聽的我茅塞頓開....聽的我熱淚盈眶..鳴鳴

    霹靂的搞笑功力越來越強了

    還是貼一下正常的解釋給大家看
      「花間一壺酒,獨酌無相親。舉杯邀明月,對影成三人。」
      花前月下,美酒當前,但只有自己獨自一人,只好邀請明月和自己的影子,主客三人,一同來共飲吧!

      「月既不解飲,影徒隨我身,暫伴月將影,行樂需及春。」
      明月既然遠在天邊,又不解風情,而身影也只能隨著我的動作而起舞,熱鬧過後,還是孤單。我只能安慰自己,春光易逝,行樂需及時。

      「我歌月徘徊,我舞影零亂,醒時同交歡,醉後各分散。」
      明月聽到我的歌聲,忍不住徘徊流連,身影也隨著我的舞姿,亦步亦趨,二者相隨,足為我心。在醒時只有短暫的相聚,醉後却有無盡的的分離。

      「永結無情遊,相期邈雲漢。」
      即使如此,我仍願和這些看似無情的同伴們攜手同遊,也相約將來在銀河仙境相會。全詩寫得熱熱鬧鬧,但一人獨白,難掩內心深處的淒涼之感。

    孤單是一種寂寞,而喧囂又何嘗不是另一種寂寞呢?但縱使藉助外力排遣,卻總難免感到熱鬧後的空虛。其實寂寞本來就是人生命的本質,難以靠外力消除。境界提高,情感昇華,那麼即使身處寂寞處,就不會再為寂寞所苦了!

    參考資料:
    淺探 李白 《月下獨酌》四首

    2008年9月4日 星期四

    Dynamic device management in Udev

    ISSUE 71 OCTOBER 2006 www.linux-magazine.com
    這是一篇linux-magazine的文章,說明udev如何管理裝置。


    資料來源:
    http://www.linux-magazine.com/w3/issue/71/

    ACPI in Linux(FAQ)

    這是一份Linux裏的ACPI 英翻簡中的FAQ


    Linux ACPI參考資料網頁:
    http://wiki.zh-kernel.org/project/linux-acpi
    ACPI Advanced Configuration & Power Interface Sepc
    小華的部落格-ACPI Spec Overview

    Interrupt in Linux

    這是一份寫Linux裏面的中斷資料,蠻不錯的大家可以看一看唷!


    資料來源:
    http://blog.chinaunix.net/u2/66786/showart.php?id=601766

    霹靂神州II之蒼玄泣 39歲月無情、40說不出的名字

    1. 精采武學:

    2. 白忘機:熯天熾地‧烈動天罡
      嘯天颺:海無邊‧天地無際‧東海揚波,海無邊‧天地無際‧濤翻蒼穹
      滄海孤劍:滄海渺一粟‧一劍翻浪
      太狂生:東風晚逐太狂步
      任劍誰:衝冠豎髮亂開雲,撕心碎膽吼驚雷,今生唯恨一劍敗
      柳生劍影:萬神劫
      黑羽恨長風:氣雙流(吸引天穹之鳴)
      不二做:風龍捲,猛電雷疾斬
      帝鵬:引雷
    3. 法術陣法:

    4. 學海無涯儒官:御筆點化(行動瞬移)
      赭杉軍:伏天王‧降天一‧元靈歸一‧靈識渡體(進入邪亦正正亦邪腦海尋花)
    5. 武器:

    6. 邪亦正正亦邪武器(敗刀殘劍)
    7. 藥草醫術:


    8. 物品:


    9. 異獸:


    10. 文學詩句:

    11. 非人磨墨墨磨人:古今閑事一紙輕,漫草行書筆通神,何得此心如硯薄,非人磨墨墨磨人。
      天穹山出現的神秘客口中所吟:霓裳飄渺眾神渡,覺來猶知夢成空,吾恨天穹無絕期,嘆留人間一殘生。
      學海無涯四儒官:正禮律,守一貫德仁。龍圖章,開百朝儒顯。董狐筆,書千代丹心。太史典,錄萬世褒貶。聖字啟學海,飛草開儒章。
      孽角:生受罪,死帶角,命中決殺。


      學海無涯儒旨內容:『北窗伏龍曲懷觴(白忘機),罔顧人命,亵瀆亡靈,悖禮妄矩,失格失儀,毀師道大化,損儒風名譽,故吾等奉執令儒旨,召爾回宮,法堂聽判。』

    底下是敗刀殘劍跟赭杉軍的對話:
      武者:殺人,人殺,何者有罪?
      赭杉軍:違逆天道,兩者皆罪。
      武者:為善而殺,為義而殺呢?
      赭杉軍:此乃擔罪,非是無罪。
      武者:昔日佛家為五百條性命而殺一人,他有罪嗎?
      赭杉軍:有罪,但他將罪業留於己身,不讓五百同受此業。此乃大義救世之舉。
      武者:有罪之身該取贖罪之心。
      赭杉軍:佛家總能有此覺悟。
      武者:吾心非佛。
      赭杉軍:是人是佛,全在個人選擇。
      武者:這口劍,名為何?
      赭杉軍:殘缺之身,名曰殘劍。
      武者:這口刀,名為何?
      赭杉軍:敗壞之身,名曰敗刀。
      武者:今,蒙你贈名,吾,敗刀殘劍。(將刀劍收起)
      夢花者:時隔數十年,你終於收起你的刀劍了。
      赭杉軍:閣下名諱?
      武者:邪亦正,正亦邪。
      赭杉軍:是正是邪,在此一念。
      武者:我的命,交你。
      赭杉軍準備進行腦海尋花的術法...

    ---------------------------------------------------------------------------
    萬血邪籙,開啟神州之柱,識界玄貘,變化天地之序
    紅樓劍閣,論定劍鋒之最,棄天魔帝,造就毀滅之端

    2008年9月3日 星期三

    符合SMBIOS規範的電腦系統訊息獲取方法

    符合SMBIOS規範的電腦系統訊息獲取方法
    作者:ramble(如需轉載請注明作者)
    SMBIOS (System Management BIOS, SMBIOS),它是一種定出主機板及系統廠商如何以標準的格式顯示產品管理資訊的規格。
    SMBIOS 及 DMI(Desktop Management Interface)規格兩者皆是由 Desktop Management Task Force (DMTF) 所草擬的,它是一個由業界所領導,實行技術規格以確認開放性標準的組織。
    符合SMBIOS規範的電腦,可以通過訪問SMBIOS的結構獲得系統息,共有兩種辦法可以訪問:

    1.通過即插即用功能接口訪問SMBIOS結構,這個在SMBIOS2.0標准裏定義了,從SMBIOS2.1開始這個訪問方法不再被推薦使用。
    2.基表結構的方法,表内容是tableentry point的數據,這個訪問方法從SMBIOS2.1以後開始被使用,從2.1開始,以後的版本都推薦使用這種訪問方式。在2.1版本中允許支這兩種方法中的任意一種和兩種都支,但在2.2已經以後的版本,必須支方法2

    市場上計算機已經均支SMBIOS2.3標准,所以只考慮方法2,基表結構的訪問方式。
    表結構訪問SMBIOS的過程是先找到EntryPoint StructureEPS)表,然後通過EntryPoint StructureEPS)表的數據找到SMBIOS數據表。
    訪問SMBIOSEPS表的操作過程如下:
    1.從物理内存0xF0000-0xFFFFF間尋找關鍵字“_SM_”
    2.找到後再向後16個字節,看後面5BYTE是否是關鍵字“_DMI_”,如果是,EPS表即找到。
    注:按照SMBIOS規範説明,找到關鍵字”_SM_”後就可以確定此處就是EPS表結構,但我在實際操作中發現有為數不少的電腦的指定64K内存中有不只一個“_SM_”,所以不能用找到”_SM_”來確定,需要繼續判斷16個字節後是否是“_DMI_”
    SMBIOSEPS表結構如下:
    位置名稱長度描述
    00H關鍵字4 byte固定是“_SM_”
    04HCheck Sum1 byte用於檢查數據
    05H表結構長度1 byteEntry Point Structure表的長度
    06HMajor版本號碼1 byte用於判斷SMBIOS版本
    07HMinor版本號碼1 byte用於判斷SMBIOS版本
    08H表結構大小2 byte用於即插即用接口方法獲得數據表結構長度
    0AHEPS修正1 byte
    0B-0FH格式區域5 byte存放解釋EPS修正的訊息
    10H關鍵字5 byte固定為“_DMI_”
    15HCheck Sum1 byteIntermediate Entry Point Structure(IEPS)Check Sum
    16H數據表長度2 byteSMBIOS數據表長度
    18H數據表位址4 byteSMBIOS數據表的真實內存位置
    1CH數據表結構數目2 byteSMBIOS數據表的結構數目
    1EHSMBIOS BCD修正1 byte
    通過EPS表結構中的12H以及14H處,得出數據表長度和數據表地址,即可通過地址訪問結構表。從EPS表中的1CH處可得知結構表結構的總數,其中TYPE0結構就是BIOSinformationTYPE1結構就是SYSTEMInformation
    每個結構的頭部是相同的,格式如下:
    位置名稱長度描述
    00HType number1 byte結構的type number
    01H長度1 byte本結的長度,就此type number的結構而言
    02HHandle2 byte用於獲得SMBIOS結構,使用方法未知
    每個結構都分為格式區域和字符串區域,格式區域就是一些本結構的信息,字符串區域是緊隨在格式區域後的一個區域。結構01H處標識的結構長度僅是格式區域的長度,字符串區域的長度是不固定的。
    下面以TYPE0BIOSinformation)為例説明格式區域和字符串區域的關系

    TYPE0BIOSinformation)格式區域如下:

    位置名稱長度描述
    00HType number1 byte結構的type number,此處是0
    01H長度1 byteType 0格式區域的長度,一般為14H,也有13H
    02HHandle2 byte一般為0000H
    04HBIOS廠商訊息1 byte此處是BIOS賣方的訊息,可能是OEM廠商名,一般為01H,代表緊隨格式區域後的字串區域的第一個字串
    05HBIOS版本1 byteBIOS版本號,一般為02H,代表字串區域的第二個字串
    06HBIOS開始位址段2 byte用於計算常駐BIOS鏡像大小的計算,方法為(10000H-BIOS開始位址段)*16
    08HBIOS發佈日期1 byte一般為04H,表示字串區第三個字串
    09HBIOS ROM Size1 byte計算方法為(n+1)*64Kn為此處讀出的數值
    0AHBIOS特徵8 byteBIOS的功能支援特徵,如PCI,PCMCIA,FLASH等等
    12HBIOS特徵擴展不定
    緊隨TYPE0BIOSinformation)結構區域之後的就是TYPE0BIOSinformation)字符串區域,如下所示:
    db‘System BIOS Vendor Name’,0 ; 字符串以零結尾,第一個字符串:賣方
    db‘4.04’,0 ; 第二個:BIOS版本
    db‘00/00/0000’,0 ; 第三個:發布日期
    db0 ; 0為整個字符區域的結尾,所以要找下一個TYPE,只要在字符區域找到連續的0000H即可
    注:當有EPS表中得到結構表的開始地址後,可以直接按結構來尋找相應的TYPE號,找到後直接讀取就是該TYPE對應的結構的格式區域息,然後向後移動結構區域長度(構區域長度由該結構的01H處讀出)個BYTE,即是該TYPE機構的字符串區域。


    由上面介紹可知,獲得BIOS息的辦法就是:

    1.通過EPS表的12H14H數據找到TYPE結構表,然後找到TYPE0的内存地址。(不一定是首個)
    2.由TYPE0結構區域中得出相應BIOS息是否存在(存在則是上面所述的 01H,02H,03H依次排布,不存在則是相應的位置上為00H)。
    3.如存在息,則從字符串區域中讀取對應BIOS息。
    獲得SYSTEM信息方法同上,只是TYPE結構區域有所不同,請參照SMBIOSReference Specification

    在Linux環境裏可以使用dmidecode這支程式去dump SMBIOS的資料
    dmidecode主要功能:
    桌面管理介面提供標準化的電腦硬體描述,包括硬體的特性,比如 BIOS 的序號、與硬體連接線。dmidecode 提供由 BIOS 輸出 DMI 的資料,通常被其他硬體偵測程式當作後端工具使用。

    一個小故事讓我們明白資金流通的意義

    “又是炎熱小鎮慵懶的一天。太陽高掛,街道無人,每個人都債台高築,靠信用度日。這時,從外地來了一位有錢的旅客,他進了一家旅館,拿出一張1000 元鈔票放在櫃檯,說想先看看房間,挑一間合適的過夜,就在此人上樓的時候---- 店主抓了這張1000 元鈔,跑到隔壁屠戶那裡支付了他欠的肉錢...