2010年5月27日 星期四

SQL Data Type 資料型別,

轉http://blog.xuite.net/tolarku/blog/20968398

SQL Data Type 資料型別, 不是什麼高深知識純粹做個紀錄罷了, 型別包含 整數 精確位數 浮點數 字串 unicode字串 Binary字串 日期時間 貨幣 Timestamp

整數 ( ^ 符號代表次方 )

資料型別 位元數 資料範圍
tinyint 1 Byte 0 ~ 2^8 -1
( 0 ~ 255 )
smallint 2 Bytes -2^15 ~ 2^15-1
( -32738 ~ 32767 )
int 4 Bytes

-2^31 ~ 2^31 -1
( -2147483648 ~ 2147483467)

bigint 8 Bytes -2^63~2^63 -1
(-9223372036854775808 ~ 9223372036854776807 )
bit 1 Byte 0 , 1 , Null

精確位數與浮點數

資料型別 位元數 資料範圍
numeric 視精確度而定 -10^38 +1 ~ 10^38 -1
decimal 視精確度而定 -10^38 +1 ~ 10^38 -1
float 8 Bytes -1.79E+308 ~ 1.79E+308
15 位數
real 4 Bytes
-3.40E+38 ~ 3.40E+38
7 位數

字串 - Unicode 字串 - Binary 字串

資料型別 位元數 資料範圍
char(n) 1字元 1Byte 1 ~ 8000 字元
varchar(n) 1字元 1Byte 1 ~ 8000 字元
varchar(max) 變動長度 max=2GB 1 ~ 2^31 -1 字元
text 變動長度 max=2GB 1 ~ 2^31 -1 字元
nchar(n) 1字元 2Byte 1 ~ 4000 字元
nvarchar(n) 1字元 2Byte 1 ~ 4000 字元
nvarchar(max) 1字元 2Byte
變動長度 max=2GB
1 ~ 2^30 -1 字元
ntext 1字元 2Byte
變動長度 max=2GB
1 ~ 2^30 -1 字元
binary(n) 固定長度 8000 Bytes,不足自動補上 0x00
1 ~ 8000 Bytes
varbinary(n) 變動長度 1 ~ 8000 Bytes
varbinary(max) 變動長度 max=2GB 1 ~ 2^31 -1 Bytes
image 變動長度 max=2GB 1 ~ 2^31 -1 Bytes

日期 - 時間 - 貨幣 - Timestamp

資料型別 位元數 資料範圍
datatime 8 Bytes 1753/1/1 ~ 9999/12/31
ex: 2008-11-27 08:08:08.888
smalldatatime 4 Bytes
1900/1/1 ~ 2079/6/6
ex: 2008-11-24 15:11
money 8 Bytes -2^63 ~ 2^63 -1 小數4位
( -922337203685477.5808 ~ 922337203685477.5807)
smallmoney 4 Bytes
-2^31 ~ 2^31 -1 小數4位
( -214748.3648 ~ 214748.3647)
timestamp 8 Bytes 8 Bytes 的 16 進位值
uniqueidentifier 16 Bytes
16 Bytes 的 16 進位值

2010年5月25日 星期二

[轉]如何以 Java 取得衛星定位資訊 ?


http://ccckmit.wikidot.com/gps

許多程式設計人員一聽到要寫衛星定位程式,就覺得那一定很難,如果又要以 Java 寫成,那可能就會打退堂鼓了,本文即是要對此問題,提出一個極簡單的做法,只要利用 javax.comm 這個函式庫,可以在 20 行之內就寫完衛星的位的接收器,這個程式就是Gps.java

簡介

衛星定位系統的運作原理,雖然極為複雜,然而要寫一個衛星定位接收程式,卻是極為簡單的,其原因是市面上所販售的衛星定位接收器,有一套固定的規格,早已把複雜的部分處理好了,我們只要利用 MS Windows 中的 COM port,就可以接收到衛星定位資訊了,而在 Java 語言中接收與寫入 COM port 的方法,就是利用 SUN 所提出來的 communication API (javax.comm),在本文中、我們將說明 javax.comm 函式庫的用法,並實做一個不到 20 行的衛星定位程式。

==衛星定位的資訊格式
市面上所販售的衛星定位器,會將衛星定位資訊,編碼成一種稱為 NMEA 的格式,這種格式是一種簡單的純文字格式,以下是其中的一小段範例:

$GPGSA,A,1,,,,,,,,,,,,,50.0,50.0,50.0*05 $GPGSV,3,1,12,26,89,000,36,29,73,000,00,28,38,000,00,18,35,000,00*79 $GPGSV,3,2,12,09,27,000,00,21,27,000,41,15,23,000,00,10,18,000,00*79 $GPGSV,3,3,12,08,15,000,00,22,07,000,,19,07,000,,03,-01,000,*51 $GPRMC,113347.950,V,0000.0000,N,00000.0000,E,0.00,,120305,,*00 $GPGGA,113348.950,0000.0000,N,00000.0000,E,0,00,50.0,0.0,M,0.0,M,0.0,0000*76 $GPGSA,A,1,,,,,,,,,,,,,50.0,50.0,50.0*05 $GPRMC,113348.950,V,0000.0000,N,00000.0000,E,0.00,,120305,,*0F $GPGGA,113349.950,0000.0000,N,00000.0000,E,0,00,50.0,0.0,M,0.0,M,0.0,0000*77 $GPGSA,A,1,,,,,,,,,,,,,50.0,50.0,50.0*05 $GPRMC,113349.950,V,0000.0000,N,00000.0000,E,0.00,,120305,,*0E 

其中,最重要的資訊是座標資訊,紀錄在以 GPSGGA 開頭的欄位中,以下是其欄位的說明:

標頭  ,收訊時間點,經度座標,北或南,緯度座標  ,東或西,品質,衛星數量,.... $GPGGA,113348.950,0000.0000,N    ,00000.0000,E     ,0   ,00      ,50.0,0.0,M,0.0,M,0.0,0000*76 

其中,一般的程式通常只需要 "經度座標,北或南,緯度座標,東或西,品質,衛星數量" 等資訊就夠了,以下我們將說明一個衛星定位接收的 Java 程式寫法。

用Java收衛星資訊

由於程式實在太簡單,因此、我們將直接用程式的註解來說明即可。

import java.io.*; import javax.comm.*;  public class Gps {   public static void main( String[] args) throws Exception {      receive(args[0]);    // 接收來自指定的 COM port 的衛星訊號。   }    // 以下是衛星接收的主要程式。   public static void receive(String port) throws Exception {     // 開啟 COM port (在 Java 中稱為 Serial Port)。     SerialPort serialPort = ( SerialPort ) CommPortIdentifier.getPortIdentifier( port ).open( "GPS", 60 );     // 設定接收參數,包含頻率為 4800bps, 8個資料位元後接著一個停止位元,沒有同位位元(parity bit)等。     serialPort.setSerialPortParams( 4800, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE );     // 將該 Serial Port 當成檔案一樣開啟,然後不斷讀取即可。     BufferedReader in = new BufferedReader( new InputStreamReader(serialPort.getInputStream()) );     while (true ) {       String msg = in.readLine();       System.out.println(msg);     }   } } 

Gps.java 程式的安裝與執行方法

欲執行 Gps.java,請將 Gps.zip 下載後解壓縮,其中有兩個特殊的檔案,必須先複製到指定位置中才能順利執行本程式,這是由於我們在程式中使用到了 javax.comm 這個不在標準 JDK 中的函式庫,因此、必須加裝這個函式庫所使用到的資源於系統中,其安裝方法稍嫌複雜,在 MS Windows 系統中的安裝步驟如下:

* 將 win32com.dll 檔案複製到 {JDK}\bin 的目錄下。  * 將 javax.comm.properties 檔案複製到 {JDK}\lib 的目錄下。 

接著,請切換到解壓縮後的目錄 {…}\Gps 中,打上下列指令以編譯該程式,並接著執行之。

javac -classpath comm.jar;. Gps.java java -classpath comm.jar;. Gps COM4 

只要你的衛星定位接收器在 COM4 的位置,就可以看到類似下列的資訊:

$GPGSA,A,1,,,,,,,,,,,,,50.0,50.0,50.0*05 $GPGSV,3,1,12,26,89,000,36,29,73,000,00,28,38,000,00,18,35,000,00*79 $GPGSV,3,2,12,09,27,000,00,21,27,000,41,15,23,000,00,10,18,000,00*79 $GPGSV,3,3,12,08,15,000,00,22,07,000,,19,07,000,,03,-01,000,*51 $GPRMC,113347.950,V,0000.0000,N,00000.0000,E,0.00,,120305,,*00 $GPGGA,113348.950,0000.0000,N,00000.0000,E,0,00,50.0,0.0,M,0.0,M,0.0,0000*76 $GPGSA,A,1,,,,,,,,,,,,,50.0,50.0,50.0*05 $GPRMC,113348.950,V,0000.0000,N,00000.0000,E,0.00,,120305,,*0F $GPGGA,113349.950,0000.0000,N,00000.0000,E,0,00,50.0,0.0,M,0.0,M,0.0,0000*77 $GPGSA,A,1,,,,,,,,,,,,,50.0,50.0,50.0*05 $GPRMC,113349.950,V,0000.0000,N,00000.0000,E,0.00,,120305,,*0E 

當然、若 COM port 不是 COM4,則程式會收不到任何訊息,此時、請確定你的 GPS 所在的 COM port,假如是 COM2 則請重新執行下列命令即可。

java -classpath comm.jar;. Gps COM2 

然而,要如何得知衛星定位接收器被安裝於哪一個 COM port 呢 ? 你只要選取 MS windows 中的裝置管理員,看一下 連接埠(COM 和 LPT) 的項目中,GPS 在哪一個 port 上即可曉得,以下作者的電腦接上 GPS 於 COM4 後的裝置管理員畫面。

ComPort.jpg

(註:裝置管理員的位置位於: 開始/控制台/系統/硬體/裝置管理員 中)

結語

以 Java 撰寫衛星定位程式,其實相當簡單,然而用途卻非常大,在行動裝置普及率愈來愈高的狀況下,衛星定位的相關應用可望蓬勃發展,本文期望藉由簡單而清楚的說明,讓大家能很容易的開發出相關的應用程式,並了解衛星定位的程式設計方式。

參考文獻

  1. Java(tm) Communications API Win32 Installation Instructions
  2. NMEA format.
  3. GPS tutorial

2010年5月20日 星期四

裝個telnetd吧!

mount -t devpts devpts /dev/pts
telnetd

2010年5月18日 星期二

轉載Who Call Me?

Who Call Me?

徐千洋
timhsu@info.sayya.org
2004/03/30

 


誰叫我? 有用過 gdb 對 core 檔作 bt (backtrace) 嗎? 所謂 backtrace 是用來回溯檢查函式呼叫的關係, 以便了解是由那一個函式呼叫
出問題的函式. 尢其是在許多錯綜複雜的龐大程式碼中, backtrace 是相當有用的 debug 技巧. 而這個題目則是用來討論如何在程式執行中
作 backtrace.

在實作這個技術前有兩個關鍵點要先解決:
1. 如何取得此 function 返回位址.
2. 如何依據返回位址查知函式名稱.



關於第一點, 必須先了解堆疊(Stack) 和函式呼叫的處理關係. 堆疊是一個後進先出(Last-In-First-Out)的資料結構. 當呼叫某個函式時, 相關的暫存器(Register) 就會被存入堆疊. 而當函式返回時便會從堆疊裡取回返回位址以便回到原來呼叫的下一個指令繼續執行. 至於暫存器(Register), 其中 EIP 是 Instruction Pointer, 用來指出 CPU 將要執行指令的位址. ESP 暫存器則是用來指向目前堆壘的位址.

我們先寫個小程式來觀察可行性.

----------- test.c -----------
void test()
{

}

int main()
{

test();
}
------------------------------

[tim@localhost whocallme]$ gcc -o test test.c
[tim@localhost whocallme]$ gdb ./test
GNU gdb 5.3-25mdk (Mandrake Linux)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i586-mandrake-linux-gnu"...
(gdb) b test
Breakpoint 1 at 0x804832f
(gdb) r
Starting program: /home/tim/research/whocallme/test

Breakpoint 1, 0x0804832f in test ()
(gdb) info reg
eax 0x0 0
ecx 0x1 1
edx 0x4014fe50 1075117648
ebx 0x4014e9a0 1075112352
esp 0xbffff698 0xbffff698
ebp 0xbffff698 0xbffff698
esi 0x40013880 1073821824
edi 0xbffff6f4 -1073744140
eip 0x804832f 0x804832f

(gdb) disas test
Dump of assembler code for function test:
0x804832c : push %ebp
0x804832d : mov %esp,%ebp
0x804832f : pop %ebp
0x8048330 : ret
End of assembler dump.

ebp 暫存器值是 0xbffff698, 也就是原來的堆疊位址. 我們知道在呼叫函式時(call) CPU 會將返回位址存入堆疊, 因此可以從 ebp 暫存器的位址裡面找到我們需要的返回位址:

(gdb) p/x *0xbffff698
$1 = 0xbffff6a8

別忘了, 一進入此函式時 push $ebp 已被執行, 因此堆疊位址已被減 4, 所以要取得正確的值還得把 4 加回去才行:

(gdb) p/x *(0xbffff698+4)
$2 = 0x8048346

這個值應該就是 test() 正確的返回位址, 來檢查看看:

(gdb) disas main
Dump of assembler code for function main:
0x8048331
: push %ebp
0x8048332 : mov %esp,%ebp
0x8048334 : sub $0x8,%esp
0x8048337 : and $0xfffffff0,%esp
0x804833a : mov $0x0,%eax
0x804833f : sub %eax,%esp
0x8048341 : call 0x804832c
0x8048346 : leave
0x8048347 : ret
0x8048348 : nop
0x8048349 : nop
0x804834a : nop
0x804834b : nop
0x804834c : nop
0x804834d : nop
0x804834e : nop
0x804834f : nop
End of assembler dump.

果然在 call 完後的下個指令是位於 0x8048346, 也就是 test() 返回位址.
接下來我們就用 C 和一些 assembly 配合來實作.

------------- test-1.c ------------------
void test()
{
unsigned long *stack;
asm ("movl %%ebp, %0\n"
: "=g"(stack));
printf("ret address = 0x%x\n", *(stack+1));

}

int main()
{

test();
}
-----------------------------------------

[tim@localhost whocallme]$ ./test-1
ret address = 0x8048394
[tim@localhost whocallme]$ gdb ./test-1
(gdb) disas main
Dump of assembler code for function main:
0x804837f
: push %ebp
0x8048380 : mov %esp,%ebp
0x8048382 : sub $0x8,%esp
0x8048385 : and $0xfffffff0,%esp
0x8048388 : mov $0x0,%eax
0x804838d : sub %eax,%esp
0x804838f : call 0x804835c
0x8048394 : leave
0x8048395 : ret
0x8048396 : nop

第一個關鍵點目前已解決, 再來要想想怎麼要能夠依記憶體位址查知所處的函式名稱呢?




首先使用 objdump -t 來觀察執行檔的符號表:
[tim@localhost whocallme]$ objdump -t ./test-1 | awk '{print $1" "$3" "$6}'|grep "F"
080482c4 F call_gmon_start
080482f0 F __do_global_dtors_aux
08048330 F frame_dummy
080484ac O __FRAME_END__
08048450 F __do_global_ctors_aux
080483f0 F __libc_csu_fini
08048250 F _init
0804835c F test
080482a0 F _start
080483a0 F __libc_csu_init
0804837f F main
08048474 F _fini
08049598 O _GLOBAL_OFFSET_TABLE_

既然 objdump -t 可以印出程式的函式名稱和記憶體位址, 那麼我們就試試利用 objdump 一樣的技巧吧.
objdump 是利用 BFD Library(The Binary File Descriptor Library)來實作的, 底下的 bfd.c 也利用 BFD Library 來讀取符號表.

--------------- bfd.c --------------------

#include
#include

int main(int argc, char **argv[])
{
bfd *abfd;
long storage_needed;
asymbol **symbol_table;
long number_of_symbols;
long i;
char **matching;
struct sec *section;
char *symbol_name;
long symbol_offset;
long section_vma;
long symbol_address;

if (argc < 2)
return 0;
printf("Open %s\n", argv[1]);
bfd_init();
abfd = bfd_openr(argv[1],NULL);
if (abfd == (bfd *)0)
{
bfd_perror("bfd_openr");
return -1;
}
if (!bfd_check_format_matches(abfd, bfd_object, &matching))
{
return -1;
}
if (!(bfd_get_file_flags (abfd) & HAS_SYMS))
{
printf("ERROR flag!\n");
return -1;
}
/* 取得符號表大小 */
storage_needed = bfd_get_symtab_upper_bound(abfd);
if (storage_needed < 0)
return -1;
symbol_table = (asymbol **) xmalloc(storage_needed);
/* 將符號表讀進所配置的記憶體裡(symbol_table), 並傳回符號表個數 */
number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table);
if (number_of_symbols < 0)
return -1;
for(i = 0; i < number_of_symbols; i++)
{
/* 檢查此符號是否為函式 */
if (symbol_table[i]->flags & (BSF_FUNCTION|BSF_GLOBAL))
{
/* 反查此函式所處的區段(section) 及區段位址(section_vma)*/
section = symbol_table[i]->section;
section_vma = section->vma;
/* 取得此函式的名稱(symbol_name), 偏移位址(symbol_offset) */
symbol_name = symbol_table[i]->name;
symbol_offset = symbol_table[i]->value;
/* 將此函式的偏移位址加上區段位址則為此函式在執行時的記憶體位址(symbol_address */
symbol_address = section_vma + symbol_offset;
/* 檢查此函式是否處在程式本文區段 */
if (section->flags & SEC_CODE)

printf("<%s> 0x%x 0x%x 0x%x\n",
symbol_name,
section_vma,
symbol_offset,
symbol_address);

}
}
bfd_close(abfd);
}

-----------------------------------------
執行結果:
[tim@localhost whocallme]$ ./bfd ./test-1
Open ./test-1
0x80482a0 0x24 0x80482c4
<__do_global_dtors_aux> 0x80482a0 0x50 0x80482f0
0x80482a0 0x90 0x8048330
<__do_global_ctors_aux> 0x80482a0 0x1b0 0x8048450
<__libc_csu_fini> 0x80482a0 0x150 0x80483f0
<_init> 0x8048250 0x0 0x8048250
0x80482a0 0xbc 0x804835c
<_start> 0x80482a0 0x0 0x80482a0
<__libc_csu_init> 0x80482a0 0x100 0x80483a0
0x80482a0 0xdf 0x804837f
<_fini> 0x8048474 0x0 0x8048474

現在, 我們可以依照函式名稱及記憶體位址作對照表, 即可方便查詢. 不過這其中還有個小問題, 那就是我們雖然知道各函式的起始位址, 但是並不知道函式的結束位址, 也不知道各函式的大小. 要解決這個小問題就必須在作對照表時先作排序, 將位址越高的函式排在越後面,
把下一個函式的起始位址當作結束位址.

--------------- bfd_dumpfun.c --------------------
/* bfd_dumpfun.c (GPL)
* gcc -o bfd_dumpfun bfd_dumpfun.c -lbfd
*
* Usage: ./bfd_dumpfun [binary]
* Note: Dump functions infomation of ELF-binary with BFD Library.
*
* by TimHsu(timhsu@info.sayya.org) 2004/03/31
*
*/

#include
#include

typedef struct function_table FUN_TABLE;

/* 宣告一個包含函式名稱和位址的結構 */
struct function_table
{
char name[80];
unsigned long addr;
};

static FUN_TABLE *fun_table;
static int table_count = 0; /* 函式個數 */

static int compare_function(const void *a, const void *b)
{
FUN_TABLE *aa = (FUN_TABLE *) a;
FUN_TABLE *bb = (FUN_TABLE *) b;
if (aa->addr > bb->addr)
return 1;
else if (aa->addr <>addr)
return -1;
else
return 0;
}

/* 增加一個函式資料至對照表 */
static void add_function_table(char *name, unsigned long address)
{

strncpy(fun_table[table_count].name, name, 80);
fun_table[table_count].addr = address;
table_count++;
}

static void dump_function_table(void)
{
int i;
for(i = 0; i < table_count; i++)
{
printf("%-30s 0x%x\n", fun_table[i].name,
fun_table[i].addr);

}
}

int main(int argc, char **argv[])
{
bfd *abfd;
asection *text;
long storage_needed;
asymbol **symbol_table;
long number_of_symbols;
long i;
char **matching;
struct sec *section;
char *symbol_name;
long symbol_offset;
long section_vma;
long symbol_address;

if (argc < 2)
return 0;
printf("Open %s\n", argv[1]);
bfd_init();
abfd = bfd_openr(argv[1],NULL);
if (abfd == (bfd *)0)
{
bfd_perror("bfd_openr");
return -1;
}

if (!bfd_check_format_matches(abfd, bfd_object, &matching))
{
return -1;
}
if (!(bfd_get_file_flags (abfd) & HAS_SYMS))
{
printf("ERROR flag!\n");
return -1;
}
storage_needed = bfd_get_symtab_upper_bound(abfd);

if (storage_needed < 0)
return -1;
symbol_table = (asymbol **) xmalloc(storage_needed);
number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table);
if (number_of_symbols < 0)
return -1;
fun_table = (FUN_TABLE **)malloc(sizeof(FUN_TABLE)*number_of_symbols);
bzero(fun_table, sizeof(FUN_TABLE)*number_of_symbols);

for(i = 0; i < number_of_symbols; i++)
{
if (symbol_table[i]->flags & (BSF_FUNCTION|BSF_GLOBAL))
{
section = symbol_table[i]->section;
section_vma = section->vma;

symbol_name = symbol_table[i]->name;
symbol_offset = symbol_table[i]->value;
symbol_address = section_vma + symbol_offset;
if (section->flags & SEC_CODE)
{
add_function_table(symbol_name,
}
}
}
bfd_close(abfd);
/* 將函式對照表作排序 */
qsort(fun_table, table_count, sizeof(FUN_TABLE), compare_function);
dump_function_table();
}
-----------------------------------------
執行結果:
[tim@localhost whocallme]$ ./bfd_dumpfun ./test-1
Open ./test-1
_init 0x8048250
_start 0x80482a0
call_gmon_start 0x80482c4
__do_global_dtors_aux 0x80482f0
frame_dummy 0x8048330
test 0x804835c
main 0x804837f
__libc_csu_init 0x80483a0
__libc_csu_fini 0x80483f0
__do_global_ctors_aux 0x8048450
_fini 0x8048474


現在, 我們已經把技術的關鍵點都處理好了, 為了讓它能夠實用化, 最好是作成函式庫, 在有需要時能夠隨時呼叫.

------------- whocallme.h ------------------------
#include

#define FUNCTION_NAME_MAXLEN 80

#define who_call_me() \
do { \
unsigned long *stack; \
asm ("movl %%ebp, %0\n" \
: "=g"(stack)); \
fprintf(stderr, ": function <%s> call me <%s>!\n", \
find_function_by_addr(*(stack+1)), who_am_i()); \
} while(0)


extern int init_function_table(char *);
----------------------------------------------------
--------------- whocallme.c ------------------------
/* whocallme.c (GPL)
*
* A runtime backtrace of function.
* http://info.sayya.org/~timhsu/research/whocallme
*
*
* by Timhsu(timhsu@sayya.org) 2004/03/31
*
*/

#include
#include
#include "whocallme.h"

typedef struct function_table FUN_TABLE;
/* 宣告一個包含函式名稱和位址的結構 */
struct function_table
{
char name[FUNCTION_NAME_MAXLEN];
unsigned long addr;
};
static int compare_function(const void *a, const void *b)
{
FUN_TABLE *aa = (FUN_TABLE *) a;
FUN_TABLE *bb = (FUN_TABLE *) b;
if (aa->addr > bb->addr)
return 1;
else if (aa->addr <>addr)
return -1;
else
return 0;
}
/* 增加一個函式資料至對照表 */
static void add_function_table(char *name, unsigned long address)
{

strncpy(fun_table[table_count].name, name, FUNCTION_NAME_MAXLEN);
fun_table[table_count].addr = address;
table_count++;
}
/* 顯示函式對照表的全部資料 */
static void dump_function_table(void)
{
int i;
for(i = 0; i < table_count; i++)
{
fprintf(stderr, "%-30s 0x%x\n",
fun_table[i].name,
fun_table[i].addr);

}
}
/* 取得目前正在執行的函式名稱 */
char * who_am_i(void)
{
unsigned long *stack; \
asm ("movl %%ebp, %0\n" \
: "=g"(stack));
return find_function_by_addr(*(stack+1));
}
/* 依照位址取得函式名稱 */
char *find_function_by_addr(unsigned long addr)
{
int i;
for(i = 0; i < table_count; i++)
{
if (addr > fun_table[i].addr)
{
if (addr < fun_table[i+1].addr)
return fun_table[i].name;
}
}

}
/* 初始化函式對照表 */
int init_function_table(char *file)
{
bfd *abfd;
long storage_needed;
asymbol **symbol_table;
long number_of_symbols;
long i;
char **matching;
struct sec *section;
char *symbol_name;
long symbol_offset;
long section_vma;
long symbol_address;

bfd_init();
abfd = bfd_openr(file,NULL);
if (abfd == (bfd *)0)
{
bfd_perror("bfd_openr");
return -1;
}
if (!bfd_check_format_matches(abfd, bfd_object, &matching))
{
return -1;
}
if (!(bfd_get_file_flags (abfd) & HAS_SYMS))
{
printf("ERROR flag!\n");
return -1;
}
/* 取得符號表大小 */
storage_needed = bfd_get_symtab_upper_bound(abfd);
if (storage_needed < 0)
return -1;
symbol_table = (asymbol **) xmalloc(storage_needed);
/* 將符號表讀進所配置的記憶體裡(symbol_table), 並傳回符號表個數 */
number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table);
if (number_of_symbols < 0)
return -1;
/* 配置空間給函式對照表 */
fun_table = (FUN_TABLE **)malloc(sizeof(FUN_TABLE)*number_of_symbols);
bzero(fun_table, sizeof(FUN_TABLE)*number_of_symbols);

for(i = 0; i < number_of_symbols; i++)
{
/* 檢查此符號是否為函式 */
if (symbol_table[i]->flags & (BSF_FUNCTION|BSF_GLOBAL))
{
/* 反查此函式所處的區段(section) 及區段位址(section_vma)*/
section = symbol_table[i]->section;
section_vma = section->vma;
/* 取得此函式的名稱(symbol_name), 偏移位址(symbol_offset) */

symbol_name = symbol_table[i]->name;
symbol_offset = symbol_table[i]->value;
/* 將此函式的偏移位址加上區段位址則為此函式在執行時的記>憶體位址(symbol_address */
symbol_address = section_vma + symbol_offset;
/* 檢查此函式是否處在程式本文區段 */
if (section->flags & SEC_CODE)
{
/* 將此函式名稱和位址加入至對照表 */
add_function_table(symbol_name,
symbol_address);
}
}
}
free(symbol_table);
bfd_close(abfd);
/* 將函式對照表作排序 */
qsort(fun_table, table_count, sizeof(FUN_TABLE), compare_function);
}

----------------------------------------------
[tim@localhost whocallme]$ gcc -c whocallme.c
[tim@localhost whocallme]$ ar -q libwhocallme.a whocallme.o

來寫個小小的測試程式試試看:

---------------- test-2.c --------------------------

#include "whocallme.h"

void test()
{
who_call_me();
}
void test_a()
{
test_b();
test_c();
}
void test_b()
{
test();
}
void test_c()
{
who_call_me();
}
int main(int argc, char *argv[])
{
init_function_table(argv[0]);
test();
test_a();
test_b();
test_c();
}
----------------------------------------------
執行結果:
[tim@localhost whocallme]$ gcc -o test-2 test-2.c -lbfd -L. -lwhocallme
[tim@localhost whocallme]$ ./test-2
: function
call me !
: function call me !
: function call me !
: function call me !
: function
call me !

2010年5月17日 星期一

修改ubuntu的hostname

# vim /etc/hostname

內容改為自己要的名字

# vim /etc/hosts
這邊最好也要改,免得有些 service 會出錯

# /etc/init.d/hostname.sh
重新啟動

# hostname
驗證是否已經修改完畢。

2010年5月13日 星期四

MySQL 的備份及復原

MySQL 的備份及復原

要備份 MySQL 資料庫主要分為兩個方法,一是將資料庫目錄完整備份:二是使用 MySQL 內建的 mysqldump 程式。

備份資料庫目錄

MySQL 預設的儲存目錄在 /var/lib/mysql 內容,底下會有以資料庫名稱的目錄,例如 mydb 目錄便應該是 mydb 資料庫的資料。

如果 MySQL 正在運行,請先停止 MySQL,原因是可能會有資料未完全寫入,而 MySQL 會 lock 在使用中的 DB 檔案。

01 /etc/rc.d/init.d/mysqld stop
02 cd /var/lib/mysql/
03 tar zxcf mydb_backup.tgz mydb
04 /etc/rc.d/init.d/mysqld start


以上指令會先停止 MySQL,然後把 mydb 資料庫製作一個 taz 檔的備份,並儲存到 mydb_backup.tgz。
在使用以上指令時,請根據個別系統的設定作出修改。

好了,以上就麼 3 句指令就完成備份了,如果不幸的事情發生了,資料庫發生錯誤而要復原資料,可使用以下指令:

01 /etc/rc.d/init.d/mysqld stop
02 cd /var/lib/mysql/
03 mv mydb mydb_error
04 tar zxvf mydb_backup.tgz
05 /etc/rc.d/init.d/mysqld start


以上指令是先把 /var/lib/mysql/mydb 移到 /var/lib/mysql/mydb_error,然後將原先製作的備份檔解壓到 /var/lib/mysql/mydb。

mysqldump

雖然以上方法十分簡單,但有一個問題存在,那就是在備份及復原時均需停止 MySQL 的運作,這樣對於實際應用十分不便。再者,這樣備份出來的檔案,如果在相同版本的 MySQL Server 應該沒有問題,但移到版本不同的 Server 則不一定可以成功復原。

因為有以上的問題,MySQL 已經內建了備份工具,它就是 mysqldump。
mysqldump 的備份方法是將資料庫內的每個資料表結構及每筆資料產生 SQL 語句,然後存到文字檔。而且它可以自訂每一個資料表一個檔案,以及將資料表結構及資料分開儲存,以下是使用例子:

01 mysqldump --user=root -p mydb > /backup/mydb.sql


以上指令會使用 mysqldump 將 mydb 備份到 /backup/mydb.sql,在輸入指令後,需要輸入 MySQL 的 root 密碼。

至於復原資料同樣簡單,只要一句指令便完成:

01 mysql --user=root -p mydb < /backup/mydb.sql
(原文使用"mysqldump"來復原,應為筆誤,改成上述指令才對)

以上指令會將 /backup/mydb.sql 備份檔復原到 mydb 裡面。如果你的 MySQL Server 不止一個資料庫,希望可以一次過將所有資料庫備份起來,可以寫一個簡單的 shell script 完成,又或者使用以下指令:

01 mysqldump --user=root -p --all-databases > /backup/mysql.sql


這個 --all-databases 代表所有資料庫,這樣 mysqldump 便會將所有資料庫備份到 /backup/mysql.sql。

2010年5月9日 星期日