购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.2.1 初始化终端和控制台

在Linux相关的文档中,可以经常看到tty、console、terminal、shell等单词。在一些不那么严格的场景中,它们会被混用,如在一些中文翻译中有可能都被翻译成终端。但实际上,在计算机发展的早期,它们分别对应不同的实体。早期的计算机往往会配备一个操作面板,上面带有大量开关和指示灯,操作员就通过这个面板操作计算机,这个面板就叫作console。Windows系统上的“控制面板”这个名词也来源于此。一台电脑通常只有一个console。随着技术的进步,计算机可以支持多个用户远程连接,每个用户手上有一个专用硬件用于登录,这个专用硬件就是terminal,中文可以翻译为终端。终端的形态是多种多样的,tele typewriter,即远程打字机,缩写为tty。所以慢慢地人们也会使用tty这个缩写来指代终端。而shell则是与内核相对应的一个概念,它是一个运行在console上的进程,也是一个负责读入用户输入、处理用户请求和用户交互的应用程序。

搞清楚这些概念以后,再来阅读Linux源码就能轻松区分代码中的这些概念了。终端tty不只包含屏幕的输出,还包括键盘的输入,你慢慢会发现原来串口通信也是一种终端,所以我们就可以先实现终端的初始化函数,然后在这个函数中进行屏幕、键盘和串口输入/输出的初始化。tty_init就负责初始化终端,而con_init则负责初始化控制台,在屏幕上进行显示这个工作显然适合由控制台来实现。console的初始化的代码如代码清单3-2所示。

代码清单3-2初始化控制台

1  /* kernel/chr_drv/console.c */

2  #include<linux/tty.h>

3

4  #define ORIG_X     (*(unsigned char *)0x90000)

5  #define ORIG_Y     (*(unsigned char *)0x90001)

6  #define ORIG_VIDEO_PAGE  (*(unsigned short *)0x90004)

7  #define ORIG_VIDEO_MODE  ((*(unsigned short *)0x90006) & 0xff)

8  #define ORIG_VIDEO_COLS  (((*(unsigned short *)0x90006) & 0xff00)>>8)

9  #define ORIG_VIDEO_LINES  ((*(unsigned short *)0x9000e) & 0xff)

10 #define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008)

11 #define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a)

12 #define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c)

13

14 #define VIDEO_TYPE_MDA   0x10

15 #define VIDEO_TYPE_CGA   0x11

16 #define VIDEO_TYPE_EGAM   0x20

17 #define VIDEO_TYPE_EGAC   0x21

18

19 static unsigned char  video_type;

20 static unsigned long  video_num_columns;

21 static unsigned long  video_num_lines;

22 static unsigned long  video_mem_base;

23 static unsigned long  video_mem_term;

24 static unsigned long  video_size_row;

25 static unsigned char  video_page;

26 static unsigned short  video_port_reg;

27 static unsigned short  video_port_val;

28

29 void con_init(){

30   char *display_desc="????";

31   char *display_ptr;

32

33   video_num_columns=ORIG_VIDEO_COLS;

34   video_size_row=video_num_columns *2;

35   video_num_lines=ORIG_VIDEO_LINES;

36   video_page=ORIG_VIDEO_PAGE;

37

38   /* 这是一个单色显示器吗? */

39   if(ORIG_VIDEO_MODE==7){

40     //部分代码略

41   }

42   else{/* color display */

43     video_mem_base=0xb8000;

44     video_port_reg=0x3d4;

45     video_port_val=0x3d5;

46

47     if((ORIG_VIDEO_EGA_BX & 0xff)!=0x10){

48      video_type=VIDEO_TYPE_EGAC;

49      video_mem_term=0xc0000;

50      display_desc="EGAc";

51     }

52     else{

53     //部分代码略

54     }

55   }

56

57   display_ptr=((char *)video_mem_base)+video_size_row-8;

58   while(*display_desc){

59      *display_ptr++= *display_desc++;

60      *display_ptr++;

61   }

62 }

因为QEMU和Bochs虚拟机都采用了彩色模式的EGA,所以上述代码在执行初始化的过程就会走到EGA的分支。其他分支是用于支持更早期的单色显示器,已经不重要了,所以正文里的代码就把其他分支略去了。第43行代码把显存的起始位置设为0xb8000,因为已经启用了保护模式分页机制,所以这里的地址实际上是虚拟地址,尽管这个值与物理地址是相同的。第49行设置显存结束地址为0xc0000。

setup模块通过使用BIOS中断取得计算机的显卡、内存、硬盘等信息,然后把它们都存储在0x90000的位置,这里就是使用显卡信息的地方。video_num_columns指示了显示器的列数,video_num_lines指示了显示器的行数。除此之外,与显卡相关的其他信息会在后面用到时再介绍。结合上述分析可以知道,第57行~第61行的作用是把代表显示器类型的字符串打印到屏幕的右上角。因为一个字符占据两个字节,第一个字节指示字符的颜色和背景色,第二个字节是字符的ASCII值,所以display_ptr就指向了第一行往前的8个字节,这里预留了显示4个字符的宽度。

同时,main函数也要增加对con_init的调用:

1  //kernel/main.c

2  void main(void){

3   tty_init();

4   __asm__ __volatile__(

5     "loop:\n\r"

6     "jmp loop"

7     ::);

8  }

9

10

11 //kernel/chr_drv/tty_io.c

12 #include<linux/tty.h>

13

14 void tty_init(){

15   con_init();

16 }

最后一步,再把这两个新增的C文件移到chr_drv目录下,并新建makefile文件以编译这个目录。目录名是character drvier的缩写,这个目录的作用是处理字符设备。字符设备的特点是它们的输入/输出都是以字符为单位的,例如本节显示器里显示字符串的操作就是向显存中逐个复制字符。与字符设备相对应的是块设备,块设备的输入/输出都是以数据块为单位的,比如读取硬盘的动作就是以扇区为单位的,一次输入/输出会传输512B,块设备的驱动会在第6章进行介绍,这里就不再详细展开。

chr_drv目录中的makefile内容如下:

1  AR:=ar

2  LD:=ld

3  GCC:=gcc

4  CCFLAG:=-m32-I../../include-nostdinc-Wall-fomit-frame-pointer-c

5  vOBJS :=tty_io.o console.o

6

7  tty_io.o:tty_io.c

8      $(GCC) $(CCFLAG)-o$@$<

9

10 console.o:console.c

11     $(GCC) $(CCFLAG)-o$@$<

12

13 chr_drv.a: $(OBJS)

14     $(AR)rcs$@$^

15     sync

16

17 clean:

18     rm *.o

19     rm chr_drv.a

这个目录最后生成的目录文件是chr_drv.a,这是一个静态库文件。静态库文件本质是一个压缩文件,是将目标文件(.o文件)打包压缩在一起,并不会对目标文件进行链接、解析符号等操作。而真正的链接操作仍然发生在构建system文件时,所以我们还要记得在kernel目录的makefile文件中添加链接chr_drv.a的操作:

1  OBJS:=head.o main.o sched.o chr_drv/chr_drv.a

2

3  system: $(OBJS)

4   $(LD) $(LDFLAG)-e startup_32-o$@$^

5

6  ...

7

8  chr_drv/chr_drv.a:chr_drv/* .c

9   cd chr_drv;make chr_drv.a;cd..

10

11 clean:

12   rm *.o

13   rm system

14   cd chr_drv;make clean;cd..

重新编译整个目录,并且在虚拟机中运行新的linux.img,就可以看到屏幕的右上角正确地打印出了字符串“EGAc”。到这里,控制台的初始化工作就完成了。 PmrItI6icmR6R8xtSwtZugvseqFoDwUogh3/Q5ALdbeaDaWt52M6cnXPVyPoGTps

点击中间区域
呼出菜单
上一章
目录
下一章
×