2010年9月20日 星期一

關於VirtualBox安裝ubuntu時的網路設定

由於每個人的網路環境不同,但大致上都是使用ADSL撥接或是直接設定固定IP(或連接IP分享器,使用DHCP配置IP)。然而再網路搜尋如何設定ubuntu的網路芳鄰及上網設定,很多人都遇到不少問題,而且設定方式都不相同;我想這是因為沒有考慮到VirtualBox的版本問題才會這樣。因此要設定網路分享必須安裝samba,這部分的介紹很多,我就不提,然而3.0之前的版本必須在virtualbox先安裝Host only adapter的Driver,並且還要到控制台/網路連線去設定橋接。其實說麻煩倒是還好,但是有時還是會失敗,失敗原因我們現在也不考究;因為我們今天要說明的是virtualbox3.0以後的版本。或許有些人會使用2.x版的作法來設定3.x版;其實這樣不是很正確。這也是我之前常犯的錯誤。首先我們先來了解其網路設定提供了哪些function:
VirtualBox 提供五種網路模式:
1.not attached----未附掛網路,就是沒有網路。
2.NAT (network address translation)----將本機 (HOST) 當作是 gateway。這是預設值,特性是 VM 能上網,但是別人不能跟他溝通。
3.Bridged Adapter----橋接器。直接透過實體網卡連外,也就是說可上網而 HOST 和其他 VM 都能跟他對連。
4.internal networking----內部網路。把 HOST 當作 hub,VM 有內網可以互相溝通但不能跟 HOST 溝通。當然也不能連外網路。
5.Host-only Adapter。顧名思義只能跟 HOST 互通,其他 VM 和對外網路都不行。
看到這裡我想大家都已經知道要選擇第3項,但是別急;一開始我就說畢竟每個網路環境不同;因此,若是你用ADSL撥接,那麼你可能還要作別的事。
首先我們先來確定需求:
1連上網際網路(Internet) 2透過網路芳鄰讓所有電腦不管是host還是guest都可以共享資料。
現在說明如何設定:
Virtualbox 在 3.0 版後簡化了網路設定,要設定 bridge adapter只需在網路設定頁面 Attached to: 選擇 Bridged Adapter,Name: 選擇要和 VM 作 bridge 的實體網卡即可。(若你是固定ip或ip分享器使用者,那麼你已經完成了virtualbox的設定,只剩下ubuntu的設定就可以了:這部分包括samba及將你在ubuntu的etnx設成和host同業的網段,等一下會舉例)。現在是針對ADSL撥接使用者的說明,雖然Bridge Adapter可以連上網,但是當透過adsl連線時,Bridge是無法與其對應的,這時你我們就可以新增一張NAT網卡,我們知道NAT就是將本機 (HOST) 當作是 gateway,若是HOST可以上網,那麼VM就可以上網。但是設定時有一點要注意:首先移除既有的Bridge網卡,然後新增NAT網卡,此時在只有一張NAT網卡的狀況下,先將ubuntu開機,並連上internet測試是否連線正常,若確定正常上網就先關機,然後再新增第二張Bridge Adapter網卡;為了能順利在ubuntu區別這兩張網卡,請先記錄其MAC Address。再此開機進入ubuntu時,進入網路的編輯,並對Bridge網卡從DHCP選項改成手動選項,假設HOST目前網路設定如下IP:192.168.1.12 MASK:255.255.255.0 Gateway:None DNS:None;那麼我們就將這張Bridge手動設定為IP:192.168.1.13 MASK:255.255.255.0 Gateway:192.168.1.12 DNS:None;此時設定完成請重新開機;因為不保證以下指令有效sudo /etc/init.d/networking restart;重開機後若是你的samba已設定完成,則網路芳鄰應該就可以使用了。請切記一定要先在單一NAT網卡先測試能正常連internet,再去新增bridge網卡,否則順序相反會造成網路芳鄰有效,但連internet失敗。
--> 閱讀更多...

2010年8月12日 星期四

關於APIC的見解

APIC(高級可編程中斷控制器)對計算機來講有兩個作用, 管理IRQ的分配,可以把傳統的16個IRQ擴展到24個(傳統的管理方式叫PIC),以適應更多的設備。
管理多CPU。由於Nf2主板並不支援多CPU,所以,APIC關閉直接的影響是減少了可用的IRQ。 不過,如果板卡不是非常多的話,關閉 APIC對系統是沒有什麼影響的。要實現SMP功能,我們使用的CPU必須具備以下要求:CPU 內部必須內置APIC單元。Intel 多處理規範的核心就是高級可編程中斷控制器(Advanced Programmable Interrupt Controllers–APICs)的使用。CPU通過彼此發送中斷來完成它們之間的通信。通過給中斷附加動作(actions),不同的CPU可以在某種程度上彼此進行控制。每個CPU有自己的APIC(成為那個CPU的本地APIC),並且還有一個I/O APIC來處理由I/O設備引起的中斷,這個I/O APIC是安裝在主板上的,但每個CPU上的APIC則不可或缺,否則將無法處理多CPU之間的中斷協調。以上內容截自http://boy-asmc.blogspot.com/2009/11/acpi-vs-apic.html 。
一般而言,若要得知BIOS是否支援APIC時會先到e0000H~f0000h之間尋找20445352h(" DSR")及20525450h(" RTP")字樣;若找到後接下來CHECK接下來的20Bytes其check_sum是否等於0。接著以目前的es:dword ptr[10h]的值為index並以es:word ptr[index+4]為cx當成loop迴圈,來搜尋"CIPA"字樣,便可找到APIC Table。
更詳細的內容請參考Intel64 and IA-32 Architectures Software Developer's Manual-3A.pdf 之Chapter 10。另外參考:
http://www.cs.usfca.edu/~cruse/cs630f06/lesson27.ppt
http://faculty.qu.edu.qa/rriley/cmpt507/minix/apic_8c-source.html
--> 閱讀更多...

2010年7月13日 星期二

關於linux/ctype.h

/include/linux/ctype.h此路徑為相對路徑,其絕對路徑依照linux源碼樹為參考:例如我的源碼樹存放在/opt/FriendlyARM/mini2440/linux-2.6.32.2;所以絕對路徑如下:/opt/FriendlyARM/mini2440/linux-2.6.32.2/include/linux/ctype.h
/include/linux/ctype.h代碼如下
#ifndef _LINUX_CTYPE_H
#define _LINUX_CTYPE_H
/*
* NOTE! This ctype does not handle EOF like the standard C
* library is required to.
*/
#define _U 0x01 /* upper */ /* 大寫字母A-Z*/
#define _L 0x02 /* lower */ /* 小寫字母a-z */
#define _D 0x04 /* digit */ /* 數字0-9 */
#define _C 0x08 /* cntrl */ /* 控制字元 */
#define _P 0x10 /* punct */ /* 標點符號*/
#define _S 0x20 /* white space (space/lf/tab) */ /* 空白字元:空格、\t、\n 等 */
#define _X 0x40 /* hex digit */ /* 十六進位數 */
#define _SP 0x80 /* hard space (0x20) */ /* 空格字元0x20 */
extern unsigned char _ctype[];
#define __ismask(x) (_ctype[(int)(unsigned char)(x)])

#define isalnum(c) ((__ismask(c)&(_U∣_L∣_D)) != 0)
#define isalpha(c) ((__ismask(c)&(_U∣_L)) != 0)
#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
#define isdigit(c) ((__ismask(c)&(_D)) != 0)
#define isgraph(c) ((__ismask(c)&(_P∣_U∣_L∣_D)) != 0)
#define islower(c) ((__ismask(c)&(_L)) != 0)
#define isprint(c) ((__ismask(c)&(_P∣_U∣_L∣_D∣_SP)) != 0)
#define ispunct(c) ((__ismask(c)&(_P)) != 0)
#define isspace(c) ((__ismask(c)&(_S)) != 0)
#define isupper(c) ((__ismask(c)&(_U)) != 0)
#define isxdigit(c) ((__ismask(c)&(_D∣_X)) != 0)

#define isascii(c) (((unsigned char)(c))<=0x7f) #define toascii(c) (((unsigned char)(c))&0x7f)
static inline unsigned char __tolower(unsigned char c) { if (isupper(c)) c -= 'A'-'a'; return c; } static inline unsigned char __toupper(unsigned char c) { if (islower(c)) c -= 'a'-'A'; return c; } #define tolower(c) __tolower(c)
#define toupper(c) __toupper(c)
#endif

以下是/lib/ctype.c
#include
unsigned char _ctype[] = {
_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
_C,_C∣_S,_C∣_S,_C∣_S,_C∣_S,_C∣_S,_C,_C, /* 8-15 */
_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
_S∣_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
_P,_U∣_X,_U∣_X,_U∣_X,_U∣_X,_U∣_X,_U∣_X,_U, /* 64-71 */
_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
_P,_L∣_X,_L∣_X,_L∣_X,_L∣_X,_L∣_X,_L∣_X,_L, /* 96-103 */
_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
_S∣_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */

所以由此我們可以來驗證:
大寫S=58h=88 查_ctype得到_U = 0x01
因此我們以以下幾個巨集來求證:
isalnum("S")=(0x01)& (ox01 ∣ 0x02 ∣ 0x04) =1,所以得到true
isalpha("S")=(0x01)& (ox01 ∣ 0x02) =1,所以得到true
iscntrl("S")=(0x01)& (ox08) =0,所以得到false
isdigit("S")=(0x01)& (ox04) =0,所以得到false

♫=0eh=14 查_ctype得到_C = 0x08
因此我們以以下幾個巨集來求證:
isalnum("S")=(0x08)& (ox01 ∣ 0x02 ∣ 0x04) =0,所以得到false
isalpha("S")=(0x08)& (ox01 ∣ 0x02) =0,所以得到false
iscntrl("S")=(0x08)& (ox08) =1,所以得到true
isdigit("S")=(0x08)& (ox04) =0,所以得到false
參考更多特殊字碼:http://cowwu.myweb.hinet.net/note/text/930421a.htm
ASCII code:http://home.educities.edu.tw/wanker742126/index.html
--> 閱讀更多...

2010年7月11日 星期日

如何在32bits保護模式下作debug

以下我所論及的主題並非是在os的保護模式底下的debug方式;而是當你有自己的執行系統,這個執行系統是從16bits模式執行後進入32bits保護模式,並且執行自己定義的ap。就類似memtest86這種從booting到run ap都能一手掌控的代碼。然而memtest在保護模式下都是C語言代碼,因此要debug相對容易,所以我們要討論的是以assembly為架構的debug方式。
首先我們知道frame buffer是以為址0xb8000~0xb8fff為vga顯示控制區域;當然其他模式mapping到的位址也不同;但我們討論的是前者。雖然在dos或16bits模式可以使用int 10h來讀寫螢幕,我們也知道int 10h的作法也是去控制frame buffer。然而進入保護模式下傳統的bios及dos中斷已不能調用。但是我們還是可以直接去存取frame buffer;所以我寫了比下代碼以assembly的方式來顯示一些除錯用途的資訊:
以下資訊為如何在保護模式下debug,並利用健盤做單步debug
;清除所有的輸出緩衝器資料
push ax
FlushOBF: in al,64h
test al,1
jz NotOBF
in al,60h
jmp short FlushOBF
NotOBF: pop ax


push ecx
push ebx
mov ecx,edi
POS_XY bx,20,23
call sub32_hexprint
pop ebx
pop ecx

;等待8042輸出緩衝器有資料送進來
push eax
WaitOBFLoop:
in al,64h
test al,1
jz WaitOBFLoop

pop eax
;以上這小段完成健盤單步執行判斷,做法是當無按鍵被按下時,便一直執行迴圈
;用法:
;先push ecx和ebx
;將要顯示的資料存到ecx
;POS_XY bx,__x,__y
;call sub32_hexprint
;pop ecx和ebx

sub32_hexprint proc
pushad
mov eax,160
mul bl
add eax,SCREEN_ADR
xchg eax,edx
mov eax,2
mul bh
add edx,eax
mov bx,8 ;count
xor edi,edi
NEXT:
call cl2digital
mov BPTR[edx+edi],al
shr ecx,4
add edi,2
dec bx
jnz NEXT
popad
ret
sub32_hexprint endp

cl2digital proc
xor eax,eax
push ecx
and ecx,0fh
cmp cl,10
jae isA2F
add cl,30h
jmp exit
isA2F:
sub cl,10
add cl,41h
exit:
mov al,cl
pop ecx
ret
cl2digital endp

下面是marco
SCREEN_ADR equ 0b8000h
POS_XY macro r_, x_, y_
mov r_, (((x_) SHL 8) + (y_))
endm


以下為鍵盤控制指令參考:
; ========================================================
; 以下為8042界面函數之內部呼叫函數
; ========================================================
;
Flush8042 PROC
; 清除所有的輸出緩衝器資料
push ax
FlushOBF: in al,64h
test al,1
jz NotOBF
in al,60h
jmp short FlushOBF
NotOBF: pop ax
retn
Flush8042 ENDP
;
WaitIBF PROC
; 等待8042輸入緩衝器有空
WaitIBFLoop: push ax
in al,64h
test al,2
pop ax
jnz WaitIBFLoop
retn
WaitIBF ENDP
;
WaitOBF PROC
; 等待8042輸出緩衝器有資料送來
WaitOBFLoop: push ax
in al,64h
test al,1
pop ax
jz WaitOBFLoop
retn
WaitOBF ENDP
;
Read8042Data PROC
; 讀取8042回應資料
; 傳回: AL = 回應資料
call WaitOBF ; 等待資料回庄
in al,60h
retn
Read8042Data ENDP
;
Write8042Data PROC
; 送出資料給8042
; 參數: AL = 8042系統命令或資料
; 備註: 本函數亦為送出系統命令或資料的函數
call WaitIBF ; 等待輸入緩衝區有空
out 60h,al ; 送出資料
call WaitIBF ; 確認8042收到
retn
Write8042Data ENDP
;
RealSend8042Cmd PROC
; 送出一般命令碼給8042
; 參數: AL = 8042一般控制命令
; 備註: 8042命令之參數或傳回值由外界處理
call WaitIBF ; 等待輸入緩衝區有空
out 64h,al ; 送出命令
call WaitIBF ; 確認8042收到
retn
RealSend8042Cmd ENDP
;
RealSend8042Sys PROC
; 送出系統命令碼或參數給8042
; 參數: AL = 8042系統控制命令或參數
; 傳回: AL = 8042回應值
; 備註: 除回音外,其餘命令或參數應檢查是否傳回ACK
call Flush8042
call Write8042Data
call Read8042Data
retn
RealSend8042Sys ENDP
;
; ========================================================
; 以下為8042界面函數,使用前必須使用CLI將岔斷禁能
; ========================================================
;
Send8042Cmd PROC
; 送出一般命令碼給8042
; 參數: AL = 8042一般控制命令
call RealSend8042Cmd
retn
Send8042Cmd ENDP
;
Read8042Cmd PROC
; 送出讀取命令給8042
; 參數: AL = 8042讀取命令
; 傳回: AL = 讀取值
call Flush8042
call RealSend8042Cmd
call Read8042Data
retn
Read8042Cmd ENDP
;
Write8042Cmd PROC
; 送出寫入命令給8042
; 參數: AL = 8042寫入命令
; AH = 寫入資料
; 備註: AX值會被破壞
call RealSend8042Cmd
xchg al,ah
call Write8042Data
retn
Write8042Cmd ENDP
;
Echo8042 PROC
; 送出8042回音命令
; 傳回: AL = EEh
mov al,0EEh ; 回音命令
call RealSend8042Sys
retn
Echo8042 ENDP
--> 閱讀更多...

2010年7月6日 星期二

u-boot 顯示logo,並說明其原理part1


jpegtopnm Benson.jpg l ppmquant 31 l ppmtobmp -bpp 8 > Benson-8bit.bmp
紅色l是管線符號,因無法正常顯示所以用小寫L取代
以上這個指令必須先安裝netpbm,其作用是將jpeg檔轉成特定格式的bmp檔。將Benson-8bit.bmp更改檔名為denx.bmp並存到tools\logos,重新compile u-boot,便可完成,如上圖。接著我們來說明其draw logo原理:
在/tools/Makefile:
LOGO_H = $(OBJTREE)/include/bmp_logo.h
及
ifeq ($(LOGO_BMP),)
LOGO_BMP= logos/denx.bmp
endif
ifeq ($(VENDOR),atmel)
LOGO_BMP= logos/atmel.bmp
endif
及
$(LOGO_H):$(obj)bmp_logo $(LOGO_BMP)
$(obj)./bmp_logo $(LOGO_BMP) >$@ #這個$@就是bmp_logo.h
也就是說$(LOGO_H)這個目標的必要條件是bmp_logo 及denx.bmp這兩個檔案,才能產生bmp_logo.h這個主角,它就是denx.bmp的位元圖陣列。
所以在uboot主目錄中的makefile中有這麼一行:@rm -f $(obj)include/bmp_logo.h。在clean 時會將其刪除。
以上資訊若使用Source Insight根本無法搜尋得到,終就還是得到linux使用gerp。像這次我的搜尋指令如下:grep -Hwn "bmp_logo.h" -r ./ 。接著我們來了解一下bmp_logo.c:這是一支單一檔案所完成的獨立執行檔;我先把代碼列出;再來加入註解;另外也必須了解點陣圖的格式,請參考點陣圖(Bitmap)檔案格式
#include (stdio.h)
#include (stdlib.h)
#if defined(__linux__)
#include (stdint.h)
#else
#ifdef __CYGWIN__
#include "elf.h"
#else
#include (inttypes.h)
#endif
#endif

typedef struct bitmap_s { /* bitmap description */
uint16_t width;
uint16_t height;
uint8_t palette[256*3]; //調色盤資料
uint8_t *data; //圖的bit_map資料指標
} bitmap_t;

#define DEFAULT_CMAP_SIZE 16 /* size of default color map */

/* Neutralize little endians. */
uint16_t le_short(uint16_t x) //因為檔案格式是依照LITTLE ENDIAN排列,因此要找出實際數值必須作移位。
{
uint16_t val;
uint8_t *p = (uint8_t *)(&x);
val = (*p++ & 0xff) 左移 0;
val = (*p & 0xff) 左移 8;
return val;
}
void skip_bytes (FILE *fp, int n)
{
while (n-- > 0) fgetc (fp);
}

int main (int argc, char *argv[])
{
int i, x;
FILE *fp;
bitmap_t bmp;
bitmap_t *b = &bmp;
uint16_t data_offset, n_colors;

if (argc < color="#cc33cc">//判斷參數少於2便顯示fail
fprintf (stderr, "Usage: %s file\n", argv[0]);
exit (EXIT_FAILURE);
}
if ((fp = fopen (argv[1], "rb")) == NULL) {//開啟檔案,若失敗顯示fail
perror (argv[1]);
exit (EXIT_FAILURE);
}
if (fgetc (fp) != 'B' fgetc (fp) != 'M') { //判斷檔頭前2個byte是否為"BM",否則顯示錯誤
fprintf (stderr, "%s is not a bitmap file.\n", argv[1]);
exit (EXIT_FAILURE);
}
/*
* read width and height of the image, and the number of colors used;
* ignore the rest
*/
skip_bytes (fp, 8);
fread (&data_offset, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 6);
fread (&b->width, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 2);
fread (&b->height, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 22);
fread (&n_colors, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 6);
/*
* Repair endianess. 求出實際數值,包括data_offset b->width b->height n_colors
*/
data_offset = le_short(data_offset);
b->width = le_short(b->width);
b->height = le_short(b->height);
n_colors = le_short(n_colors);

/* assume we are working with an 8-bit file */
if ((n_colors == 0) or (n_colors > 256 - DEFAULT_CMAP_SIZE)) {
/* reserve DEFAULT_CMAP_SIZE color map entries for default map */
n_colors = 256 - DEFAULT_CMAP_SIZE;
}
//使用printf便可印到bmp_logo.h那是因為$(obj)./bmp_logo $(LOGO_BMP) >$@ 。 這個">"轉向指令。
以下的代碼就不用再加註解,只要比對一下產生出來的bmp_logo.h,就知道了
printf ("/*\n"
" * Automatically generated by \"tools/bmp_logo\"\n"
" *\n"
" * DO NOT EDIT\n"
" *\n"
" */\n\n\n"
"#ifndef __BMP_LOGO_H__\n"
"#define __BMP_LOGO_H__\n\n"
"#define BMP_LOGO_WIDTH\t\t%d\n"
"#define BMP_LOGO_HEIGHT\t\t%d\n"
"#define BMP_LOGO_COLORS\t\t%d\n"
"#define BMP_LOGO_OFFSET\t\t%d\n"
"\n",
b->width, b->height, n_colors,
DEFAULT_CMAP_SIZE);

/* allocate memory */
if ((b->data = (uint8_t *)malloc(b->width * b->height)) == NULL) {
fclose (fp);
printf ("Error allocating memory for file %s.\n", argv[1]);
exit (EXIT_FAILURE);
}

/* read and print the palette information */
printf ("unsigned short bmp_logo_palette[] = {\n");

for (i=0; ipalette[(int)(i*3+2)] = fgetc(fp);
b->palette[(int)(i*3+1)] = fgetc(fp);
b->palette[(int)(i*3+0)] = fgetc(fp);
x=fgetc(fp);

printf ("%s0x0%X%X%X,%s",
((i%8) == 0) ? "\t" : " ",
(b->palette[(int)(i*3+0)] >> 4) & 0x0F,
(b->palette[(int)(i*3+1)] >> 4) & 0x0F,
(b->palette[(int)(i*3+2)] >> 4) & 0x0F,
((i%8) == 7) ? "\n" : ""
);
}

/* seek to offset indicated by file header */
fseek(fp, (long)data_offset, SEEK_SET);

/* read the bitmap; leave room for default color map */
printf ("\n");
printf ("};\n");
printf ("\n");
printf ("unsigned char bmp_logo_bitmap[] = {\n");
for (i=(b->height-1)*b->width; i>=0; i-=b->width) {
for (x = 0; x <>width; x++) {
b->data[(uint16_t) i + x] = (uint8_t) fgetc (fp) + DEFAULT_CMAP_SIZE;
}
}
fclose (fp);

for (i=0; i<(b->height*b->width); ++i) {
if ((i%8) == 0)
putchar ('\t');
printf ("0x%02X,%c", b->data[i], ((i%8) == 7) ? '\n' : ' ');
}
printf ("\n"
"};\n\n"
"#endif /* __BMP_LOGO_H__ */\n"
);

return (0);
}
--> 閱讀更多...

2010年6月29日 星期二

memtest86關於smp實作

As we know memtest86+ is to originate from memtest86.但是memtest86 3.5裡頭關於SMP的源碼,在memtest86+ 4.0及4.1獲得延續。4.0及4.1雖然從代碼仍可看出SMP足跡,但實際上已經沒有支援SMP了。因此我們若要查看SMP的完整實作,必需Download memtest86 3.5 source code。
在main.c/test_start/initialise_cpus();首先要支援SMP就必需先調用這個routine,此處的test_start相當於memtest86+版的do_test。
void initialise_cpus(void)
{
int cpu_num, j;
smp_init_bsp();
num_cpus = smp_num_cpus();
/* let the BSP initialise the APs. */
for(cpu_num = 1; cpu_num < color="#3333ff">smp_boot_ap(cpu_num);
if(!smp_mode) {
/* some error in booting the AP,
* halt the already started APs */
for (j = 1; j <=cpu_num; j++) { jmp_address[j] = (void *)ap_halt; } break; } } } smp_init_bsp要看懂這個routine要先查詢一些資訊:小木偶裡頭有關cpuid的指令說明;Intel64 and IA-32 Architectures Software Developer's Manual-3A.pdf第8章。主要是找出CPU的核心數及尋找mp_config_table表。
smp_boot_ap這個routine才是重點。
以下資料取自於http://blog.chinaunix.net/u1/41699/showart_537820.html
多核初始化和啟動過程
多核初始化協議定義了兩類處理器:bootstrap processor(BSP) 和 application processors (APs). 上電或者MP重定後,系統硬體動態選擇一個作為BSP,其餘的作為APs。BSP的IA32_APIC_BASE中的BSP flag將被置位,其他的處理器中該位將被清空.上電或者復位後,AP等待BSP的SIPI(startup signal)信號,接收到SIPI信號後,AP執行BIOS中的AP配置代碼,然後進入halt狀態。對於支援Hyper_Threading的處理器,每個邏輯處理器將被作為一個單獨的處理器對待,都會有一個唯一的APIC ID。
志強(xeon)處理器MP初始化協定演算法:
1)基於系統拓撲結構,每個邏輯處理器將分配一個8位的APIC ID.這個ID將被寫入到每個處理器的local APIC ID寄存器中.
2)BSP被確定後,BSP創建一個ACPI的table和一個MP的table並將自己的初始APIC ID加到這些表中
3)在boot-strap流程的最後,BSP設置處理器數目為1,並開始廣播SIPI. 這裏,SIPI包含BIOS AP初始化代碼的向量
4)AP收到SIPI後,第一個得到信號量的AP開始執行初始化代碼,將自己的APIC ID加到ACPI和MP的表裏面,並將處理器數目加1.完成初始化後,AP執行一個CLI指令並halt自己
5)所有的AP都初始化完後,BSP得到一個系統處理器的個數,完成boot-strap代碼,進入OS.
6)此時AP保持halt狀態,等待INITs, NMIs和SMIs.
就我所了解以上的部分應該是BIOS所完成的,參考:http://www.biosren.com/thread-1395-1-1.html

BSP初始化過程
1. 初始化memory
2. Load microcode到處理器
3. 初始化記憶體範圍寄存器(MTRRs)
4. enable cache
5. 確定BSP是否是"GenuineIntel"
6. 執行CPUID,保存CPU資訊為將來使用
7. load AP的啟動代碼到低1M的一個4K的頁裏
8. 切換到保護模式
9. 轉換4K的頁基址為一個8位的向量. 例如 0x000BD000H --> 0xBDH
10.設置APIC的SVR的bit8來enable local APIC
11.建立錯誤處理handler
12.初始化鎖信號量
13.探測系統中的AP, 方法如下:
- 設置處理器COUNT為1
- 啟動一個timer,BSP開始等待
- 此時AP開始初始化,並將COUNT加1
- timer到期,BSP檢查COUNT,如果沒有增加,就表示系統中沒有AP.
14. 等timer中斷,檢查COUNT並建立處理器數目
AP初始化
1. 獲取信號量,開始初始化
2. load microcode到處理器
3. 初始化記憶體範圍寄存器(MTRRs)
4. enable cache
5. 檢查AP是否是"GenuineIntel"
6. 保存CPUID資訊,為將來使用
7. 切換到保護模式
8. 配置AP的共存記憶體介面執行環境
9. 將處理器個數加1
10.釋放信號量
11. 執行CLI並且進入halt狀態
12.等待INIT IPI

了解以上步驟變可從代碼來查看如何實現. update中....
--> 閱讀更多...

2010年6月16日 星期三

從start_armboot routine來了解u-boot--PART2

接續上回談到env_init,列出相關代碼:注意,這裡的代碼分散在不同的檔案
typedef struct environment_s {
uint32_t crc; /* CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;


#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
# define ENV_HEADER_SIZE (sizeof(uint32_t) + 1)
#else
# define ENV_HEADER_SIZE (sizeof(uint32_t))
#endif


#ifdef ENV_IS_EMBEDDED //這個marco未定義
extern uchar environment[];
env_t *env_ptr = (env_t *)(&environment[0]); //因此以上兩行未執行
#else /* ! ENV_IS_EMBEDDED */
env_t *env_ptr = 0;
#endif /* ENV_IS_EMBEDDED */


#define local static
#define ZEXPORT


#ifdef DYNAMIC_CRC_TABLE //在u-boot的code裡並未定義這個MARCO;所以並不會動態產生CRC_TABLE
local int crc_table_empty = 1;
local uint32_t crc_table[256];
local void make_crc_table OF((void));


local void make_crc_table()
{
uint32_t c;
int n, k;
uLong poly; /* polynomial exclusive-or pattern */
/* terms of polynomial defining this crc (except x^32): */
static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};

/* make exclusive-or pattern from polynomial (0xedb88320L) */
poly = 0L;
for (n = 0; n < n =" 0;" c =" (uLong)n;" k =" 0;" c =" c">> 1) : c >> 1;
crc_table[n] = c;
}
crc_table_empty = 0;
}
#else
/* ========================================================================
* Table of CRC-32's of all single-byte values (made by make_crc_table)
*/
local const uint32_t crc_table[256] = { //這個crc_table會被配置
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
#endif /*DYNAMIC_CRC_TABLE */


#ifndef OF /* function prototypes */
# ifdef STDC
# define OF(args) args
# else
# define OF(args) ()
# endif
#endif


#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
#define DO2(buf) DO1(buf); DO1(buf);
#define DO4(buf) DO2(buf); DO2(buf);
#define DO8(buf) DO4(buf); DO4(buf);

//以下這個routine在lib_generic/crc32.c

uint32_t ZEXPORT crc32 (uint32_t crc, const Bytef *buf, uInt len)
{
#ifdef DYNAMIC_CRC_TABLE
if (crc_table_empty) make_crc_table(); //這行不會被執行
#endif
crc = crc ^ 0xffffffffL;
while (len >= 8)
{
DO8(buf);
len -= 8;
}
if (len) do {
DO1(buf);
} while (--len);
return crc ^ 0xffffffffL;
}


先說明關於CRC,以CRC-16為例來說明其生成過程:
CRC-16碼由兩個位元組構成,在開始時CRC寄存器的每一位都預置為1,然後把CRC寄存器與8-bit的資料進行異或(異或:二進位運算相同為0,不同為1;0^0=0;0^1=1;1^0=1;1^1=0),之後對CRC寄存器從高到低進行移位,在最高位(MSB)的位置補零,而最低位(LSB,移位後已經被移出CRC寄存器)如果為1,則把寄存器與預定義的多項式碼進行異或,否則如果LSB為零,則無需進行異或。重複上述的由高至低的移位8次,第一個8-bit資料處理完畢,用此時CRC寄存器的值與下一個8-bit資料異或並進行如前一個資料似的8次移位元。所有的字元處理完成後CRC寄存器內的值即為最終的CRC值。
下面為CRC的計算過程:
来源:(http://blog.sina.com.cn/s/blog_639260ff0100g1ms.html) - 循环冗余校验CRC_Striker_新 1.設置CRC寄存器,並給其賦值FFFF(hex)。
2.將資料的第一個8-bit字元與16位元CRC寄存器的低8位進行異或,並把結果存入CRC寄存器。
3.CRC寄存器向右移一位,MSB補零,移出並檢查LSB。
4.如果LSB為0,重複第三步;若LSB為1,CRC寄存器與多項式碼相異或。
5.重複第3與第4步直到8次移位元全部完成。此時一個8-bit資料處理完畢。
6.重複第2至第5步直到所有資料全部處理完成。
7.最終CRC寄存器的內容即為CRC值。
常用的CRC迴圈冗餘校驗標準多項式如下:
CRC(16位) = X16+X15+X2+1
CRC(CCITT) = X16+X12 +X5+1
CRC(32位) = X32+X26+X23+X16+X12+X11+X10+ X8+X7+X5+X4+X2+X+1

--> 閱讀更多...

2010年6月14日 星期一

從start_armboot routine來了解u-boot--PART1

有關這方面ㄉ相關資訊參考:http://blog.chinaunix.net/u2/70445/showart_1852111.html
http://6xudonghai.blog.163.com/blog/static/33640629200911364735696/
http://www.63da.com/?All1-uboot%D4%B4%C2%EB%B7%D6%CE%F6/
(1):typedef int (init_fnc_t) (void);
(2):init_fnc_t *init_sequence[] ={
cpu_init, /* basic cpu dependent setup */ (1)
board_init, /* basic board dependent setup */(2)
interrupt_init, /* set up exceptions */ (3)
env_init, /* initialize environment */ (4)
init_baudrate, /* initialze baudrate settings */(5)
serial_init, /* serial communications setup */ (6)
console_init_f, /* stage 1 init of console */ (7)
display_banner, /* say that we are here */ (8)
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */(9)
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */ (10)
#endif
#if defined(CONFIG_HARD_I2C) defined(CONFIG_SOFT_I2C)
init_func_i2c, (11)
#endif
dram_init, /* configure available RAM banks */ (12)
display_dram_config, (13)
NULL,
};

(3):init_fnc_t **init_fnc_ptr;

(4)for (init_fnc_ptr = init_sequence ; *init_fnc_ptr ; ++init_fnc_ptr){
if ((*init_fnc_ptr)() != 0)
{hang (); }
}
以上四段code分散在board.c中,但是它們息息相關,而最下面的for迴圈更是高竿的程式寫法:以下我們就先針對這四段code了解其如何對硬體層面作完整的初始化。
我想大家對c語言有了解的應該都知道int main(int argc,char**argv){ };其實也可以這樣寫int main(int argc,char*argv[]){ };當然以下這樣也行int main(int argc,char argv[][]){ }; 所以上面的code不言自明,說穿了也是函式指標的應用。
剛才講到for迴圈,它主要的工作是硬體週邊初始化;和硬體週邊相關的代碼必需查datasheet,才能了解其實作,前三個:cpu_init、board_init、interrupt_init我認為較簡單,而且代碼上都有註解。其他部份我想未來有談到再補充。
在此我認為較難懂的是env_init,因為實際上在u-boot中這個routine就有9個之多,所以我在上面貼了一個參考連結,專門講述這個routine。雖然裡頭並未說明---實際呼叫的是env_nand.c或是env_dataflash.c或是env_flash.c...中的env_init();實際查看在common/makefile如下:
# environment
COBJS-y += env_common.o
COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o
COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o
COBJS-y += env_embedded.o
COBJS-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o
COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
COBJS-$(CONFIG_ENV_IS_IN_NVRAM) += env_nvram.o
COBJS-$(CONFIG_ENV_IS_IN_ONENAND) += env_onenand.o
COBJS-$(CONFIG_ENV_IS_IN_SPI_FLASH) += env_sf.o
COBJS-$(CONFIG_ENV_IS_NOWHERE) += env_nowhere.o
然後再查看include\configs\smdk2410.h如下:
#define CONFIG_ENV_IS_IN_FLASH 1
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
●所以在此會被編譯的只有env_flash.c。
然而在tekkaman所改版的部分它將smdk2410.h重新命名為mini2440.h,並修改其中內容如下:
//#define CONFIG_ENV_IS_IN_EEPROM 1/* use EEPROM for environment vars */
//#define CONFIG_ENV_OFFSET 0x000 /* environment starts at offset 0 */
//#define CONFIG_ENV_SIZE 0x400 /* 1KB */
#define CONFIG_ENV_IS_IN_NAND 1
//#define CONFIG_ENV_IS_IN_FLASH 1 //注意此行被mark掉了
#define CONFIG_ENV_OFFSET 0X60000
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
所以在此會被編譯的是env_nand.c。

另外題外話:最近使用arm-linux-objdump -D u-boot >u-boot.asm這個指令將u-boot反組譯後有助於trace參考之方便性。
--> 閱讀更多...

2010年6月10日 星期四

S3C2440與NAND FLASHK9F1208的接線分析

圖一
圖二
圖三
圖四
圖五
本文來自: 高校自動化網(http://www.zdh1909.com/) 詳細出處參考(轉載請保留本鏈結):http://www.zdh1909.com/html/MCS51/8883.html
NAND FLASH的接線方式和NOR FLASH,SDRAM都不一樣。以TQ2440開發板用的K9F1208為例,分析NAND FLASH的接線方式。K9F1208結構如圖一所示:
K9F1208位寬是8bit。
一頁: 512byte + 16byte 最後16byte是用於存儲校驗碼和其他資訊用的,不能存放實際的資料。
一個塊有32 page:(16k+512)byte
K9F1208有4096個塊:(64M+2M)byte,總共有64Mbyte可操作的晶片容量
NAND FLASH以頁為單位讀寫資料,以塊為單位擦除數據。
S3C24440和K9F1208的接線如圖二:
圖三是S3C2440的NAND FLASH引腳配置:
當選定一個NAND FLASH的型號後,要根據選定的NAND FLASH來確定S3C2440的NCON,GPG13,GPG14,GPG15的狀態。
圖四是S3C2440中4個腳位元狀態的定義:
K9F1208的一頁是512byte,所以NCON接低電平,GPG13接高電平。
K9F1208需要4個定址命令,所以GPG14接高電平K9F1208的位寬是8,所以GPG15接低電平。
NAND FLASH定址對K9F1208來說,地址和命令只能在I/O[7:0]上傳遞,資料寬度是8位元。
地址傳遞分為4步,如圖五
:
第1步發送列位址,既選中一頁512BYTE中的一個位元組。512byte需要9bit來選擇,這裏只用了A0-A7,原因是把一頁分成了2部分,每部分256位元組。通過發送的讀命令字來確定是讀的前256位元組還是後256位元組。
當要讀取的起始地址(Column Address)在0~255內時我們用00h命令,當讀取的起始位址是在256~511時,則使用01h命令。
一個塊有32頁,用A9-A13共5位來選擇一個塊中的某個頁。
總共有4096個塊,用A14-A25共12位來選擇一個塊。
K9F1208總共有64Mbyte,需要A0-A25共26個位址位。
例如要讀NAND FLASH的第5000位元組開始的內容。把5000分解成列地址和行地址。
Column_address = 5000%512 = 392
Page_address = (5000>>9) = 9
因為column_address>255,所以用01h命令讀
發送命令和參數的順序是:
NFCMMD = 0x01;從後256位元組開始讀
NFADDR = column_address & 0xff;取column_address的低8位元送到數據線
NFADDR = page_address & 0xff;發送A9-A16
NFADDR = (page_address >>8) & 0xff; 發送A17-A24
NFADDR = (page_address >> 16) & 0xff;發送A25
上面的NFCMMD,NFADDR.是S3C2440的NAND FLASH控制寄存器。讀取的數據會放在NFDATA中。
--> 閱讀更多...

2010年5月24日 星期一

函式指標的用法

函式指標的應用,可減少判斷的次數;把函式當成參數傳遞。此處轉貼一個學c的連結:C程式語言教學
方法一:
ulong FUNC(ulong param1, ulong param2, ulong param3){
........
........
return retval;
}
void Call_FUNC(int paramX, void* paramY){
........
........
return;
}
void Main_FUNC(){
........
Call_FUNC( x , (void*)FUNC);
........
return;
}
方法二:
struct STRUCTNAME{
int A;
unsigned B;
char *NAME;
long (*FUNC_X)(void);
void (*FUNC_Y)(int,long);
};
static long ptr1_func(void){
......
}
static void ptr2_func(int i, long k){
......
}
static long ptr3_func(void){
......
}
static void ptr4_func(int a, long b){
......
}
static long ptr5_func(void){
......
}
static void ptr6_func(int v, long u){
......
}
static long ptr7_func(void){
......
}
static void ptr8_func(int n, long f){
......
}
static struct STRUCTNAME struct_array[] = {
{ x, x , "xxxx", ptr1_func , ptr2_func(x,y)},
{ x, x , "xxxx", ptr3_func , ptr4_func(d,e)},
{ x, x , "xxxx", ptr5_func , ptr6_func(f,g)},
{ x, x , "xxxx", ptr7_func , ptr2_func(j,k)},
{ x, x , "xxxx", ptr3_func , ptr8_func(r,t)}
};
函式指標呼叫如下
struct_array[index].FUNC_X();
struct_array[index].FUNC_Y(intN,longM);
以下轉貼自程式設計俱樂部http://www.programmer-club.com.tw/ShowSameTitleN/c/33283.html

最近在trace code看到一個函式指標的觀念想請教一下,我寫了一個範例code如下
#include
#include
int fun(int, int);
typedef int(*fun_one)(int, int); //書上大部分介紹函式指標的寫法
typedef int(fun_two)(int, int); //這是在trace linux kernel code看到的寫法
int test1(fun_one);
int test2(fun_two *);
int main(void){
test1(fun);

test2(fun);

return 0;
}

int fun(int x, int y){
printf("x : %d, y : %d\n", x, y);
}
int test1(fun_one f1)//這邊不用多加一個指標(*)的符號
{f1(1,1);}
int test2(fun_two *f2)//要宣告f2是指向fun_two的指標
{f2(2,2);}
這邊兩種方式都可以run。
--> 閱讀更多...