??xml version="1.0" encoding="utf-8" standalone="yes"?>
(1)cygwin 2009Q集成gcc-4.3.2QrxvtQ非帔R合一般的~程开发,集成iso?8M
(2)i686-elf的cygwin toolchainQ工具链版本Qgcc-4.3.2, binutils-2.19, glibc-2.7Q测试正常工?br>以此工具铄译最新的linux内核2.6.28工作正常Qkernel打一些补丁:
cd linux-2.6.28
sed -i 's/R_386_32/1/' ./scripts/mod/modpost.c
sed -i 's/R_386_PC32/2/' ./scripts/mod/modpost.c
sed -i 's/R_ARM_ABS32/2/' ./scripts/mod/modpost.c
sed -i 's/R_ARM_PC24/1/' ./scripts/mod/modpost.c
sed -i 's/R_MIPS_HI16/5/' ./scripts/mod/modpost.c
sed -i 's/R_MIPS_LO16/6/' ./scripts/mod/modpost.c
sed -i 's/R_MIPS_26/4/' ./scripts/mod/modpost.c
sed -i 's/R_MIPS_32/2/' ./scripts/mod/modpost.c
sed -i 's/STT_COMMON/5/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/STV_DEFAULT/0/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/STV_INTERNAL/1/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/STV_HIDDEN/2/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/STV_PROTECTED/3/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_NONE/0/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_32/1/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_PC32/2/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_GOT32/3/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_PLT32/4/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_COPY/5/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_GLOB_DAT/6/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_JMP_SLOT/7/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_RELATIVE/8/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_GOTOFF/9/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_GOTPC/10/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/R_386_NUM/11/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/ELF32_ST_VISIBILITY(sym->st_other)/sym->st_other \& 0x03/g' ./arch/x86/boot/compressed/relocs.c
sed -i 's/else rm -f $(@D)\/.tmp_$(@F); exit 1;/else mv -f $(@D)\/.tmp_$(@F) $@;/' ./arch/x86/vdso/Makefile
menuconfig中把netfilterLQ里面有一些文件名一P大小写不一PQipv6也去掉,~译的elf文g正常?br>
上v漕河?br>2009.01.06
]]>
]]>
]]>
如果pȝ处于仿真的初期节点,例如操作pȝ的初始化Q调试还是很有必要的。这里是我在linux环境调试操作pȝ的相兛_Pos开发者可参考,Ƣ迎指正Q)
Q?Q工具安装:
linux安装Qbochs-2.3Qinsight-6.6Qgcc-3.4Q用g++和asQbinutils包中Q)
windows安装QXmanager Enterprise2.1
因ؓ|管没有lotus和clearcase在linuxpȝ下的支持Q所以只好用两个操作pȝ了。这样也比较好,一个用于programQ一个用于调试,毕竟bochscpu的,p她干zd了?br>bochs安装Q?/configure --with-all-libs --enable-vbe --enable-gdb-stub && make && make install
insight-6.6安装Q包含了tck/tkQgdb-6.6Qbfd{工P使用insight时最好这栯|环境变量:
export LC_ALL=en_US
否则q行时可能会报错Q?br>Tcl_Init failed: can't read "env(TCL_LIBRARY)": no such variable
配置xserver用于q程讉KLinux囑Ş界面Q这样可以在windows上通过ssh执行linux的图形界面程序?br>
Q?Q相xӞ
bochs配置文gd如下节:
gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
gdb调试脚本Q?br>gdb的命令集可以写入C个文件中去,q样避免了在启动时输入一大堆命oQ?00%鼠标操作Qfaint
保存所有命令到一个文Ӟ每行一个命令,如下Q?br>file ./vmjinix
target remote 127.0.0.1:1234
dir ./arch/i386
dir ./init
dir ./kernel
dir ./drivers
dir ./drivers/video
dir ./drivers/video/console
show dir
break start_kernel
continue
list 0
gdb和gdb前端执行如下Q?br>gdb -q -x gdb.command
insight -q -x gdb.command
其他脚本Q磁盘自动创建分区,自动安装grubQ拷贝内核,及Makefile脚本Q,q些贴出来太长,׃好几个小时写好的Q需要可以和我联p(MSN & EmailQjinglexy at yahoo dot com dot cnQ?br>
Q?Q调试方?br>内核(jinix-1.2.1是我正在~写的一个C++ 开源OSQ欢q参与)拯到linuxLQ配|samba׃nQ这样可以在windows上开发(推荐使用slickedit 2007Q哪位有linux上的2007版本可否发一个给我)?br>使用xshellQssh方式Q登录到linuxL上,~译和调试都在这里了?br>在ssh上执行bochs -f bochsrc.txt.linuxQ?br>在ssh上执行insight -q -x gdb.command
截图如下Q?br>
汇编语言节点也可用用bochsQgdb调试Q在gdb断点时候执行:
disassemble $pc $pc+100Q从当前断点处反汇编100字节Q?br>需要注意的是,在os的汇~初始化的前期阶D,分页机制往往未开启,W号和地址不能一一对应Q?br>q个时候不能进行源码汇编调试Q只能用最即便的反汇编调试了?br>bochs-2.3中好像有个bug没有解决Qnexti执行和stepi在call的时候居然一P如果要断点到指定行,可以使用物理地址断点?br>
Q?Q文章会不断更新Q如有什么好的想法可以在原博客讨论:
http://www.shnenglu.com/jinglexy
Q?Q整理的一份常用gdb指o
x /4wx ds:0x1234 x是线性地址I间
xp /4wx 0x1234 xp是物理地址I间
backtrace
print variable 打印变量?br>print variable@10 打印变量后面?0个整数?br>set variable=2 赋?br>whatis variable 昄变量cd
ptype variable 昄数据l构Q变量类型加强版Q?br>
断点cdQ?br>break init_kernel.cpp:start_kernel 断点在文件的函数
break init_kernel.cpp:101 断点在文件的101?br>break init_kernel.cpp:101 if var==100 条g断点
break *0xc0102030
info break 查看所有断?br>delete breakpoint 3
delete breakpoint 删除所有断?br>isable breakpoint 2
enable breakpoint 2
search string1 搜烦字符Ԍ从listl束行开?br>reverse-search string1 方向搜烦
set history expansion on 使用历史命o
clear 删除刚才停止处的断点
continue 从断点开始l执?br>info break 昄当前断点清单Q包括到达断点处的次数等
info files 昄被调试文件的详细信息
info func 昄所有的函数名称
info local 昄当函C的局部变量信?br>info prog 昄被调试程序的执行状?br>info var 昄所有的全局和静态变量名U?br>info all
run
continue
step, next, stepi, nexti i后缀表示执行一条汇~指?br>
/*********************************************************************************
格式说明 /
/*********************************************************************************
x /nuf addr查位于线性地址addr处的内存内容Q若addr不指定,则默认ؓ下一个单元地址?br>xp /nuf addr查位于物理地址addr处的内存内容?br>其中的可选参数n、u和f的分别可为:
nƲ显C内存单元的计数|默认gؓ1?br>u表示单元大小Q默认选择?w'Q?br>b QBytesQ?字节Q?br>h QHalfwordsQ?字节Q?br>w QWordsQ?字节Q?br>g QGiantwordsQ?字节?br>注意Q这些羃略符与Intel的不同,主要是ؓ了与GDB调试器的表示法一致?br>f昄格式Q默认选择?x'Q?br>x QhexQ显CZؓ十六q制敎ͼ默认选择Q;
d QdecimalQ显CZؓ十进制数Q?br>u QunsignedQ显C成无符号十q制敎ͼ
o QoctalQ显C成八进制数Q?br>t QbinaryQ显C成二进制数?br>c QcharQ显C字节代码对应的字符。若不是可显C字W代码,q接显CZ码?br>*********************************************************************************/
说明一下:bochs ?insight本n单个执行非常慢Q而且insight是通过ssh方式链接q程xserver执行Q所以速度巨慢Q本文所q完全可以全部在linux上操作,不限于^台?br>
]]>
1)g++选项-nostartfilesQ用L境的在main之前调用的代码,当然不能使用?br>2)全局对象Q每U类型都有自q构造函敎ͼ如果不自q写代码调用,它们不会执行?br>q包括所有全局对象和局部static对象Q徏议的做法是在内核栈徏立后Qc++q行代码执行之前
调用构造函敎ͼ如果构造函数没有运行(假设里面有分配内存之cȝ操作Q,后果很严重:Q?br>我们可以q样做:
先修改gnu-ld链接脚本
.data : {
start_ctors = .;
*(.ctor*)
end_ctors = .;
start_dtors = .;
*(.dtor*)
end_dtors = .;
*(.data)
}
q样构造函数的指针都保存在start_ctors 和end_ctors之间的内存中了,构造函数其实就是void foo(void);形式的函敎ͼ~写一个for循环
调用它即可;析构函数也是一L。当每个构造函数调用完后,gcc会自动调用一个函敎ͼ
int __cxa_atexit(void (* f)(void *), void *p, void *d);
当内栔R出时Q会执行一个函敎ͼ
void __cxa_finalize(void *d);
q两个函数必L上面格式定义Qg++是这栯定的。看看下面的代码明白了Q?br>extern "C"
{
int __cxa_atexit(void (*f)(void *), void *p, void *d);
void __cxa_finalize(void *d);
};
void *__dso_handle; /*only the address of this symbol is taken by gcc*/
struct object
{
void (*f)(void*);
void *p;
void *d;
} object[32] = {0};
unsigned int iObject = 0;
int __cxa_atexit(void (*f)(void *), void *p, void *d)
{
if (iObject >= 32) return -1;
object[iObject].f = f;
object[iObject].p = p;
object[iObject].d = d;
++iObject;
return 0;
}
/* This currently destroys all objects */
void __cxa_finalize(void *d)
{
unsigned int i = iObject;
for (; i > 0; --i)
{
--iObject;
object[iObject].f(object[iObject].p);
}
}
3)new和deleteQ在完成内存理后,重蝲cȝnew和delete函数
4)-nostdlibQ把标准库禁用掉Q最q有了移植stl到内核的x
5)RTTIQ最好是止它,q样不能用typeid ?dynamic_cast?br>6)用异常Q?fno-exceptionsQ这个和操作pȝ太紧密了
7)U虚函数Q如果子cL有实现父cM的纯虚函敎ͼ链接C面默认例E:
extern "C" void __cxa_pure_virtual()
{
// print error message
}
虽然不是Z定义U虚cȝ对象Q但是链接时~译器会抱怨,所以定义上面函Cɾ~译通过?br>8)如果一定要使用异常QrttiQnew/deleteQgcc中提供了静态库Qlibgcc/libsupc++Q?br>q得写这个库的一些基函数Q觉得它应该是在上层抽象出接口,底层实现空出来l用户实现?br>而且代码本n非常复杂Q网l上也没有Q何中文资料?br>指oQ?br>readelf -a `gcc -print-libgcc-file-name`
里面定义了很多的函数?br>
]]>
目录Q?br>
自己动手写内核(序)......................... 3
W? 课:环境需?............................ 4
W? 课:引导E序............................. 5
W? 课:保护模式............................. 8
W? 课:辅助函数............................. 17
W? 课:中断和异?.......................... 25
W? 课:中断和异?.......................... 39
W? 课:多Q?.............................. 45
W? 课:文gpȝ............................. 56
W? 课:内存理............................. 70
W? 课:pȝ调用和可执行E序................. 76
一份免责声?................................ 81
]]>
W?span lang="EN-US">8课:内存理 下蝲源代?/span>
声明Q{载请保留Q?span lang="EN-US">
译者:http://www.shnenglu.com/jinglexy
原作者:xiaoming.mo at skelix dot org
MSN & Email: jinglexy at yahoo dot com dot cn
目标
抱歉Q其实还没有实现。在d分配独立?span lang="EN-US">4G地址I间上调试失败了Q现在只使能了分|Ӟ异常。大量的工作未实玎ͼ有兴的同学可以搜烦buddy?span lang="EN-US">slab的相兌料,l典的内存管理算法?span lang="EN-US">
分页
386处理器的内存理单元可以实现d独立地址I间QQ务间内存保护。每个Q务可以拥有独立的4G虚拟地址I间。内存映是内存理很重要的一步,可以分ؓ两部分:分段和分c前面的评中已l讨分段机制了,通过分段可以隔开不同的代码,数据Q堆栈等Q分单元把虚拟地址映射成物理地址Q还可以用来实现虚拟内存Q和盘分区q行交换Q,现在我们来了解一下它?span lang="EN-US">
对于每个dQ我们无法分?span lang="EN-US">4G的物理内存,所以用了一些机制来理内存Q及虚拟内存机制。该机制有处理器的分部分来实现Q首先我们将内存分成一些块Q每个块大小?span lang="EN-US">4kQ通常我们UCZ个页帧。操作系l通过늛录和表来管理这些页帧。页目录是相当于W一U页表,其中的每一再理一个下U页表。(更详l过E请参?span lang="EN-US">intel?span lang="EN-US">IA 32/64手册Q?span lang="EN-US">
当分|制开启时Q处理器把Q务中的虚拟地址转换成物理地址Q步骤如下:
1.查找D选择子在GDT ?span lang="EN-US"> LDT 中的描述W,做一些权限检查,看看能否讉K
2.以描q符中的基址相加늛录基址得到一个线性地址
3.在页表中索引虚拟地址所对应的页表项Q得到页地址
4.查找偏移得到实际物理地址?span lang="EN-US">
如果实际物理不存在Q可能交换到盘中去了)Q则引发异常Q可以在q个异常里面做想要做的事情(加蝲盘中的交换,或?span lang="EN-US">killq个E序Q?span lang="EN-US">Segment FaultQ等{)
处理器用的늛录或者页表,都是?span lang="EN-US">32 位的组成:
늛录项Q?/span>
31 12
11 9 876 5
43 2 1 0
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
?nbsp; 指向表的物理地址 ?用户定义 ?nbsp; X ?nbsp;A? X ?U/S?R/W?P ?o:p>
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
表:
31 12
11 9 87 6 5
43 2 1 0
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
?nbsp; 指向的物理地址 ?用户定义 ?nbsp;X┃D?nbsp;A?X ? U/S?R/W?P ?o:p>
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
从上面可以知道,늛录项和页表项的结构很cMQ下面逐个说明一下其中的域:
Bit 0 |
P |
存在位(presentQ,?span lang="EN-US">0 表示该页帧或表不在内存中。如果访问该将发生异常?span lang="EN-US"> |
Bit 1 |
R/W |
表示表或页帧指向的内存只读Q=0Q,或可写(Q?span lang="EN-US">1Q?span lang="EN-US"> |
Bit 2 |
U/S |
表示表或页帧的权限Q当ҎUؓ0Ӟ只有ring0?span lang="EN-US">2的特权可以讉K它,否则所有的ring3d都可以访问。这个域非常重要?span lang="EN-US"> |
Bits 3, 4, (6), 7, 8 |
X |
Intel 保留位,讄?span lang="EN-US">0p?span lang="EN-US"> |
Bit 5 |
A |
该页是否已访?span lang="EN-US"> |
Bits 9-11 |
用户定义 |
我们使用W?span lang="EN-US">11位,表示该页帧是否被交互到硬盘上?span lang="EN-US"> |
늛录的每一:即页表的物理地址Q它的高20 位地址表示有个的v始地址Q正好和4k寚w?span lang="EN-US">2^20可以表示1M范围Q每个页帧大是4kQ所以可以烦?span lang="EN-US">1M * 4K地址I间。页目录中q有一?span lang="EN-US">D 位,它用来表CZ个页帧是否已修改Q?span lang="EN-US">linux用它来表CZ个页面释放是脏页面,q个位非常有用,当一个页帧交换到盘上后Q如果该q没有被修改Q而且是已l从盘交换出来的,则简单取消以后的交换?span lang="EN-US">
Z逻辑地址转换成物理地址Q逻辑地址被分?span lang="EN-US">3 部分Q?span lang="EN-US">
Bits 31-22 |
늛录项的烦引下标,由它可以得到表的物理地址 |
Bits 21-12 |
表的索引下标Q由它可以得到页帧的物理地址 |
Bits 11-0 |
相对起始地址的偏U?span lang="EN-US"> |
举例来说Q我们有一个逻辑地址Q?span lang="EN-US">0x3E837B0A。前提条ӞCR3寄存器指向的늛录地址?span lang="EN-US"> 0x0005C000Q这个寄存器存储了当前页目录所使用的页帧的物理地址Q通常也叫?span lang="EN-US">
PDBR?span lang="EN-US">
先取它的?span lang="EN-US">10位, 是0x0FAQ由它可以烦引到늛录的W?span lang="EN-US">0x0FA,我们取得q一的|假设得到的地址值是0x0003F000。然后我们取虚拟地址的中?span lang="EN-US">10位,是0x037Q再取出0x0003F000指向的第0x037的|假设?span lang="EN-US">0x0001B000。这个地址是我们要找的虚拟地址对应的物理地址的页帧的起始地址Q最后加上偏Ud|?span lang="EN-US">12位)Q即0xB0AQ得到实际的物理地址是:0x0001BB0A?span lang="EN-US">
相关的知识可以参?span lang="EN-US"> Intel ?span lang="EN-US">IA 32/64手册?span lang="EN-US">
CR3寄存器必d分页机制开启前p载好Q可以?span lang="EN-US">MOV 指o或者在d切换时?span lang="EN-US">TSS中的CR3域的倹{当处理器访问不存在的页帧时Q发生一个异常,CR2 寄存器存引发异常的逻辑地址Q同旉误码也会压入到堆栈中Q错误码格式如下Q?span lang="EN-US">
31
3 2 1 0
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
?nbsp; 未?span lang="EN-US"> ?U/S?R/W?P ?o:p>
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
异常处理例程通常采取如下的步骤:
查找一个空闲的或从盘中将交换出来Q重新设|正的늛录项或页表项的|hTLB。处理器通常保存最q最多访问的늛录或表到一?span lang="EN-US">cache中,以避免每ơ都q行虚拟地址到物理地址的{换,q个cache叫?span lang="EN-US">TLB。只有我们改动了늛录或表,应当刷?span lang="EN-US">TLB。方法很单,是重新加蝲CR3 寄存器?span lang="EN-US">
现在我们来看看代码段Q内存管理通常不了大量的宏定义:
08/include/kernel.h
#define PAGE_DIR
((HD0_ADDR+HD0_SIZE+(4*1024)-1) & 0xfffff000)
物理内存安排Q?span lang="EN-US">IDTQ在0x40000Q,接下来是GDTQ接下来?span lang="EN-US">HD0使用Q然后才是页目录Q?span lang="EN-US">
所以这个宏看v来有炚w?span lang="EN-US">
08/include/mm.h
#define PAGE_SIZE
(4*1024) /*
_度 */
#define PAGE_TABLE (PAGE_DIR+PAGE_SIZE) /*
表物理地址 */
#define MEMORY_RANGE (4*1024)
/* skelix只管?span lang="EN-US">4M 内存暂时 */
08/mm.c
/* 物理内存使用情况的位图表 */
static char mmap[MEMORY_RANGE/PAGE_SIZE] = {PG_REVERSED,
};
void
mm_install(void) {
unsigned int *page_dir = ((unsigned int *)PAGE_DIR);
unsigned int *page_table = ((unsigned int *)PAGE_TABLE);
unsigned int address = 0;
int i;
for(i=0; i<MEMORY_RANGE/PAGE_SIZE; ++i) {
/* 表属性设|ؓ: kernel, r/w, present */
page_table[i] = address|7;
address += PAGE_SIZE;
};
// 上面循环初始化了0~4M对应的所有页表项
page_dir[0] = (PAGE_TABLE|7);
// 늛录项只需要第一个就可以了,因ؓ只有4M内存
for (i=1; i<1024; ++i)
page_dir[i] = 6;
// 其他?span lang="EN-US">1023个页目录设|ؓI,如果q?span lang="EN-US">1024w讄Q可讉K4G内存I间
// 讄0?span lang="EN-US">1M内存为已使用?span lang="EN-US">
for (i=(1*1024*1024)/PAGE_SIZE-1; i>=0; --i)
mmap[i] = PG_REVERSED;
// 因ؓ内核只用C低于1M的内存,所以保留它们,q样׃会被交换出去?span lang="EN-US">
__asm__ (
"movl
%%eax,
%%cr3\n\t" // 加蝲늛录基址到寄存器
"movl
%%cr0, %%eax\n\t"
"orl $0x80000000,
%%eax\n\t"
"movl
%%eax,
%%cr0"::"a"(PAGE_DIR)); // 开启分|ӞCR0的最高位
}
通过mmap位图Q我们可以清楚的知道内存的用情况,q样可以分配空闲页帧了Q如下:
08/mm.c
unsigned int
alloc_page(int type) {
int i;
for (i=(sizeof mmap)-1; i>=0 && mmap[i]; --i)
;
if (i < 0) {
kprintf(KPL_PANIC, "NO MEMORY
LEFT");
halt();
}
mmap[i] = type;
return
i; // q回?span lang="EN-US">
}
void *
page2mem(unsigned int nr)
{ // 转换拟地址
return (void *)(nr * PAGE_SIZE);
}
void
do_page_fault(enum KP_LEVEL kl,
unsigned int
ret_ip, unsigned int ss, unsigned int gs,
unsigned int
fs, unsigned int es, unsigned int ds,
unsigned int
edi, unsigned int esi, unsigned int ebp,
unsigned int
esp, unsigned int ebx, unsigned int edx,
unsigned int
ecx, unsigned int eax, unsigned int isr_nr,
unsigned int
err, unsigned int eip, unsigned int cs,
unsigned int
eflags,unsigned int old_esp, unsigned int old_ss) {
unsigned int cr2, cr3;
(void)ret_ip; (void)ss; (void)gs; (void)fs; (void)es;
(void)ds; (void)edi; (void)esi; (void)ebp; (void)esp;
(void) ebx; (void)edx; (void)ecx; (void)eax;
(void)isr_nr; (void)eip; (void)cs; (void)eflags;
(void)old_esp; (void)old_ss; (void)kl;
__asm__ ("movl %%cr2, %%eax":"=a"(cr2));
__asm__ ("movl %%cr3, %%eax":"=a"(cr3));
kprintf(KPL_PANIC, "\n The fault at %x cr3:%x was
caused by a %s. "
"The accessing
cause of the fault was a %s, when the "
"processor was
executing in %s mode, page %x is free\n",
cr2, cr3,
(err&0x1)?"page-level protection voilation":"not-present
page",
(err&0x2)?"write":"read",
(err&0x4)?"user":"supervisor",
alloc_page(PG_NORMAL));
}
异常函敎ͼ它什么也没有做,知识昄一些错误信息?span lang="EN-US">
现在我们来动态的分配一些内存,我们修改一下Q务函敎ͼ
08/init.c
static void
new_task(unsigned int eip) {
struct TASK_STRUCT *task = page2mem(alloc_page(PG_TASK));
memcpy(&(task->tss), &(TASK0.tss), sizeof(struct
TSS_STRUCT));
task->tss.esp0 = (unsigned int)task + PAGE_SIZE;
task->tss.eip = eip;
task->tss.eflags = 0x3202;
task->tss.esp = (unsigned int)page2mem(alloc_page(PG_TASK))+PAGE_SIZE;
task->tss.cr3 = PAGE_DIR;
task->priority = INITIAL_PRIO;
task->ldt[0] = DEFAULT_LDT_CODE;
task->ldt[1] = DEFAULT_LDT_DATA;
task->next = current->next;
current->next = task;
task->state = TS_RUNABLE;
}
自己分配的Q务数据结构和d堆栈Q是不是很有成就感:Q?span lang="EN-US">
最后在init.c中添加初始化代码Q?span lang="EN-US">
08/init.c
void
init(void) {
char wheel[] = {'\\', '|', '/', '-'};
int i = 0;
idt_install();
pic_install();
mm_install(); /* 初始化函数调?span lang="EN-US"> */
kb_install();
timer_install(100);
set_tss((unsigned long long)&TASK0.tss);
set_ldt((unsigned long long)&TASK0.ldt);
__asm__ ("ltrw
%%ax\n\t"::"a"(TSS_SEL));
__asm__ ("lldt
%%ax\n\t"::"a"(LDT_SEL));
kprintf(KPL_DUMP, "Verifing disk partition
table....\n");
verify_DPT();
kprintf(KPL_DUMP, "Verifing file systes....\n");
verify_fs();
kprintf(KPL_DUMP, "Checking / directory....\n");
verify_dir();
sti();
new_task((unsigned int)task1_run);
new_task((unsigned int)task2_run);
__asm__ ("movl %%esp,%%eax\n\t" \
"pushl %%ecx\n\t"
\
"pushl
%%eax\n\t" \
"pushfl\n\t" \
"pushl
%%ebx\n\t" \
"pushl
$1f\n\t" \
"iret\n" \
"1:\tmovw
%%cx,%%ds\n\t" \
"movw
%%cx,%%es\n\t" \
"movw %%cx,%%fs\n\t"
\
"movw
%%cx,%%gs" \
::"b"(USER_CODE_SEL),"c"(USER_DATA_SEL));
__asm__ ("incb 0xeeffeeff");
/* 试Q?/span>触发一个异?span lang="EN-US"> */
for (;;) {
__asm__ ("movb
%%al, 0xb8000+160*24"::"a"(wheel[i]));
if (i == sizeof wheel)
i = 0;
else
++i;
}
}
异常处理例程中什么也没做Q访问内存出错则LQ?span lang="EN-US">
08/exceptions.c
void
page_fault(void) {
__asm__ ("pushl
%%eax;call do_page_fault"::"a"(KPL_PANIC));
halt();
}
最后把mm.o d?span lang="EN-US"> Makefile ?span lang="EN-US">KERNEL_OBJS 中去Q?span lang="EN-US">
08/Makefile
KERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o
kb.o task.o kprintf.o hd.o exceptions.o fs.o mm.o
一?/span>solaris 内核及应用程序源E序Q?/span>
http://src.opensolaris.org/source 主页
http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/ls/ls.c 范例Q?/span>ls源程?/span>
二、比较多?/span>gcc资料
三?/span>linux内核交叉引用Q从0.1?.0Q?.0Q?.2Q?.4Q?/span>
http://www.oldlinux.org/lxr/http/source/
四?/span>C 语言常见问题?/span>(中英?/span>)
http://c-faq-chn.sourceforge.net/
五、操作系l相关的一些资料,包括linux源程序分?/span>
六?/span>linus自传
http://www.bookcool.com/online/zhuanji/happyking-gb/0/content.htm
七、嵌入式及OS开发资料(英文Q?/span>
八?/span>Testing and debugging KOSQ英文)
http://kos.enix.org/~d2/snapshots/kos_current/doc/testingen-html
九?/span>the Single UNIX Specification Version 3Q推荐:可作?/span>posix的替代参考资料)
http://www.unix.org/single_unix_specification
十?/span>OS设计参考(英文Q?/span>
http://www.nondot.org/~sabre/os/articles
声明Q{载请保留Q?/span>
译?/span>Q?/span>http://www.shnenglu.com/jinglexy
原作者:xiaoming.mo at skelix dot org
MSN & Email: jinglexy at yahoo dot com dot cn目标 下蝲源程?/span>
q节课我们讲q的内容与操作系l暂无太大关p,但是q些基础函数非常重要Qƈ且在后面的课E中l常用到。这是我们l常听到的内核库。如果你对这些不是很感兴,知道kprintf?span lang="EN-US">c语言里面?span lang="EN-US">print一样工作就行了。简单掠q即可?span lang="EN-US">
C用户库里面的printfh高度可~性,也很Ҏ理解Q相比之?span lang="EN-US">CQ+中的IOq算W就比较难了。ؓ了在屏幕上显C字W串或数据,我们现在需要实现类?span lang="EN-US">C
q里有一U方法来实现Q我们直到象func(int arg1, int arg2, int arg3)q样一个函数被调用Ӟ它汇~后的指令应该如下(所有从左向叛_栈的~译器应该从地球上彻底消失)Q?span lang="EN-US">
pushl arg3
pushl arg2
pushl arg1
call func
我们看到Q参C叛_左一个个入栈Q参数越多,入栈深。如果是可变参数那我们怎么知道有多个参数呢?{案?span lang="EN-US">printf格式化字W串中参数判断:有多个%XQ就有多个参数要解析。在32位模式下Q所有小?span lang="EN-US">4字节的参数都被当?span lang="EN-US">4字节处理。例如一?span lang="EN-US">char型参敎ͼ入栈时就?span lang="EN-US">int型了Q所以在解析参数时务必保证正?span lang="EN-US">
我们q样设计kprintf参数Q?span lang="EN-US">kprintf(color,
format string, arguments...)
W一个参数定义输出的前景/背景颜色。我们定义了很多宏来解析栈,如果你熟?span lang="EN-US">C语言应该很容易理解它们?span lang="EN-US">
03/kprintf.c
#define args_list char
* // q个宏用例{换栈I间为字W串指针
#define _arg_stack_size(type)
(((sizeof(type)-1)/sizeof(int)+1)*sizeof(int))
// q个宏四舍五入参数大ؓ4字节的倍数
#define args_start(ap, fmt) do { \
ap = (char *)((unsigned int)&fmt + _arg_stack_size(&fmt));
\
} while (0)
//
参数从格式化字W串后面开始解析,?span lang="EN-US">fmt是栈顶Q上面这个宏是取参数的首地址
#define args_end(ap) //
到现在ؓ止,什么也不做
#define args_next(ap, type) (((type *)(ap+=_arg_stack_size(type)))[-1])
//
?span lang="EN-US">‘当前’参数地址Q然后设|指针ؓ下一个参数地址Q暧昧的函数名!
03/kprintf.c
static char buf[1024] =
{-1}; // 注意没有锁保护,引用该变量的函数不可重入Q?span lang="EN-US">
static int ptr = -1;
下面两个函数解析gؓ指定的进制数Q?span lang="EN-US">
static void
parse_num(unsigned int value, unsigned int base)
{ // 可以打印于{于10q制的数
unsigned int n = value / base;
int r = value % base;
if (r < 0) {
r += base;
--n;
}
if (value >= base)
parse_num(n, base);
buf[ptr++] = (r+'0');
}
static
void //
打印16q制?span lang="EN-US">
parse_hex(unsigned int value) {
int i = 8;
while (i-- > 0) {
buf[ptr++] =
"0123456789abcdef"[(value>>(i*4))&0xf];
}
}
现在我们来看一?span lang="EN-US"> kprintfq个函数Q它支持的格式:%s,
%c, %x, %d, %%
void
kprintf(enum KP_LEVEL kl, const char *fmt, ...) {
int i = 0;
char *s;
/* must be the same size as enum KP_LEVEL */
struct KPC_STRUCT {
COLOUR fg;
COLOUR bg;
} KPL[] = {
{BRIGHT_WHITE, BLACK},
{YELLOW, RED},
};
enum KP_LEVEL {KPL_DUMP, KPL_PANIC} 定义?span lang="EN-US"> include/kprintf.h, 它表CZU输出方?span lang="EN-US">, KPL_DUMP 使用黑色背景白色前景昄字符Q?span lang="EN-US">KPL_PANIC 使用黄色前景和红色背景。颜色常量定义在 include/scr.h,
后面会介l到.
args_list args;
args_start(args, fmt);
ptr = 0;
for (; fmt[i]; ++i) {
if ((fmt[i]!='%') &&
(fmt[i]!='\\')) {
buf[ptr++] = fmt[i];
continue;
} else if (fmt[i] == '\\') {
/* \a \b \t \n \v \f
\r \\ */
switch (fmt[++i]) {
case 'a': buf[ptr++] =
'\a'; break;
case 'b': buf[ptr++] =
'\b'; break;
case 't': buf[ptr++] =
'\t'; break;
case 'n': buf[ptr++] =
'\n'; break;
case 'r': buf[ptr++] =
'\r'; break;
case '\\':buf[ptr++] =
'\\'; break;
}
continue;
}
/* 下面是支持的打印格式 */
switch (fmt[++i]) {
case 's':
s = (char
*)args_next(args, char *);
while (*s)
buf[ptr++] = *s++;
break;
case 'c':
buf[ptr++] =
(char)args_next(args, int);
break;
case 'x':
parse_hex((unsigned
long)args_next(args, unsigned long));
break;
case 'd':
parse_num((unsigned
long)args_next(args, unsigned long), 10);
break;
case '%':
buf[ptr++] = '%';
break;
default:
buf[ptr++] = fmt[i];
break;
}
}
buf[ptr] = '\0';
args_end(args);
for (i=0; i<ptr; ++i)
print_c(buf[i],
KPL[kl].fg,
KPL[kl].bg); /*
print_c() 是下层的昄函数Q本文后面会有讲?span lang="EN-US"> */
}
׃是内核程序,我们无法使用C用户库。所以一?span lang="EN-US">memcpyQ?span lang="EN-US">memsetQ?span lang="EN-US">memcpy函数需要自己实玎ͼ但是需要注意的是在BSDpȝ中,即便使用?span lang="EN-US">-nostdlibQ编译器仍然会?span lang="EN-US">System V中相关的memcpy{代码,具体情况我也不是很清除。这些函数的效率当然无法?span lang="EN-US">linux内核中的内嵌汇编相比Q我们暂时这样实现它们吧?span lang="EN-US">
03/libcc.c
/* 下面函数寚w叠区域也q行了处?span lang="EN-US">
*/
void
bcopy(const void *src, void *dest, unsigned int n) {
const char *s = (const char *)src;
char *d = (char *)dest;
if (s <= d)
for (; n>0; --n)
d[n-1] = s[n-1];
else
for (; n>0; --n)
*d++ = *s++;
}
void
bzero(void *dest, unsigned int n) {
memset(dest, 0, n);
}
void *
memcpy(void *dest, const void *src, unsigned int n) {
bcopy(src, dest, n);
return dest;
}
void *
memset(void *dest, int c, unsigned int n) {
char *d = (char *)dest;
for (; n>0; --n)
*d++ = (char)c;
return dest;
}
int
memcmp(const void *s1, const void *s2, unsigned int n) {
const char *s3 = (const char *)s1;
const char *s4 = (const char *)s2;
for (; n>0; --n) {
if (*s3 > *s4)
return 1;
else if (*s3 < *s4)
return -1;
++s3;
++s4;
}
return 0;
}
int
strcmp(const char *s1, const char *s2) {
while (*s1 && *s2) {
int r = *s1++ - *s2++;
if (r)
return r;
}
if (*s1 == *s2)
return 0
else
return (*s1)?1:-1;
}
char *
strcpy(char *dest, const char *src) {
char *p = dest;
while ( (*dest++ = *src++))
;
*dest = 0;
return p;
}
unsigned int
strlen(const char *s) {
unsigned int n = 0;
while (*s++)
++n;
return n;
}
print_c函数
直接操作昑֭区域一点也不方便,所以我们需要一个显C模块。这个就是我们的‘昑֍驱动’了,是不是不敢相信驱动是q么单的事情Q我们先来看一下一些常量定义:
03/include/scr.h
#define MAX_LINES
25 //
bios默认讄屏幕?span lang="EN-US"> 80x25大小Q彩色字W模?span lang="EN-US">
#define MAX_COLUMNS 80
#define TAB_WIDTH
8
// 必须是:2^n
#define VIDEO_RAM
0xb8000 // 昑֭地址
我们曄要提到过q个地址Q在字符模式下,适配器?span lang="EN-US">0xB8000-0xBF000作ؓ视频内存。通常我们处于80x25大小屏幕Q有16U颜艌Ӏ由于一个屏q只需?span lang="EN-US">80x25x2个字节,?span lang="EN-US">4kQ所以该视频内存可以分ؓ多个c我们用所有的,但是当前只能有一个页面可见。ؓ了显CZ个字W,用?span lang="EN-US">2个字节,一个字节是字符|另一个字节是字符属性(即颜Ԍ。属性字节定义如下:
To display a single character, two bytes are being used
which called the character byte and the attribute byte. The character byte
contains the value of the character. The attribute byte is defined like this:
Bit 7 |
闪烁 |
Bits 6-4 |
背景?span lang="EN-US"> |
Bit 3 |
明亮模式 |
Bit3 2-0 |
前景?span lang="EN-US"> |
#define LINE_RAM (MAX_COLUMNS*2)
#define PAGE_RAM (MAX_LINE*MAX_COLUMNS)
#define BLANK_CHAR (' ')
#define BLANK_ATTR (0x70)
/* 白色前景Q黑色背?span lang="EN-US"> */
#define CHAR_OFF(x,y)
(LINE_RAM*(y)+2*(x)) /* 计算l定坐标xQ?span lang="EN-US">y的偏Ud址Q相?span lang="EN-US">0xB8000Q?span lang="EN-US"> */
Calculates the offset of a given ordinary x, y from 0xB8000
typedef enum COLOUR_TAG
{ /*
颜色?span lang="EN-US"> */
BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, WHITE,
GRAY, LIGHT_BLUE, LIGHT_GREEN, LIGHT_CYAN,
LIGHT_RED, LIGHT_MAGENTA, YELLOW, BRIGHT_WHITE
} COLOUR;
坐标pd下:
___________________\
| Q?span lang="EN-US">0Q?span lang="EN-US">0Q?span lang="EN-US"> /
|
\|/
03/scr.c
static int csr_x = 0;
static int csr_y = 0;
׃我们只用C一个视频页Q所以上面两个变量就可以存储坐标了。关于多|C可以在|络上查扄兌料?span lang="EN-US">
static void
scroll(int lines) { 向上滚动屏幕多少行,是一些内存复写?span lang="EN-US">
short *p = (short *)(VIDEO_RAM+CHAR_OFF(MAX_COLUMNS-1,
MAX_LINES-1));
int i = MAX_COLUMNS-1;
memcpy((void *)VIDEO_RAM, (void
*)(VIDEO_RAM+LINE_RAM*lines),
LINE_RAM*(MAX_LINES-lines));
for (; i>=0;
--i) // 说明q个for循环有问题,觉得应该Ҏ下面q样Q?span lang="EN-US">
// for (i = i * lines;
i>=0; --i)
*p-- =
(short)((BLANK_ATTR<<4)|BLANK_CHAR);
}
下面函数讄光标可能会引发竞态条Ӟ但是print_c只准备在内核中用,所以没有关中断。它可能会引起一?span lang="EN-US">bugQ但是我没有扑ֈ。译注:全局变量没有锁保护在设计上就是一U错误。这里的代码保护实是没有做Q读者应用到自己的内核时要小心了?span lang="EN-US">
void
set_cursor(int x, int y) {
csr_x = x;
csr_y = y;
outb(0x0e,
0x3d4);
讄光标?span lang="EN-US">8位的准备工作
outb(((csr_x+csr_y*MAX_COLUMNS)>>8)&0xff,
0x3d5); 讄光标?span lang="EN-US">8?span lang="EN-US">
outb(0x0f,
0x3d4);
讄光标?span lang="EN-US">8位的准备工作
outb(((csr_x+csr_y*MAX_COLUMNS))&0xff,
0x3d5); 讄光标?span lang="EN-US">8?span lang="EN-US">
}
void
get_cursor(int *x, int *y) {
*x = csr_x;
*y = csr_y;
}
void
print_c(char c, COLOUR fg, COLOUR bg) {
// 用这个函数来昄一个具体的字符到屏q,我们可以把它看作‘昑֍驱动’
char *p;
char attr;
p = (char *)VIDEO_RAM+CHAR_OFF(csr_x,
csr_y); // 取光标位|?span lang="EN-US">
attr =
(char)(bg<<4|fg);
// 属?span lang="EN-US">
switch (c) {
case '\r':
csr_x = 0;
break;
case '\n':
for (; csr_x<MAX_COLUMNS; ++csr_x) {
*p++ = BLANK_CHAR;
*p++ = attr;
}
break;
case '\t':
c =
csr_x+TAB_WIDTH-(csr_x&(TAB_WIDTH-1));
c = c<MAX_COLUMNS?c:MAX_COLUMNS;
for (; csr_x<c; ++csr_x) {
*p++ = BLANK_CHAR;
*p++ = attr;
}
break;
case '\b':
if ((! csr_x) && (! csr_y))
return;
if (! csr_x) {
csr_x = MAX_COLUMNS -
1;
--csr_y;
} else
--csr_x;
((short *)p)[-1] = (short)((BLANK_ATTR<<4)|BLANK_CHAR);
break;
default:
*p++ = c;
*p++ = attr;
++csr_x;
break;
}
if (csr_x >= MAX_COLUMNS) {
csr_x = 0;
if (csr_y < MAX_LINES-1)
++csr_y;
else
scroll(1);
}
set_cursor(csr_x,
csr_y); // 讄光标位置
}
函数比较单,没有分析的必要了Q大家自q吧?span lang="EN-US">
声明Q{载请保留Q?/span>
译?/span>Q?/span>http://www.shnenglu.com/jinglexy
原作者:xiaoming.mo at skelix dot org
MSN & Email: jinglexy at yahoo dot com dot cn目标Q?/span>?span lang="EN-US">"system"从Y盘启动,q打?span lang="EN-US">"Hello World!" 下蝲源程?/span>
内存d
处理器以‘字节’理和访问内存,每个字节都有独立的地址Q即物理地址。有两种地址映射方式Q分D和分页Q?span lang="EN-US">skelix内核中都用到了?a name="Memory_Addressing">
D对于我们来说再熟悉不过了,先回一?span lang="EN-US">dos时期的段吧。它是一?span lang="EN-US">16位的寄存器,所以最多可以直接访?span lang="EN-US">2^16字节的内存,?span lang="EN-US">64K。这对应用程序来说太了Q于?span lang="EN-US">Intel使用Segment:Offsetl合方式来表CZ个虚拟地址。段寄存器左U?span lang="EN-US">4位加上偏Ud得到实际的物理地址了。例如,0x
+ 0189
-------
现在我们来计最大可以访问的地址Q?span lang="EN-US">FFFF:FFFF
FFFF0
+ FFFF
-------
10FFEF
q个范围?st1:chmetcnv unitname="m" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">1M + 65519 bytes, 因ؓ?span lang="EN-US">80386中用了20位地址U,所以可以额外多讉K65519个字节虚拟地址Q例如地址0x100010被映到地址0x10Q访问这两个地址是等L?span lang="EN-US">
表示同一个物理地址有多U方式,例如
另一个概忉|U性地址Q这个是32位地址Q只有当分页机制开启时才有效,文章后面会提到它?span lang="EN-US">
引导q程
当系l上甉|RESETӞ处理器将执行一些列的初始化Q寄存器被设|成非预知状态,q且cpu处于实模式。也怽想知?span lang="EN-US">cpu是怎样讄segment:offset为物理地址FFFF0的(0xf000:0xfff0是bios入口地址Q,q是因ؓcs寄存器有一个非可见部分Q它保存?span lang="EN-US">ffff:0000地址Qƈ?span lang="EN-US">cs在初始化时会被装?span lang="EN-US">f000倹{此后以正常方式使用它。当bois取得控制权后Q根据用户配|(从Y驱,盘Q或cdromQ中dW一?span lang="EN-US">sector?st1:chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">00007C00Qƈ跌{到该地址执行Q就是引导程?span lang="EN-US">bootstrapQ。在bootstrap中我们可以?span lang="EN-US">bios中断Q但是进?span lang="EN-US">kernel后就不能再用了?span lang="EN-US">
E序一Q?span lang="EN-US">as?span lang="EN-US">ld的范?/span>
你可以在下蝲源程序的01/first.cry/bootsect.s
.text .text表示代码D?span lang="EN-US">
.globl start表示start可以用作外部W号
.code16 GCC默认使用32位地址和操作数Q这里告诉它使用16?span lang="EN-US">
start:
jmp
start d@?span lang="EN-US">
.org 0x1fe,
0x90 .org NEW-LC,
FILLQ?/span>说明Q这里填?span lang="EN-US">0x90Q是nop指o的机器码
.word 0xaa55
讲解Q?span lang="EN-US">.org指o指示下一个数据地址Qؓ了编译这个程序,我们写了一?span lang="EN-US">MakefileQM能老是敲命令吧Q呵c?span lang="EN-US">
|络上可以找到很多写Makefile的资料,~译选项才是我们x的焦炏V?span lang="EN-US">
01/first.cry/Makefile
AS=as
gcc汇编工具
LD=ld
gccq接?span lang="EN-US">
.s.o:
${AS} -a $< -o $*.o >$*.map
all: final.img
final.img: bootsect
mv bootsect final.img
bootsect: bootsect.o
${LD} --oformat binary -N -e start -Ttext 0x
讲解Q?span lang="EN-US">ld可以被配|ؓ支持多于一U的目标文g. binary表示没有E序头和其他信息Q仅仅是一些裸数据。如果没有这个选项Q将被默认链接ؓelf格式?span lang="EN-US">-N?span lang="EN-US">text?span lang="EN-US">data节设|ؓ可读写?span lang="EN-US">-Ttext?span lang="EN-US">text节v始地址讄?span lang="EN-US">0x
现在我们q行make指o~译一下:
[root@root~/source/os/skelix/01/first.cry]$ ls
bootsect.s COPYING Makefile
[root@root~/source/os/skelix/01/first.cry]$ make
as -a bootsect.s -o bootsect.o >bootsect.map
ld --oformat binary -N -e start -Ttext 0x
mv bootsect final.img
[root@root~/source/os/skelix/01/first.cry]$ ls
bootsect.map bootsect.o
bootsect.s COPYING final.img
Makefile
[root@root~/source/os/skelix/01/first.cry]$
现在Q我们启?span lang="EN-US">vmwareQ运行,载入软驱映象文g"final.img"Q我们得C个黑屏,q是正确的,因ؓ我们什么也没有做?span lang="EN-US">
E序一Q显C?Hello World!
好了Q上面的黑屏E序q不是太好玩Q现在我们尝试在上面打印"Hello World!"
01/hello.world/bootsect.s
.text
.globl start
.code16
start:
jmp code
msg:
使用jmp指o跌该变量,q是我们Z么在Makefile使用-N链接选项?span lang="EN-US">
.string "Hello World!\x0"
code:
movw $0xb800,%ax
movw
%ax,
%es esD设|成B800Q如前所qͼsegment:offset地址映射方式Q它指向B8000Q?span lang="EN-US">
q意味着W一个字节地址?span lang="EN-US">0Q映到B8000Q,属性字节是1Q映到B8001Q?span lang="EN-US">
B8001D|ؓ0x07可以这?span lang="EN-US">byte颜色讄为黑底白字?span lang="EN-US">
xorw
%ax, %ax
movw
%ax, %ds
movw $msg,
%si ?span lang="EN-US">movsb指o讄正确?span lang="EN-US">si?span lang="EN-US">di
xorw
%di, %di
cld
movb $0x07,
%al 字的颜色
1:
cmp
$0, (%si)
je
movsb
stosb
jmp 1b
1: jmp 1b
.org 0x1fe, 0x90
.word 0xaa55
声明Q{载请保留Q?/span>
译?/span>Q?/span>http://www.shnenglu.com/jinglexy
原作者:xiaoming.mo at skelix dot org
MSN & Email: jinglexy at yahoo dot com dot cn
GCC
Skelix 使用c语言~写Q当然也用了汇编语言Q?span lang="EN-US">at&t风格Q,?span lang="EN-US">linux下?span lang="EN-US">gcc~译?span lang="EN-US">
[root@root ~]$ gcc -v
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--disable-checking --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)
在每教E中都给Z源程序和软盘映象文gQ你可以直接使用它们。如果你需要编译这些源E序Q编译环境必L。我们推荐的环境?span lang="EN-US">linux2.6.x内核Q?span lang="EN-US">gcc3.x~译器?span lang="EN-US">
׃在源E序中用了__asm__, __attribute__, __extention__Q以?span lang="EN-US">gcc内嵌汇编Q还?span lang="EN-US">unsigned long long(直到C99才开始支?span lang="EN-US">)Q如果你使用了其他编译器Q需要修改对应的源程序。且~译器必L32位,q样做的目的是保持源E序z清晰?span lang="EN-US">
对于windows用户可以使用 cygwinQ它提供?span lang="EN-US">windows下的linux环境。不q我没有试使用它,因ؓ我的电脑上没有安?span lang="EN-US">windows操作pȝ。也可以在你?span lang="EN-US">windowspȝ上安装一个虚拟机上的linuxQ如果你的电脑够快的话?span lang="EN-US">
VMWARE
Zq行教程中的范例Q一个虚拟机必不可少Q?span lang="EN-US">virtual pc2007已经可以免费使用了,?span lang="EN-US">M$的官方网站上可以扑ֈ下蝲。当然也可以使用qemu?span lang="EN-US">bochs之类的虚拟机。推荐的虚拟机是VMWARE?span lang="EN-US">
Things Are Good To Know
如果能看?/span>Makefile最好了Q这?/span>*nixE序员必L握的一基本知识。另外,如果你熟悉内存地址映射Q中断,异常Q?/span>GDTQ?/span>LDTQ?/span>IDTQ分|Ӟ范围端口更好了。当然不懂也没关p,Intel的三h册是案头必备Q?/span>http://www.intel.com
IA-32 Intel Architecture Software Developer's Manual
Volume1: Basic Architecture
IA-32 Intel Architecture Software Developer's Manual Volume
IA-32 Intel Architecture Software Developer's Manual Volume3B: System
Programming Guide Part2
读者对q些东西不必紧张Q我在教E中会解释相关的知识?span lang="EN-US">c语言和汇~是最基本的要求,能够很清楚的了解什么是堆和栈。关?span lang="EN-US">c语言的数据成千上万,但是保护模式斚w的书c比哈雷慧星q少Q据说每76q可以买C本,如果你够幸q的话:Q?a name="Color_Pattern">
风格U定
原文中的格式被擅自去掉了Q翻译后的风格应该可以一看就懂?/span>