linux内核0.11开发,Linux 环境下编译 0.11版本内核 kernel

本文记录了从零开始编译Linux 0.11内核的全过程,包括解决各种编译错误及警告的方法。作者分享了如何安装必要的编译工具、修改源代码以适配现代编译器等经验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在看《内核0.11完全注释》一书,由于书中涉及汇编语言的地方众多,本人在大学时汇编语言学得一塌糊涂,所以实在看不下去了,头都大了只好匆匆看了个头尾(前面几章和最后一章)。看来即使有《九阴真经》这样的武功秘籍,内功不够也是修炼不出来神马来的。于是索性下了个0.11版本的kernel下来尝试编译一把。

-0.11.tar.gz地址:

下面开始工作:

1、tar xvfz linux-0.11.tar.gz

2、cd linux-0.11

3、make

make: as86: Command not finded

make: ***

出错原因:as86汇编器未安装

解决办法:

yum install dev86*(请务必保证网络畅通,如果使用Debian Linux系统命令改为apt-get install dev86*)

dev86-0.16.3-8.i386.rpm安装地址

4、make

gas -c -o boot/head.o boot/head.s

make: gas: Command not finded

make: *** [boot/head.o] Error 127

出错原因:gas汇编器未安装

解决办法:

yum install binutils* (请确保网络正常)

下载binutils-2.20.tar.gz安装下载地址

tar xvfz binutils-2.20.tar.gz

./configure

make

make install

确认Gnu assembler已经安装

which as

/usr/local/bin/as

5、make

gas -c -o boot/head.o boot/head.s

make: gas: Command not found

make: *** [boot/head.o] Error 127

出错原因:gas、gld的名称已经过时,现在GNU assembler的名称是as

解决办法:

修改主Makefile文件

将AS =gas修改为AS =as

将LD =gld修改为LD =ld

6、make

as -c -o boot/head.o boot/head.s

as: unrecognized option '-c'

make: *** [boot/head.o] Error 1

出错原因:as语法和1991年的时候有了一些变化

解决办法:

修改Makefile文件

将$(AS) -c -o $*.o $

7、make

as -o boot/head.o boot/head.s

boot/head.s: Assembler messages:

boot/head.s:231: Error: alignment not a power

of 2

make: *** [boot/head.o] Error 1

出错原因:.align

2是汇编语言指示符,其含义是指边界对齐调整;

“2”表示把随后的代码或数据的偏移位置调整到地址值最后2比特位为零的位置(2^2),即按4字节对齐内存地址。

不过现在GNU

as直接是写出对齐的值而非2的次方值了。

.align 2应该改为.align 4

.align 3应该改为.align 8

解决办法:

修改boot/head.s文件

将.align 2应该改为.align 4

.align 3应该改为.align 8

8、make

cc1: error: unrecognized command line option

"-mstring-insns"

cc1: error: unrecognized command line option

"-fcombine-regs"

make: *** [init/main.o] Error 1

解决办法:

修改Makefile文件

将-fcombine-regs -mstring-insns删除或者注释掉

9、make

In file include from init/main.c:9:

include/unistd.h:207: warning: function

return types not compatible due to 'volatile'

include/unistd.h:208: warning: function

return types not compatible due to 'volatile'

init/main.c:24: error: static declaration of

'fork' follows non-static declaration

init/main.c:26: error: static declaration of

'pause' follows non-static declaration

include/unistd.h:224: note: previous

declaration of 'pause' was here

init/main.c:30: error: static declaration of

'sync' follows non-static declaration

include/unistd.h:235: note: previous

declaration of 'sync' was here

init/main.c:108: warning: return type of

'main' is not 'int'

make: *** [init/main.o] Error 1

解决办法:

修改init/main.c文件

将static inline _syscall0(int,fork)修改为inline _syscall0(int,fork)

static inline _syscall0(int,pause)修改为inline _syscall0(int,pause)

static inline _syscall1(int,setup,void

*,BIOS)修改为inline _syscall1(int,setup,void

*,BIOS)

static inline _syscall0(int,sync)修改为inline _syscall0(int,sync)

10、make

(cd kernel; make)

make[1]: Entering directory '***/linux-0.11/kernel'

gcc -Wall -O -fstrength-reduce

-fomit-frame-pointer -fcombine-regs -finline-functions -mstring-insns -nostdinc

-I../include \

-c -o sched.o sched.c

cc1: error: unrecognized command line option

"-mstring-insns"

cc1: error: unrecognized command line option

"-fcombine-regs"

make[1]: *** [sched.o] Error 1

make[1]: Leaving directiory

'***/linux-0.11/kernel'

make: *** [kernel/kernel.o] Error 2

解决办法:

修改kernel目录下Makefile文件

将-fcombine-regs -mstring-insns删除或者注释掉

11、make

gas -c -o system_call.o system_call.s

make[1]: gas: Command not found

make[1]: *** [system_call.o] Error 127

make[1]: Leaving directory

'***/linux-0.11/kernel'

make: *** [kernel/kernel.o] Error 2

解决办法:

修改kernel目录下Makefile文件

将AS =gas修改为AS =as

将LD =gld修改为LD =ld

将$(AS) -c -o $*.o $

12、make

fork.c:In function 'copy_process':

fork.c:121: warning: suggest parentheses

around assignment used as truth value

fork.c:In function 'copy_mem':

fork.c:54: error: can't find a register in

class 'DREG' while reloading 'asm'

fork.c:55: error: can't find a register in

class 'DREG' while reloading 'asm'

fork.c:46: error: 'asm' operand has

impossible constraints

fork.c:47: error: 'asm' operand has

impossible constraints

fork.c:54: error: 'asm' operand has impossible

constraints

fork.c:55: error: 'asm' operand has

impossible constraints

make[1]: *** [fork.o] Error 1

make[1]: Leaving directory

'***/linux-0.11/kernel'

make: *** [kernel/kernel.o] Error 2

在kernel/fork.c第54行:

可以看到这样的调用set_base(p->ldt[1],new_code_base);

在include/linux/sched.h第188 —— 211行:

可以看到set_base的实现

#define set_base(ldt,base) _set_base( ((char

*)&(ldt)) , base )

#define _set_base(addr,base) \

__asm__("movw %%dx,%0\n\t" \

"rorl $16,%%edx\n\t" \

"movb %%dl,%1\n\t" \

"movb %%dh,%2" \

::"m" (*((addr)+2)), \

"m" (*((addr)+4)), \

"m" (*((addr)+7)), \

"d" (base) \

:"dx")

因为这里涉及到汇编的知识,哥卡在这里一直没解决并且郁闷了好久,最后只好看回《linux内核0.11完全注释》一书。赵炯博士在这里提供了修改linux-0.11-060618-gcc4.tar.gz好的0.11版本的内核。

于是这个时候比较工具Beyond Compare就派上用场了。

将#define _set_base(addr,base) \修改为#define _set_base(addr,base) \

__asm__("movw %%dx,%0\n\t" \

__asm__("push %%edx\n\t" \

"rorl $16,%%edx\n\t" \ "movw

%%dx,%0\n\t" \

"movb %%dl,%1\n\t" \ "rorl

$16,%%edx\n\t" \

"movb %%dh,%2" \ "movb

%%dl,%1\n\t" \

::"m" (*((addr)+2)), \ "movb

%%dh,%2\n\t" \

"m" (*((addr)+4)), \ "pop

%%edx" \

"m" (*((addr)+7)), \

::"m" (*((addr)+2)), \

"d" (base) \ "m"

(*((addr)+4)), \

:"dx") "m" (*((addr)+7)),

\

"d" (base) )

将#define switch_to(n) {\

struct {long a,b;} __tmp; \

__asm__("cmpl %%ecx,_current\n\t" \

"je 1f\n\t" \

"movw %%dx,%1\n\t" \

"xchgl %%ecx,_current\n\t" \

"ljmp %0\n\t" \

------------------------------这里修改为"ljmp *%0\n\t" \

"cmpl

%%ecx,_last_task_used_math\n\t" \

"jne 1f\n\t" \

"clts\n" \

"1:" \

::"m" (*&__tmp.a),"m"

(*&__tmp.b), \

"d" (_TSS(n)),"c" ((long)

task[n])); \

}

将#define _get_base(addr) ({\修改为static inline unsigned long _get_base(char * addr)

unsigned long __base; \ {

__asm__("movb %3,%%dh\n\t" \

unsigned long __base;

movb %2,%%dl\n\t" \ __asm__("movb

%3,%%dh\n\t" \

shll $16,%%edx\n\t" \ "movb

%2,%%dl\n\t" \

movw %1,%%dx" \ "shll

$16,%%edx\n\t" \

"=d" (__base) \ "movw

%1,%%dx" \

"m" (*((addr)+2)), \

:"=&d" (__base) \

"m" (*((addr)+4)), \ :"m"

(*((addr)+2)), \

"m" (*((addr)+7))); \ "m"

(*((addr)+4)), \

__base;}) "m" (*((addr)+7)));

return __base;

}

注意:

由于GNU

as汇编器不断进化的原因,需要将*.s文件中

类似

.globl _idt,_gdt,_pg_dir,_tmp_floppy_area

修改为

.globl idt,gdt,pg_dir,tmp_floppy_area

不然虽然编译通过,但是连接的时候将出现类似这样的错误

boot/head.o: In function 'setup_idt':

(.text+0x87): undefined reference to '_idt'

boot/head.o: In function 'idt_descr':

(.text+0x54ac): undefined reference to '_idt'

kernel/kernel.o: In function 'sched_init':

(.text+0x37c): undefined reference to '_idt'

GCC中基本的内联汇编:__asm____volatile__("InstructionList");

__asm__(

"movl $1,%eax\r\t"

"xor %ebx,%ebx\r\t"

"int $0x80"

);

带有C/表达式的内联汇编:

__asm__ __volatile__("InstructionList"

:Output

:Input

:Clobber/Modify);

这4个部分都不是必须的,任何一个部分都可以为空,其规则为:

1、如果Clobber/Modify为空,则其前面的冒号(:)必须省略。

2、如果Output,Input,Clobber/Modify都为空,Output,Input之前的冒号(:)既可以省略,也可以不省略。

3、如果Input,Clobber/Modify为空,但Output不为空,Input前的冒号(:)既可以省略,也可以不省略。

4、如果后面的部分不为空,而前面的部分为空,则前面的冒号(:)都必须保留,否则无法说明不为空的部分究竟是第几部分。

每一个Input和Output表达式都必须指定自己的操作约束Operation Constraint,这里将讨论在80386平台上所可能使用的操作约束。

当前的输入或输出需要借助一个寄存器时,需要为其指定一个寄存器约束,可以直接指定一个寄存器的名字。

常用的寄存器约束的缩写

约束 意义

r表示使用一个通用寄存器,由GCC在%eax/%ax/%al,%ebx/%bx/%bl,%ecx/%cx/%cl,%edx/%dx/%dl中选取一个GCC认为合适的。

g表示使用任意一个寄存器,由GCC在所有的可以使用的寄存器中选取一个GCC认为合适的。

q表示使用一个通用寄存器,和约束r的意义相同。

a表示使用%eax/%ax/%al

b表示使用%ebx/%bx/%bl

c表示使用%ecx/%cx/%cl

d表示使用%edx/%dx/%dl

D表示使用%edi/%di

S表示使用%esi/%si

f表示使用浮点寄存器

t表示使用第一个浮点寄存器

u表示使用第二个浮点寄存器

如果一个Input/Output操作表达式的C/表达式表现为一个内存地址,不想借助于任何寄存器,则可以使用内存约束。比如:

__asm__("lidt%0":"=m"(__idt_addr));

__asm__("lidt%0"::"m"(__idt_addr));

修饰符 输入/输出 意义

= O表示此Output操作表达式是Write-Only的。

+ O表示此Output操作表达式是Read-Write的。

& O表示此Output操作表达式独占为其指定的寄存器。

% I表示此Input操作表达式中的C/C++表达式可以和下一 个Input操作表达式中的C/C++表达式互换

--------------------------------------------------------------------------------------------------------------------------------------------------------------

13、make

In file included from stat.c:13:

../include/asm/segment.h: Assembler messages:

../include/asm/segment.h:27: Error: bad register name

'%sil'

make[1]: *** [stat.o] Error 1

make[1]: Leaving directory '***/linux-0.11/fs'

make: *** [fs/fs.o] Error 2

出错原因:

fs目录下的Makefile中编译选项使用了-O优化选项导致寄存器错误

解决方法:

将fs目录下的Makefile文件中的

CFLAGS =-Wall -O -fstrength-reduce

-fomit-frame-pointer \

修改为

CFLAGS =-Wall -fstrength-reduce -fomit-frame-pointer \

14、make

tools/build.c: In function 'main':

tools/build.c:75: warning: implicit declaration of

function 'MAJOR'

tools/build.c:76: warning: implicit declaration of

function 'MINOR'

tmp/ccsMKTAS.o: In function 'main':

build.c:(.text+0xe1): undefined reference to 'MAJOR'

build.c:(.text+0xf7): undefined reference to 'MINOR'

collect2: ld returned 1 exit status

出错原因:'MAJOR'和'MINOR'未定义

解决办法:

我们可以在include/linux/fs.h文件中找到

#define MAJOR(a) (((unsigned)(a))>>8)

#define MINOR(a) ((a)&0xff)

而在tools/build.c中也有包含#include

那么再看第一层目录中的主Makefile文件

tools/build: tools/build.c

$(CC) $(CFLAGS) \

-o tools/build tools/build.c

好象确实没有引用头文件

简单的添加-Iinclude

重新编译后出现一堆报标准C库头文件的错误

再添加-nostdinc

又报stderr fprintf之类的错误

没折,只好将

#define MAJOR(a) (((unsigned)(a))>>8)

#define MINOR(a) ((a)&0xff)

添加到tools/build.c文件中,然后删除#include

15、make

make[1]: Leaving directory '***/linux-0.11/lib'

ld -s -x -M boot/head.o init/main.o \

kernel/kernel.o mm/mm.o fs/fs.o \

kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \

kernel/math/math.a \

lib/lib.a \

-o tools/system > System.map

ld: warning: cannot find entry symbol _start;

defaulting to 08048a0

gcc -Wall -O -fstrength-reduce -fomit-frame-pointer \

-o tools/build tools/build.c

tools/build boot/bootsect boot/setup tools/system

/dev/hd6 > Image

/dev/hd6: No such file or directory

Couldn't stat root device.

make: *** [Image] Error 1

解决办法:

将第一层主Makefile文件中的

tools/system: boot/head.o init/main.o \

$(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS)

$(LD) $(LDFLAGS) boot/head.o init/main.o \

$(ARCHIVES) \

$(DRIVERS) \

$(MATH) \

$(LIBS) \

-o tools/system > System.map

修改为

tools/system: boot/head.o init/main.o \

$(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS)

$(LD) $(LDFLAGS) boot/head.o init/main.o \

$(ARCHIVES) \

$(DRIVERS) \

$(MATH) \

$(LIBS) \

-o tools/system

nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\(

[aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'| sort > System.map

nm命令将目标文件中的各种符号列出来。

ROOT_DEV=/dev/hd6修改为ROOT_DEV=

16、make

/DISCARD/

*(.note.GNU-stack)

*(.gnu_debuglink)

*(.gnu.lto_*)

OUTPUT(tools/system elf32-i386)

nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\(

[aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'| sort > System.map

nm: tools/system: no symbols

gcc -Wall -O -fstrength-reduce -fomit-frame-pointer \

-o tools/build tools/build.c

tools/build boot/bootsect boot/setup tools/system >

Image

Root device is (3, 6)

Boot sector 512 bytes.

Setup is 312 bytes.

Non-Gcc header of 'system'

make: *** [Image] Error 1

解决办法:

将第一层主Makefile文件中的

LDFLAGS =-s -x -M

修改为

LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32

Image: boot/bootsect boot/setup tools/system tools/build

tools/build boot/bootsect boot/setup tools/system

$(ROOT_DEV) > Image

sync

修改为

Image: boot/bootsect boot/setup tools/system

tools/build

objcopy -O binary -R .note -R .comment tools/system

tools/kernel

tools/build boot/bootsect boot/setup tools/kernel $(ROOT_DEV)

> Image

rm tools/kernel -f

sync

objcopy命令能复制和转化目标文件

objcopy -O binary -R .note -R .comment tools/system

tools/kernel

-O binary tools/system tools/kernel将tools/system生成二进制文件tools/kernel

-R .note -R .comment删除.note段 和.comment段

将tools/build.c文件中的

if (((long *) buf)[5] != 0)

die("Non-GCC header of 'system'");

这段代码注释掉

//if (((long *) buf)[5] != 0)

// die("Non-GCC header of 'system'");

17、make

ld -m elf_i386 -Ttext 0 -e startup_32 boot/head.o

init/main.o \

kernel/kernel.o mm/mm.o fs/fs.o \

kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \

kernel/math/math.a \

lib/lib.a \

-o tools/system

nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\(

[aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'| sort > System.map

gcc -Wall -O -fstrength-reduce -fomit-frame-pointer \

-o tools/build tools/build.c

objcopy -O binary -R .note -R .comment tools/system

tools/kernel

tools/build boot/bootsect boot/setup tools/system >

Image

Root device is (3, 6)

Boot sector 512 bytes.

Setup is 312 bytes.

System is 128323 bytes.

rm tools/kernel -f

sync

终于编译linux内核0.11版本成功了!

最 后也可以利用赵炯博士在这里提供了修改linux-0.11-060618-gcc4.tar.gz好的0.11版本的内核进行编译,只要修改以下Makefile里-mcpu=i386为-march=i386还需要将kernel/blk_drv/blk.h文件第87行 将#elif修改为#else就可以编译通过了。

总结:编译需要一个过程,学习也是同样需要一个过程。虽然可以利用赵博士修改好的kernel-0.11版快速的编译内核,但是那样就不会遇到太多有价值的编译问题,而解决这些问题就是一个学习过程,相信赵博士在编译0.11版本内核的时候也遇到了这些问 题。这样我想起了高中解数学难题的时候,高手解体时总是省略了一些因式分解的过程,而对于菜鸟来说这些省略的过程是非常重要的。

/* Style Definitions */

table.MsoNormalTable

{mso-style-name:普通表格;

mso-tstyle-rowband-size:0;

mso-tstyle-colband-size:0;

mso-style-noshow:yes;

mso-style-parent:"";

mso-padding-alt:0cm 5.4pt 0cm 5.4pt;

mso-para-margin:0cm;

mso-para-margin-bottom:.0001pt;

mso-pagination:widow-orphan;

font-size:10.0pt;

font-family:"Times New Roman";

mso-fareast-font-family:"Times New Roman";

mso-ansi-language:#0400;

mso-fareast-language:#0400;

mso-bidi-language:#0400;}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值