時間:2015-06-28 00:00:00 來源:IT貓撲網(wǎng) 作者:網(wǎng)管聯(lián)盟 我要評論(1)
對于規(guī)范模式,要讀滿一行才會返回用戶空間.例如我們在shell上輸入指令的時候,要按下enter鍵指令才會進(jìn)行處理.在 tty->read_flags數(shù)組中定義了一些滿行的標(biāo)志,如果read_buf中對應(yīng)的數(shù)據(jù)在tty->read_flags中被置位. 就會認(rèn)為這次讀入已經(jīng)到結(jié)尾了.在這里還要注意的是,不要將__DISABLED_CHAR即’/0’拷貝到用戶空間.
對于原始模式,只需要將read_buf中的數(shù)據(jù)讀入到用戶空間就可以返回了.在這里需要注意read_buf是一個環(huán)形緩存,需要copy兩次.例如tail在head之前的情況.
/* If there is enough space in the read buffer now, let the
* low-level driver know. We use n_tty_chars_in_buffer() to
* check the buffer, as it now knows about canonical mode.
* Otherwise, if the driver is throttled and the line is
* longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
* we won't get any more characters.
*/
if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
n_tty_set_room(tty);
check_unthrottle(tty);
}
OK.到這里,read_buf中或多或少已經(jīng)有數(shù)據(jù)被取出了.如果當(dāng)前的數(shù)據(jù)量少于TTY_THRESHOLD_UNTHROTTLE.就可以調(diào)用check_unthrottle()將其它的寫進(jìn)程喚醒了
if (b - buf >= minimum)
break;
if (time)
timeout = time;
}
mutex_unlock(&tty->atomic_read_lock);
remove_wait_queue(&tty->read_wait, &wait);
if (!waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = minimum;
__set_current_state(TASK_RUNNING);
已經(jīng)讀完了數(shù)據(jù),是該到清理的時候了.將進(jìn)程移出等待隊(duì)列,并當(dāng)進(jìn)程狀態(tài)設(shè)為TASK_RUNNING
size = b - buf;
if (size) {
retval = size;
if (nr)
clear_bit(TTY_PUSH, &tty->flags);
} else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
goto do_it_again;
//更新剩余空間數(shù)
n_tty_set_room(tty);
return retval;
}
TTY_PUSH:是由底層驅(qū)動程序在讀到一個EOF字符并將其放入緩存區(qū)造成的,表示用戶要盡快將緩存區(qū)數(shù)據(jù)取走.
如果本次操作沒有讀取任何數(shù)據(jù),且被設(shè)置了TTY_PUSH,則跳轉(zhuǎn)到do_it_again,繼續(xù)執(zhí)行.如果本次操作讀取了數(shù)據(jù),可以等到下一次read的時候再來取.
最后,更新read_buf的剩余空間數(shù).
五:控制終端數(shù)據(jù)的來源
從這個函數(shù)里面我們可以看到,數(shù)據(jù)是從read_buf中取出來的,但是誰將數(shù)據(jù)放入到read_buf中的呢?為了探究出它的根源.我們還得要從vty_init()說起.
在之前分析過. vty_init()會調(diào)用一個表面字義看起來與鍵盤相關(guān)的一個子函數(shù): kbd_init().跟蹤這個函數(shù):
int __init kbd_init(void)
{
int i;
int error;
for (i = 0; i < MAX_NR_CONSOLES; i++) {
kbd_table[i].ledflagstate = KBD_DEFLEDS;
kbd_table[i].default_ledflagstate = KBD_DEFLEDS;
kbd_table[i].ledmode = LED_SHOW_FLAGS;
kbd_table[i].lockstate = KBD_DEFLOCK;
kbd_table[i].slockstate = 0;
kbd_table[i].modeflags = KBD_DEFMODE;
kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
}
error = input_register_handler(&kbd_handler);
if (error)
return error;
tasklet_enable(&keyboard_tasklet);
tasklet_schedule(&keyboard_tasklet);
return 0;
}
暫時用不到的部份我們先不與分析。 在這里注冊了一個input handler。結(jié)合前面我們分析的input子系統(tǒng),在handler里會處理input device上報(bào)的事件。跟進(jìn)這個handler看一下:
kbd_handler定義如下:
static struct input_handler kbd_handler = {
.event???? = kbd_event,
.connect?? = kbd_connect,
.disconnect = kbd_disconnect,
.start = kbd_start,
.name??? = "kbd",
.id_table?? = kbd_ids,
};
Id_table是用來匹配input device的。跟進(jìn)去看一下,看哪些device的事件,才會交給它處理:
static const struct input_device_id kbd_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = { BIT_MASK(EV_KEY) },
},
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = { BIT_MASK(EV_SND) },
},
{ },??? /* Terminating entry */
};
從這個id_table中看來,只要是能支持EV_KEY或者是EV_SND的設(shè)備都會被這個hnadler匹配到。相應(yīng)的。也就能夠處理input device上報(bào)的事件了.
根據(jù)之前的input子系統(tǒng)分析,在input device和handler 進(jìn)行匹配的時候會調(diào)用handler->connect.即kbd_connect().代碼如下:
static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct input_handle *handle;
int error;
int i;
for (i = KEY_RESERVED; i < BTN_MISC; i++)
if (test_bit(i, dev->keybit))
break;
if (i == BTN_MISC && !test_bit(EV_SND, dev->evbit))
return -ENODEV;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = dev;
handle->handler = handler;
handle->name = "kbd";
error = input_register_handle(handle);
if (error)
goto err_free_handle;
error = input_open_device(handle);
if (error)
goto err_unregister_handle;
return 0;
err_unregister_handle:
input_unregister_handle(handle);
err_free_handle:
kfree(handle);
return error;
}
在這段代碼里,它申請分初始化了一個hande結(jié)構(gòu),并將其注冊。Open。這些都是我們之前分析過的東東。在注冊handle的時候。又會調(diào)用到hande->start.函數(shù)如下:
static void kbd_start(struct input_handle *handle)
{
unsigned char leds = ledstate;
tasklet_disable(&keyboard_tasklet);
if (leds != 0xff) {
input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
input_inject_event(handle, EV_LED, LED_NUML,??? !!(leds & 0x02));
input_inject_event(handle, EV_LED, LED_CAPSL,?? !!(leds & 0x04));
input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
}
tasklet_enable(&keyboard_tasklet);
}
這里就是對鍵盤上的LED進(jìn)行操作。啟用了tasklent。這些都不是我們所關(guān)心的重點(diǎn)。
來看下它的事件處理過程:
static void kbd_event(struct input_handle *handle, unsigned int event_type,
unsigned int event_code, int value)
{
if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))
kbd_rawcode(value);
if (event_type == EV_KEY)
kbd_keycode(event_code, value, HW_RAW(handle->dev));
tasklet_schedule(&keyboard_tasklet);
do_poke_blanked_console = 1;
schedule_console_callback();
}
不管對應(yīng)鍵盤的那一種模式。后面的數(shù)據(jù)流程都會轉(zhuǎn)入到input_queue()進(jìn)等處理。
實(shí)際上。控制終端由vc_cons[ ]數(shù)組表示。數(shù)組中的每一個項(xiàng)都表示一個控制終端。由全局變量fg_console來指示當(dāng)前所用的cosole/另外。對于鍵盤等輸出設(shè)備也對應(yīng)一個數(shù)組。即kbd_table[ ].用來表示當(dāng)前終端的控制信息.
其余的都不是我們想關(guān)心的。來跟蹤一下這個函數(shù)的實(shí)現(xiàn):
static void put_queue(struct vc_data *vc, int ch)
關(guān)鍵詞標(biāo)簽:linux
相關(guān)閱讀
熱門文章 安裝紅帽子RedHat Linux9.0操作系統(tǒng)教程 Tomcat9.0如何安裝_Tomcat9.0環(huán)境變量配置方法 多種操作系統(tǒng)NTP客戶端配置 Linux操作系統(tǒng)修改IP
人氣排行 Linux下獲取CPUID、硬盤序列號與MAC地址 dmidecode命令查看內(nèi)存型號 linux tc實(shí)現(xiàn)ip流量限制 安裝紅帽子RedHat Linux9.0操作系統(tǒng)教程 linux下解壓rar文件 lcx.exe、nc.exe、sc.exe入侵中的使用方法 Ubuntu linux 關(guān)機(jī)、重啟、注銷 命令 查看linux服務(wù)器硬盤IO讀寫負(fù)載