java开发操作系统:启动任意多控制台窗口
當前我們的系統已經能夠實現多個控制臺窗口,要想生成兩個以上的控制臺窗口,只需在窗口生成時的for循環處,增加循環次數即可。現在我們系統啟動時,一下子會看到兩個控制臺窗口,這看起來太詭異了。
對于windows來說,我們完全可以在一個控制臺窗口中運行命令start cmd.exe來啟動一個新的控制臺窗口,本節我們將模仿該功能,使得我們能在一個控制臺窗口中,通過命令來啟動新的控制臺窗口。
在完成這個復雜的功能前,我們先實現一個稍微簡單點的功能練練手。我們先實現通過按鍵shift+w 來啟動一個新的控制臺窗口。先在kernel.asm中增加段選擇子的數目:
[SECTION .gdt]; 段基址 段界限 屬性 LABEL_GDT:.... %rep 30 Descriptor 0, 0, 0 %endrep然后在CMain主入口函數中添加如下代碼:
void CMain(void) {....struct SHEET *sht_cons[2];int console_count = 0;sht_cons[0] = launch_console(0);console_count++;//sheet_slide(shtctl, sht_cons[1], 156, 176);sheet_slide(shtctl, sht_cons[0], 8, 2);// sheet_updown(shtctl, sht_cons[1], 1);sheet_updown(shtctl, sht_cons[0], 2);first_task_cons_selector = task_cons[0]->sel; ....for(;;) {if (fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) +fifo8_status(&timerinfo) == 0) {io_sti();} else if(fifo8_status(&keyinfo) != 0){io_sti();data = fifo8_get(&keyinfo);....if (key_shift != 0 && data == 0x11) {sht_cons[console_count] = launch_console(console_count);sheet_slide(shtctl, sht_cons[console_count], 156, 176);sheet_updown(shtctl, sht_cons[console_count], 1);console_count++;}} }.... }此外我們在cmd_hlt函數中再做一些修改:
void cmd_hlt() {....int code_seg = 21 + task->sel - first_task_cons_selector;int mem_seg = 30 + task->sel - first_task_cons_selector;.... }first_task_cons_selector 是第一個控制臺進程對應的段選擇子,做上面的修改后,在不同的控制臺中執行hlt命令啟動用戶程序時,不同的用戶程序的代碼段和數據段就會指向不同的段,這樣當我們后面實現多控制臺功能后,通過不同控制臺啟動的用戶程序就不會發送段沖突。上面代碼完成后,加載內核,系統啟動后,同時按下shift鍵和w鍵,可以看到有一個新的控制臺出現在界面上:
接下來,我們要擴展該功能,使得系統能夠生成任意多個控制臺進程。當控制臺進程可以無限多時,我們原來用于存儲進程的數組和控制臺圖層窗口的數組就沒有用了,因此代碼要做相應改動,在write_vga_desktop.c中,去掉task_cons數組,換成一個執行控制臺進程的指針:
//static struct TASK *task_cons[2]; int first_task_cons_selector = 0; static struct TASK *task_main = 0, *current_console_task;我們會生成多個控制臺進程,而在任何時候只會有一個控制臺進程處于激活狀態,這個被激活的進程將會用current_console_task這個指針變量來代替。當我們點擊tab鍵時,要實現主進程和激活控制臺進程的切換,而且當有按鍵信息輸入時,按鍵內容要傳遞給當前正在處于激活狀態的控制臺進程,因此相關代碼的修改如下:
void CMain(void) {....struct SHEET* sht_cons;//*sht_cons[2];int console_count = 0;sht_cons = launch_console(0);console_count++;//sheet_slide(shtctl, sht_cons[1], 156, 176);sheet_slide(shtctl, sht_cons, 8, 2);// sheet_updown(shtctl, sht_cons[1], 1);sheet_updown(shtctl, sht_cons, 2);// first_task_cons_selector = task_cons[0]->sel; ....for(;;) {....if (data == 0x0f) {int msg = -1;if (key_to == 0) {key_to = 1;if (current_console == 1) {current_console = 0;} else {current_console = 1;}make_wtitle8(shtctl, shtMsgBox,"task_a", 0);//change heremake_wtitle8(shtctl, current_console_task->sht, "console", 1);set_cursor(shtctl, shtMsgBox, cursor_x, 28 ,COL8_FFFFFF); msg = PROC_RESUME;} else {key_to = 0;make_wtitle8(shtctl, shtMsgBox, "task_a",1);make_wtitle8(shtctl, current_console_task->sht, "console", 0);msg = PROC_PAUSE;} sheet_refresh(shtctl, shtMsgBox, 0, 0, shtMsgBox->bxsize, 21);//change heresheet_refresh(shtctl, current_console_task->sht, 0, 0, current_console_task->sht->bxsize, 21);//send_message(task_a, task_cons[current_console], msg);}if (key_to == 0) {if (transferScanCode(data) != 0 && cursor_x < 144) {set_cursor(shtctl, shtMsgBox, cursor_x,28 ,COL8_FFFFFF);char c = transferScanCode(data);char buf[2] = {c, 0};showString(shtctl, shtMsgBox, cursor_x, 28, COL8_000000, buf);cursor_x += 8;stop_task_A = 1;set_cursor(shtctl, shtMsgBox, cursor_x, 28, cursor_c);} } else if (isSpecialKey(data) == 0) {//change herefifo8_put(&(current_console_task->fifo), data);if (fifo8_status(&keyinfo) == 0) { task_sleep(task_a);}}}....} }我們修改主入口處代碼,讓系統啟動時只創建一個控制臺進程,同時當用戶按下tab鍵時,讓主進程與當前正處于激活狀態的控制臺窗口進行切換,并且當用戶點擊鍵盤時,鍵盤信息會被主進程先拿到,然后主進程再把按鍵消息通過指針current_console_task,傳遞給當前活躍的控制臺進程。
現在問題是,當如果系統上有多個控制臺進程時,我們如何決定哪一個是激活的呢,由于控制臺窗口可能會有多個,因此我們不能像以前一樣根據tab按鍵來固定的切換不同進程,這次我們修改為,用戶用鼠標點擊哪個控制臺窗口,那么它就轉換為激活的控制臺進程,控制鼠標動作的函數是show_mouse_info,因此我們要對它的代碼加以修改:
void show_mouse_info(struct SHTCTL *shtctl, struct SHEET *sht_back,struct SHEET *sht_mouse) {....if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {//change heremake_wtitle8(shtctl, current_console_task->sht, "console", 0);sheet_refresh(shtctl,current_console_task->sht, 0, 0, sht->bxsize, sht->bysize);current_console_task = sht->task;make_wtitle8(shtctl, current_console_task->sht, "console", 1);sheet_refresh(shtctl, sht, 0, 0, sht->bxsize, sht->bysize);}....}當鼠標挪動并點擊時,上面的代碼會被調用。代碼先查詢當前鼠標落入哪個窗口中,它先把上一個幾乎窗口的標題欄由藍色轉換為灰色,接著把當前鼠標點擊的窗口標題欄設置成藍色,并把current_console_task指針指向當前圖層所對應的進程對象。要完成上面的代碼,我們需要修改一下TASK和SHEET這兩個結構體的設置,一是在TASK結構體中增加一個圖層對象,二是在SHEET結構體中增加一個TASK對象,打開multi_task.h和win_sheet.h做如下修改:
struct TASK {int sel, flags;int priority;int level;struct FIFO8 fifo;struct TSS32 tss;struct CONSOLE console;struct Buffer *pTaskBuffer;//change herestruct SHEET *sht; };struct SHEET {unsigned char *buf;int bxsize, bysize, vx0, vy0, col_inv, height, flags;struct TASK *task; };我們在啟動控制臺進程時,也要做相應修改,當內核創建一個控制臺進程時,它會調用launch_console函數,這個函數執行時,不但要像原來一樣啟動一個新的控制臺進程,還需要把current_console_task指向它,并且把原來的活躍進程設置成非激活狀態,代碼修改如下:
struct SHEET* launch_console(int i) {....//change hereif (i > 0) {make_window8(shtctl, sht_cons, "console", 1);} else {make_window8(shtctl, sht_cons, "console", 0);}//change heretask_console->sht = sht_cons;sht_cons->task = task_console;//inactive last console windowif (current_console_task != 0) {make_wtitle8(shtctl, current_console_task->sht, "console", 0);sheet_refresh(shtctl, current_console_task->sht, 0, 0, current_console_task->sht->bxsize, current_console_task->sht->bysize);}current_console_task = task_console;....// task_cons[i] = task_console;.... }如果創建新的控制臺進程時,current_console_task不為空,那表明當前進程并非第一個,因此它會把原來激活的進程設置成非激活狀態,并把當前新建進程設置成激活狀態。
由于當前控制臺進程變多,在控制臺中啟動用戶進程時,需要做相應修改:
void cmd_hlt() { .... //change here select is multiply of 8, divided by 8 get the original valueint code_seg = 21 + (task->sel - first_task_cons_selector) / 8;int mem_seg = 30 + (task->sel - first_task_cons_selector) / 8;//22;.... }啟動用戶進程時,需要為其分配代碼段和內存段描述符,不同控制臺啟動的用戶進程,必須對應不同的描述符,我們用當前進程的段描述符減去第一個進程的段描述符,然后分別加上21和30后得到的結果作為代碼段描述符和內存段描述符的下標。其中除以8是因為,我們在指定描述符下標時,需要左移3位,也就相當于乘以8,因此求差值時要除以8,這樣才能得到當前進程與第一個控制臺進程間的間隔。
上面代碼完成后,加載內核,運行效果如下: 
 
系統每次通過shift+w創建一個新控制臺進程時,新窗口會自動變為激活狀態,你用鼠標點擊其中某個控制臺窗口后,被點擊的控制臺會自動轉換成激活態。雖然我們現在控制臺進程的數量可以無限制增長,但我們在multi_task.h中定義了進程的最大數量:
#define MAX_TASKS 10也就是內核當前支持的進程數量最大是10個。
當控制臺窗口可以一次生成多個后,如何把他們關閉就變得很有必要。接下來我們實現把控制臺窗口關閉的功能,現在global_define.h中修改一下CONSOLE結構體的定義:
struct CONSOLE {struct SHEET *sht;int cur_x, cur_y, cur_c;char s[2];struct TIMER *timer;//change herechar *cmdline; };增加cmdline指針的目的是,在窗口關閉后能把它對應的內存給釋放掉。接著我們增加幾個關于結構體關閉及相關內存釋放的功能:
//change here add console closing function void close_constask(struct TASK *task) {task_sleep(task);//problemmemman_free_4k(memman, task->cons_stack, 64 * 1024);memman_free(memman, (int)task->fifo.buf, 128);memman_free(memman, (int)task->console.cmdline, 30);task->flags = 0;current_console_task = 0; }void close_console(struct SHEET *sht) {struct TASK *task = sht->task;timer_free(task->console.timer);memman_free_4k(memman, (int)sht->buf, 256 * 165);sheet_free(shtctl, sht);close_constask(task); }void cmd_exit(struct TASK *cons_task) {io_cli();//send msg to keyboad queue of main processfifo8_put(&keyinfo, 255);io_sti(); }我們要實現的功能之一是,在控制臺輸入命令”exit”時,控制臺能自我關閉,當控制臺執行命令exit時,它會調用函數cmd_exit, 在該函數的實現中,它向主進程發送了一個特別的命令255,這個數值告訴主進程把當前激活的控制臺進程給關閉掉,于是主進程受到這個消息后,會調用close_console把對應的控制臺進程給關閉,之所以要繞個彎,發消息給主進程來關閉激活的控制臺進程,是因為在close_constask實現中調用了task_sleep函數,如果由控制臺進程來調用close_constask的話,因為它自己把自己給休眠了,那么后面的代碼就無法執行了,因此我們對主進程的代碼也做相應修改:
else if(fifo8_status(&keyinfo) != 0){....//when receive data > 768, it should be a console closing messageif (data == 255) {close_console(current_console_task->sht );continue;} .... }同時系統支持通過鼠標點擊控制臺窗口右上角的x按鈕把進程關閉,于是在show_mouse_info函數中也要做代碼修改:
void show_mouse_info(struct SHTCTL *shtctl, struct SHEET *sht_back,struct SHEET *sht_mouse) {....if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19 && sht->task != 0) {//change here/*io_cli();sheet_free(shtctl, sht);int addr_code32 = get_code32_addr();sht->task->tss.eip = (int)kill_process - addr_code32;io_sti();*/cmd_exit(sht->task);}break;.... }接著修改控制臺進程的主函數,讓他能執行exit命令,這個修改比較簡單:
void console_task(struct SHEET *sheet, int memtotal) {....//change hereelse if ( strcmp(cmdline, "exit") == 1) {cmd_exit(task);}.... }完成上面代碼后,我們可以通過命令或點擊控制臺右上角x按鈕的方式把打開的控制臺進程全部關閉:
更詳細的講解和代碼調試演示過程,請參看視頻 
 Linux kernel Hacker, 從零構建自己的內核
更多技術信息,包括操作系統,編譯器,面試算法,機器學習,人工智能,請關照我的公眾號: 
 
總結
以上是生活随笔為你收集整理的java开发操作系统:启动任意多控制台窗口的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 服务器无法取消指令方块显示,我的世界服务
- 下一篇: 执行python generate_tf
