源码剖析之epoll(3)

本文主要剖析了epoll的源码,详细讲解了epoll_wait、ep_poll、ep_send_events、ep_scan_ready_list和ep_send_events_proc等关键函数的工作流程,阐述了epoll如何检查就绪链表并处理事件。

1. 源码剖析

本篇主要分析epoll_wait以及相关函数

以下源码取自4.10

1.1 epoll_wait

fs/eventpoll.c

/*
 * Implement the event wait interface for the eventpoll file. It is the kernel
 * part of the user space epoll_wait(2).
 */
SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,
        int, maxevents, int, timeout)
{
    int error;
    struct fd f;
    struct eventpoll *ep;

    /* The maximum number of event must be greater than zero */
    if (maxevents <= 0 || maxevents > EP_MAX_EVENTS)
        return -EINVAL;

    /* Verify that the area passed by the user is writeable */
    if (!access_ok(VERIFY_WRITE, events, maxevents * sizeof(struct epoll_event)))
        return -EFAULT;

    /* Get the "struct file *" for the eventpoll file */
    f = fdget(epfd);
    if (!f.file)
        return -EBADF;

    /*
     * We have to check that the file structure underneath the fd
     * the user passed to us _is_ an eventpoll file.
     */
    error = -EINVAL;
    if (!is_file_epoll(f.file))
        goto error_fput;

    /*
     * At this point it is safe to assume that the "private_data" contains
     * our own data structure.
     */
    ep = f.file->private_data;

    /* Time to fish for events ... */
    /* 实际上调用`ep_poll`函数 */
    error = ep_poll(ep, events, maxevents, timeout);

error_fput:
    fdput(f);
    return error;
}

1.3 ep_poll

epoll_wait会调用的函数

fs/eventpoll.c

/**
 * ep_poll - Retrieves ready events, and delivers them to the caller supplied
 *           event buffer.
 *
 * @ep: Pointer to the eventpoll context.
 * @events: Pointer to the userspace buffer where the ready events should be
 *          stored.
 * @maxevents: Size (in terms of number of events) of the caller event buffer.
 * @timeout: Maximum timeout for the ready events fetch operation, in
 *           milliseconds. If the @timeout is zero, the function will not block,
 *           while if the @timeout is less than zero, the function will block
 *           until at least one event has been retrieved (or an error
 *           occurred).
 *
 * Returns: Returns the number of ready events which have been fetched, or an
 *          error code, in case of error.
 */
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
           int maxevents, long timeout)
{
    int res = 0, eavail, timed_out = 0;
    unsigned long flags;
    u64 slack = 0;
    wait_queue_t wait;
    ktime_t expires, *to = NULL;

    /* 如果指定了超时时间timeout,初始化相关结构*/
    if (timeout > 0) {
        struct timespec64 end_time = ep_set_mstimeout(timeout);

        slack = select_estimate_accuracy(&end_time);
        to = &expires;
        *to = timespec64_to_ktime(end_time);

    /* 如果timeout == 0,则为nonblocking,直接check rdlist */
    } else if (timeout == 0) {
        /*
         * Avoid the unnecessary trip to the wait queue loop, if the
         * caller specified a non blocking operation.
         */
        timed_out = 1;
        spin_lock_irqsave(&ep->lock, flags);
        goto check_events;
    }

fetch_events:
    spin_lock_irqsave(&ep->lock, flags);
    /* 如果当前没有就绪事件 */
    if (!ep_events_available(ep)) {
        /*
         * We don't have any available event to return to the caller.
         * We need to sleep here, and we will be wake up by
         * ep_poll_callback() when events will become available.
         */
        init_waitqueue_entry(&wait, current);
        __add_wait_queue_exclusive(&ep->wq, &wait);
        /* 一直等待,直到有事件发生/timeout/有signal */
        for (;;) {
            /*
             * We don't want to sleep if the ep_poll_callback() sends us
             * a wakeup in between. That's why we set the task state
             * to TASK_INTERRUPTIBLE before doing the checks.
             */
            /* 如果有就绪事件或超时 */
            if (ep_events_available(ep) || timed_out)
                break;

            /* 检测是否有signal */
            if (signal_pending(current)) {
                res = -EINTR;
                break;
            }
            spin_unlock_irqrestore(&ep->lock, flags);
            if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
                timed_out = 1;

            spin_lock_irqsave(&ep->lock, flags);
        }

        __remove_wait_queue(&ep->wq, &wait);
        __set_current_state(TASK_RUNNING);
    }
check_events:
    /* Is it worth to try to dig for events ? */
    eavail = ep_events_available(ep);

    spin_unlock_irqrestore(&ep->lock, flags);

    /*
     * Try to transfer events to user space. In case we get 0 events and
     * there's still timeout left over, we go trying again in search of
     * more luck.
     */
    if (!res && eavail &&
        !(res = ep_send_events(ep, events, maxevents)) && !timed_out)
        goto fetch_events;

    return res;
}

1.4 ep_send_events

fs/eventpoll.c

static int ep_send_events(struct eventpoll *ep,
              struct epoll_event __user *events, int maxevents)
{
    /**
     *  struct ep_send_events_data {
     *      int maxevents;
     *      struct epoll_event __user *events;
     *  };
     */
    struct ep_send_events_data esed;
    esed.maxevents = maxevents;
    esed.events = events;
    /* 调用ep_scan_ready_list ,实质是调用ep_send_events_proc */
    return ep_scan_ready_list(ep, ep_send_events_proc, &esed, 0, false);
}

1.5 ep_scan_ready_list

fs/eventpoll.c

/**
 * ep_scan_ready_list - Scans the ready list in a way that makes possible for
 *                      the scan code, to call f_op->poll(). Also allows for
 *                      O(NumReady) performance.
 *
 * @ep: Pointer to the epoll private data structure.
 * @sproc: Pointer to the scan callback.
 * @priv: Private opaque data passed to the @sproc callback.
 * @depth: The current depth of recursive f_op->poll calls.
 * @ep_locked: caller already holds ep->mtx
 *
 * Returns: The same integer error code returned by the @sproc callback.
 */
static int ep_scan_ready_list(struct eventpoll *ep,
                  int (*sproc)(struct eventpoll *,
                       struct list_head *, void *),
                  void *priv, int depth, bool ep_locked)
{
    int error, pwake = 0;
    unsigned long flags;
    struct epitem *epi, *nepi;
    LIST_HEAD(txlist);

    /*
     * We need to lock this because we could be hit by
     * eventpoll_release_file() and epoll_ctl().
     */

    if (!ep_locked)
        mutex_lock_nested(&ep->mtx, depth);

    /*
     * Steal the ready list, and re-init the original one to the
     * empty list. Also, set ep->ovflist to NULL so that events
     * happening while looping w/out locks, are not lost. We cannot
     * have the poll callback to queue directly on ep->rdllist,
     * because we want the "sproc" callback to be able to do it
     * in a lockless way.
     */
    spin_lock_irqsave(&ep->lock, flags);

    /* 拷贝rdlist至txlist,并将rdlist清空 */
    list_splice_init(&ep->rdllist, &txlist);
    ep->ovflist = NULL;
    spin_unlock_irqrestore(&ep->lock, flags);

    /*
     * Now call the callback function.
     * 调用callback ep_send_events_proc.
     */
    error = (*sproc)(ep, &txlist, priv);

    spin_lock_irqsave(&ep->lock, flags);
    /*
     * During the time we spent inside the "sproc" callback, some
     * other events might have been queued by the poll callback.
     * We re-insert them inside the main ready-list here.
     */
    for (nepi = ep->ovflist; (epi = nepi) != NULL;
         nepi = epi->next, epi->next = EP_UNACTIVE_PTR) {
        /*
         * We need to check if the item is already in the list.
         * During the "sproc" callback execution time, items are
         * queued into ->ovflist but the "txlist" might already
         * contain them, and the list_splice() below takes care of them.
         */
        if (!ep_is_linked(&epi->rdllink)) {
            list_add_tail(&epi->rdllink, &ep->rdllist);
            ep_pm_stay_awake(epi);
        }
    }
    /*
     * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after
     * releasing the lock, events will be queued in the normal way inside
     * ep->rdllist.
     */
    ep->ovflist = EP_UNACTIVE_PTR;

    /*
     * Quickly re-inject items left on "txlist".
     */
    list_splice(&txlist, &ep->rdllist);
    __pm_relax(ep->ws);

    if (!list_empty(&ep->rdllist)) {
        /*
         * Wake up (if active) both the eventpoll wait list and
         * the ->poll() wait list (delayed after we release the lock).
         */
        if (waitqueue_active(&ep->wq))
            wake_up_locked(&ep->wq);
        if (waitqueue_active(&ep->poll_wait))
            pwake++;
    }
    spin_unlock_irqrestore(&ep->lock, flags);

    if (!ep_locked)
        mutex_unlock(&ep->mtx);

    /* We have to call this outside the lock */
    if (pwake)
        ep_poll_safewake(&ep->poll_wait);

    return error;
}

1.6 ep_send_events_proc

fs/eventpoll.c

static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
                   void *priv)
{
    struct ep_send_events_data *esed = priv;
    int eventcnt;
    unsigned int revents;
    struct epitem *epi;
    struct epoll_event __user *uevent;
    struct wakeup_source *ws;
    poll_table pt;

    /* 初始化poll的callback为NULL,即只获得文件目前的就绪事件掩码,不做其它事情 */
    init_poll_funcptr(&pt, NULL);

    /*
     * We can loop without lock because we are passed a task private list.
     * Items cannot vanish during the loop because ep_scan_ready_list() is
     * holding "mtx" during this call.
     * 遍历rdlist
     */
    for (eventcnt = 0, uevent = esed->events;
         !list_empty(head) && eventcnt < esed->maxevents;) {
        epi = list_first_entry(head, struct epitem, rdllink);

        /*
         * Activate ep->ws before deactivating epi->ws to prevent
         * triggering auto-suspend here (in case we reactive epi->ws
         * below).
         *
         * This could be rearranged to delay the deactivation of epi->ws
         * instead, but then epi->ws would temporarily be out of sync
         * with ep_is_linked().
         */
        ws = ep_wakeup_source(epi);
        if (ws) {
            if (ws->active)
                __pm_stay_awake(ep->ws);
            __pm_relax(ws);
        }

        list_del_init(&epi->rdllink);

        /* 取得epi关注文件的就绪事件掩码 */
        revents = ep_item_poll(epi, &pt);

        /*
         * If the event mask intersect the caller-requested one,
         * deliver the event to userspace. Again, ep_scan_ready_list()
         * is holding "mtx", so no operations coming from userspace
         * can change the item.
         */
        if (revents) {
            /* 将就绪事件transfer给用户空间 */
            if (__put_user(revents, &uevent->events) ||
                __put_user(epi->event.data, &uevent->data)) {
                list_add(&epi->rdllink, head);
                ep_pm_stay_awake(epi);
                return eventcnt ? eventcnt : -EFAULT;
            }
            eventcnt++;
            uevent++;
            if (epi->event.events & EPOLLONESHOT)
                epi->event.events &= EP_PRIVATE_BITS;
            else if (!(epi->event.events & EPOLLET)) {
                /*
                 * If this file has been added with Level
                 * Trigger mode, we need to insert back inside
                 * the ready list, so that the next call to
                 * epoll_wait() will check again the events
                 * availability. At this point, no one can insert
                 * into ep->rdllist besides us. The epoll_ctl()
                 * callers are locked out by
                 * ep_scan_ready_list() holding "mtx" and the
                 * poll callback will queue them in ep->ovflist.
                 */
                /* 如果是LT模式,将就绪事件重新添加到rdlist中 */
                list_add_tail(&epi->rdllink, &ep->rdllist);
                ep_pm_stay_awake(epi);
            }
        }
    }

    return eventcnt;
}

1.7 summary

epoll_wait -> ep_poll -> ep_send_events -> ep_scan_ready_list -> ep_send_events_proc -> ep_item_poll -> __put_user

  1. ep_poll中,会不断的去check就绪链表,直到有事件发生、超时或有signal
  2. 如果在timeout时间内,有就绪事件发生,调用ep_send_events -> ep_scan_ready_list -> ep_send_events_proc
  3. 在函数ep_send_events_proc中,对就绪链表中的每一项调用ep_item_poll得到最新的就绪事件掩码
  4. 将所有所有的就绪的events通过__put_user发送到用户空间
  5. wakeup所有阻塞在epoll_waitpoll的进程/线程
内容概要:本文围绕“考虑电动汽车聚合可调节能力的含波动性电源电氢耦合系统多目标优化运行”展开研究,提出了一种基于Matlab代码实现的多目标优化模型。该模型深度融合电-氢耦合系统与高比例波动性可再生能源(如风电、光伏),充分挖掘电动汽车(EV)集群作为移动储能单元的灵活调节潜力,通过聚合调控提升系统对新能源的消纳能力与运行经济性。研究系统构建了电动汽车可调度能力、电解水制氢与储氢动态过程、多能源协同互补的优化调度框架,并结合智能优化算法实现经济性、低碳性与运行稳定性等多重目标的协同优化。文中配套提供了完整的Matlab仿真代码、相关数据及可能的论文支撑材料,极大地方便了模型的复现、验证与后续深化研究。; 适合人群:具备电力系统、综合能源系统、优化理论或新能源技术等相关领域基础知识的研究生、科研人员,以及从事新型电力系统规划、清洁能源消纳与智慧能源管理的工程技术人员。; 使用场景及目标:①开展高渗透率可再生能源接入下的综合能源系统多目标优化调度研究;②探究电动汽车集群在电网削峰填谷、平抑新能源出力波动及提供辅助服务方面的应用价值与潜力;③学习并掌握电氢耦合系统的建模方法、多目标优化求解技术及其在Matlab/Simulink环境下的仿真实现流程。; 阅读建议:此资源不仅提供可运行的代码,更蕴含了前沿的科研思路与创新方法,建议读者结合所提供的代码、数据与可能的论文文档,系统性地学习从问题建模、算法设计到仿真分析的完整科研过程,并重点关注其中关于需求侧资源聚合、多能互补协同与绿色低碳运行的核心理念。
内容概要:本文档名为《经济学期刊论文复现:数字化转型能促进企业的高质量发展吗》,表面上聚焦于经济学领域中数字化转型对企业高质量发展影响的研究,实则是一份涵盖多学科交叉的科研仿真代码资源合集。资源以Matlab、Simulink、Python为主要工具,系统整合了电力系统仿真、微电网优化调度、路径规划、信号处理、图像处理、机器学习预测模型等方向的可复现算法与仿真模型。尽管标题指向经济学实证分析,但内容重心在于提供顶级期刊论文的复现代码,如企业全要素生产率(TFP)测算方法(OL、FE、LP、OP、GMM)、风光储氢系统优化、需求响应与综合能源系统调度等,并融合智能优化算法与深度学习技术进行数据建模与预测分析,体现出极强的工程化与科研实用性。; 适合人群:具备一定编程基础,熟练掌握Matlab/Simulink/Python等仿真工具,从事工程仿真、经济实证研究或交叉学科科研工作的研究生、高校教师及科研人员。; 使用场景及目标:① 复现经济学顶刊论文中的计量经济模型,深入探究数字化转型对企业全要素生产率的影响机制;② 借助提供的代码资源开展电力系统故障仿真、微电网优化、多能系统调度等科研项目的算法验证与仿真分析;③ 应用机器学习与深度学习模型完成负荷预测、风电光伏出力预测、电池健康状态评估等典型实证任务; 阅读建议:此资源虽冠以经济学论文之名,实质为多领域高价值仿真代码集成,建议读者依据自身研究方向筛选适配内容,优先关注“顶刊复现”“论文复现”类项目,结合配套数据与代码进行实证推演,并通过公众号“荔枝科研社”获取完整资料与持续技术支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值