linux-0.11分析:init文件 main.c的第五个初始化函数tty_init()第七篇随笔
5、第五个初始化函数,tty_init()
参考 [github这个博主的 厉害][ https://github.com/sunym1993/flash-linux0.11-talk ]
先看看这个tty_init()
的这段代码:
kernel文件 -> chr_drv文件 ->tty_io.c
void tty_init(void) { rs_init(); con_init(); }
可以看到这两个函数调用了rs_init()和con_init()
这两个函数:
第一个方法:rs_init()
kernel文件 -> chr_drv文件 ->serial.c
void rs_init(void) { set_intr_gate(0x24,rs1_interrupt); set_intr_gate(0x23,rs2_interrupt); init(tty_table[1].read_q.data); init(tty_table[2].read_q.data); outb(inb_p(0x21)&0xE7,0x21); }
可以看出它里面又调用了三个函数:set_intr_gate、init、outb
,
可以看到第一个set_intr_gate很熟悉,就是在注册中断信息,注册了24(rs1_interrupt),23(rs2_interrupt)中断信息
set_intr_gate()
的
#define set_intr_gate(n,addr) \ _set_gate(&idt[n],14,0,addr)
init()
,传入的就是tty_table
这个结构体传入的端口
static void init(int port) { outb_p(0x80,port+3); /* 设置线路控制寄存器的DLAB */ outb_p(0x30,port); /* 除数的倒数(48->2400 bps) */ outb_p(0x00,port+1); /* 除数的/*MS */ outb_p(0x03,port+3); /* 重置DLAB */ outb_p(0x0b,port+4); /* 设置DTR、RTS和OUT_ 2 */ outb_p(0x0d,port+1); /* 启用除写入之外的所有Intr */ (void)inb(port); /* 读取数据端口以重置内容(?) */ }
这里就是通过outb_p()
函数设置了一些东西,具体我也不是很了解
第二个方法:con_init()
这个方法比较长,我们一部分一部分来看
kernel文件 -> chr_drv文件 ->console.c
void con_init(void) { register unsigned char a; char *display_desc = "????"; char *display_ptr; video_num_columns = ORIG_VIDEO_COLS; //(((*(unsigned short *)0x90006) & 0xff00) >> 8) video_size_row = video_num_columns * 2; video_num_lines = ORIG_VIDEO_LINES; video_page = ORIG_VIDEO_PAGE; video_erase_char = 0x0720; if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */ { video_mem_start = 0xb0000; video_port_reg = 0x3b4; video_port_val = 0x3b5; if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) { video_type = VIDEO_TYPE_EGAM; video_mem_end = 0xb8000; display_desc = "EGAm"; } else { video_type = VIDEO_TYPE_MDA; video_mem_end = 0xb2000; display_desc = "*MDA"; } } else /* If not, it is color. */ { video_mem_start = 0xb8000; video_port_reg = 0x3d4; video_port_val = 0x3d5; if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) { video_type = VIDEO_TYPE_EGAC; video_mem_end = 0xbc000; display_desc = "EGAc"; } else { video_type = VIDEO_TYPE_CGA; video_mem_end = 0xba000; display_desc = "*CGA"; } } /* Let the user known what kind of display driver we are using */ display_ptr = ((char *)video_mem_start) + video_size_row - 8; while (*display_desc) { *display_ptr++ = *display_desc++; display_ptr++; } /* Initialize the variables used for scrolling (mostly EGA/VGA) */ origin = video_mem_start; scr_end = video_mem_start + video_num_lines * video_size_row; top = 0; bottom = video_num_lines; gotoxy(ORIG_X,ORIG_Y); set_trap_gate(0x21,&keyboard_interrupt); outb_p(inb_p(0x21)&0xfd,0x21); a=inb_p(0x61); outb_p(a|0x80,0x61); outb(a,0x61); }
第一部分:
首先先看看这个文件上面的一些define:提取对下面有用的
#define ORIG_X (*(unsigned char *)0x90000) #define ORIG_Y (*(unsigned char *)0x90001) #define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004) #define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff) #define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8) #define ORIG_VIDEO_LINES (25) #define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008) #define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a) #define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c) #define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ #define VIDEO_TYPE_CGA 0x11 /* CGA Display */ #define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ #define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */ #define NPAR 16
...... register unsigned char a; char *display_desc = "????"; char *display_ptr; video_num_columns = ORIG_VIDEO_COLS; //这个是在设置为,显示模式 // video_num_columns = (((*(unsigned short *)0x90006) & 0xff00) >> 8) video_size_row = video_num_columns * 2; video_num_lines = ORIG_VIDEO_LINES; //是在设置行数 // video_num_lines = (25) video_page = ORIG_VIDEO_PAGE; //在设置显示页面 // video_page = (*(unsigned short *)0x90004) video_erase_char = 0x0720; ......
这是就是在初始化一些东西,video_xxx_xx都是一些全局变量,后面的都是一些定义好的数据在这个文件的上面
还记得在setup.s阶段有一个存储设备相关信息表
地址 | 字节 | 存储的东西 |
---|---|---|
0x90000 | 1 | 光标列号 |
0x90001 | 1 | 光标行号 |
0x90002 | 2 | 内存大小 |
0x90004 | 2 | 显示页面 |
0x90006 | 1 | 显示模式 |
0x90007 | 1 | 窗口宽度 |
0x90008 | 2 | 配置参数 |
0x9000A | 2 | 配置参数 |
0x9000C | 2 | 配置参数 |
0x90080 | 16 | hd0硬盘信息 |
0x90010 | 16 | hd1硬盘信息 |
第二部分:
if (ORIG_VIDEO_MODE == 7) /* 这是单色显示器吗 */ { video_mem_start = 0xb0000; video_port_reg = 0x3b4; video_port_val = 0x3b5; if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) { video_type = VIDEO_TYPE_EGAM; 0x20 video_mem_end = 0xb8000; display_desc = "EGAm"; } else { video_type = VIDEO_TYPE_MDA; 0x10 video_mem_end = 0xb2000; display_desc = "*MDA"; } } else /* 它配置了颜色. */ { video_mem_start = 0xb8000; video_port_reg = 0x3d4; video_port_val = 0x3d5; if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) { video_type = VIDEO_TYPE_EGAC; 0x21 video_mem_end = 0xbc000; display_desc = "EGAc"; } else { video_type = VIDEO_TYPE_CGA; 0x11 video_mem_end = 0xba000; display_desc = "*CGA"; } }
基本上可以看出就是一些不同if else判断设置一些东西;设置:video_mem_start、video_port_reg、video_port_val、video_type、video_mem_end、display_desc
这些参数
总共有四个分支
第一个分支结果 第二个分支结果 video_mem_start = 0xb0000; video_mem_start = 0xb0000; video_port_reg = 0x3b4; video_port_reg = 0x3b4; video_port_val = 0x3d5; video_port_val = 0x3d5; video_type = 0x20; video_type = 0x10; video_mem_end = 0xb8000; video_mem_end = 0xb2000; display_desc = "EGAm"; display_desc = "*MDA"; 第三个分支结果 第四个分支结果 video_mem_start = 0xb8000; video_mem_start = 0xb8000; video_port_reg = 0x3d4; video_port_reg = 0x3d4; video_port_val = 0x3b5; video_port_val = 0x3b5; video_type = 0x21; video_type = 0x11; video_mem_end = 0xbc000; video_mem_end = 0xba000; display_desc = "EGAc"; display_desc = "*CGA";
第三部分:
/*让用户知道我们使用的是哪种显示驱动程序 */ display_ptr = ((char *)video_mem_start) + video_size_row - 8; while (*display_desc) { *display_ptr++ = *display_desc++; display_ptr++; }
这是通过上面的结果去判断是那种显示的驱动
第四部分:
/* 初始化用于滚动的变量(主要是EGA/VGA) */ origin = video_mem_start; scr_end = video_mem_start + video_num_lines * video_size_row; top = 0; bottom = video_num_lines; gotoxy(ORIG_X,ORIG_Y); // ORIG_X (*(unsigned char *)0x90000)光标列号 // ORIG_Y (*(unsigned char *)0x90001)光标行号 set_trap_gate(0x21,&keyboard_interrupt); outb_p(inb_p(0x21)&0xfd,0x21); a=inb_p(0x61); outb_p(a|0x80,0x61); outb(a,0x61);
这是在设置光标位置,开启0x21键盘中断
这里学过汇编就应该知道,一般开起int 0x21中断后,就会根据不同寄存器的值跳转到中断位置进行操作
gotoxy(ORIG_X,ORIG_Y);
就在进行操了,看看这个函数
static unsigned long pos; static unsigned long x,y; static inline void gotoxy(unsigned int new_x,unsigned int new_y) { if (new_x > video_num_columns || new_y >= video_num_lines) return; x=new_x; y=new_y; pos=origin + y*video_size_row + (x<<1); }
这里先判断是否超过行或者列
在用x,y,pos
来保存,x:列号,y:行号,pos:对应的内存地址
然后就是开起0x21中断:
具体过程如下:
这个中断在这个文件下的keyboard.S中,比较长就不一一列举了
_keyboard_interrupt: ...... call _do_tty_interrupt ......
在kernel文件 -> chr_drv文件 ->tty_io.c
void do_tty_interrupt(int tty) { copy_to_cooked(tty_table+tty); }
void copy_to_cooked(struct tty_struct * tty) { signed char c; ...... tty->write(tty); ..... wake_up(&tty->secondary.proc_list); }
在kernel文件 -> chr_drv文件 ->console.c
void con_write(struct tty_struct * tty) { int nr; char c; ...... __asm__("movb _attr,%%ah\n\t" "movw %%ax,%1\n\t" ::"a" (c),"m" (*(short *)pos) :"ax"); pos += 2; x++; ......
可以看到这段内联汇编,在进行光标的移动操作;省略的一部分就是一边界操作:都在console.c的函数
static void lf(void) //换行 static void ri(void) //退行 ......
就不一一列举了
到这里就结束了这个tty_init()
初始化;这个时候键盘的功能解决大致完成了,就看通过键盘输入来进行中断了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)