main 函数解析(二)—— Linux-0.11 学习笔记(六)
main函數(shù)解析(二)——Linux-0.11 學(xué)習(xí)筆記(六)
4.6 blk_dev_init函數(shù)
void blk_dev_init(void) {int i;for (i=0 ; i<NR_REQUEST ; i++) {request[i].dev = -1; //表示空閑request[i].next = NULL;} }這里的request是一個全局的數(shù)組。
/** The request-struct contains all necessary data* to load a nr of sectors into memory*/ struct request request[NR_REQUEST]; //請求項(xiàng)數(shù)組 #define NR_REQUEST 32 ...struct request {int dev; //設(shè)備號,若為-1,則表示空閑int cmd; // 命令 READ or WRITE int errors; //操作時產(chǎn)生的錯誤次數(shù)unsigned long sector; // 起始扇區(qū)unsigned long nr_sectors; // 讀/寫扇區(qū)數(shù)char * buffer; //數(shù)據(jù)緩沖區(qū)struct task_struct * waiting; // 任務(wù)等待操作執(zhí)行完成的地方struct buffer_head * bh; // 緩沖區(qū)頭指針struct request * next; //指向下一個struct request,構(gòu)成單向鏈表 };以上的代碼要想完全理解,就牽扯到塊設(shè)備驅(qū)動程序了。
每個塊設(shè)備的當(dāng)前請求指針與請求項(xiàng)數(shù)組中該設(shè)備的請求項(xiàng)鏈表共同構(gòu)成了該設(shè)備的請求隊(duì)列。項(xiàng)與項(xiàng)之間利用字段next指針形成鏈表。因此塊設(shè)備項(xiàng)和相關(guān)的請求隊(duì)列形成如圖所示結(jié)構(gòu)。請求項(xiàng)采用數(shù)組加鏈表結(jié)構(gòu)的主要原因是為了滿足兩個目的:一是利用請求項(xiàng)的數(shù)組結(jié)構(gòu)在搜索空閑請求塊時可以進(jìn)行循環(huán)操作,搜索訪問時間復(fù)雜度為常數(shù),因此程序可以編制得很簡潔;二是為滿足電梯算法 (Elevator Algorithm) 插入請求項(xiàng)操作,因此也需要采用鏈表結(jié)構(gòu)。圖中畫出了硬盤設(shè)備當(dāng)前具有4個請求項(xiàng),軟盤設(shè)備具有1個請求項(xiàng),而虛擬盤設(shè)備目前暫時沒有讀寫請求項(xiàng)。
對于一個當(dāng)前空閑的塊設(shè)備,當(dāng) ll_rw_block()函數(shù)為其建立第一個請求項(xiàng)時,會讓該設(shè)備的當(dāng)前請求項(xiàng)指針current_request直接指向剛建立的請求項(xiàng),并且立刻調(diào)用對應(yīng)設(shè)備的請求項(xiàng)操作函數(shù)開始執(zhí)行塊設(shè)備讀寫操作。當(dāng)一個塊設(shè)備已經(jīng)有幾個請求項(xiàng)組成的鏈表存在,ll_rw_block()就會利用電梯算法,根據(jù)磁頭移動距離最小原則,把新建的請求項(xiàng)插入到鏈表適當(dāng)?shù)奈恢锰帯?/p>
以上內(nèi)容摘自趙炯博士的《Linux內(nèi)核完全剖析》。
雖然看完不甚理解,但至少明白blk_dev_init函數(shù)了。其實(shí)很簡單,就是把數(shù)組的每一項(xiàng)的設(shè)備號設(shè)為 -1,表示空閑,然后把 next 設(shè)為 NULL(因?yàn)榭臻e,所以沒有被插入鏈表)。
4.7 chr_dev_init函數(shù)
此函數(shù)實(shí)現(xiàn)為空。
void chr_dev_init(void) { }4.8 tty_init函數(shù)
void tty_init(void) {rs_init(); // 初始化串行中斷程序和串行接口1、2 con_init(); // 初始化控制臺終端 }4.8.1 rs_init函數(shù)
void rs_init(void) {set_intr_gate(0x24,rs1_interrupt); // 設(shè)置串行口1的中斷門向量set_intr_gate(0x23,rs2_interrupt); // 設(shè)置串行口2的中斷門向量init(tty_table[1].read_q.data); // 初始化串行口1,參數(shù)是端口基地址init(tty_table[2].read_q.data); // 初始化串行口2,參數(shù)是端口基地址outb(inb_p(0x21)&0xE7,0x21); // 允許8259A主片響應(yīng)IRQ3、IRQ4中斷請求, 0x21 對應(yīng) 8259A 主片的中斷屏蔽寄存器 }4.8.1.1 設(shè)置中斷門
#define set_intr_gate(n,addr) \_set_gate(&idt[n],14,0,addr)set_intr_gate(n,addr)的宏展開是_set_gate(&idt[n],14,0,addr)。
上圖是中斷門描述符的格式,根據(jù) [11:8] = 14,可以知道代碼中的14表示中斷門。
宏_set_gate(gate_addr,type,dpl,addr)用于設(shè)置門描述符。具體的分析參見 main函數(shù)解析(一)——Linux-0.11 學(xué)習(xí)筆記(五)
這個宏根據(jù)參數(shù)中的
- 門描述符類型 type
- 描述符特權(quán)級 dpl
- 中斷或異常處理過程的偏移地址地址 addr
設(shè)置位于地址 gate_addr 處的門描述符。
所以, set_intr_gate(n,addr)表示在元素idt[n](idt[]是中斷描述符表,其實(shí)是數(shù)組,一共有 256 個表項(xiàng),一個表項(xiàng)占8字節(jié)。)位置安裝一個中斷處理過程的偏移地址地址為 addr 的、特權(quán)級為0的中斷門描述符。
Linux-0.11 系統(tǒng)
- 把主片的中斷號設(shè)置為 0x20~0x27;
- 把從片的中斷號設(shè)置為 0x28~0x2f。
根據(jù)上圖可以知道,串行口1的中斷號是0x24,串行口2的中斷號是0x23;
順便提一下:使用中斷門(interrupt gate) 來構(gòu)造中斷調(diào)用機(jī)制的,當(dāng) processor 進(jìn)入 interrupt handler 執(zhí)行前, 會將 eflags 的值壓入棧中保存并且會清除 eflags.IF 標(biāo)志位,這意味著進(jìn)入中斷后不再響應(yīng)其他的可屏蔽中斷。
4.8.1.2 串口的初始化
static void init(int port) // port 是端口基地址 {... }串口初始化,說白了就是配置一些寄存器,使串口可以收發(fā)數(shù)據(jù)。
說到這個串口,內(nèi)容還挺多,不了解的一定要參考我的博文 PC 機(jī) UART(NS8250)詳解
init(tty_table[1].read_q.data);傳入的參數(shù)是tty_table[1].read_q.data,如果沒有猜錯的話,tty_table[1].read_q.data一定是端口的基地址。為了驗(yàn)證猜測,我們找找和tty_table有關(guān)的定義。
果然,在kernel\chr_drv\tty_io.c中看到了以下代碼:
struct tty_struct tty_table[] = {{...},{...{0x3f8,0,0,0,""}, /* rs 1 */{0x3f8,0,0,0,""},{0,0,0,0,""}},{...{0x2f8,0,0,0,""}, /* rs 2 */{0x2f8,0,0,0,""},{0,0,0,0,""}} };0x3f8和0x2f8正是串口1和2的端口基地址。
static void init(int port) // port 是端口基地址 {outb_p(0x80,port+3); /* set DLAB of line control reg */outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */outb_p(0x00,port+1); /* MS of divisor */outb_p(0x03,port+3); /* reset DLAB */outb_p(0x0b,port+4); /* set DTR,RTS, OUT_2 */outb_p(0x0d,port+1); /* enable all intrs but writes */(void)inb(port); /* read data port to reset things (?) */ }目前遇到的讀寫端口的宏有4個:
outb_p(value,port)—— 把value寫入端口port.
inb_p(port)——讀取端口port的值。
outb(value,port)—— 把value寫入端口port.
inb(port)——讀取端口port的值。
前2個帶延時,后2個不帶延時。
這4個宏,具體的定義和分析可以參考我的博文 main函數(shù)解析(一)——Linux-0.11 學(xué)習(xí)筆記(五)
第3行:outb_p(0x80,port+3); 把0x80寫入端口(0x3f8+3=0x3fb,即 LCR 寄存器),也就是使 DLAB=1。
第4行:outb_p(0x30,port);寫波特率因子的低字節(jié)為0x30
第5行:outb_p(0x00,port+1);寫波特率因子的高字節(jié)為0x00
所以,波特率因子為 0x0030=48。
波特率和因子之間的關(guān)系是:
公式變形一下:
所以
波特率=1.8432MHz48?16=1843200768=2400波特率=1.8432MHz48?16=1843200768=2400
第6行:outb_p(0x03,port+3);無奇偶校驗(yàn)位、8 位數(shù)據(jù)位和 1 位停止位,同時復(fù)位 DLAB;
第7行:outb_p(0x0b,port+4);若要讓 UART 的中斷請求信號能夠送到 8259A 中斷控制器,就需要把MODEM 控制寄存器 MCR 的位3(OUT2) 置位。因?yàn)樵赑C 機(jī)中,該位控制著 INTRPT 引腳到 8259A 的電路。MCR的位 1 和位 0 分別用于控制 MODEM , 當(dāng)這兩位置位時,UART的數(shù)據(jù)終端就緒引腳(DTR)和請求發(fā)送引腳(RTS)輸出有效。
第8行:outb_p(0x0d,port+1);寫IER(Interrupt enable register,中斷允許寄存器 )
0x0d = 1101b
[3] = 1 允許modem狀態(tài)中斷;
[2] = 1 允許接收器線路狀態(tài)中斷;
[1] = 0 禁止發(fā)送保持寄存器空中斷;
[0] = 1 允許接收到數(shù)據(jù)中斷;
第9行:(void)inb(port);讀接收緩存寄存器 ,以進(jìn)行復(fù)位??其實(shí)這句話我也不清楚是否必要,先在這里留個疑問。
4.8.2 con_init()函數(shù)
con_init()函數(shù)(在文件linux/kernel/console.c中)首先根據(jù) setup.s 程序取得的系統(tǒng)硬件參數(shù)初始化幾個本函數(shù)專用的靜態(tài)全局變量。然后根據(jù)顯示卡模式(單色還是彩色)和顯示卡類型(EGA/VGA、CGA、MDA 等) 分別設(shè)置顯示內(nèi)存起始位置以及顯示索引寄存器和顯示數(shù)值寄存器的端口號。最后設(shè)置鍵盤中斷陷阱描述符并打開對鍵盤中斷的屏蔽位。
顯卡的歷史
磨刀不誤砍柴工,在理解這個函數(shù)之前,不妨先了解一下顯卡的歷史。
顯卡的前身
MDA
最早的顯卡稱為顯示適配器,在“黑底白字”的DOS年代,對顯示的要求是極低的。最早的顯示類型是 MDA(Monochrome Display Adapter),只能區(qū)別出黑白兩色。早期的8080、8088,一直到80286都是使用這種類型的顯示適配器。它的功能極為簡單,一般集成 16KB 顯存,是不為人關(guān)注的電腦配件。
CGA
到了286時,PC上出現(xiàn)了一些和圖形相關(guān)的軟件,因此出現(xiàn)了一種四色適配器,只能識別三原色和黑白。由于這是第一種彩色的顯示適配器,所以稱為 CGA(Color Graphics Adaptor,彩色圖形適配器)。CGA 時代對顯卡的要求已經(jīng)大幅度提高,但是當(dāng)時的制作工藝仍然遠(yuǎn)遠(yuǎn)高于顯卡芯片的需求,因此 CGA 顯示適配器依舊被整合在主板上,以一塊單芯片的方式來實(shí)現(xiàn),所以“顯卡”尚未誕生。
EGA
CGA 的分辨率太低,于是又有了 EGA(Enhanced Graphics Adapter,增強(qiáng)圖形適配器)。在顯示性能方面(顏色和分辨率),EGA 介于 CGA 和 VGA 之間,可以在高達(dá)640x350的分辯率下達(dá)到16色。
顯卡的誕生與換代
以上MDA、CGA、EGA 三種標(biāo)準(zhǔn)都是以 TTL 數(shù)字信號輸出,而之后的 VGA 標(biāo)準(zhǔn)采用模擬信號輸出,因而其彩色顯示能力大大加強(qiáng),原則上可以顯示無窮多的顏色。VGA 最初代表分辨率,在個人電腦的啟蒙時代,能夠輸出 VGA(640×480)這樣的分辨率并不是一件易事,VGA 標(biāo)準(zhǔn)的出現(xiàn)對顯示輸出設(shè)備首次提出了較高的要求,于是催生了 VGA Card,顯卡正式誕生!
● 第一代顯卡:VGA Card,支持256色顯示,1988年
● 第二代顯卡:Graphics Card,支持Windows圖形加速,1991年
● 第三代顯卡:Video Card,支持視頻加速,1994年
● 第四代顯卡:3D Accelerator Card,支持3D加速,1994年
● 第五代顯卡:GPU圖形處理器,支持硬件 T&L(Transform and Lighting,多邊形轉(zhuǎn)換與光源處理),1999年
● 現(xiàn)代和未來顯卡:GPGPU(General-purpose computing on graphics processing units,簡稱 GPGPU 或 GP2U)通用計(jì)算圖形處理器,支持幾何著色、物理加速、高清解碼、科學(xué)計(jì)算……
和顯示有關(guān)的靜態(tài)全局變量的初始化
void con_init(void) {register unsigned char a;// 定義寄存器變量 a,該變量將被保存在一個寄存器中,以便于高效訪問和操作。若想指定存放的寄存器(如 eax) ,則可寫成 register unsigned char a asm("ax");char *display_desc = "????";char *display_ptr;video_num_columns = ORIG_VIDEO_COLS;video_size_row = video_num_columns * 2;video_num_lines = ORIG_VIDEO_LINES; //每屏25行video_page = ORIG_VIDEO_PAGE; // 我覺得有點(diǎn)問題video_erase_char = 0x0720; // 擦除字符(0x20是空格,0x07是屬性)...在文件linux/kernel/console.c中有宏定義:
#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)右邊的地址,其實(shí)對應(yīng) setup.s 中取得的系統(tǒng)硬件參數(shù)的存放位置。如果忘了,可以參考我的博文 setup.s 分析—— Linux-0.11 學(xué)習(xí)筆記(二)
第9行:video_num_columns = (((*(unsigned short *)0x90006) & 0xff00) >> 8);
對比匯編代碼,也就是把AH的值(字符列數(shù))賦給全局變量video_num_columns ;
`setup.s中相關(guān)代碼如下
! 獲取顯示卡當(dāng)前的顯示模式! 調(diào)用 BIOS 中斷 0x10,功能號 ah = 0x0f! 返回: ah=字符列數(shù); al=顯示模式;bh=當(dāng)前顯示頁。! ds = 0x9000! 0x90004(l個字)存放當(dāng)前頁;0x90006(1字節(jié))存放顯示模式;0x90007(1字節(jié))存放字符列數(shù)。mov ah,#0x0fint 0x10mov [4],bx ! bh = 當(dāng)前顯示頁mov [6],ax ! al = 顯示模式, ah = 字符列數(shù)(窗口寬度)第10行:video_size_row = video_num_columns * 2;算出每行字符需使用的字節(jié)數(shù)。
第12行:video_page = (*(unsigned short *)0x90004);
注意,video_page是static unsigned char類型。
根據(jù)匯編代碼第8行,應(yīng)該是0x90005處的一個字節(jié)存放當(dāng)前活動頁碼,所以我認(rèn)為第12行應(yīng)該改為:
video_page = (*(unsigned char *)0x90005);在所有源碼文件中搜了一波發(fā)現(xiàn)video_page這個變量沒有被用到,好吧,暫且不管,接著往下看。
顯示模式
#define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff)
根據(jù)上面匯編代碼的第9行,ORIG_VIDEO_MODE中的值是顯示模式。
其取值對應(yīng)的含義如下表。
| 0 | text | 40x25 | 8x8* | 16/8 (shades) | CGA,EGA | b800 | Composite |
| 1 | text | 40x25 | 8x8* | 16/8 | CGA,EGA | b800 | Comp,RGB,Enh |
| 2 | text | 80x25 | 8x8* | 16/8 (shades) | CGA,EGA | b800 | Composite |
| 3 | text | 80x25 | 8x8* | 16/8 | CGA,EGA | b800 | Comp,RGB,Enh |
| 4 | graphic | 320x200 | 8x8 | 4 | CGA,EGA | b800 | Comp,RGB,Enh |
| 5 | graphic | 320x200 | 8x8 | 4 (shades) | CGA,EGA | b800 | Composite |
| 6 | graphic | 640x200 | 8x8 | 2 | CGA,EGA | b800 | Comp,RGB,Enh |
| 7 | text | 80x25 | 9x14* | 3 (b/w/bold) | MDA,EGA | b000 | TTL Mono |
| 8,9,0aH | PCjr modes | ||||||
| 0bH,0cH | (reserved; internal to EGA BIOS) | ||||||
| 0dH | graphic | 320x200 | 8x8 | 16 | EGA,VGA | a000 | Enh,Anlg |
| 0eH | graphic | 640x200 | 8x8 | 16 | EGA,VGA | a000 | Enh,Anlg |
| 0fH | graphic | 640x350 | 8x14 | 3 (b/w/bold) | EGA,VGA | a000 | Enh,Anlg,Mono |
| 10H | graphic | 640x350 | 8x14 | 4 or 16 | EGA,VGA | a000 | Enh,Anlg |
| 11H | graphic | 640x480 | 8x16 | 2 | VGA | a000 | Anlg |
| 12H | graphic | 640x480 | 8x16 | 16 | VGA | a000 | Anlg |
| 13H | graphic | 640x480 | 8x16 | 256 | VGA | a000 | Anlg |
Notes: With EGA, VGA, and PCjr you can add 80H to AL to initialize a video mode without clearing the screen.
*The character cell size for modes 0-3 and 7 varies, depending on the hardware. On modes 0-3: CGA=8x8, EGA=8x14, and VGA=9x16. For mode 7, MDPA and EGA=9x14, VGA=9x16, LCD=8x8.
if (ORIG_VIDEO_MODE == 7) // 等于7說明是單色{video_mem_start = 0xb0000; // 設(shè)置內(nèi)存起始地址video_port_reg = 0x3b4; // 設(shè)置索引寄存器端口video_port_val = 0x3b5; // 設(shè)置數(shù)據(jù)寄存器端口// 注意,這里使用了 BL 在調(diào)用中斷 int 0x10 前后是否被改變的方法來判斷卡的類型。if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10){ // BL之前設(shè)置的值是0x10,調(diào)用中斷后發(fā)生改變,則說明是EGA //#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode*/ video_type = VIDEO_TYPE_EGAM; // 設(shè)置顯示類型video_mem_end = 0xb8000; // 設(shè)置顯示內(nèi)存末端地址display_desc = "EGAm"; // 設(shè)置顯示描述字符串}else // 沒有改變說明是MDA{video_type = VIDEO_TYPE_MDA;video_mem_end = 0xb2000;display_desc = "*MDA";}}else // 說明是彩色 {video_mem_start = 0xb8000; // 設(shè)置內(nèi)存起始地址video_port_reg = 0x3d4; // 設(shè)置索引寄存器端口video_port_val = 0x3d5; // 設(shè)置數(shù)據(jù)寄存器端口if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10){video_type = VIDEO_TYPE_EGAC; // 說明是EGA或者VGA顯卡video_mem_end = 0xbc000; // 設(shè)置顯示內(nèi)存末端地址display_desc = "EGAc"; // EGA彩色}else{video_type = VIDEO_TYPE_CGA; // 設(shè)置顯示類型為CGAvideo_mem_end = 0xba000;display_desc = "*CGA";}}setup.s中相關(guān)代碼是:
! 檢查顯示方式(EGA/VGA)并獲取參數(shù)。! 調(diào)用 BIOS 中斷 0x10,功能號: ah = 0xl2,子功能號: bl = 0xl0! 返回:bh=顯示狀態(tài)。 0x00-彩色模式,I/O 端口=0x3dX! 0x01-單色模式,I/O 端口=0x3bX! bl = 安裝的顯示內(nèi)存。0x00 - 64k! 0x01 - 128k! 0x02 - 192k! 0x03 - 256k! cx = 顯示卡特性參數(shù)。!mov ah,#0x12 ! 功能號mov bl,#0x10 ! 子功能號int 0x10mov [8],ax ! 我也不知道這個是什么(╯︵╰)mov [10],bx ! bh=顯示狀態(tài)(單色模式/彩色模式),bl=已安裝的顯存大小mov [12],cx ! ch=特性連接器比特位信息,cl=視頻開關(guān)設(shè)置信息注意,C代碼使用了 BL 在調(diào)用中斷 int 0x10 前后是否被改變的方法來判斷卡的類型。BL在調(diào)用中斷之前被賦值為 0x10 ,在調(diào)用中斷后,其值可能不變(CGA 或 MDA),也可能變?yōu)?~3中的一個(EGA 或 VGA)。
在屏幕右上角顯示顯卡類型
下面的代碼作用是在屏幕右上角顯示描述字符串。采用的方法是直接將字符串寫到顯示內(nèi)存的相應(yīng)位置處。首先將顯示指針display_ptr指到屏幕第1行最右端起第4個字符處(每個字符需 2 個字節(jié),因此減 8 ) ,然后循環(huán)復(fù)制字符串的字符。
display_ptr = ((char *)video_mem_start) + video_size_row - 8;while (*display_desc) // 循環(huán)能停止是因?yàn)樽址┪驳?#39;\0'{*display_ptr++ = *display_desc++; // 復(fù)制字符display_ptr++; // 跳過屬性字節(jié)}在我的實(shí)驗(yàn)環(huán)境調(diào)試,截圖如下:
和滾屏有關(guān)的變量
初始化用于滾屏的變量(主要用于EGA/VGA):
/* Initialize the variables used for scrolling (mostly EGA/VGA) */origin = video_mem_start; // 滾屏起始顯存地址scr_end = video_mem_start + video_num_lines * video_size_row;// 結(jié)束地址top = 0; // 最頂端行號bottom = video_num_lines; // 最底端行號 #define ORIG_X (*(unsigned char *)0x90000) // 列號 #define ORIG_Y (*(unsigned char *)0x90001) // 行號 ...// 初始化當(dāng)前光標(biāo)所在位置(x,y)和光標(biāo)對應(yīng)的顯存位置 pos gotoxy(ORIG_X,ORIG_Y);第1~2行對應(yīng)的匯編代碼(setup.s)是
mov ax,#INITSEG !INITSEG = 0x9000mov ds,ax ! ds = 0x9000mov ah,#0x03 ! 功能號=3,獲取光標(biāo)的位置xor bh,bh ! bh = 頁號 = 0(輸入)int 0x10 ! 輸出: DH=行號,DL=列號mov [0],dx ! 保存光標(biāo)的行號和列號到 0x90000,共占2字節(jié).gotoxy函數(shù)(kernel\chr_drv\console.c)的實(shí)現(xiàn)如下:
/* NOTE! gotoxy thinks x==video_num_columns is ok */ 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; // 記錄下當(dāng)前光標(biāo)所在的列y=new_y; // 記錄下當(dāng)前光標(biāo)所在的行pos=origin + y*video_size_row + (x<<1);// 記錄當(dāng)前光標(biāo)所在位置對應(yīng)的顯存地址。我調(diào)試時,video_size_row = 160 }第4行:判斷參數(shù)是否合法。在我的實(shí)驗(yàn)環(huán)境中,video_num_lines = 25,即new_y的取值是[0,24];video_num_columns = 80,即new_x的取值是[0,80],為什么可以等于80呢?目前還不知道,我想作者這樣寫肯定有他的道理,后面多留個心。
和顯示有關(guān)的代碼就暫時結(jié)束了。后面是關(guān)于鍵盤的。
允許鍵盤工作
set_trap_gate(0x21,&keyboard_interrupt); //安裝陷阱門,鍵盤的中斷號是0x21, 對應(yīng)8259A主片的 IRQ1 // 已經(jīng)反復(fù)強(qiáng)調(diào), Linux-0.11 系統(tǒng)把主片的中斷號設(shè)置為 `0x20~0x27`; // 把從片的中斷號設(shè)置為 `0x28~0x2f`。outb_p(inb_p(0x21)&0xfd,0x21); // 允許鍵盤中斷a=inb_p(0x61); // 讀取鍵盤端口 0x61(8255A端口PB)到 a(之前定義的寄存器變量)outb_p(a|0x80,0x61); // b7置位,禁止鍵盤工作outb(a,0x61); // 再允許鍵盤工作,用以復(fù)位鍵盤第4行:0x21是 8259A 主片命令字OCW1的端口地址,(注意:不是第1行的那個0x21)用于對其中斷屏蔽寄存器 IMR 進(jìn)行讀/寫操作。
寫到這里,盡管篇幅較長,可是才分析了 2 個函數(shù)。
blk_dev_init();chr_dev_init(); //實(shí)現(xiàn)為空tty_init();數(shù)了數(shù),距main函數(shù)結(jié)束,還有10多個函數(shù)呢……好了,今天就到這里,明日繼續(xù)精進(jìn)。
參考資料
[0]《Linux內(nèi)核完全剖析》(趙炯,機(jī)械工業(yè)出版社,2006)
[1] https://blog.csdn.net/mao0514/article/details/24730097
總結(jié)
以上是生活随笔為你收集整理的main 函数解析(二)—— Linux-0.11 学习笔记(六)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 家用工具套装_家居工具一:成为家居维修达
- 下一篇: 关于体育的python毕业设计_Pyth