spice-client 源碼解析之event_sources_p.cpp

這個函數是用來對event進行處理的函數,通過一系列的函數獲取event的flag以及文件描述符,並對event執行創建和刪除工作。

首先,補充這部分涉及到的I/O知識:

1.I/O阻塞模式
通常IO操作都是阻塞I/O的,也就是說當你調用read時,如果沒有數據收到,那麼線程或者進程就會被掛起,直到收到數據,再進行進行讀寫操作。應用的函數進行調用,但是內核一直沒有返回,就一直等着。應用的函數長時間處於等待結果的狀態,我們就稱爲阻塞I/O。

2.I/O非阻塞模式
調用一個I/O操作的時候,會等待數據,如果沒有數據過來,就即刻返回一個error。這樣就不會有線程的阻塞,但是需要一直採用輪詢的方式查詢操作。這個輪詢過程是很浪費時間的。

3.I/O的多路複用
多路複用是指使用一個線程來檢查多個文件描述符(Socket)的就緒狀態,spice中使用了select()函數,一次性地傳入多個文件描述符fd,如果有一個文件描述符就緒,則返回,否則阻塞直到超時一但得到就緒狀態後就進行真正的操作。

然後就是代碼部分啦

void EventSources_p::add_event(int fd, EventSource* source)
void EventSources_p::remove_event(EventSource* source)
bool EventSources::wait_events(int timeout_msec)

通過使用以上三個函數進行event的添加,刪除以及輪詢操作;

1.wait_events(int timeout_msec)
1.1 設置等待時間:timeout_msec;
1.2 調用系統函數FD_ZERO(&rfds):清空文件描述符,這裏每次使用完內存如果沒有釋放的話,再次使用的數據就有可能出錯,所以舊的數據進行清空;
1.3 調用ready = ::select(maxfd+1, &rfds, NULL, NULL, tvp):spice使用這個函數進行事件的輪詢;
輪詢結果:
(1).wait error select failed :並沒有數據可以處理
(2).if (FD_ISSET(_fds[i], &rfds)) {
_events[i]->action();
return false;
}//該事件可以處理,需要判斷是否已經在執行了

如果對象已經在執行了:return false;
如果對象未被執行:使用如下代碼添加對象到執行隊列,並創建pipe;
調用set_non_blocking(_event_fd)使能非阻塞I/O;

void EventSources::add_trigger(Trigger& trigger)
EventSources::Trigger::Trigger()

當對象結束執行,調用:

void EventSources::remove_trigger(Trigger& trigger)

2.add_event(int fd, EventSource* source)
重新定義事件隊列大小,並將event裝入_event[size],_fds[size]中裝入對應的文件描述符;

3.remove_event(EventSource* source)
清空event和fds;

當然,2和3過程還涉及到了,socket和fd的添加,event的重置等;

void EventSources::add_socket(Socket& socket)
{
    add_event(socket.get_socket(), &socket);
    set_non_blocking(socket.get_socket());
}

void EventSources::remove_socket(Socket& socket)
{
    remove_event(&socket);
    int fd = socket.get_socket();
    set_blocking(fd);
}

void EventSources::add_file(File& file)
{
    add_event(file.get_fd(), &file);
}

void EventSources::remove_file(File& file)
{
    remove_event(&file);
}

void EventSources::add_handle(Handle& file)
{
}

void EventSources::remove_handle(Handle& file)
{
}

補充:
整個過程的select()函數是有弊端的:
【1】每次調用select()都需要把fd(文件描述符)從用戶態拷貝到內核態,開銷比較大
【2】每次都需要在內核遍歷傳入的fd(文件描述符)
【3】select支持文件數量比較小,默認是1024
參考鏈接:
https://www.cnblogs.com/skiler/p/6852493.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章