rt-smart select的实现

电子说

1.2w人已加入

描述

select介绍

select()是常用的多路IO复用的posix调用接口。select () 函数指示指定的文件描述符中的哪些已准备好读取、准备好写入或有待处理的错误条件。如果指定的条件对于所有指定的文件描述符都为假, 则 select() 阻塞,直到发生超时或直到指定的条件对于至少一个指定的文件描述符为真。

rt-smart select的实现

rt-smart是一个包含用户层内核层包含MMU硬件功能的OS,用户层发送的系统调用请求,会通过特定的指令使cpu陷入异常,并进行相应的异常处理,其中用户态的select函数最终会调用lwp_syscall.c中的sys_select函数。sys_select函数会调用rt-smart的虚拟文件系统dfs实现的select函数(所在文件)。而select函数则会调用rt-smart的虚拟文件系统dfs实现的poll函数(所在文件)。

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的表,然后调用poll_do函数。

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设置为了0.

关于poll_do的函数解释,写在了函数注释中。

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函数会调用对应的设备节点的poll回调函数 /
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计数增加 /
{
num ++;
pt->req._proc = RT_NULL;
}
pf ++;
}
pt->req._proc = RT_NULL;
/ 如果num大于0或istimeout不为0则跳出循环 /
if (num || istimeout)
break;
/ 如果poll_wait_timeout返回值大于0则标记为超时,之后会再调用do_pollfd,但是无论do_pollfd的结果如何最终由于istimeout不为0,都会导致循环退出 /
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函数在中途会调用 rt_schedule()触发系统调度,当前线程被切回来以后会检查pt->triggered的值来确定函数的返回值。

poll函数的实现
int test_dev_poll(struct dfs_fd *fd, struct rt_pollreq *req)
{
/ 这里的waitqueue是设备节点dev中的waitqueue /
rt_poll_add(waitqueue, req);
if(is_sould_return)
return POLLIN | POLLRDNORM;
return 0;
}

这个函数的逻辑是当设备节点的poll函数回调被调用时,需要看一下此时有没有数据可以让用户态去读取,而这个有没有数据的信息需要驱动自己维护。如果有的话就返回非0的值,如果没有的话就直接返回0。

而rt_poll_add(waitqueue, req);会挂载一个req资源到waitqueue中,如果有人唤醒了这个队列,那么前面的poll_wait_timeout就会被唤醒。rt_poll_add会调用req的_proc函数,这个函数在前面的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。之后队列唤醒的时候这个回调函数会被调用。

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函数用于唤醒一个正在因队列等待而休眠的线程,该函数会去查找entry的wakeup回调函数,这个回调函数就是前面提到的__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函数最终将triggered置位了1,代表poll_wait_timeout被wakeup的话,其返回值就是0。poll_do函数由于循环的原因会再次调用poll函数。

那么rt_wqueue_wakeup这个函数,在正常的设备驱动中一般就由中断函数来调用,如果中断函数代表有数据需要应用层读取处理的话。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分