linux工作队列 - 把work_struct加入工作队列

文章系列

1.linux工作队列 - workqueue总览
2.linux工作队列 - workqueue_struct创建
3.linux工作队列 - 把work_struct加入工作队列
4.linux工作队列 - work_struct被调用过程

把work_struct加入工作队列

把work_struct加入到工作队列workqueue的API在系列文章1有介绍,这些API虽然用法不一,但是最终都会调用__queue_work()函数来进行实际加入操作,比如API:queue_work_on:

bool queue_work_on(int cpu, struct workqueue_struct *wq,
           struct work_struct *work)
{
    bool ret = false;
    unsigned long flags;

    local_irq_save(flags);

    if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
        __queue_work(cpu, wq, work);------调用到__queue_work
        ret = true;
    }

    local_irq_restore(flags);
    return ret;
}

下面分析__queue_work的代码,如下:

static void __queue_work(int cpu, struct workqueue_struct *wq,
             struct work_struct *work)
{
    struct pool_workqueue *pwq;
    struct worker_pool *last_pool;
    struct list_head *worklist;
    unsigned int work_flags;
    unsigned int req_cpu = cpu;

    /*
     * While a work item is PENDING && off queue, a task trying to
     * steal the PENDING will busy-loop waiting for it to either get
     * queued or lose PENDING.  Grabbing PENDING and queueing should
     * happen with IRQ disabled.
     */
    WARN_ON_ONCE(!irqs_disabled());

    debug_work_activate(work);

    /* if draining, only works from the same workqueue are allowed */
    if (unlikely(wq->flags & __WQ_DRAINING) &&
        WARN_ON_ONCE(!is_chained_work(wq)))
        return;
retry:
    if (req_cpu == WORK_CPU_UNBOUND)
        cpu = wq_select_unbound_cpu(raw_smp_processor_id());

    //根据传入的参数struct workqueue_struct *wq来选择需要的类型
    /* pwq which will be used unless @work is executing elsewhere */
    if (!(wq->flags & WQ_UNBOUND))
        pwq = per_cpu_ptr(wq->cpu_pwqs, cpu);----------------得到bound类型的pool_workqueue 
    else
        pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu));-----得到unbound类型的pool_workqueue 

    /*
     * If @work was previously on a different pool, it might still be
     * running there, in which case the work needs to be queued on that
     * pool to guarantee non-reentrancy.
     */
    //work有可能已经存在另一个不同的work pool中,它可能仍旧在运行,为了避免重入,work仍然要加入到原来的work pool
    last_pool = get_work_pool(work);
    if (last_pool && last_pool != pwq->pool) {
        struct worker *worker;

        spin_lock(&last_pool->lock);

        worker = find_worker_executing_work(last_pool, work);

        if (worker && worker->current_pwq->wq == wq) {
            pwq = worker->current_pwq;----------------------得到last的pool_workqueue
        } else {
            /* meh... not running there, queue here */
            spin_unlock(&last_pool->lock);
            spin_lock(&pwq->pool->lock);
        }
    } else {
        spin_lock(&pwq->pool->lock);
    }

    /*
     * pwq is determined and locked.  For unbound pools, we could have
     * raced with pwq release and it could already be dead.  If its
     * refcnt is zero, repeat pwq selection.  Note that pwqs never die
     * without another pwq replacing it in the numa_pwq_tbl or while
     * work items are executing on it, so the retrying is guaranteed to
     * make forward-progress.
     */
    if (unlikely(!pwq->refcnt)) {
        if (wq->flags & WQ_UNBOUND) {
            spin_unlock(&pwq->pool->lock);
            cpu_relax();
            goto retry;
        }
        /* oops */
        WARN_ONCE(true, "workqueue: per-cpu pwq for %s on cpu%d has 0 refcnt",
              wq->name, cpu);
    }

    /* pwq determined, queue */
    trace_workqueue_queue_work(req_cpu, pwq, work);

    if (WARN_ON(!list_empty(&work->entry))) {
        spin_unlock(&pwq->pool->lock);
        return;
    }

    pwq->nr_in_flight[pwq->work_color]++;
    work_flags = work_color_to_flags(pwq->work_color);

    if (likely(pwq->nr_active < pwq->max_active)) {
        trace_workqueue_activate_work(work);
        pwq->nr_active++;
        worklist = &pwq->pool->worklist;----------------得到pool_workqueue的worker_pool的worklist
        if (list_empty(worklist))
            pwq->pool->watchdog_ts = jiffies;
    } else {
        work_flags |= WORK_STRUCT_DELAYED;
        worklist = &pwq->delayed_works;
    }

    insert_work(pwq, work, worklist, work_flags);-----把work加入到worklist,并唤醒

    spin_unlock(&pwq->pool->lock);
}

看函数insert_work代码:

static void insert_work(struct pool_workqueue *pwq, struct work_struct *work,
            struct list_head *head, unsigned int extra_flags)
{
    struct worker_pool *pool = pwq->pool;

    /* we own @work, set data and link */
    set_work_pwq(work, pwq, extra_flags);
    list_add_tail(&work->entry, head);---------------加入work pool的链表
    get_pwq(pwq);

    /*
     * Ensure either wq_worker_sleeping() sees the above
     * list_add_tail() or we see zero nr_running to avoid workers lying
     * around lazily while there are works to be processed.
     */
    smp_mb();

    if (__need_more_worker(pool))
        wake_up_worker(pool);----------------------唤醒work pool
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 内核中,工作队列是一种异步执行的机制,它允许内核模块或驱动程序在后台执行工作工作队列工作项(work item)组成,每个工作项表示一个要执行的任务。内核模块或驱动程序可以将工作项添加到工作队列中,然后由内核调度器异步执行。 工作项是通过 work_struct 结构体来表示的。work_struct 结构体定义如下: ```c struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; ``` 其中,data 字段是一个原子长整型,用于存储工作项的状态信息。entry 字段是一个链表节点,用于将工作项添加到工作队列中。func 字段是一个指向工作项处理函数的指针,该函数将在工作项执行时被调用。 要创建一个新的工作项,可以使用 INIT_WORK 宏: ```c void INIT_WORK(struct work_struct *work, work_func_t func); ``` 其中,work 是指向要初始化的工作项的指针,func 是一个指向工作项处理函数的指针。 要将工作项添加到工作队列中,可以使用 queue_work 函数: ```c int queue_work(struct workqueue_struct *wq, struct work_struct *work); ``` 其中,wq 是指向要添加工作项的工作队列的指针,work 是指向要添加的工作项的指针。如果工作项成功添加到工作队列中,函数将返回 1,否则返回 0。 工作项处理函数的原型为: ```c typedef void (*work_func_t)(struct work_struct *work); ``` 其中,work 是指向正在处理的工作项的指针。 在处理工作项时,可以使用 container_of 宏将工作项的指针转换为包含它的结构体的指针: ```c struct my_work { struct work_struct work; int param1; char *param2; }; static void my_work_handler(struct work_struct *work) { struct my_work *my_work = container_of(work, struct my_work, work); int param1 = my_work->param1; char *param2 = my_work->param2; /* do something with param1 and param2 */ } ``` 在上面的示例中,我们定义了一个包含工作项的结构体 my_work,并在工作项处理函数中使用 container_of 宏将工作项的指针转换为 my_work 结构体的指针。然后,我们可以从 my_work 结构体中获取工作项的参数并使用它们进行处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值