2010年5月4日 星期二

(轉)Linux 2.6內核Makefile分析

(轉)Linux 2.6內核Makefile分析
由於Linux的獨特優勢,使越來越多的企業和科研機構把目光轉向Linux的開發和研究上。目前Linux最新的穩定內核版本為2.6.17,但是當今絕大部分對於Linux Makefile的介紹文章都是基於2.4內核的,可以說關於2.6內核Makefile相關的文章鳳毛麟角,筆者抽時間完成了這篇分析文章,讓讀者迅速熟悉Linux最新Makefile體系,從而加深對內核的理解,同時也希望能對Linux在公司的推廣起到一定的推動作用,算是抛磚引玉吧!
1 Makefile組織層次
Linux的Make體系由如下幾部分組成:
Ø 頂層Makefile
頂層Makefile通過讀取配置檔,遞迴編譯內核代碼樹的相關目錄,從而產生兩個重要的目標檔:vmlinux和模組。
Ø 內核相關Makefile
位於arch/$(ARCH) 目錄下,為頂層Makefile提供與具體硬體體協結構相關的資訊。
Ø 公共編譯規則定義檔。
包括Makefile.build 、Makefile.clean、Makefile.lib、Makefile.host等檔組成。這些檔位於scripts目錄中,定義了編譯需要的公共的規則和定義。
Ø 內核配置檔 .config
通過調用make menuconfig或者make xconfig命令,用戶可以選擇需要的配置來生成期望的目標檔。
Ø 其他Makefile
主要為整個Makefile體系提供各自模組的目標檔定義,上層Makefile根據它所定義的目標來完成各自模組的編譯。
2 Makefile的使用
在編譯內核之前,用戶必須首先完成必要的配置。Linux內核提供了數不勝數的功能,支援眾多的硬體體系結構,這就需要用戶對將要生成的內核進行裁減。內核提供了多種不同的工具來簡化內核的配置,最簡單的一種是字元介面下命令行工具:
make config
這個工具會依次遍曆內核所有的配置項,要求用戶進行逐項的選擇配置。這個工具會耗費用戶太多時間,除非萬不得以(你的編譯主機不支援其他配置工具)一般不建議使用。
用戶還可以使用利用ncurse庫編制的圖形介面工具,這就是大名鼎鼎的:
make menuconfig
相信以前對2.4內核比較熟悉的用戶一定不會陌生。當然在2.6內核中提供了更漂亮和方便的基於X11的圖形配置工具:
make xconfig
當用戶使用這個工具對Linux內核進行配置時,介面下方會出現這個配置項相關的幫助資訊和簡單描述,當你對內核配置選項不太熟悉時,建議你使用這個工具來進行內核配置。
當用戶完成配置後,配置工具會自動生成.config檔,它被保存在內核代碼樹的根目錄下。用戶可以很容易找到它,當然用戶也可以直接對這個檔進行簡單的修改。但是當你修改過配置檔之後,你必須通過下面的命令來驗證和更新配置:
make oldconfig
跟2.4版本的不同之處在於,用戶不需要顯示的調用make dep命令來生成依賴檔,內核會自動維護代碼間的依賴關係。
當一切工作完成以後,用戶只需要簡單鍵入make,剩下所有的工作makefile就會自動替你完成了。
3 Makefile編譯流程
當用戶使用Linux的Makefile編譯內核版本時,Makefile的編譯流程如下:
Ø 使用命令行或者圖形介面配置工具,對內核進行裁減,生成.config配置檔
Ø 保存內核版本資訊到 include/linux/version.h
Ø 產生符號鏈結 include/asm,指向實際目錄 include/asm-$(ARCH)
Ø 為最終目標檔的生成進行必要的準備工作
Ø 遞迴進入 /init 、/core、 /drivers、 /net、 /lib等目錄和其中的子目錄來編譯生成所有的目標檔
Ø 鏈結上述過程產生的目標檔生成vmlinux,vmlinux存放在內核代碼樹的根目錄下
Ø 最後根據 arch/$(ARCH)/Makefile檔定義的後期編譯的處理規則建立最終的映象bootimage,包括創建引導記錄、準備initrd映象和相關處理
4 Makefile關鍵規則和定義描述
1) 目標定義
目標定義是Makefile檔的核心部分,目標定義通知Makefile需要生成哪些目標檔、如何根據特殊的編譯選項鏈結目標檔,同時控制哪些子目錄要遞迴進入進行編譯。
這個例子Makefile檔位於/fs/ext2目錄 :
#
# Makefile for the linux ext2-filesystem routines.
#
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
ioctl.o namei.o super.o symlink.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
ext2-$(CONFIG_EXT2_FS_SECURITY) += xattr_security.o
ext2-$(CONFIG_EXT2_FS_XIP) += xip.o
這表示與ext2相關的目標檔由 ext2-y定義的檔列表組成,其中ext2-$(*)是由內核配置檔.config中的配置項決定,最終Makefile會在這個目錄下統一生成一個目標檔ext2.o(由obj-$(CONFIG_EXT2_FS)決定)。其中obj-y表示為生成vmlinux檔所需要的目標檔集合,具體的檔依賴於內核配置。
Makefile會編譯所有的$(obj-y)中定義的檔,然後調用鏈結器將這些檔鏈結到built-in.o文件中。最終built-in.o檔通過頂層Makefile鏈結到vmlinux中。值得注意的是$(obj-y)的檔順序很重要。列表檔可以重複,檔第一次出現時將會鏈結到built-in.o中,後來出現的同名檔將會被忽略。檔順序直接決定了他們被調用的順序,這一點讀者需要特別注意。
讀者可能會在某些Makefile中發現lib-y定義,所有包含在lib-y定義中的目標檔都將會被編譯到該目錄下一個統一的庫檔中。值得注意的是lib-y定義一般被限制在 lib 和arch/$(ARCH)/lib 目錄中。
體系makefile檔和頂層makefile檔共同定義了如何建立vmlinux檔的規則。
$(head-y) 列舉首先鏈結到vmlinux的物件檔。
$(libs-y) 列舉了能夠找到lib.a檔的目錄。
其餘的變數列舉了能夠找到内嵌物件檔的目錄。
$(init-y) 列舉的物件位於$(head-y)物件之後。
然後是如下位置順序:
$(core-y), $(libs-y), $(drivers-y) 和 $(net-y)。
頂層makefile定義了所有通用目錄,arch/$(ARCH)/Makefile檔只需增加體系相關的目錄。
例如: #arch/i386/Makefile
libs-y += arch/i386/lib/
core-y += arch/i386/kernel/ \
arch/i386/mm/ \
arch/i386/$(mcore-y)/ \
arch/i386/crypto/
drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/
drivers-$(CONFIG_PCI) += arch/i386/pci/
…………………………………………
2) 目錄遞迴
Makefile檔只負責當前目錄下的目標檔,子目錄中的檔由子目錄中的makefile負責編譯,編譯系統使用obj-y 和 obj-m來自動遞迴編譯各個子目錄中的檔。
對於fs/Makefile:
obj-$(CONFIG_EXT2_FS) += ext2/
如果在內核配置檔.config中,CONFIG_EXT2_FS被設置為y或者m,則內核makefile會自動進入ext2目錄來進行編譯。內核Makefile只使用這些資訊來決定是否需要編譯這個目錄,子目錄中的makefile規定哪些檔編譯為模組,哪些檔編譯進內核。
3) 依賴關係
Linux Makefile通過在編譯過程中生成的 .檔案名.o.cmd(比如對於main.c檔,它對應的依賴檔案名為.main.o.cmd)來定義相關的依賴關係。
一般檔的依賴關係由如下部分組成:
Ø 所有的前期依賴檔(包括所有相關的*.c 和 *.h)
Ø 所有與CONFIG_選項相關的檔
Ø 編譯目標檔所使用到的命令行
位於init目錄下的main.c檔的依賴檔.main.o.cmd內容如下,讀者可以結合起來理解上述檔依賴關係的三個組成部分:
cmd_init/main.o := gcc -m32 -Wp,-MD,init/.main.o.d -nostdinc -isystem /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include -D__KERNEL__ -Iinclude -Iinclude2 -I/home/linux/linux-2.6.17.11/include -include include/linux/autoconf.h -I/home/linux/linux-2.6.17.11/init -Iinit -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Os -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -march=i686 -mcpu=pentium4 -mregparm=3 -ffreestanding -I/home/linux/linux-2.6.17.11/include/asm-i386/mach-default -Iinclude/asm-i386/mach-default -D"KBUILD_STR(s)=\#s" -D"KBUILD_BASENAME=KBUILD_STR(main)" -D"KBUILD_MODNAME=KBUILD_STR(main)" -c -o init/.tmp_main.o /home/linux/linux-2.6.17.11/init/main.c
deps_init/main.o := \
/home/linux/linux-2.6.17.11/init/main.c \
$(wildcard include/config/x86/local/apic.h) \
$(wildcard include/config/acpi.h) \
# 由於篇幅的關係,此處略去一些定義
……………………………………..
include2/asm/mpspec_def.h \
/home/linux/linux-2.6.17.11/include/asm-i386/mach-default/mach_mpspec.h \
include2/asm/io_apic.h \
include2/asm/apic.h \
init/main.o: $(deps_init/main.o)
$(deps_init/main.o):
4) 特殊規則
特殊規則使用在內核編譯需要規則定義而沒有相應定義的時候。典型的例子如編譯時頭檔的產生規則。其他例子有體系makefile編譯引導映射的特殊規則。特殊規則寫法同普通的makefile規則。
編譯程序在makefile所在的目錄不能被執行,因此所有的特殊規則需要提供前期檔和目標檔的相對路徑。
定義特殊規則時將使用到兩個變數:
$(src): $(src)是對於makefile檔目錄的相對路徑,當使用代碼樹中的檔時
使用該變數$(src)。
$(obj): $(obj)是目標檔目錄的相對路徑。生成檔使用$(obj)變數。
例如: #drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
這就是使用普通語法的特殊編譯規則。
目標檔依賴於兩個前提檔。目標檔的首碼是$(obj), 前提檔的首碼是
$(src)(因為它們不是生成檔)。
5) 引導映象
體系makefile檔定義了編譯vmlinux檔的目標物件,將它們壓縮和封裝成引導代碼,並複製到合適的位置。這包括各種安裝命令。在Linux中Makefile無法為所有的體系結構提供標準化的方法,因此常需要具體硬體體系結構下makefile提供附加處理規則。
附加處理過程常位於arch/$(ARCH)/下的boot/目錄。
內核編譯體系無法在boot/目錄下提供一種便捷的方法創建目標系統檔。因此arch/$(ARCH)/Makefile要調用make命令在boot/目錄下建立目標系統檔。建議使用的方法是在arch/$(ARCH)/Makefile中設置調用,並且使用完整路徑引用arch/$(ARCH)/boot/Makefile。
例如: #arch/i386/Makefile
boot := arch/i386/boot
bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
建議使用"$(Q)$(MAKE) $(build)=
"方式在子目錄中調用make命令。
當執行不帶參數的make命令時,將首先編譯第一個目標物件。在頂層makefile中第一個目標物件是all:。
一個體系結構需要定義一個默認的可引導映射。
增加新的前提檔給all目標可以設置不同於vmlinux的默認目標物件。
例如: #arch/i386/Makefile
all: bzImage
當執行不帶參數的"make"命令時,bzImage文件將被編譯。
6) 常用編譯命令
if_changed
如果必要,執行傳遞的命令。
用法:
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
當這條規則被使用時它將檢查哪些檔需要更新,或命令行被改變。後面這種情況將迫使
重新編譯編譯選項被改變的執行檔。使用if_changed的目標物件必須列舉在$( builtin-target)中,否則命令行檢查將失敗,目標一直會編譯。
if_changed_dep
如果必要,執行傳遞的命令並更新依賴檔。
用法:
%.o: %.S FORCE
$(call if_changed_dep,as_o_S)
當這條規則被使用時它將檢查哪些檔需要更新,或命令行被改變。同時它會重新檢測依賴關係的改變並將生成新的依賴檔。這是與if_changed命令的區別。
7) 定制命令
當正常執行帶編譯命令時命令的簡短資訊會被顯示(要想顯示詳細的命令,請在命令行中加入V=1)。要讓定制命令具有這種功能需要設置兩個變數:
quiet_cmd_ - 將被顯示的內容
cmd_ - 被執行的命令
例如: #
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(BUILDFLAGS) \
$(obj)/vmlinux.bin > $@
targets += bzImage
$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: $@ is ready'
執行make命令編譯$(obj)/bzImage目標時將顯示:
BUILD arch/i386/boot/bzImage
8) 預處理鏈結腳本
當編譯vmlinux映射時將使用arch/$(ARCH)/kernel/vmlinux.lds鏈結腳本。
相同目錄下的vmlinux.lds.S檔是這個腳本的預處理的變體。內核編譯系統知曉.lds
文件。並使用規則*lds.S -> *lds。
例如: #arch/i386/kernel/Makefile
always := vmlinux.lds
#Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
$(always)賦值語句告訴編譯系統編譯目標是vmlinux.lds。$(CPPFLAGS_vmlinux.lds)
賦值語句告訴編譯系統編譯vmlinux.lds目標的編譯選項。
編譯*.lds時將使用到下面這些變數:
CPPFLAGS : 定義在頂層Makefile
EXTRA_CPPFLAGS : 可以設置在編譯的makefile檔中
CPPFLAGS_$(@F) : 目標編譯選項。注意要使用檔全名。
9) 主機輔助程式的編譯
內核編譯系統支援在編譯階段編譯主機可執行程式。為了使用主機程式需要兩個步驟:第一個步驟使用hostprogs-y變數告訴內核編譯系統有主機程式可用。第二步給主機程式添加潛在的依賴關係。有兩種方法,在規則中增加依賴關係或使用$(always)變數。這一部分的內容相對於其他內核檔的編譯要簡單的多,感興趣的讀者可以參考scripts/Makefile.build中的相關內容。
10) Clean機制
clean命令清除在編譯內核生成的大部分檔,例如主機程式,列舉在 $(hostprogs-y)、$(hostprogs-m)、$(always)、$(extra-y)和$(targets)中目標檔都將被刪除。代碼目錄數中的"*.[oas]"、"*.ko"檔和一些由編譯系統產生的附加檔也將被刪除。
附加檔可以使用$(clean-files)進行定義。
例如: #drivers/pci/Makefile
clean-files := devlist.h classlist.h
當執行"make clean"命令時, "devlist.h classlist.h"兩個檔將被刪除。內核編譯系統默認這些檔與makefile具有相同的相對路徑,否則需要設置以'/'開頭的絕對路徑。
刪除整個目錄使用以下方式:
例如: #scripts/package/Makefile
clean-dirs := $(objtree)/debian/
這樣就將刪除包括子目錄在內的整個debian目錄。如果不使用以'/'開頭的絕對路徑內核編譯系統見默認使用相對路徑。
通常內核編譯系統根據"obj-* := dir/"進入子目錄,但是在體系makefile中需要顯式使用如下方式:
例如: #arch/i386/boot/Makefile
subdir- := compressed/
上面賦值語句指示編譯系統執行"make clean"命令時進入compressed/目錄。
在編譯最終的引導映射檔的makefile中有一個可選的目標物件名稱是archclean。
例如: #arch/i386/Makefile
archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
當執行"make clean"時編譯器進入arch/i386/boot並象通常一樣工作。arch/i386/boot 中的makefile檔可以使用subdir-標識進入更下層的目錄。
注意1: arch/$(ARCH)/Makefile不能使用"subdir-",因為它被包含在頂層makefile檔中,在這個位置編譯機制是不起作用的。
注意2: 所有列舉在core-y、libs-y、drivers-y和net-y中的目錄將被"make clean"命令清除。
4 小結
隨著Linux的飛速發展,越來越多的開發人員將關注的焦點集中到Linux的研究和開發上。如果想對Linux內核進行研究和開發,就必須首先熟悉Linux 內核Makefile的組織和編譯過程。目前Linux最新的穩定內核版本為2.6.17,但是當今絕大部分對於Linux Makefile的介紹都是基於2.4內核的,可以說關於2.6內核Makefile相關的文章鳳毛麟角,我特意抽時間完成了這篇分析文章,讓讀者迅速熟悉Linux最新Makefile體系,從而加深對內核的理解,同時也希望能對Linux在公司的推廣起到一定的推動作用。
本文來自CSDN博客,http://blog.csdn.net/mbtrend/archive/2008/10/05/3016889.aspx
--> 閱讀更多...

linux kernel 配置系統----新舊版差異

由於每個人接觸到的核心版本有新有舊,因此會在編譯核心並配置編譯選項後會想去查看各個核心樹裡的Makefile,而要看懂這些makefile須要得知哪些編譯選項是被選取的,然而這種配置方式在2.6.X版後有了更新。因此轉載這兩種不同配置方式如下:
linux内核配置系统分析 、 内核的 Kconfig & Makefile
linux内核配置系统(舊版)
隨著 Linux 作業系統的廣泛應用,特別是 Linux 在嵌入式領域的發展,越來越多的人開始投身到 Linux 內核級的開發中。面對日益龐大的 Linux 內核原始程式碼,開發者在完成自己的內核代碼後,都將面臨著同樣的問題,即如何將原始程式碼融入到 Linux 內核中,增加相應的 Linux 配置選項,並最終被編譯進 Linux 內核。這就需要瞭解 Linux 的內核配置系統。
  眾所周知,Linux 內核是由分佈在全球的 Linux 愛好者共同開發的,Linux 內核每天都面臨著許多新的變化。但是,Linux 內核的組織並沒有出現混亂的現象,反而顯得非常的簡潔,而且具有很好的擴展性,開發人員可以很方便的向 Linux 內核中增加新的內容。原因之一就是 Linux 採用了模組化的內核配置系統,從而保證了內核的擴展性。
  本文首先分析了 Linux 內核中的配置系統結構,然後,解釋了 Makefile 和設定檔的格式以及配置語句的含義,最後,通過一個簡單的例子--TEST Driver,具體說明如何將自行開發的代碼加入到 Linux 內核中。在下面的文章中,不可能解釋所有的功能和命令,只對那些常用的進行解釋,至於那些沒有討論到的,請讀者參考後面的參考文獻。
1. 配置系統的基本結構
  Linux內核的配置系統由三個部分組成,分別是:
  Makefile:分佈在 Linux 內核原始程式碼中的 Makefile,定義 Linux 內核的編譯規則;
  設定檔(config.in):給使用者提供配置選擇的功能;
  配置工具:包括配置命令直譯器(對配置腳本中使用的配置命令進行解釋)和配置使用者介面(提供基於字元介面、基於 Ncurses 圖形介面以及基於 Xwindows 圖形介面的使用者配置介面,各自對應於 Make config、Make menuconfig 和 make xconfig)。
  這些配置工具都是使用指令碼語言,如 Tcl/TK、Perl 編寫的(也包含一些用 C 編寫的代碼)。本文並不是對配置系統本身進行分析,而是介紹如何使用配置系統。所以,除非是配置系統的維護者,一般的內核開發者無須瞭解它們的原理,只需要知道如何編寫 Makefile 和設定檔就可以。所以,在本文中,我們只對 Makefile 和設定檔進行討論。另外,凡是涉及到與具體 CPU 體系結構相關的內容,我們都以 ARM 為例,這樣不僅可以將討論的問題明確化,而且對內容本身不產生影響。
2. Makefile
2.1 Makefile 概述
  Makefile 的作用是根據配置的情況,構造出需要編譯的原始檔案清單,然後分別編譯,並把目標代碼連結到一起,最終形成 Linux 內核二進位檔案。
  由於 Linux 內核原始程式碼是按照樹形結構組織的,所以 Makefile 也被分佈在目錄樹中。Linux 內核中的 Makefile 以及與 Makefile 直接相關的檔有:
  Makefile:頂層 Makefile,是整個內核配置、編譯的總體控制文件。
  .config:內核設定檔,包含由使用者選擇的配置選項,用來存放內核配置後的結果(如 make config)。
  arch/*/Makefile:位於各種 CPU 體系目錄下的 Makefile,如 arch/arm/Makefile,是針對特定平臺的 Makefile。
  各個子目錄下的 Makefile:比如 drivers/Makefile,負責所在子目錄下原始程式碼的管理。
  Rules.make:規則檔,被所有的 Makefile 使用。
  使用者通過 make config 配置後,產生了 .config。頂層 Makefile 讀入 .config 中的配置選擇。頂層 Makefile 有兩個主要的任務:產生 vmlinux 檔和內核模組(module)。為了達到此目的,頂層 Makefile 遞迴的進入到內核的各個子目錄中,分別調用位於這些子目錄中的 Makefile。至於到底進入哪些子目錄,取決於內核的配置。在頂層 Makefile 中,有一句:include arch/$(ARCH)/Makefile,包含了特定 CPU 體系結構下的 Makefile,這個 Makefile 中包含了平臺相關的資訊。
  位於各個子目錄下的 Makefile 同樣也根據 .config 給出的配置資訊,構造出當前配置下需要的原始檔案清單,並在檔的最後有 include $(TOPDIR)/Rules.make。
  Rules.make 文件起著非常重要的作用,它定義了所有 Makefile 共用的編譯規則。比如,如果需要將本目錄下所有的 c 程式編譯成彙編代碼,需要在 Makefile 中有以下的編譯規則:
    %.s: %.c
    $(CC) $(CFLAGS) -S $< -o $@   有很多子目錄下都有同樣的要求,就需要在各自的 Makefile 中包含此編譯規則,這會比較麻煩。而 Linux 內核中則把此類的編譯規則統一放置到 Rules.make 中,並在各自的 Makefile 中包含進了 Rules.make(include Rules.make),這樣就避免了在多個 Makefile 中重複同樣的規則。對於上面的例子,在 Rules.make 中對應的規則為:     %.s: %.c     $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F)) $(CFLAGS_$@) -S $< -o $@
2.2 Makefile 中的變數
  頂層 Makefile 定義並向環境中輸出了許多變數,為各個子目錄下的 Makefile 傳遞一些資訊。有些變數,比如 SUBDIRS,不僅在頂層 Makefile 中定義並且賦初值,而且在 arch/*/Makefile 還作了擴充。
  常用的變數有以下幾類:
  1) 版本資訊
  版本資訊有:VERSION,PATCHLEVEL, SUBLEVEL, EXTRAVERSION,KERNELRELEASE。版本資訊定義了當前內核的版本,比如 VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXATAVERSION=-rmk7,它們共同構成內核的發行版本本KERNELRELEASE:2.4.18-rmk7
  2) CPU 體系結構:ARCH
  在頂層 Makefile 的開頭,用 ARCH 定義目標 CPU 的體系結構,比如 ARCH:=arm 等。許多子目錄的 Makefile 中,要根據 ARCH 的定義選擇編譯原始檔案的列表。
  3) 路徑資訊:TOPDIR, SUBDIRS
  TOPDIR 定義了 Linux 內核原始程式碼所在的根目錄。例如,各個子目錄下的 Makefile 通過 $(TOPDIR)/Rules.make 就可以找到 Rules.make 的位置。
  SUBDIRS 定義了一個目錄清單,在編譯內核或模組時,頂層 Makefile 就是根據 SUBDIRS 來決定進入哪些子目錄。SUBDIRS 的值取決於內核的配置,在頂層 Makefile 中 SUBDIRS 賦值為 kernel drivers mm fs net ipc lib;根據內核的配置情況,在 arch/*/Makefile 中擴充了 SUBDIRS 的值,參見4)中的例子。
  4) 內核組成資訊:HEAD, CORE_FILES, NETWORKS, DRIVERS, LIBS
  Linux 內核檔 vmlinux 是由以下規則產生的:
  vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
   $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o --start-group $(CORE_FILES) $(DRIVERS) $(NETWORKS) $(LIBS) --end-group -o vmlinux
  可以看出,vmlinux 是由 HEAD、main.o、version.o、CORE_FILES、DRIVERS、NETWORKS 和 LIBS 組成的。這些變數(如 HEAD)都是用來定義連接生成 vmlinux 的目的檔案和庫檔列表。其中,HEAD在arch/*/Makefile 中定義,用來確定被最先連結進 vmlinux 的檔清單。比如,對於 ARM 系列的 CPU,HEAD 定義為:
  HEAD      := arch/arm/kernel/head-$(PROCESSOR).o           arch/arm/kernel/init_task.o
  表明 head-$(PROCESSOR).o 和 init_task.o 需要最先被連結到 vmlinux 中。PROCESSOR 為 armv 或 armo,取決於目標 CPU。 CORE_FILES,NETWORK,DRIVERS 和 LIBS 在頂層 Makefile 中定義,並且由 arch/*/Makefile 根據需要進行擴充。 CORE_FILES 對應著內核的核心檔,有 kernel/kernel.o,mm/mm.o,fs/fs.o,ipc/ipc.o,可以看出,這些是組成內核最為重要的文件。同時,arch/arm/Makefile 對 CORE_FILES 進行了擴充:
  # arch/arm/Makefile
  # If we have a machine-specific directory, then include it in the build.
  MACHDIR     := arch/arm/mach-$(MACHINE)
  ifeq ($(MACHDIR),$(wildcard $(MACHDIR)))
  SUBDIRS     += $(MACHDIR)
  CORE_FILES   := $(MACHDIR)/$(MACHINE).o $(CORE_FILES)
  endif
  HEAD      := arch/arm/kernel/head-$(PROCESSOR).o           arch/arm/kernel/init_task.o
  SUBDIRS     += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe
  CORE_FILES   := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
  LIBS      := arch/arm/lib/lib.a $(LIBS)
  5) 編譯資訊:CPP, CC, AS, LD, AR,CFLAGS,LINKFLAGS
  在 Rules.make 中定義的是編譯的通用規則,具體到特定的場合,需要明確給出編譯環境,編譯環境就是在以上的變數中定義的。針對交叉編譯的要求,定義了 CROSS_COMPILE。比如:
  CROSS_COMPILE  = arm-linux-
  CC       = $(CROSS_COMPILE)gcc
  LD       = $(CROSS_COMPILE)ld
  ......
  CROSS_COMPILE 定義了交叉編譯器首碼 arm-linux-,表明所有的交叉編譯工具都是以 arm-linux- 開頭的,所以在各個交叉編譯器工具之前,都加入了 $(CROSS_COMPILE),以組成一個完整的交叉編譯工具檔案名,比如 arm-linux-gcc。
  CFLAGS 定義了傳遞給 C 編譯器的參數。
  LINKFLAGS 是連結生成 vmlinux 時,由連結器使用的參數。LINKFLAGS 在 arm/*/Makefile 中定義,比如:
  # arch/arm/Makefile
  LINKFLAGS    :=-p -X -T arch/arm/vmlinux.lds
  6) 配置變數CONFIG_*
  .config 檔中有許多的配置變數等式,用來說明使用者配置
linux内核配置系统(新版) 使用內核的 Kconfig & Makefile
在內核編譯中如何將各個目錄樹中的檔組織起來編譯是一個很重要的問題,並且要根據使用者配置來編譯特有的內核。為瞭解決這個問題,內核使用兩種檔,Makefie和Kconfig。分佈到各目錄的Kconfig構成了一個分散式的內核配置資料庫,每個Kconfig分別描述了所屬目錄來原始檔案相關的內核配置功能表,就是我們使用命令 make menuconfig(或者xconfig)後產生的配置功能表,此功能表包含多層,每個層次都是由各個目錄中的Kconfig產生的。使用者根據需求來選擇如何編譯內核,然後將配置結果保存到.config中,然後執行Makefile時就會根據.config的結果來實現內核的編譯。
這個過程是由kbuild系統來完成的,Linux編譯系統會兩次掃描Linux的Makefile:首先編譯系統會讀取Linux內核頂層的Makefile,然後根據讀到的內容第二次讀取Kbuild的Makefile來編譯Linux內核。內核編譯系統或者說kbuild,是一種在編譯內核時,可以對內核配置選項進行選擇的機制。2.6內核樹中已經更新了這種機制,新版本的kbuild 不僅高速而且備有更完善的文檔。Kbuild機制完全依賴於原始程式碼的層次結構。
Makefile文件
面對樹狀結構的內核源碼目錄,內核編譯採用了各個子目錄擁有自己目錄相關的Makefile(被稱為sub-Makefile或kbuild Makefile),內核編譯依賴於各個子目錄下的子makefile(sub-Makefile)檔,這些sub-Makefile定義了根據該子目錄下的源碼檔構建目的檔案的規則,並且僅對該目錄下的檔作適當的修改。頂層Makefile採用遞迴的方式調用位元於init/, drivers/, sound/, net/, lib/ ,usr/等目錄下的各個子目錄中的 Makefile檔。在遞迴呼叫之前,kbuild首先要確定是否已經滿足一些必要的條件,包括在必要時更新include/Linux/version.h檔,並設置符號連結include/asm,使之指向與目標體系結構相關的檔。例如,如果為PPC編譯代碼,則include/asm指向include/asm-ppc。kbuild還要對文件include/Linux/autoconf.h和include/Linux/config進行編譯。之後,從根目錄開始進行遞迴。
各個子Makefile檔比較簡單,指出了該如何編譯目的檔案,例如/mm目錄下的Makefile片段:
16 obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o
17 obj-$(CONFIG_BOUNCE) += bounce.o
18 obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o
19 obj-$(CONFIG_HAS_DMA) += dmapool.o
20 obj-$(CONFIG_HUGETLBFS) += hugetlb.o
Kconfig文件
Kconfig的作用就是為了讓使用者配置內核,在Kconfig中定義了一些變數,使用者通過設置變數的值來選擇如何個性化自己的系統內核。定義的變數將在
每個功能表都有一個關鍵字標識,最常見的就是config
語法:
config
symbol是個新的標記的功能表項目,options是在這個新的功能表項目下的屬性和選項
其中options部分有:

1、類型定義:
每個config功能表項目都要有類型定義,bool布林類型、 tristate三態:內建、模組、移除 string字串、 hex十六進位、 integer整型
例如config HELLO_MODULE
bool “hello test module”
bool 類型的只能選中或不選中,tristate類型的功能表項目多了編譯成內核模組的選項,假如選擇編譯成內核模組,則會在.config中生成一個 CONFIG_HELLO_MODULE=m的配置,假如選擇內建,就是直接編譯成內核影響,就會在.config中生成一個 CONFIG_HELLO_MODULE=y的配置.
2、依賴型定義depends on或requires
指此功能表的出現和否依賴於另一個定義
config HELLO_MODULE
bool “hello test module”
depends on ARCH_PXA
這個例子表明HELLO_MODULE這個功能表項目只對XScale處理器有效。
3、幫助性定義
只是增加幫助用關鍵字help或—help—

.config文件
上面提到了利用內核配置工具自動生成名為.config的內核設定檔,這是編譯內核的第一步。.config檔位於原始程式碼根目錄下,描述所有內核配置選項,可以借助內核配置工具來選擇這些選項。每個內核配置選項都有相關的名字和變數值。其名字形如CONFIG_,其中是對相關選項的標識,在Kconfig檔中定義;變數可以有三個值:y,m或n。y代表“yes”,表示該選項將會被編譯到內核原始程式碼中,或者說會被編譯到系統中。m代表“module” ,表示該選項將會以模組的方式編譯到內核中。如果未選擇該選項(即將該選項的變數值設為n,代表“no”),那麼.config檔中就會出現下列注釋:“CONFIG_ is not set”。.config檔中選項的位置根據它們在內核配置工具中的位置進行排序,注釋部分說明該選項位於哪個功能表下。我們來看看一個.config文件的節選:
1 #
2 # Automatically generated make config: don’t edit
3 #
4 CONFIG_X86=y
5 CONFIG_MMU=y
6 CONFIG_UID16=y
7 CONFIG_GENERIC_ISA_DMA=y
8
9 #
10 # Code maturity level options
11 #
12 CONFIG_EXPERIMENTAL=y
13 CONFIG_CLEAN_COMPILE=
14 CONFIG_STANDALONE=y
15 CONFIG_BROKEN_ON_SMP=y
16
17 #
18 # General setup
19 #
20 CONFIG_SWAP=y
21 CONFIG_SYSVIPC=y
22 #CONFIG_POSIX_MQUEUE is not set
23 CONFIG_BSD_PROCESS_ACCT=y
上述.config檔指出第4到第7行的選項位於頂層功能表中,第12到第15行的選項位於代碼成熟度選項功能表中,第20行到第23行的選項位於通用設置選項功能表中。
所有配置工具都會產生上述功能表,並且已經看到前幾個選項、代碼成熟度選項、及通用設置選項都位於頂層。後面兩個選項被擴展為包含多個選項的子功能表。這些功能表都是在調用xconfig命令時,由qconf配置工具提供的。配置工具顯示的功能表都預設用於X86體系結構。
五、DIY:向內核添加自己的程式
A.在Linux內核中增加自己的程式步驟(注意這裡只是程式檔):
1.將編寫的原始程式碼複製到Linux內核原始程式碼的相應目錄中。
2.在目錄的Kconfig檔中增加新原始程式碼對應專案的編譯配置選項
3.在目錄的Makefile檔中增加對新原始程式碼的編譯條目。
B.在Linux內核drivers/目錄中增加目錄和子目錄步驟:
1.所加目錄為daiq,檔如下:
[daiq@localhost daiq]$ tree
.
-- Kconfig
-- Makefile
-- led
-- Kconfig
-- Makefile
`-- led.c
`-- test.c

#注意此時各個目錄中的Makefile和Kconfig檔是空的
2.在新增的相應目錄添加Kconfig和Makefile檔,上面的目錄中已經添加。
3.修改新增目錄的父目錄的Kconfig和Makefile檔,以便新增的Kconfig和
Makefile能被引用。向父目錄中的Makefile添加:
obj-y += daiq/
表示在編譯過程中包含子目錄daiq目錄。然後修改Kconfig檔,添加:
source “drivers/daiq/Kconfig”
表示在配置時引用子目錄daiq中的設定檔Kconfig。
4.實際上,要讓drivers/daiq/Kconfig有效,要在arch/arm/Kconfig文件中添加:
source “drivers/daiq/Kconfig”
父目錄drivers/Kconfig的修改可以不要。
5.經過上面一步,內核就可以找到所加的目錄daiq了,然後就是編輯各個目錄中的Makefile和Kconfig檔,在你添加的目錄daiq中的Makefile加入:
obj-$(CONFIG_TEST) += test.o #因為在daiq目錄中要編譯test.c文件
#所以會根據CONFIG_TEST來決定編譯選項
obj-y += led/#編譯daiq目錄中的子目錄led
然後Kconfig文件是:
menu "DaiQ device support" #在make menuconfig時要顯示的功能表入口
config DAIQ_TEST
bool "Test"
help
DaiQ device support
source "drivers/daiq/led/Kconfig"
endmenu
注意:menu和endmenu的前後要加回車,不然make menuconfig的時候會出錯。
再看led目錄下的Makefile和Kconfig:
Makefile為文件:
obj-$(CONFIG_LED)+=led.o
Kconfig文件:
config LED
tristate “led support”
5.現在可以make menuconfig來配置添加自己目錄daiq的驅動了!
--> 閱讀更多...

2010年5月3日 星期一

linux study 重點截錄

深入理解 Linux 2.6 的 initramfs 機制
ARM Linux源代码分析(1) for :linux/arch/arm/kernel/head.S
基于arm的Linux的启动分析
linux内核配置系统分析
Kconfig语法_config.in的新格式
MCUOL.com
内核的 Kconfig & Makefile
arm kconfig Serach
内核启动源码分析
為什麼我要學習linux,到底又要學些什麼?至少要有個明確的目標,否則"study linux"這幾個英文字母可是會耗掉我很多的青春(其實我已經沒有青春)。不論是一窩蜂或是潮流影響所致,linux的確給人帶來很大的改變,當大家都在談論它時或許只是一時的流行趨勢;然而那不是我想跟隨的流行;我只想跟著我的想法前進。那麼我的想法是什麼呢?"as far as possible close to Low Level"儘可能的貼進底層。
唯有這樣才可能真正了解KERNEL,了解KERNEL的目的是什麼?說真的好處太多,但我只針對我的需求:1.了解KERNEL使我更符合企業需求. 2.通盤了解才可能創新(雖然很難辦到). 3.讓更多的硬體可正常的運作於Linux. 4.所謂通盤了解並不是指要把整個核心 Source Code全部看懂過一遍,當然那是不可能達成的,而是針對主架構的程式碼要懂.....如此內心才不致覺的空虛. 5.提升編程能力.6.消磨時間、使腦袋瓜不會空轉。
--> 閱讀更多...

2010年4月28日 星期三

2440init.s 2440slib.s說明

2440init.s 2440slib.s這兩個檔案相當於是在ADS1.2開發環境中的簡易bootloader,其實只要輸入這兩個檔名就可搜尋到相關的中文註解說明例如S3C2440 2440init.s分析 2440启动代码注解 ;由於它的組合語言語法與GNU上的組合語言雖然一樣,但是卻有一些假指令,因此我把假指令的相關說明貼於此:arm偽指令 ARM汇编伪指令介绍。
其實在2440init.s中很多代碼是可以拿掉的;特別是那些以左右方括號[ ]作為以if endif,以l做為else的那些判斷式,原因可參考上面連結的相關說明。
今天我們其實重點是擺在2440slib.s這個檔案;2440slib.s注解,然而我個人覺得這個註解寫的並不很清楚,因此我打算自己來搞一遍。
這個檔裡頭都定義了一些和MMU相關聯的底層Routine:例如MMU_EnableICache MMU_DisableICache MMU_EnableDCache MMU_EnableMMU‧‧這裡也列出一些相關連結說明:ARM处理器架构-内存映射 内存管理单元(MMU)介绍 、 FS2410 开发板上启用 MMU 实现虚拟内存管理 、 内存管理单元(MMU)和协处理器CP15介绍 、 s3c2410 MMU(存储器管理单元)讲解 、 ARM920T关闭MMU,cache以及写缓冲区,CP15详解 、 ARM920T的MMU与Cache之操作MMU和Cache的内核启动代码 、 嵌入式Linux学习笔记(四)-内存管理单元mmu。内核关键链接脚本。
在linux kernel有關mmu設定請參考linux/arch/arm/boot/compressed/head.S
對以上的連結看過後就知道其實就是在搞cp15協同處理器;2440slib.s代碼實際內容如下:
目前我只把code擺上來,未來會逐一update
;==========================================
; File Name : 2440slib.s
; Function : S3C2440 (Assembly)
; Date : March 09, 2002
; Revision : Programming start (February 26,2002) -> SOP
; Revision : 03.11.2003 ver 0.0 Attatched for 2440
;==========================================
;Interrupt, FIQ/IRQ disable
NOINT EQU 0xc0 ; 1100 0000
;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.
GBLL THUMBCODE ;宣告一個全域邏輯變數THUMBCODE 預設值為FALSE
[ {CONFIG} = 16 ;如果CONFIG=16
THUMBCODE SETL {TRUE} ;設定THUMBCODE為TRUE
CODE32 ;
假指令CODE32,表示以下的代碼段為ARM 32bit 編碼
l ;else
THUMBCODE SETL {FALSE} ; 設定THUMBCODE為FALSE
] ;
endif

MACRO ;巨集宣告
MOV_PC_LR ;巨集名稱
[ THUMBCODE ;如果THUMBCODE為TRUE,則執行下一行,否則跳到下二行else處
bx lr ;
同call r14
l ;else
mov pc,lr ;r15<-r14
] ;endif
MEND ;巨集結尾

AREA C$$code, CODE, READONLY ;段宣告 段名稱C$$code 段屬性為代碼段唯讀
EXPORT EnterCritical
EnterCritical
mrs r1, cpsr
str r1, [r0]
orr r1, r1, #NOINT
msr cpsr_cxsf, r1
MOV_PC_LR
;restore cpsr, r0 = address to restore cpsr
EXPORT ExitCritical
ExitCritical
ldr r1, [r0]
msr cpsr_cxsf, r1
MOV_PC_LR
;==============
; CPSR I,F bit
;==============
;int SET_IF(void);
;The return value is current CPSR.
EXPORT SET_IF
SET_IF
;This function works only if the processor is in previliged mode.
mrs r0,cpsr
mov r1,r0
orr r1,r1,#NOINT
msr cpsr_cxsf,r1
MOV_PC_LR

;void WR_IF(int cpsrValue);
EXPORT WR_IF
WR_IF
;This function works only if the processor is in previliged mode.
msr cpsr_cxsf,r0
MOV_PC_LR


;void CLR_IF(void);
EXPORT CLR_IF
CLR_IF
;This function works only if the processor is in previliged mode.
mrs r0,cpsr
bic r0,r0,#NOINT
msr cpsr_cxsf,r0
MOV_PC_LR

EXPORT outportw
outportw strh r0, [r1]
MOV_PC_LR

EXPORT inportw
inportw ldrh r0, [r0]
MOV_PC_LR


;====================================
; MMU Cache/TLB/etc on/off functions
;====================================
R1_I EQU (1<<12)>EXPORT MMU_EnableICache
MMU_EnableICache
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_I
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableICache(void)
EXPORT MMU_DisableICache
MMU_DisableICache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_I
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_EnableDCache(void)
EXPORT MMU_EnableDCache
MMU_EnableDCache
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_C
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableDCache(void)
EXPORT MMU_DisableDCache
MMU_DisableDCache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_C
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_EnableAlignFault(void)
EXPORT MMU_EnableAlignFault
MMU_EnableAlignFault
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_A
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableAlignFault(void)
EXPORT MMU_DisableAlignFault
MMU_DisableAlignFault
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_A
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_EnableMMU(void)
EXPORT MMU_EnableMMU
MMU_EnableMMU
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_M
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableMMU(void)
EXPORT MMU_DisableMMU
MMU_DisableMMU
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_M
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_SetFastBusMode(void)
; FCLK:HCLK= 1:1
EXPORT MMU_SetFastBusMode
MMU_SetFastBusMode
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_iA:OR:R1_nF
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_SetAsyncBusMode(void)
; FCLK:HCLK= 1:2
EXPORT MMU_SetAsyncBusMode
MMU_SetAsyncBusMode
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;=========================
; Set TTBase
;=========================
;void MMU_SetTTBase(int base)
EXPORT MMU_SetTTBase
MMU_SetTTBase
;ro=TTBase
mcr p15,0,r0,c2,c0,0
MOV_PC_LR

;=========================
; Set Domain
;=========================
;void MMU_SetDomain(int domain)
EXPORT MMU_SetDomain
MMU_SetDomain
;ro=domain
mcr p15,0,r0,c3,c0,0
MOV_PC_LR

;=========================
; ICache/DCache functions
;=========================
;void MMU_InvalidateIDCache(void)
EXPORT MMU_InvalidateIDCache
MMU_InvalidateIDCache
mcr p15,0,r0,c7,c7,0
MOV_PC_LR

;void MMU_InvalidateICache(void)
EXPORT MMU_InvalidateICache
MMU_InvalidateICache
mcr p15,0,r0,c7,c5,0
MOV_PC_LR

;void MMU_InvalidateICacheMVA(U32 mva)
EXPORT MMU_InvalidateICacheMVA
MMU_InvalidateICacheMVA
;r0=mva
mcr p15,0,r0,c7,c5,1
MOV_PC_LR

;void MMU_PrefetchICacheMVA(U32 mva)
EXPORT MMU_PrefetchICacheMVA
MMU_PrefetchICacheMVA
;r0=mva
mcr p15,0,r0,c7,c13,1
MOV_PC_LR

;void MMU_InvalidateDCache(void)
EXPORT MMU_InvalidateDCache
MMU_InvalidateDCache
mcr p15,0,r0,c7,c6,0
MOV_PC_LR

;void MMU_InvalidateDCacheMVA(U32 mva)
EXPORT MMU_InvalidateDCacheMVA
MMU_InvalidateDCacheMVA
;r0=mva
mcr p15,0,r0,c7,c6,1
MOV_PC_LR

;void MMU_CleanDCacheMVA(U32 mva)
EXPORT MMU_CleanDCacheMVA
MMU_CleanDCacheMVA
;r0=mva
mcr p15,0,r0,c7,c10,1
MOV_PC_LR

;void MMU_CleanInvalidateDCacheMVA(U32 mva)
EXPORT MMU_CleanInvalidateDCacheMVA
MMU_CleanInvalidateDCacheMVA
;r0=mva
mcr p15,0,r0,c7,c14,1
MOV_PC_LR

;void MMU_CleanDCacheIndex(U32 index)
EXPORT MMU_CleanDCacheIndex
MMU_CleanDCacheIndex
;r0=index
mcr p15,0,r0,c7,c10,2
MOV_PC_LR

;void MMU_CleanInvalidateDCacheIndex(U32 index)
EXPORT MMU_CleanInvalidateDCacheIndex
MMU_CleanInvalidateDCacheIndex
;r0=index
mcr p15,0,r0,c7,c14,2
MOV_PC_LR

;void MMU_WaitForInterrupt(void)
EXPORT MMU_WaitForInterrupt
MMU_WaitForInterrupt
mcr p15,0,r0,c7,c0,4
MOV_PC_LR

;===============
; TLB functions
;===============
;voic MMU_InvalidateTLB(void)
EXPORT MMU_InvalidateTLB
MMU_InvalidateTLB
mcr p15,0,r0,c8,c7,0
MOV_PC_LR

;void MMU_InvalidateITLB(void)
EXPORT MMU_InvalidateITLB
MMU_InvalidateITLB
mcr p15,0,r0,c8,c5,0
MOV_PC_LR

;void MMU_InvalidateITLBMVA(U32 mva)
EXPORT MMU_InvalidateITLBMVA
MMU_InvalidateITLBMVA
;ro=mva
mcr p15,0,r0,c8,c5,1
MOV_PC_LR

;void MMU_InvalidateDTLB(void)
EXPORT MMU_InvalidateDTLB
MMU_InvalidateDTLB
mcr p15,0,r0,c8,c6,0
MOV_PC_LR

;void MMU_InvalidateDTLBMVA(U32 mva)
EXPORT MMU_InvalidateDTLBMVA
MMU_InvalidateDTLBMVA
;r0=mva
mcr p15,0,r0,c8,c6,1
MOV_PC_LR

;=================
; Cache lock down
;=================
;void MMU_SetDCacheLockdownBase(U32 base)
EXPORT MMU_SetDCacheLockdownBase
MMU_SetDCacheLockdownBase
;r0= victim & lockdown base
mcr p15,0,r0,c9,c0,0
MOV_PC_LR

;void MMU_SetICacheLockdownBase(U32 base)
EXPORT MMU_SetICacheLockdownBase
MMU_SetICacheLockdownBase
;r0= victim & lockdown base
mcr p15,0,r0,c9,c0,1
MOV_PC_LR

;=================
; TLB lock down
;=================
;void MMU_SetDTLBLockdown(U32 baseVictim)
EXPORT MMU_SetDTLBLockdown
MMU_SetDTLBLockdown
;r0= baseVictim
mcr p15,0,r0,c10,c0,0
MOV_PC_LR

;void MMU_SetITLBLockdown(U32 baseVictim)
EXPORT MMU_SetITLBLockdown
MMU_SetITLBLockdown
;r0= baseVictim
mcr p15,0,r0,c10,c0,1
MOV_PC_LR

;============
; Process ID
;============
;void MMU_SetProcessId(U32 pid)
EXPORT MMU_SetProcessId
MMU_SetProcessId
;r0= pid
mcr p15,0,r0,c13,c0,0
MOV_PC_LR

END
--> 閱讀更多...

2010年4月20日 星期二

GRUB Tracing 2

先列出configure.ac的巨集:截錄自gcc/gdb/make/autotool 文件/教學
AC_INIT(FILE)這個巨集用來檢查原始碼所在的路徑,autoscan會自動產生,我們不必修改它。
AM_INIT_AUTOMAKE(PACKAGE,VERSION)使用 Automake 所必備的巨集,PACKAGE是我們所要產生軟體套件的名稱,VERSION 是版本編號。
AC_CONFIG_AUX_DIR(dir)configure 系統時所使用的所有檔案( install-sh,config.sub,config.guess )若不想放在 configure 所在的目錄下, 可以利用此巨集指定在子目錄中
AC_CONFIG_HEADER(header.h) autoheader 會根據 AC_CHECK_HEADERS、AC_DEFINE 等巨集,產生 header.h.in , configure 參考此檔產生 header.h, 套件中的 header files只要含入header.h,即可解決編譯時大量的 '-D' 選項 。
AC_PROG_CC檢查系統可用的 C 編譯器,如果原始程式是用 C 寫的就需要這個巨集。其他程式檢查巨集請查看 autoconf/acprograms
AC_PROG_INSTALL檢察系統內是否有 BSD 相容 install 工具程式, 否則以 automake 的install-sh 替代。
AC_SUBST(VARIABLE) configure 程式會將 AC_OUTPUT 巨集所列出檔案中與指定變數名稱相同的位置,取代為該變數的值。
AC_CHECK_HEADERS( header.h)檢查系統中是否存在 header.h
AC_DEFINE(variable, define , comment)定義 C preprocessor variable, 可省略後兩項參數, 但預先必須在acconfig.h 中定義。ex: #undef USE_DNS
AC_DEFINE_UNQUOTED(variable, 可展開的 definem, comment )類似 AC_DEFINE , 其中的 define 若包含變數, 則會以內容展開變數ex:
AC_CONFIG_HEADER( conf.h )
AC_CHECK_HEADERS( unistd.h )
AC_DEFINE(USE_DNS, 1 , 使用DNS)
AC_DEFINEi_UNQUOTED(EDITOR, "$EDITOR", editor 的路徑)

則 autoheader 產生 conf.h.in 內含:
------------------------------------------------------------
/* Define as 1 if you have unistd.h */
#define HAVE_UNISTD_H 0
/* 使用DNS */
#undef USE_DNS
/* editor 的路徑 */
#undef EDITOR
若 configure 時, 若有 unistd.h、及指定 EDITOR=/usr/bin/vi 則
產生之 conf.h 如下
-----------------------------------------------------------
/* Define as 1 if you have unistd.h */
#define HAVE_UNISTD_H 1
/* 使用DNS */
#define USE_DNS 1
/* editor 的路徑 */
#define EDITOR /usr/bin/vi
AC_CHECK_PROG(variable,program,value-if-found,value-if-found,path1:path2)依指定路徑 path1:path2 尋找指定的 program, 若找到將 variable指定為 value-if-found, 若沒有找到 variable 指定為value-if-not-found,設定 variable 使用檔案的絕對檔案名稱。
AC_PATH_PROG(variable, program, value-if-not-found, path1:path2)依指定路徑 path1:path2 尋找指定的 program, 若找到將 variable指定為其完整路徑, 若沒有找到 variable 指定為 value-if-not-found。
AC_CANONICAL_HOST檢查系統類型, 並將其值存入 $host 變數
AC_CHECK_FUNCS(funcs, action-if-found, action-if-not-found)找尋是否有指定的 function 存在, 若存在執行 active-if-found 之 shell command, 反之則執行 active-if-not-found。可被檢查函式請參考autoconf/acfunctions
AC_ARG_ENABLE( feature,help-string,action-if-given,action-if-not-given)如果執行 configure 給定 --enable-feature 或 --disable-feature, 則會啟動相關的 action , enable 會執行 action-if-given, disable 會執行action-if-not-given。通常與 AC_DEFINE 搭配, 定義是否編譯某一項功能。
AC_OUTPUT(FILE)設定 configure 所要產生的檔案,如果是 Makefile 的話,configure便會把它檢查出來的結果 Makefile.in 檔然後產生合適的 Makefile。
ex:
configure.in 片段 Makefile.in 片段
--------------------------------------------------------------
FOO="hello" CFLAGS = -D@FOO@
AC_SUBST(FOO)
則執行 configure 後會產生 Makefile 包含以下片段: CFLAGS = -Dhello
* 2. 編輯 Makefilea.am , automake 根據 configure.in 中的巨集,將 Makefile.am轉變成 Makefile.in, 執行 automake --add-missing --copy 即可。
--add-missing 將包裝好 configure 所需之檔案補齊( 預設為 link 的方式 )
--copy 所需的檔案以 copy 的方式補齊
automake 支援、認可的 configure.in 巨集
----------------------------------------------------------------------
AC_INIT_AUTOMAKE(package, version)定義 PACKAGE、VERSION 兩變數
AM_CONFIG_HEADER( header.h )讓 automake 產生可自動再生成 header.h 的規則,使用此巨集必須先定義stamp-h.in, 用於標記 header.h 產生的時間。
AC_CANONICAL_HOST
AC_CHECK_TOOL automake 會確認 config.guess 及 config.sub 的存在。config.guess 用於猜測系統類型、config.sub用於提供檢查工具。
Makefile.am 選項說明
----------------------------------------------------------------------------
AUTOMAKE_OPTIONS設定 automake 的選項。Automake 主要是幫助開發 GNU 軟體的人員維護軟體套件,所以在執行 automake 時,會檢查目錄下是否存在標準GNU 軟體套件中應具備的文件檔案,例如 'NEWS'、'AUTHOR'、'ChangeLog'等文件檔。設成 foreign 時,automake 會改用一般軟體套件的標準來檢查。
SUBDIRS automake 會產生能夠遞迴進入指定目錄的 Makefile 規則。
bin_PROGRAMS定義我們所要產生的執行檔檔名。如果要產生多個執行檔,每個檔名用空白字元隔開。
hello_SOURCES定義 'hello' 這個執行檔所需要的原始檔。如果 'hello' 這個程式是由多個原始檔所產生,必須把它所用到的原始檔都列出來,以空白字元隔開。假設 'hello' 這個程式需要 'hello.c'、'main.c'、'hello.h'三個檔案的話,則定義hello_SOURCES= hello.c main.c hello.h
如果我們定義多個執行檔,則對每個執行檔都要定義相對的 filename_SOURCES。
pkgdata_DATA將 pkgdata_DATA 所指定的檔案安裝到 pkgdatadir 指定的目錄,*_DATA 對應*dir,如 localstate_DATA 安裝至 localstatedir。
EXTRA_DIST在 make dist 時將指定的檔案一起打包。
AC_PREREQ確保使用的是足夠新的Autoconf版本。如果用於創建configure的Autoconf的版本比version 要早,就在標準錯誤輸出列印一條錯誤消息並不會創建configure。
AC_CONFIG_SRCDIR([main.c])用來偵測所指定的源碼檔是否存在,來確定源碼目錄的有效性。
AC_CONFIG_HEADER([config.h])用於生成config.h檔,以便autoheader使用。
* 3. 建構 GNU build 系統aclocal -> autoheader -> autoconf -> automake
GRUB中文指南
我想到此就可以知道大概了;可以直接到makefile
--> 閱讀更多...

原來....我也是生存在這種輪迴之中

今天爬文不小心看到以下這一小段:
標題:大學生不如美女值錢.
苦工-----資本家----美女----苦工
這樣的資金流動在中國比較流行.
苦工勞動生產產品,資本家得到最大利益,
得到最大利益的資本家,本性需求高,花錢包美女
美女,愛美,愛花錢,買產品,
在這個資產迴圈中美女,是一重要因素.當然核心不是資本家.

看了之後‧‧‧‧‧‧
我想人生中至少也有美好的事物,若是硬要以物質衡量,那心靈方面的滿足又算什麼?
--> 閱讀更多...

2010年4月15日 星期四

學習GRUB Programming初啼

GRUB(Gran Unified Bootloader)是個bootloader,為何我對它這樣著迷,其實應該說道理還是一樣,它看起來不大,而且目前已經可以支援ATA SATA USB CDROM 等開機,因此絕對可以學到很多實作及概念並認識各式檔案系統如何運作,未來將OS安裝在USB DEVICE的狀況將會越來越常見 .重點當然也是因為它和linux kernel比較起來小很多,我想trace code應該也會比較得心應手.說真的像linux kernel那麼大也別想用source insight來管理,就連U-Boot都有點吃力了.所以越大的project還是要在linux環境來trace code,較沒有效能上的疑慮.然而我實際編譯一次後才發現它的檔案量還真不少,install到系統的模組也有1百多個;網上相關的編程教學也少的可憐;看來是要自立自強了。
一開始只知道它的評價比LILO好,其他說真的還一無所知.首先先來點開味菜ㄅ:雖然本文並非討論GRUB如何使用,但trace code需要的狀況還是會稍微提到。
configure及makefile;這個makefile是執行configure shell script後產生的;這是很正常的程序,只是我覺得寫這個shell script的人不正常。
然而實際上configure這個shell script也是自動產生的;說著說著好像越來越模糊了。說真的我越來越怕看到(自動)這兩個字了;因為那表示又有一些相關技術要k了。果然網上隨便輸入"automake" "autoconf" "makefile" "makefile.am" "makefile.in"等關鍵字保證又是一堆資料等著你去把相關知識給整合起來。要完成一個自動產生的makefile,流程大概如下:
1.autoscan 產生一個 configure.scan,更名為 configure.in
2.修改 configure.in 的內容
3.執行 aclocal 和 autoconf,分別會產生 aclocal.m4 及 configure 兩個檔案
4.使用編輯器,建立 Makefile.am 檔
5.使用 automake --add-missing 將 Makefile.in 產生出來
6.執行 ./configure,產生makefile
然而在grub project裡頭將以上幾個步驟寫成autogen.sh shell script內容如下:而且它的實作方式稍微不同,它不採用makefile.am
#! /bin/sh
set -e
aclocal
autoconf #到此產生configure
autoheader
# FIXME: automake doesn't like that there's no Makefile.am
automake -a -c -f true
echo timestamp > stamp-h.in
python util/import_gcry.py lib/libgcrypt/ .
for rmk in conf/*.rmk ${GRUB_CONTRIB}/*/conf/*.rmk
do
if test -e $rmk ; then
ruby genmk.rb < $rmk > `echo $rmk sed 's/\.rmk$/.mk/'`
fi
done
sh gendistlist.sh > DISTLIST
exit 0
雖然不到20行,但是卻牽扯到python、sed、ruby,所以我說學習shell programming並不簡單,應該是說你不可能只學習一種script語言;這樣會使你創作能力降低,但要一下子就搞這麼多東西;我想我會瘋掉、會瘋掉。更何況有用的script還有像perl、awk‧‧‧就是說光想在linux上programming;並不是想像中那樣;但script畢竟是比較簡單;雖然很多人總寫出一堆令人費解的敘述;寫到這裡不禁感嘆:年事已高的我,未來的路還很長。
無論我們是否要搞懂這個autogen.sh,至少要知道這個檔是否對未來整個專案的理解有很大關聯;所以實際去執行./configure後直接把makefile打開來看;果不其然還真有寸步難行之感。所以還是要乖乖就範,把這個autogen.sh給搞懂;因為它和之前描述的那6個步驟作法不同,也沒有執行autoscan所以沒有configure.scan,因此就不會有configure.in。不使用makefile.am,有看到執行automake,但是卻早有makefile.in這個檔案;總之用ㄌ一些自動化的工具卻不走正統路線;所以只好再去找一些相關資訊,例如:GNU Coding Standards FreeBSD Porter 手册 autoconf 和 automake 生成 Makefile 文件。
原來GRUB使用configure.ac,我想接下來應該是把重點擺到這個檔案上。因為aclocal是一個perl 腳本程式,它的定義是:aclocal - create aclocal.m4 by scanning configure.ac 。
終於好不容易找到了以下連結可供參考:Configure Makefile.am Makefile.in Makefile文件之間關係 automake Introduction英文 簡體 GRUB 運作原理
--> 閱讀更多...

2010年4月14日 星期三

DOS記憶體管理

DOS不管電腦擴充了幾MB的記憶體, 當你用MEM.EXE 來觀察時, 傳統記憶體(Conventional memory) 固定都只有640K Bytes。這640KB 就是一般應用程式所能使用的範圍。如果你使用5.0 版以前的DOS,進入中文糸統, 再要執行其他較佔記憶體的應用程式時, 就有可能產生 "記憶體不足" 的訊息, 不管你在640K以外還有多少記憶體。而現在DOS 5.0 最為人稱道的地方, 就是它提供了許多管理記憶體的方法, 讓程式有更多的記憶體空間可以運用。
DOS 5.0 提供的記憶體管理, 指的是位址為640K以上的記憶體。如果你的電腦只有640K 的RAM, 那麼DOS 5.0 對你就沒有多大用處。一般電腦最基本配備有1 Mega的記憶體, 就有384 K 的延伸記憶體可以運用。記憶體容量愈大, 運用範圍愈廣。像WINDOW這類多工軟體, 就需要龐大的記憶體來增加其速度。
640K的傳統記憶體
640K的限制由何處來的? 這必須回顧PC和CPU 的歷史。CPU 的定址能力, 是由硬體線路所限定的。在早期APPLEⅡ 使用的6502 CPU, 它是8 位元的微處理機, 故具有8 條的資料線路。其資料處理是以位元組(Byte)為單位, 每一個Byte可表示2^8 = 256 種數字。同時它用兩個Byte的組合來指示記憶體的位址。因此它須具有16條的位址線路, 共可表達2^16 = 65536 =64K 種的位址。換句話說, 16條的位址線路的定址能力的最高限制就是64KB 。1978年Intel 公司推出16位元的8086微處理機, 以16位元的字組( Word,相當於兩個Byte) 為資料處理的單位。位址線路則增加到20條;因此必須用兩個Word來表示記憶體的位址。這兩個Word如果也採用線性對映的定址方式, 那麼共可表示: 2 ^32 = 2^2x2^10x2^10x2^10 = 4 x K x K x K = 4G 種的位址!這在當時看來是軟硬體皆不可能達成的數字。更何況位址線又沒有32條,只有20條而已, 就是說實際定址能力的最高限制是2^20 Bytes = 1MB。那時的RAM 也很昂貴, 所以認為1MB (是64KB 的16倍) 是夠大的了。
因此8086採取了重疊對映的定址方式 (悲劇的開始!)。由兩個Word以XXXX:YYYY 的方式來組成一個20位元的線性位址, 前者為節區段位址(Segment),後面稱偏移段位址(Offset)。
公式如下:Address = Segment * 16 + Offset
格式:Segment:offset =>(段位址):(偏移位址) =>1 2 3 4 : 2 3 4 5
1 2 3 4 0
+2 3 4 5
(真實記憶體位址) --------------
1 4 6 8 5 h
以上的記憶體位址是16進位數字, 在數字後面附加小寫h 來表示。例如: 640K (10進位) = A0000 h = A000:0000 等到1982年, 藍色巨人IBM 推出最早型的IBM PC XT,以Intel 8088微處理機作為CPU,8088與8086都只有20條位址線(A0~A19) 。IBM 對可定址的1MB 記憶體位址做個規劃, 其中最前面的640K RAM供DOS 與應用程式使用, 這塊區域叫主記憶體(Base Memory) 或傳統記憶體。640K到1MB 的記憶體區域保留給外加擴充的界面卡和BIOS使用, 這塊區域叫上層記憶體 (Upper Memory),一般應用程式不可輕易動用。這也就是DOS和一般應用程式最多只能控制640K的原因。
1983年Intel 推出了80286, IBM立刻選用為最新的CPU,於1984年底推出IBM PC AT(AT是Advanced Technology 先進技術之意) 。80286 是真正的16位元微處理機(CPU內部與I/O 均以16位元處理),運作速度更快。它有24條位址線, 故最多可存取2^24 = 16MB 的記憶體。
事實上80286 採用了兩種定址模式:一、真實模式 (Real Mode)在此模式下,286使用和8086/8088 相同的重疊對映的定址方式。這是為了讓原有的DOS 和應用程式能在PC AT 上相容使用。因此, 在真實模式下, 也只能控制1MB 記憶體而已。二、虛擬保護模式 (Virtual Protected Mode)在保護模式下, 才能隨意使用1MB 以上的記憶體。後來陸續推出32位元的80386和80486 (皆有32條位址線),又提供了功能更強的保護模式。但是也都保留了真實模式, 以滿足往前的相容。MS-DOS改版至今, 一直是在真實模式下運作。對於在286/386/486超過1024K 的記憶體, 只能拿來當虛擬磁碟機, 或硬碟快取程式(Disk cache), 或者使用一些其他的驅動程式 (如EMM或XMM) 來支援, 而不能像在傳統記憶體一樣方便的使用。 擴展記憶體和延伸記憶體早在XT時代, 一些大型軟體就有記憶體不足的困擾了。因此, Lotus/Intel/Microsoft三家公司共同制定了一個擴展記憶體規格(Expanded Memory Spec.;EMS),採用記憶庫切換(bank swapping) 的方式來指定位置段落。擴展記憶體規格(EMS) 包含了硬體的EMS 擴充界面卡, 和軟體的管理程式(Expanded Memory Manager ;EMM)。這種EMS 記憶體就是擴展記憶體 (Expanded Memory) 。
隨著PC AT 的普及, 程式可透過保護模式存取位址為1MB 以上的記憶體。這些位址為1MB 以上的記憶體就稱為延伸記憶體( Extended Memory) 。為了避免各程式取用的延伸記憶體的區域相衝突, Mircrosoft、Intel、Lotus等公司制定了一個延伸記憶體規格(Extended Memory Spec. ;XMS), 規定了高記憶區、上層記憶體與延伸記憶體的存取標準。一般的程式只要呼叫管理程式(eXtended Memory Manager ;XMM), 就能有效運用XMS 的資源。像MS-DOS 5.0的HIMEM.SYS 就是符合XMS 標準的管理程式。
Expanded(擴展)表示向橫的方向擴展, Extended則是縱向的延伸。EMS 和XMS 不同的地方是: EMS 是XT時代發展出的規格; 擴展記憶體、EMS 卡是隨著EMS 發表的, 它的重點是在1024K 的定址範圍內使用更多的記憶體, 實際的定址限制仍是1024K。
XMS 則是在有了延伸記憶體之後才訂定的規格, 用來管理640K以外的記憶體。延伸記憶體可用軟體方式模擬成EMS 標準, 讓只支援EMS 的較早期程式也能夠使用。如果你要擴充記憶體容量, 且你的主機板上仍有空的記憶體插槽,就可直接買RAM 來插, 即增加延伸記憶體, 就可做為XMS 或EMS 使用,也比EMS 卡便宜。若您的主機板上已無空的記憶體插槽, 那只好買EMS卡來插在擴充槽上了。有些EMS 卡上有開關, 可將卡上的記憶體調成延伸記憶體; 否則就只能當做擴展記憶體了。
EMS 使用記憶庫切換的方法, 在有限位址內使用更多的記憶體, 事實上只解決資料的問題, 要執行程式仍然很麻煩。這方法和SuperVGA的切頁方式類似, 若用SuperVGA則較容易理解 "切頁對映" 的觀念。PC分配給彩色螢幕的視訊對映位址只有64K (A0000h ~ AFFFFh),而SuperVGA若要顯示1024x768x256色的模式, 則需要768K 的記憶體(所以SuperVGA卡要有1MB RAM), 64K 的位址如何夠用呢? SuperVGA就把這1MB RAM 分成16個64K 等分,CPU每次可存取其中的64K,而用一個暫存器來選擇切換。
高記憶區 HMA 與 上層記憶區塊 UMB 雖然一般程式在真實模式下只能使用640K, 但DOS 5.0 提供了HMA和UMB 記憶區的使用, 在真實模式下突破了640K的限制。約64K 的高記憶區 ( HMA, High Memory Area )真實模式下的最大位址可達FFFF:FFFF = 10FFEF h的位址, 這已超過真實模式的1 Mega上限。所以多出來的100000h 到10FFEFh 就會捲繞(wrapping)重新對映到位址0 ~FFEFh 的地方。對286 以上的CPU,如果將位址線A20 致能(enable), 則不會發生捲繞, 多出來的這64K-16bytes就是HMA 。一般說HMA 有64K,其實是64K - 16 bytes 。上層記憶區塊 ( UMB, Upper Memory Block )PC把位址為640K到1MB (即A0000 h ~FFFFF h) 的上層記憶體, 規劃給界面卡和BIOS使用。這部分的ROM 除了系統BIOS是在主機母板之外, 其他的ROM (或RAM) 則是在使用該位址的界面卡上。
通常我們都只用到上層記憶體的位址的一部分而已, 其他可用的位址就浪費掉了。使用DOS 5.0 的EMM386.EXE, 可以將這些沒有使用到的上層記憶體位址, 改成對映到延伸記憶體上, 就叫做UMB 。這樣你在真實模式下, 就又多出一部分可使用的位址了。
這些多出來的UMB 記憶體, 通常用來存放各種佔記憶體的驅動程式和常駐程式, 儘量空出傳統記憶體空間來執行大型程式。
UMB 依據個人週邊配備和設定的不同, 約可多出60K~200K 左右的可使用位址。EMM386.EXE就是管理UMB 的工具。QEMM386 (Quarterdeck公司的軟體) 功能比EMM386強大,不用加參數即可規劃出最大的可用UMB, 因此很多人使用。但你還是要瞭解EMM386, 以便可隨時更替, 因為EMM386的功能較穩定。
使用EMM386必須自行指定可用位址。若不指定則EMM386自行規劃的UMB 空間有限。EMM386.EXE 可用的參數如下 :
NOEMS : 不模擬EMS 的功能, 但要使用UMB 。
RAM : 將記憶體模擬EMS , 且要使用UMB 。
RAM=xxxx-yyyy : 則將兩個段位址之間的記憶體留給UMB用。
size : 設定EMS 的大小, 需為16的倍數。自定值為256KB。
FRAME=xxxx : EMS 的映射頁框, 會佔用掉64KB的UMB 。
I=xxxx-yyyy : 指定段位址xxxx~yyyy可做為UMB 供LOADHI。
X=xxxx-yyyy : 避開段位址xxxx~yyyy不可做為UMB 。
EMM386自定的UMB 使用位址為C800~DFFF (共96K),若介面卡用到此位址, 則須以參數 X= 避開。
要瞭解I/O 界面卡的位址分配, 才知道那些位址可以使用或該避開。若是界面卡位址(如倚天卡版)是可調整的, 則可儘量留下最大可用的UMB 空間。
640K 到 1 Mega 的 I/O 位址分配 :
A0000 h ~ AFFFF h (64K) 彩色螢幕圖形介面
B0000 h ~ B7FFF h (32K) 單色螢幕圖文介面
B8000 h ~ BFFFF h (32K) 彩色螢幕文字介面
C0000 h ~ C7FFF h (32K) 彩色螢幕 BIOS 程式碼存放區
C8000 h ~ CFFFF h (32K) SCSI/ESDI 硬碟控制卡
D0000 h ~ DDFFF h
DE000 h ~ DFFFF h ( 8K) 倚天中文卡字型ROM 預設位址
*E0000 h ~ EFFFF h (64K) 通常是未使用, 可做UMB 。
F0000 h ~ FFFFF h (64K) 系統 ROM BIOS
顧名思義, EMM386.EXE只能用在386 或486 的PC, 若你使用286 的PC AT,就無法使用UMB 的功能了。 使用 HIMEM.SYS 和 EMM386.EXE 在原始情況下, DOS 5.0 大約佔60K 的傳統記憶體。你可以把根目錄下的CONFIG.SYS和AUTOEXEC.BAT刪除 (或用REN 更名),重新開機後,再用 MEM /C 來觀察, 則傳統記憶體的使用情況如下(註1):
> \dos\mem /c (未加config.sys和autoexec.bat的情況)
MSDOS 57184 ( 55.8K) DF60
COMMAND 4704 ( 4.6K) 1260
FREE 593328 (579.4K) 90DB0
接著如果你使用HMA,則可將約45K 的DOS 核心搬移至HMA 中, 而有約17K 的DOS 碼留在傳統記憶體中。要使用HMA,則須在CONFIG.SYS檔中加入HIMEM.SYS,和 DOS=HIGH 這兩行。作法和觀察步驟如下:
> copy con \config.sys device=\dos\himem.sys dos= high ^Z ( 按F6, Enter )
( 重新開機後 ) > \dos\mem /c
( 使用HMA 的情況 )
MSDOS 12.5K HIMEM.sys 1.2K
COMMAND.com 2.6K
FREE memory 623.6K
而且用MEM 觀察的結果, 可使用的XMS 記憶體剛好減少了64K,確實是被拿去當HMA 使用了。計算的方法, 是將全部(total) 連續延伸記憶體, 減掉可用(available) 之XMS 延伸記憶體, 則等於65536 bytes,再除以1024即為64K。 如果你使用HMA 並且在CONFIG.SYS檔中設定 BUFFERS=n 這個命令,那麼磁碟緩衝區(disk buffer) 也會自動置於HMA 中。磁碟緩衝區的設定個數, 一般是根據硬碟的容量大小來設定。大容量硬碟的BUFFERS 若太小, 則速度會變慢。以下是一些參考數據:
40 MB 以下 : BUFFERS=20
40 至 79 MB : BUFFERS=30
79 至119 MB : BUFFERS=40
120 MB 以上 : BUFFERS=50
如果HMA 不足以放下所有的磁碟緩衝區, 剩餘部分也會自動轉到傳統記憶體去儲存。事實上HMA 除了放DOS 核心程式外, 約可再放下44個BUFFERS,因此若你的硬碟容量不大, 則可設 BUFFERS=44。
接著要使用EMM386.EXE來管理UMB,則要加上 DOS=UMB 命令,且需在HIMEM.SYS 下使用。以下是使用HMA 和UMB 的CONFIG.SYS 基本內容:
device=\dos\himem.sys
dos=high umb
device=\dos\emm386.exe noems i=e000-efff
buffers=44
files=30
注意HIMEM.SYS 必須在第一行, 因為所有的XMS 功能都經由它處理。重新開機後, 由開機訊息或MEM/C 觀察, 發現增加了約160K 的UMB可以使用; 但可使用的XMS 記憶體竟然又少了245K。這是因為UMB 的位址並非連續可用的, 所以XMS 無法百分之百轉換為UMB 使用。
若你使用單色螢幕, 那麼趕快在EMM386這行後面再加個參數如下:
device=\dos\emm386.exe noems i=e000-efff i=a000-afff
重新開機後, 再以MEM 觀察, 發現傳統記憶體居然變成704K (多了64K), 連可執行的程式最大容量也增加了! 這是使用單色螢幕 386/486才有的特權。
以下再列出彩色/單色螢幕的CONFIG.SYS 設定實例 :
彩色 386/486
DEVICE=\DOS\HIMEM.SYS
DOS=HIGH UMB
DEVICE=\DOS\EMM386.EXE NOEMS I=C800-EFFF
BUFFERS=50,8
FILES=30
單色 386/486
DEVICE=\DOS\HIMEM.SYS
DOS=HIGH UMB
DEVICE=\DOS\EMM386.EXE NOEMS I=A000-AFFF I=C000-EFFF
BUFFERS=50,8
FILES=30(若只有384K延伸記憶體, 則不夠UMB 使用, 第二個I=要改成C600-EFFF)有了UMB 以後, 要執行常駐程式就可使用LOADHIGH (可簡寫為LH)命令來把常駐程式載入到UMB 了。同樣也可寫在AUTOEXEC.BAT等批次檔中。若是由CONFIG.SYS設定的驅動程式, 則把DEVICE= 改用DEVICEHIGH=來載入。但並非每個程式都可放到UMB,如 SMARTDRV 就不適合。使用UMB 的實例如下: DOS命令或用在.BAT檔中 :
LH DOSKEY
LH APPEND C:\TC\LIB;C:\JB;
CONFIG.SYS 檔的設定中 :
DEVICEHIGH=\DOS\ANSI.SYS
DEVICEHIGH=\MOUSE.SYS 2
若是UMB 客滿了, 常駐程式會自動轉置於傳統記憶體中。你可用MEM/C來觀察傳統記憶體和UMB 的使用情形。 節省記憶體的其他技巧 DOS 使用HMA 和UMB 在真實模式下作出最後的"掙扎", 但HMA和UMB增加的記憶體終究有限, 若是那天還是碰上"(傳統)記憶體不足"時要怎麼辦呢? 就只有找出下列的最後掙扎的最後技巧了。
1. 在CONFIG.SYS中加入下列命令: STACK=0,0
FCBS=1
一般應用程式很少用到STACK,設為0 可省下1K左右。而FCB 更是很落伍的軟體才用得上的。FCBS=1可省下176 bytes 。
2. 設定BUFFERS 值小一點, 例如44以下。
3. 減少FILES 值 (可開檔數目),每少一個可省下53 bytes 。
4. 檢討UMB 位址是否充分利用, 重新設定參數。或者改用QEMM386.SYS來代替 EMM386.EXE 和HIMEM.SYS 。
5. 審視所有的驅動程式和常驅程式, 沒用到的不要載入系統, 或是儘量LOADHI。如果你只在WINDOW中使用滑鼠, 可把外部的滑鼠驅動程式(如:MOUSE.COM)拿走, 因為WINDOW有自己的滑鼠驅動程式。 虛擬磁碟和磁碟快取用完了HMA 和UMB 剩下來的延伸記憶體, 對一般應用程式都用不著了, 要如何處理呢? 如果你只有384K的延伸記憶體, 也用得差不多了,就到此為止。如果你還有更多的記憶體, 就可拿來作虛擬磁碟, 或是作磁碟快取區, 來減少硬碟的讀取磨損, 並加速系統的運行。 但首先你要瞭解自己還剩下多少可使用的記憶體, 這可用MEM 來觀察, 再除以1024得到K 數。另外倚天中文3.1 版可將字型檔等載入延伸記憶體; 所以你要先執行中文系統(3.1版) 再執行MEM , 才能確定剩下多少延伸記憶體可以使用。如果還使用WINDOW等其他會使用延伸記憶體的軟體, 就需要再調整分配。 虛擬磁碟(Virtual Disk 或 RAM Disk)是以記憶體模擬磁碟, 存取檔案的速度比硬碟快, 適用於處理大量檔案或經常讀取的資料。但注意若是寫入資料到虛擬磁碟, 最後記得要轉存到硬碟上, 否則電源一關,記憶體的資料就消失了。磁碟快取(Disk Cache)是將較重要或經常讀取的硬碟資料, 存在磁碟快取區, 若有磁碟I/O 時, 就可直接從快取區拿取資料。一、虛擬磁碟工具: RAMDRIVE.SYS在 CONFIG.SYS 的設定實例:
DEVICE=\DOS\RAMDRIVE.SYS 320 512 120 /E
說明:
第一個參數: 320,使用虛擬磁碟的K 數。此值需是64(K) 的整數倍。
第二個參數: 512,每個磁區的bytes 數。512 與磁碟磁區大小一致。
第三個參數: 120,根目錄下最多可存放的檔案和子目錄個數。
參數 /E 表示使用延伸記憶體。若是 /A 則使用擴展記憶體。
不設定 /E 或 /A 則使用傳統記憶體。
二、磁碟快取工具: SMARTDRV.SYS在 CONFIG.SYS 的設定實例: DEVICE=\DOS\SMRATDRV.SYS 1024 說明:
第一個參數: 1024, 使用磁碟快取區的K 數。此值最少為128 (K)。
第二個參數: 沒設, 這是快取區的最小K 數。若使用WINDOW等會佔用延伸記憶體的軟體, 快取區會自動調成此值。另外,參數 /A 表示使用擴展記憶體,沒設則自定使用延伸記憶體。
若是你有PC-CACHE.COM或NCACHE.EXE等其他功能較強的磁碟快取程式, 可用來取代DOS 的SMARTDRV, 參數用法請參考相關的說明書。DOS 的RAMDRIVE.SYS和SMARTDRV.SYS都需配合HIMEM.SYS 使用。---------------------------------------------------------(註1) 表中MSDOS 的數值是只有一部硬碟C 的情況。若還有硬碟D,則 MSDOS 的大小是57312 Bytes,否則有可能是感染病毒。另外如果有AUTOEXEC.BAT, 則有64 Bytes的FREE區, 為環境變數區。 MEM/C 所觀察的UMB 第一項為64K ~160K 的SYSTEM, 是表示 UMB 分配給系統I/O 或ROM-BIOS使用之數量。
掛載EMM386.EXE時,碰到預期外的行為時,考慮以下列參數選項來解決:X = a000-f7ff
如果不包括整個上層記憶體區域 (UMA) 可以解決系統問題,EMM386.EXE 可能會太積極地掃描,以及設定上層記憶體區塊 (UMBs) 在一些介面卡的最上層 ROM 或 RAM。使用任何可用的硬體文件 (包括附加元件的硬體裝置,例如視訊、 網路,以及磁碟控制器卡上的文件),來識別任何 ROM 或 RAM 出現在 [UMA 中為該裝置,並排除所有相關的區域。如果硬體文件無法使用,或者並不會提供必要的資訊,您可以使用 [Microsoft 診斷公用程式 」 (MSD) 來識別記憶體區域。
NOEMS
如果 NOEMS 參數已修正與 EMM386.EXE 問題,EMM386.EXE 可能與某些硬體 ROM 或 [UMA 中的 RAM 位址不慎衝突時嘗試建立擴充的記憶體 (EMS) 頁面框架。如果執行檢查 DOS 為主的應用程式所需 EMS,使用參數框架 = 或 M (是已定義的十六進位位址) 明確地指定 nonconflicting 區域中的 [EMS 頁面框架的位置。如果沒有應用程式需要 EMS,只是繼續使用 NOEMS 參數。
NOVCPI
NOVCPI 切換控制會停用虛擬控制項程式介面 (VCPI) 支援,並且可以用於只能在配合 NOEMS 參數中。 如果使用 NOVCPI 更正問題,應用程式可能不是與 EMM386.EXE VCPI 配置配置完全相容。請繼續使用 NOVCPI] 參數,或使用應用程式時不要載入 EMM386.EXE。
NOMOVEXBDA某些機器使用最後的千位元組的傳統記憶體擴充的 BIOS 資料區域。預設情況下,EMM386.EXE remaps 這個記憶體區域到 UMA,而非傳統記憶體。如果這會導致未預期的系統行為,NOMOVEXBDA 參數必須用。
NOTR
EMM386.EXE 有偵測程式碼,以搜尋權杖環網路介面卡的存在。此偵測程式碼可能會造成某些電腦停止回應。NOTR 切換來停用此搜尋。


P.S. 回顧

在最初的x86的硬體結構下,真實模式下不能存取1M以上記憶體。這1M的記憶體可以分為前640K常設記憶體和640K到1M的高端記憶體(UMA)。但是在主機板設計人員的努力下,x86的真實模式可以通過頁切換機制把主機板上的1M以上的記憶體區(EMS/XMS)映射到高端記憶體區,從而可以實現真實模式下對EMS/XMS的讀寫。所以並不是只在保護模式下才能讀寫EMS。否則,很多較大的DOS應用都無法運行了。當然對於dos程式師來說,需要一些特定軟體的支援。EMS和XMS就是兩種1M以上記憶體存取軟體介面的規範。具體的產品就有我們熟悉的Himem.sys和emm386.exe。這兩種介面實際上都是OS的陷阱。當你調用這些介面時,陷入dos內核,himem或emm的代碼將負責把EMS/XMS映射到UMA中。這兩種介面具體的實現機制有一些差別,可以查看相關的資料。但是這種介面只能在EMS/XMS中存放資料,存放的代碼將無法運行。所以程式的程式碼片段仍然必須駐留在1M以下的常設記憶體中。所以前640K對於程式師仍然是一個潛在的障礙。如果你的程式碼段的長度超過了640K(實際上還要小一些),你就會得到在dos下最常見的資訊"not enough memory under 640K",儘管實際上你可能安裝了16M或更多的記憶體。當然,這些都是歷史了。不過瞭解一下這些歷史,會幫助你更好地理解今天的電腦。

--> 閱讀更多...

himem.sys及emm386說明


himem.sys及emm386說明轉載自http://club.it.sohu.com/r-os-239366-0-13-900.html
DOS的環境下,系統中存在以下四種記憶體:
   常規記憶體(Conventional Memory)
   高端記憶體(Upper Memory)
   延伸記憶體(Expanded Memory) EMS
   擴展記憶體(Extended Memory) XMS
DOS在實模式下,能直接定址的範圍是1MB。而這1MB分為640KB的常規記憶體和384KB的高端記憶體,加在一起就是1024KB也就是1MB。因為DOS使用16位段基址:偏移量格式(segment:offset),只能使用低端的640KB,這就是有名的640KB限制。其中最低端的1KB,即00000H~003FFH存放的是中斷(IRQ)向量表;接下來是256B(0FFH)的BIOS資料區;DOS及應用程式使用00500H~9FFFFH。這在開始使用DOS的20世紀80年代是完全能夠滿足要求的,因為當時PC上安裝的實體記憶體容量也是640KB,甚至更少。(前面地址中的H代表16進制)系統硬體使用的記憶體位於位址區域的高端,範圍是A0000H~FFFFFH,共384KB。其中有用於顯示的視頻緩衝區和BIOS程式空間,例如顯卡,網卡和主板BIOS。
地址FFFF0H在PC中有特別的用途。
電腦在加電啟動時,CPU中的CS=F000H,IP=FFF0H,即從位址FFFF0H處開始執行,這個區域屬於系統BIOS。F000:FFF0=EA5BE000F0(是JMP F000:E05B指令的十六進位表示),它立即跳轉到BIOS的初始化程式,開始系統自檢。(這段跟DOS沒有關係,只是查資料時看到了,就也寫上了。是想讓大家知道,在按下主機電源開關後,CPU都做了些什麼,為什麼BIOS開始工作,自檢硬體設備)
最後我附了一張圖,本來想自己畫的,可一搜發現已經有人畫好了,並且畫的肯定比我好,我就用人家的圖了,一看這個,肯定就清楚多了,比文字怎麼寫的都強。
上面說的延伸記憶體是一種硬體,那個年代的主板專門預留了擴展槽,可以插上,我是沒見過。擴展記憶體就是記憶體條上大於1M的部分,通過DOS下的一些驅動可以將XMS的一部分虛擬成EMS,以滿足一些為EMS開發的程式的需要。
中間有一段叫高端記憶體,指位於常規記憶體之上的384K記憶體。程式一般不能使用這個記憶體區域,但是EMM386.exe可以啟動高端記憶體的一部分,並且它允許用戶將某些設備驅動程式和用戶程式用Devicehigh或LH(即loadhigh)裝入高端記憶體。dos=high,umb也是把DOS的一部分裝到高端記憶體裏。這裏的umb是高端記憶體塊(Upper Memory Block)的縮寫。(以後我會專門寫config.sys的部分)
DOS的程式,主要還是要使用常規記憶體的,如我刷HP筆記本的BIOS的時候,連點Shift進入最小的DOS模式也就是為了節省640K這部分的空間,以便讓HP的刷寫程式有足夠的空間運行,不然刷寫程式就要報錯,就算XMS再大也沒用。
還有一個叫DOS4GW.EXE的程式,在DOS玩過一些大一些的遊戲如仙劍,紅警等,在啟動的時候,有時候能看到有個DOS4GW一閃而過,它可以使用CPU直接進入保護模式,直接訪問XMS,不過這時就已經脫離了DOS狀態。後來的Windows 3.x,95,98也都運行在保護模式下的,但都需要DOS帶一下。
有些東西要看一下計算機組成原理或彙編之類的書才能理解,尤其是記憶體的定址與CPU的寄存器(上面提到的CS,IP等)。提到了DOS的記憶體管理,從前文的圖中,大家也看到了EMM386.EXE與HIMEM.SYS,這是實際管理記憶體的模組。
EMM386.EXE從名稱上可以很真接的看出,這個是要在386以及之後的CPU上才可以使用的,它的作用是利用XMS創建出EMS,以前的DOS也有N多版本,有的叫EMM386.SYS有的叫EMM386.EXE,在我使用過的DOS我只見到過EMM386.EXE。像其他的擴展記憶體管理一樣EMM386使處理器虛擬8086的模式。而在386增強模式中,視窗會話期間會臨時關閉,同時在視窗保護模式中內核會接管它的角色。最終的作用是在UMA中,EMM386.EXE會把記憶體映射成未使用的塊。允許設備驅動和TSRs(我也不知道是什麼東西)被載入到UMA中,而保留那可憐的640K的常規記憶體。
HIMEM.SYS也是一個DOS設備驅動,他允許DOS程式把資料存到XMS中去。HIMEM.SYS實際上是非常重要的,後來的多種Windows作業系統(本質上底層是依賴於DOS)都需要先載入HIMEM.SYS之後才能正常運行。
從MSDOS 5.0起,HIMEM.SYS被用來把DOS內核的部分代碼載入到HMA中,目的同樣是為了節省那640K的常規記憶體。在config.sys用dos=high設置,config.sys的詳細資訊,後文會有涉及。
HIMEM.SYS提供了一種訪問超過1M實體記憶體的方法,而這正是windows 9x/me作業系統載入圖型化介面所必需的,看前面的圖很清楚,EMM386.EXE的管轄範圍是640K向上到1M之間這部分共384K被稱作上位記憶體UMA的區域。而HIMEM.SYS管轄的是1M再向上的部分,而從1M開始有一小段叫高位記憶體HMA,專門用來放一部分DOS內核。
--> 閱讀更多...

2010年4月12日 星期一

Novell Netware3.12安裝在VMware Workstation 6.5(中)

圖一
圖二
圖三
圖四
要安裝SYS卷及public公用程式的安裝比較簡單所以我只把圖show出來。流程大致上是1.check HD partition 2.create SYS volume產生系統卷 3.copy System and Public Files。設定過程使用預設值便可。首先進入system console後輸入load install。
--> 閱讀更多...