select介紹
select()是常用的多路IO復(fù)用的posix調(diào)用接口。select () 函數(shù)指示指定的文件描述符中的哪些已準(zhǔn)備好讀取、準(zhǔn)備好寫入或有待處理的錯誤條件。如果指定的條件對于所有指定的文件描述符都為假, 則 select() 阻塞,直到發(fā)生超時或直到指定的條件對于至少一個指定的文件描述符為真。
rt-smart select的實(shí)現(xiàn)
rt-smart是一個包含用戶層內(nèi)核層包含MMU硬件功能的OS,用戶層發(fā)送的系統(tǒng)調(diào)用請求,會通過特定的指令使cpu陷入異常,并進(jìn)行相應(yīng)的異常處理,其中用戶態(tài)的select函數(shù)最終會調(diào)用lwp_syscall.c中的sys_select函數(shù)。sys_select函數(shù)會調(diào)用rt-smart的虛擬文件系統(tǒng)dfs實(shí)現(xiàn)的select函數(shù)(所在文件)。而select函數(shù)則會調(diào)用rt-smart的虛擬文件系統(tǒng)dfs實(shí)現(xiàn)的poll函數(shù)(所在文件)。
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
int num;
struct rt_poll_table table;
poll_table_init(&table);
num = poll_do(fds, nfds, &table, timeout);
poll_teardown(&table);
return num;
}
這里會首先初始化一個poll的表,然后調(diào)用poll_do函數(shù)。
static void poll_table_init(struct rt_poll_table *pt)
{
pt->req._proc = _poll_add;
pt->triggered = 0;
pt->nodes = RT_NULL;
pt->polling_thread = rt_thread_self();
}
poll_table_init中將table的triggered設(shè)置為了0.
關(guān)于poll_do的函數(shù)解釋,寫在了函數(shù)注釋中。
static int poll_do(struct pollfd *fds, nfds_t nfds, struct rt_poll_table *pt, int msec)
{
while (1)
{
pf = fds;
num = 0;
for (n = 0; n < nfds; n ++)
{
/ do_pollfd函數(shù)會調(diào)用對應(yīng)的設(shè)備節(jié)點(diǎn)的poll回調(diào)函數(shù) /
ret = do_pollfd(pf, &pt->req);
if(ret < 0)
{
/*dealwith the device return error -1 */
pt->req._proc = RT_NULL;
return ret;
}
else if(ret > 0) / 如果返回值大于0,num計(jì)數(shù)增加 /
{
num ++;
pt->req._proc = RT_NULL;
}
pf ++;
}
pt->req._proc = RT_NULL;
/ 如果num大于0或istimeout不為0則跳出循環(huán) /
if (num || istimeout)
break;
/ 如果poll_wait_timeout返回值大于0則標(biāo)記為超時,之后會再調(diào)用do_pollfd,但是無論do_pollfd的結(jié)果如何最終由于istimeout不為0,都會導(dǎo)致循環(huán)退出 /
if (poll_wait_timeout(pt, msec))
istimeout = 1;
}
return num;
}
static int poll_wait_timeout(struct rt_poll_table *pt, int msec)
{
if (timeout != 0 && !pt->triggered)
{
if (rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE) == RT_EOK)
{
rt_hw_interrupt_enable(level);
rt_schedule();
level = rt_hw_interrupt_disable();
}
}
ret = !pt->triggered; / 這個值會在wakeup中被修改 /
rt_hw_interrupt_enable(level);
return ret;
}
wait函數(shù)在中途會調(diào)用 rt_schedule()觸發(fā)系統(tǒng)調(diào)度,當(dāng)前線程被切回來以后會檢查pt->triggered的值來確定函數(shù)的返回值。
poll函數(shù)的實(shí)現(xiàn)
int test_dev_poll(struct dfs_fd *fd, struct rt_pollreq *req)
{
/ 這里的waitqueue是設(shè)備節(jié)點(diǎn)dev中的waitqueue /
rt_poll_add(waitqueue, req);
if(is_sould_return)
return POLLIN | POLLRDNORM;
return 0;
}
這個函數(shù)的邏輯是當(dāng)設(shè)備節(jié)點(diǎn)的poll函數(shù)回調(diào)被調(diào)用時,需要看一下此時有沒有數(shù)據(jù)可以讓用戶態(tài)去讀取,而這個有沒有數(shù)據(jù)的信息需要驅(qū)動自己維護(hù)。如果有的話就返回非0的值,如果沒有的話就直接返回0。
而rt_poll_add(waitqueue, req);會掛載一個req資源到waitqueue中,如果有人喚醒了這個隊(duì)列,那么前面的poll_wait_timeout就會被喚醒。rt_poll_add會調(diào)用req的_proc函數(shù),這個函數(shù)在前面的poll_table_init中被賦值為了_poll_add。
static void _poll_add(rt_wqueue_t *wq, rt_pollreq_t *req)
{
node->wqn.key = req->_key;
rt_list_init(&(node->wqn.list));
node->wqn.polling_thread = pt->polling_thread;
node->wqn.wakeup = __wqueue_pollwake;
node->next = pt->nodes;
node->pt = pt;
pt->nodes = node;
rt_wqueue_add(wq, &node->wqn);
}
這里比較重要的是node->wqn.wakeup被賦值為了__wqueue_pollwake。之后隊(duì)列喚醒的時候這個回調(diào)函數(shù)會被調(diào)用。
void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)
{
if (!(rt_list_isempty(queue_list)))
{
for (node = queue_list->next; node != queue_list; node = node->next)
{
entry = rt_list_entry(node, struct rt_wqueue_node, list);
if (entry->wakeup(entry, key) == 0)
{
rt_thread_resume(entry->polling_thread);
need_schedule = 1;
rt_wqueue_remove(entry);
break;
}
}
}
}
wakeup函數(shù)用于喚醒一個正在因隊(duì)列等待而休眠的線程,該函數(shù)會去查找entry的wakeup回調(diào)函數(shù),這個回調(diào)函數(shù)就是前面提到的__wqueue_pollwake。
static int __wqueue_pollwake(struct rt_wqueue_node *wait, void *key)
{
struct rt_poll_node *pn;
if (key && !((rt_ubase_t)key & wait->key))
return -1;
pn = rt_container_of(wait, struct rt_poll_node, wqn);
pn->pt->triggered = 1;
return __wqueue_default_wake(wait, key);
}
__wqueue_pollwake函數(shù)最終將triggered置位了1,代表poll_wait_timeout被wakeup的話,其返回值就是0。poll_do函數(shù)由于循環(huán)的原因會再次調(diào)用poll函數(shù)。
那么rt_wqueue_wakeup這個函數(shù),在正常的設(shè)備驅(qū)動中一般就由中斷函數(shù)來調(diào)用,如果中斷函數(shù)代表有數(shù)據(jù)需要應(yīng)用層讀取處理的話。
-
處理器
+關(guān)注
關(guān)注
68文章
18938瀏覽量
227330 -
驅(qū)動器
+關(guān)注
關(guān)注
51文章
8014瀏覽量
145069 -
觸發(fā)器
+關(guān)注
關(guān)注
14文章
1990瀏覽量
60891 -
MMU
+關(guān)注
關(guān)注
0文章
91瀏覽量
18169 -
串口中斷
+關(guān)注
關(guān)注
0文章
64瀏覽量
13800
發(fā)布評論請先 登錄
相關(guān)推薦
評論