select介紹
select()是常用的多路IO復(fù)用的posix調(diào)用接口。select () 函數(shù)指示指定的文件描述符中的哪些已準(zhǔn)備好讀取、準(zhǔn)備好寫入或有待處理的錯(cuò)誤條件。如果指定的條件對(duì)于所有指定的文件描述符都為假, 則 select() 阻塞,直到發(fā)生超時(shí)或直到指定的條件對(duì)于至少一個(gè)指定的文件描述符為真。
rt-smart select的實(shí)現(xiàn)
rt-smart是一個(gè)包含用戶層內(nèi)核層包含MMU硬件功能的OS,用戶層發(fā)送的系統(tǒng)調(diào)用請(qǐng)求,會(huì)通過特定的指令使cpu陷入異常,并進(jìn)行相應(yīng)的異常處理,其中用戶態(tài)的select函數(shù)最終會(huì)調(diào)用lwp_syscall.c中的sys_select函數(shù)。sys_select函數(shù)會(huì)調(diào)用rt-smart的虛擬文件系統(tǒng)dfs實(shí)現(xiàn)的select函數(shù)(所在文件)。而select函數(shù)則會(huì)調(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;
}
這里會(huì)首先初始化一個(gè)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ù)會(huì)調(diào)用對(duì)應(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)記為超時(shí),之后會(huì)再調(diào)用do_pollfd,但是無論do_pollfd的結(jié)果如何最終由于istimeout不為0,都會(huì)導(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; / 這個(gè)值會(huì)在wakeup中被修改 /
rt_hw_interrupt_enable(level);
return ret;
}
wait函數(shù)在中途會(huì)調(diào)用 rt_schedule()觸發(fā)系統(tǒng)調(diào)度,當(dāng)前線程被切回來以后會(huì)檢查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;
}
這個(gè)函數(shù)的邏輯是當(dāng)設(shè)備節(jié)點(diǎn)的poll函數(shù)回調(diào)被調(diào)用時(shí),需要看一下此時(shí)有沒有數(shù)據(jù)可以讓用戶態(tài)去讀取,而這個(gè)有沒有數(shù)據(jù)的信息需要驅(qū)動(dòng)自己維護(hù)。如果有的話就返回非0的值,如果沒有的話就直接返回0。
而rt_poll_add(waitqueue, req);會(huì)掛載一個(gè)req資源到waitqueue中,如果有人喚醒了這個(gè)隊(duì)列,那么前面的poll_wait_timeout就會(huì)被喚醒。rt_poll_add會(huì)調(diào)用req的_proc函數(shù),這個(gè)函數(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ì)列喚醒的時(shí)候這個(gè)回調(diào)函數(shù)會(huì)被調(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ù)用于喚醒一個(gè)正在因隊(duì)列等待而休眠的線程,該函數(shù)會(huì)去查找entry的wakeup回調(diào)函數(shù),這個(gè)回調(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)的原因會(huì)再次調(diào)用poll函數(shù)。
那么rt_wqueue_wakeup這個(gè)函數(shù),在正常的設(shè)備驅(qū)動(dòng)中一般就由中斷函數(shù)來調(diào)用,如果中斷函數(shù)代表有數(shù)據(jù)需要應(yīng)用層讀取處理的話。
-
處理器
+關(guān)注
關(guān)注
68文章
19896瀏覽量
235266 -
驅(qū)動(dòng)器
+關(guān)注
關(guān)注
54文章
8697瀏覽量
149970 -
觸發(fā)器
+關(guān)注
關(guān)注
14文章
2039瀏覽量
62145 -
MMU
+關(guān)注
關(guān)注
0文章
92瀏覽量
18753 -
串口中斷
+關(guān)注
關(guān)注
0文章
67瀏覽量
14342
發(fā)布評(píng)論請(qǐng)先 登錄
RT-Smart的資料合集
rt-smart中斷阻塞問題是怎么引起的
請(qǐng)問rt-smart gdbserver是閉源的嗎?
請(qǐng)問rt-smart gdbserver是閉源的嗎?
基于RT-Thread操作系統(tǒng)衍生rt-smart實(shí)時(shí)操作系統(tǒng)簡介
如何實(shí)現(xiàn)一種基于Ubuntu Linux環(huán)境通過USB方式燒錄rt-smart內(nèi)核的設(shè)計(jì)呢
rt-smart應(yīng)用程序系統(tǒng)調(diào)用實(shí)現(xiàn)過程是怎么樣的?
樹莓派上rt-smart的應(yīng)用編程入門

rt-smart移植分析:從樹莓派3b入手

優(yōu)雅的在D1S上運(yùn)行RT-Smart
絲滑的在RT-Smart用戶態(tài)運(yùn)行LVGL
RT-Smart riscv64匯編注釋
零基礎(chǔ)上手rt-smart適配bsp

評(píng)論