0.11核心維護著3個終端,一個顯示器和鍵盤,另外兩個是串口1,2. 每個終端有維護著3個隊列讀寫和輔助緩衝隊列。對於每個緩衝隊列,讀操作從隊列左邊取字元,並將隊尾指標向右移動。寫操作從隊列右邊寫字元,並將隊頭指標向右移動。任何一個移動到隊列尾端則折回到左端從新開始。
主要的終端讀寫函數包括tty_read(),tty_write(),copy_to_cooked().
tty_read(),tty_write()在檔案系統操作字元裝置檔案時被調用。例如一個程式讀寫/dev/tty就會執行系統調用sys_read(fs/read_write.c),這個系統調用在判斷出讀的是一個字元裝置檔案後會調用rw_char函數(fs/char_dev.c),該函數會根據所讀裝置的子裝置號等資訊,由字元裝置讀寫函數表調用rw_tty,最終調用到終端的讀函數tty_read()。
copy_to_cooked()由鍵盤的中斷處理程序呼叫(do_tty_interruptt),用於根據終端termios結構設定的各種標誌對read_q隊列進行處理,傳化成規範模式字元序列並儲存在輔助隊列secondary中,供tty_read讀取。如果回顯標誌置位還會把字元隊列放入write_q,並調用終端處理函數(con_write)輸出到螢幕,如果是串口裝置則調用(rs_write)發送到串列終端。最後copy_to_cooked還會喚醒等待輔助隊列的進程。
//將3個終端定義成表格
struct tty_struct tty_table[] = {
{
//第一個是顯示器終端,初始化其tty_struct結構體
{ICRNL, /* change incoming CR to NL */
OPOST|ONLCR, /* change outgoing NL to CRNL */
0,
ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
0, /* console termio */
INIT_C_CC},
0, /* initial pgrp */
0, /* initial stopped */
con_write, //終端輸出函數
{0,0,0,0,""}, /* console read-queue */
{0,0,0,0,""}, /* console write-queue */
{0,0,0,0,""} /* console secondary queue */
},{ //下邊初始串口1,2
{0, /* no translation */
0, /* no translation */
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
rs_write, //串口輸出函數
{0x3f8,0,0,0,""}, /* rs 1,串口讀寫隊列的data域對應著UART寄存器的起始地址 */
{0x3f8,0,0,0,""},
{0,0,0,0,""}
},{
{0, /* no translation */
0, /* no translation */
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
rs_write,
{0x2f8,0,0,0,""}, /* rs 2 */
{0x2f8,0,0,0,""},
{0,0,0,0,""}
}
};
/*
* these are the tables used by the machine code handlers.
* you can implement pseudo-tty's or something by changing
* them. Currently not done.
*/
//將3個終端中的讀寫隊列定義成表格
struct tty_queue * table_list[]={
&tty_table[0].read_q, &tty_table[0].write_q,
&tty_table[1].read_q, &tty_table[1].write_q,
&tty_table[2].read_q, &tty_table[2].write_q
};
void tty_init(void)
{
rs_init(); //串口初始化,設定中斷,初始化傳輸速率和允許寫保持空以外的所有終端
con_init();//顯示終端的初始化
}
void tty_intr(struct tty_struct * tty, int mask)
{
int i;
if (tty->pgrp <= 0) //tty->pgrp為零時,表示為初始init進程該進程不控制終端
return;
for (i=0;i<NR_TASKS;i++)
if (task[i] && task[i]->pgrp==tty->pgrp)
task[i]->signal |= mask;
}
static void sleep_if_empty(struct tty_queue * queue)
{
cli();
//如果當前進程沒有訊號要處理,並且隊列為空白,則將當前進程加入到等待該隊列的進程鏈表中
while (!current->signal && EMPTY(*queue))
interruptible_sleep_on(&queue->proc_list);
sti();
}
static void sleep_if_full(struct tty_queue * queue)
{
if (!FULL(*queue))
return;
cli();
while (!current->signal && LEFT(*queue)<128)
interruptible_sleep_on(&queue->proc_list);
sti();
}
void wait_for_keypress(void)
{
sleep_if_empty(&tty_table[0].secondary);
}
用到幾個宏:
#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1)) //緩衝區剩餘空間大小
#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)]) //取最近用的字元??
#define FULL(a) (!LEFT(a)) //緩衝區滿了
#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1)) //緩衝區中字元個數
#define GETCH(queue,c) /
(void)({c=(queue).buf[(queue).tail];INC((queue).tail);}) //取緩衝區字元,從隊尾取,並將隊列尾指標+1
#define PUTCH(c,queue) /
(void)({(queue).buf[(queue).head]=(c);INC((queue).head);})//向緩衝區寫字元,從隊頭寫,並將隊列頭指標+1