int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{
ktime_t expire, *to = NULL;
struct poll_wqueues table;
poll_table *wait;
int retval, i, timed_out = 0;
unsigned long slack = 0;
///这里为了获得集合中的最大描述符,这样可减少循环中遍历的次数。
///也就是为什么linux中select第一个参数为何如此重要了
rcu_read_lock();
retval = max_select_fd(n, fds);
rcu_read_unlock();
if (retval < 0)
return retval;
n = retval;
////初始化 poll_table结构,其中一个重要任务是把 __pollwait函数地址赋值给它,
poll_initwait(&table);
wait = &table.pt;
if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
wait = NULL;
timed_out = 1;
}
if (end_time && !timed_out)
slack = estimate_accuracy(end_time);
retval = 0;
///主循环,将会在这里完成描述符的状态轮训
for (;;) {
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
inp = fds->in; outp = fds->out; exp = fds->ex;
rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
const struct file_operations *f_op = NULL;
struct file *file = NULL;
///select中 fd_set 以及 do_select 中的 fd_set_bits 参数,都是按照位来保存描述符,意思是比如申请一个1024位的内存,
///如果第 28位置1,说明此集合有 描述符 28,
in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex; // 检测读写异常3个集合中有无描述符
if (all_bits == 0) {
i += __NFDBITS;
continue;
}
for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
int fput_needed;
if (i >= n)
break;
if (!(bit & all_bits))
continue;
file = fget_light(i, &fput_needed); ///通过 描述符 index 获得 struct file结构指针,
if (file) {
f_op = file->f_op; //通过 struct file 获得 file_operations,这是操作文件的回调函数集合。
mask = DEFAULT_POLLMASK;
if (f_op && f_op->poll) {
wait_key_set(wait, in, out, bit);
mask = (*f_op->poll)(file, wait); //调用我们的设备中实现的 poll函数,
//因此,为了能让select正常工作,在我们设备驱动中,必须要提供poll的实现,
}
fput_light(file, fput_needed);
if ((mask & POLLIN_SET) && (in & bit)) {
res_in |= bit;
retval++;
wait = NULL; /// 此处包括以下的,把wait设置为NULL,是因为检测到mask = (*f_op->poll)(file, wait); 描述符已经就绪
/// 无需再把当前进程添加到等待队列里,do_select 遍历完所有描述符之后就会退出。
}
if ((mask & POLLOUT_SET) && (out & bit)) {
res_out |= bit;
retval++;
wait = NULL;
}
if ((mask & POLLEX_SET) && (ex & bit)) {
res_ex |= bit;
retval++;
wait = NULL;
}
}
}
if (res_in)
*rinp = res_in;
if (res_out)
*routp = res_out;
if (res_ex)
*rexp = res_ex;
cond_resched();
}
wait = NULL; //已经遍历完一遍,该加到等待队列的,都已经加了,无需再加,因此设置为NULL
if (retval || timed_out || signal_pending(current)) //描述符就绪,超时,或者信号中断就退出循环
break;
if (table.error) {//出错退出循环
retval = table.error;
break;
}
/*
* If this is the first loop and we have a timeout
* given, then we convert to ktime_t and set the to
* pointer to the expiry value.
*/
if (end_time && !to) {
expire = timespec_to_ktime(*end_time);
to = &expire;
}
/////让进程休眠,直到超时,或者被就绪的描述符唤醒,
if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
to, slack))
timed_out = 1;
}
poll_freewait(&table);
return retval;
}
void poll_initwait(struct poll_wqueues *pwq)
{
init_poll_funcptr(&pwq->pt, __pollwait); //设置poll_table的回调函数为 __pollwait,这样当我们在驱动中调用poll_wait 就会调用到 __pollwait
........
}
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
poll_table *p)
{
...................
init_waitqueue_func_entry(&entry->wait, pollwake); // 设置唤醒进程调用的回调函数,当在驱动中调用 wake_up唤醒队列时候,
// pollwake会被调用,这里其实就是调用队列的默认函数 default_wake_function
// 用来唤醒睡眠的进程。
add_wait_queue(wait_address, &entry->wait); //加入到等待队列
}
int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
fd_set __user *exp, struct timespec *end_time)
{
........
//把描述符集合从用户空间复制到内核空间
if ((ret = get_fd_set(n, inp, fds.in)) ||
(ret = get_fd_set(n, outp, fds.out)) ||
(ret = get_fd_set(n, exp, fds.ex)))
.........
ret = do_select(n, &fds, end_time);
.............
////把do_select返回集合,从内核空间复制到用户空间
if (set_fd_set(n, inp, fds.res_in) ||
set_fd_set(n, outp, fds.res_out) ||
set_fd_set(n, exp, fds.res_ex))
ret = -EFAULT;
............
}
struct epitem {
/* RB tree node used to link this structure to the eventpoll RB tree */
struct rb_node rbn; // 红黑树节点,
struct epoll_filefd ffd; // 存储此变量对应的描述符
struct epoll_event event; //用户定义的结构
/*其他成员*/
};
struct eventpoll {
/*其他成员*/
.......
/* Wait queue used by file->poll() */
wait_queue_head_t poll_wait;
/* List of ready file descriptors */
struct list_head rdllist; ///描述符就绪队列,挂载的是 epitem结构
/* RB tree root used to store monitored fd structs */
struct rb_root rbr; /// 存储 新添加的 描述符的红黑树根, 此成员用来存储添加进来的所有描述符。挂载的是epitem结构
.........
};
//epoll_create
SYSCALL_DEFINE1(epoll_create1, int, flags)
{
int error;
struct eventpoll *ep = NULL;
/*其他代码*/
......
//分配 eventpoll结构,这个结构是epoll的灵魂,他包含了所有需要处理得数据。
error = ep_alloc(&ep);
if (error < 0)
return error;
error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep,
flags & O_CLOEXEC); ///打开 eventpoll 的描述符,并把 ep存储到 file->private_data变量里。
if (error < 0)
ep_free(ep);
return error;
}
SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
struct epoll_event __user *, event)
{
/*其他代码*/
.....
ep = file->private_data;
......
epi = ep_find(ep, tfile, fd); ///从 eventpoll的 rbr里查找描述符是 fd 的 epitem,
error = -EINVAL;
switch (op) {
case EPOLL_CTL_ADD:
if (!epi) {
epds.events |= POLLERR | POLLHUP;
error = ep_insert(ep, &epds, tfile, fd); // 在这个函数里添加新描述符,同时修改重要的回调函数。
//同时还调用描述符的poll,查看就绪状态
} else
error = -EEXIST;
break;
/*其他代码*/
........
}
static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
struct file *tfile, int fd)
{
..... /*其他代码*/
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);//设置 poll_tabe回调函数为 ep_ptable_queue_proc
//ep_ptable_queue_proc会设置等待队列的回调指针为 ep_epoll_callback,同时添加等待队列。
........ /*其他代码*/
revents = tfile->f_op->poll(tfile, &epq.pt); //调用描述符的poll回调,在此函数里 ep_ptable_queue_proc会被调用
....... /*其他代码*/
ep_rbtree_insert(ep, epi); //把新生成关于epitem添加到红黑树里
...... /*其他代码*/
if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {
list_add_tail(&epi->rdllink, &ep->rdllist); //如果 上边的poll调用,检测到描述符就绪,添加本描述符到就绪队列里。
if (waitqueue_active(&ep->wq))
wake_up_locked(&ep->wq);
if (waitqueue_active(&ep->poll_wait))
pwake++;
}
...... /*其他代码*/
/* We have to call this outside the lock */
if (pwake)
ep_poll_safewake(&ep->poll_wait); // 如果描述符就绪队列不为空,则唤醒 epoll_wait所在的进程。
......... /*其他代码*/
}
//这个函数设置等待队列回调函数为 ep_poll_callback,
//这样到底层有数据唤醒等待队列时候,ep_poll_callback就会被调用,从而把就绪的描述符加到就绪队列。
static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,
poll_table *pt)
{
struct epitem *epi = ep_item_from_epqueue(pt);
struct eppoll_entry *pwq;
if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {
init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
pwq->whead = whead;
pwq->base = epi;
add_wait_queue(whead, &pwq->wait);
list_add_tail(&pwq->llink, &epi->pwqlist);
epi->nwait++;
} else {
/* We have to signal that an error occurred */
epi->nwait = -1;
}
}
static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
int pwake = 0;
unsigned long flags;
struct epitem *epi = ep_item_from_wait(wait);
struct eventpoll *ep = epi->ep;
......... /*其他代码*/
if (!ep_is_linked(&epi->rdllink))
list_add_tail(&epi->rdllink, &ep->rdllist); // 把当前就绪的描述epitem结构添加到就绪队列里
......... /*其他代码*/
if (pwake)
ep_poll_safewake(&ep->poll_wait); //如果队列不为空,唤醒 epoll_wait所在进程
......... /*其他代码*/
}
epoll_wait内核代码里主要是调用ep_poll,列出ep_poll部分代码片段:
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
int maxevents, long timeout)
{
int res, eavail;
unsigned long flags;
long jtimeout;
wait_queue_t wait;
......... /*其他代码*/
if (list_empty(&ep->rdllist)) {
init_waitqueue_entry(&wait, current);
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue(&ep->wq, &wait);
// 如果检测到就绪队列为空,添加当前进程到等待队列,并执行否循环
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (!list_empty(&ep->rdllist) || !jtimeout) //如果就绪队列不为空,或者超时则退出循环
break;
if (signal_pending(current)) { //如果信号中断,退出循环
res = -EINTR;
break;
}
spin_unlock_irqrestore(&ep->lock, flags);
jtimeout = schedule_timeout(jtimeout);//睡眠,知道被唤醒或者超时为止。
spin_lock_irqsave(&ep->lock, flags);
}
__remove_wait_queue(&ep->wq, &wait);
set_current_state(TASK_RUNNING);
}
......... /*其他代码*/
if (!res && eavail &&
!(res = ep_send_events(ep, events, maxevents)) && jtimeout)
goto retry;
// ep_send_events主要任务是把就绪队列的就绪描述符copy到用户空间的 epoll_event数组里,
return res;
}
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有