workqueue (2)

有关workqueue 数据结构是怎样组织的?

/************************************************************************/

首先是per-cpu变量:
struct global_cwq:赋值为:实际上per-cpu变量是global变量,可以直接得到
        INIT_LIST_HEAD(&gcwq->worklist);/*这里的worklist指的是什么?*/
        gcwq->cpu = cpu;
根据输入参数global_cwq创建执行实体worker
struct worker *create_worker(struct global_cwq *gcwq, bool bind)

创建workqueue_struct
    struct workqueue_struct *wq;
    wq = kzalloc(sizeof(*wq) + namelen, GFP_KERNEL);
    INIT_LIST_HEAD(&wq->list);
    list_add(&wq->list, &workqueues);
然后根据该workqueue_struct创建per-cpu变量 struct cpu_workqueue_struct:

到这里已经创建了变量:global_cwq/ worker; workqueue_struct/cpu_workqueue_struct
还有一个常见的work_struct没有create那?

这些数据结构是怎样组织在一起的那?

从进程的执行体开始看:

create_worker -> kthread_create_on_node/kthread_create: worker_thread.

/**
 * worker_thread - the worker thread function
 * @__worker: self
 *
 * The gcwq worker thread function.  There's a single dynamic pool of
 * these per each cpu.  These workers process all works regardless of
 * their specific target workqueue.  The only exception is works which
 * belong to workqueues with a rescuer which will be explained in
 * rescuer_thread().
 */

static int worker_thread(void *__worker)
{
    struct worker *worker = __worker;
    struct global_cwq *gcwq = worker->gcwq;

    /* tell the scheduler that this is a workqueue worker
         * scheduler会对PF_WQ_WORKER进行特殊处理?
     */
    worker->task->flags |= PF_WQ_WORKER;
    /*worker_leave_idle:
     *idle worker减1,从worker的 idle list中删除一项?
         **/
    worker_leave_idle(worker);

    /* no more worker necessary?
         *need_more_worker-> !list_empty(&gcwq->worklist)
         *这里的worklist是什么元素:work_struct?
      */
    if (!need_more_worker(gcwq))
        goto sleep;

    /*
     * When control reaches this point, we're guaranteed to have
     * at least one idle worker or that someone else has already
     * assumed the manager role.
     */
    worker_clr_flags(worker, WORKER_PREP);

    do {
        /*从gcwq->worklist得到其所在的work_struct*/
        struct work_struct *work =
            list_first_entry(&gcwq->worklist,
                     struct work_struct, entry);
        /*最终调到process_scheduled_works
        *worker->scheduled所在元素也是 work_struct,从这里看这个thread有两层循环
        *外层是gcwq->worklist,内层是worker->scheduled
        *static void process_scheduled_works(struct worker *worker)
        *{
        *    while (!list_empty(&worker->scheduled)) {
        *        struct work_struct *work = list_first_entry(&worker->scheduled,
        *                        struct work_struct, entry);
        *        process_one_work(worker, work);
        *    }
        *}
         **/
        if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) {
            /* optimization path, not strictly necessary */
            process_one_work(worker, work);
            if (unlikely(!list_empty(&worker->scheduled)))
                process_scheduled_works(worker);
        } else {
            move_linked_works(work, &worker->scheduled, NULL);
            process_scheduled_works(worker);
        }
    } while (keep_working(gcwq));

    worker_enter_idle(worker);
    __set_current_state(TASK_INTERRUPTIBLE);
    schedule();
    goto woke_up;
}

/**
 * process_one_work - process single work
 * @worker: self
 * @work: work to process
 *
 * Process @work.  This function contains all the logics necessary to
 * process a single work including synchronization against and
 * interaction with other workers on the same cpu, queueing and
 * flushing.  As long as context requirement is met, any worker can
 * call this function to process a work.
 */
static void process_one_work(struct worker *worker, struct work_struct *work)
{
    struct cpu_workqueue_struct *cwq = get_work_cwq(work);
    struct global_cwq *gcwq = cwq->gcwq;
    work_func_t f = work->func;

    work_clear_pending(work);
    /*这个函数还是很复杂的,但是最后总是用调用 work function*/
    f(work);

}

从这里看关键是怎样赋值gcwq->worklist and worker->scheduled

/*****************************************************************************/
/**还是从queue_work开始查吧!
 * queue_work - queue work on a workqueue
 * @wq: workqueue to use
 * @work: work to queue
 *
 * Returns 0 if @work was already on a queue, non-zero otherwise.
 *
 * We queue the work to the CPU on which it was submitted, but if the CPU dies
 * it can be processed by another CPU.
 */
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
    int ret;

    ret = queue_work_on(get_cpu(), wq, work);
    put_cpu();

    return ret;
}

crash> work_struct
struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
}
SIZE: 16

/**
 * queue_work_on - queue work on specific cpu
 * @cpu: CPU number to execute work on
 * @wq: workqueue to use
 * @work: work to queue
 *
 * Returns 0 if @work was already on a queue, non-zero otherwise.
 *
 * We queue the work to a specific CPU, the caller must ensure it
 * can't go away.
 */
int
queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work)
{
    int ret = 0;

    if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
        __queue_work(cpu, wq, work);
        ret = 1;
    }
    return ret;
}

/* WQ_UNBOUND = 1 << 1, not bound to any cpu */
static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
             struct work_struct *work)
{
    struct global_cwq *gcwq;
    struct cpu_workqueue_struct *cwq;
    struct list_head *worklist;
    unsigned int work_flags;

    gcwq = get_gcwq(cpu);

    /* determine gcwq to use */
    /* gcwq determined, get cwq and queue */
    cwq = get_cwq(gcwq->cpu, wq);
    work_flags = work_color_to_flags(cwq->work_color);
    if (likely(cwq->nr_active < cwq->max_active)) {
        cwq->nr_active++;/*number of active works*/
        worklist = gcwq_determine_ins_pos(gcwq, cwq);
        /*&gcwq->worklist;*/
    } else {
        work_flags |= WORK_STRUCT_DELAYED;
        worklist = &cwq->delayed_works;
    }/*得到cpu_workqueue_struct, worklist 把work加入到worklist中*/
    insert_work(cwq, work, worklist, work_flags);
}

static void insert_work(struct cpu_workqueue_struct *cwq,
            struct work_struct *work, struct list_head *head,
            unsigned int extra_flags)
{
    struct global_cwq *gcwq = cwq->gcwq;

    list_add_tail(&work->entry, head);
    wake_up_worker(gcwq);
}

/**
 * wake_up_worker - wake up an idle worker
 * @gcwq: gcwq to wake worker for
 *
 * Wake up the first idle worker of @gcwq.
 *
 * CONTEXT:
 * spin_lock_irq(gcwq->lock).
 */
static void wake_up_worker(struct global_cwq *gcwq)
{
    struct worker *worker = first_worker(gcwq);

    if (likely(worker))
        wake_up_process(worker->task);
}

static struct worker *first_worker(struct global_cwq *gcwq)
{
    if (unlikely(list_empty(&gcwq->idle_list)))
        return NULL;

    return list_first_entry(&gcwq->idle_list, struct worker, entry);
}

到此,有关workqueue心里亮敞了吗?


怎样得到当前没有处理的wok_struct

crash> global_cwq
PER-CPU DATA TYPE:
  struct global_cwq global_cwq;
PER-CPU ADDRESSES:
  [0]: c1118680
  [1]: c1120680

crash> set radix 16
output radix: 16 (hex)
crash> struct global_cwq -o
struct global_cwq {
    [0x0] spinlock_t lock;
   [0x14] struct list_head worklist;
   [0x1c] unsigned int cpu;
   [0x20] unsigned int flags;
   [0x24] int nr_workers;
   [0x28] int nr_idle;
   [0x2c] struct list_head idle_list;
   [0x34] struct hlist_head busy_hash[64];
  [0x134] struct timer_list idle_timer;
  [0x150] struct timer_list mayday_timer;
  [0x16c] struct ida worker_ida;
  [0x194] struct task_struct *trustee;
  [0x198] unsigned int trustee_state;
  [0x19c] wait_queue_head_t trustee_wait;
  [0x1b8] struct worker *first_idle;
}
SIZE: 0x1c0
crash> struct global_cwq c1118680
struct global_cwq {
  lock = {
    {
      rlock = {
        raw_lock = {
          lock = 0
        },
        break_lock = 0,
        magic = 3735899821,
        owner_cpu = 4294967295,
        owner = 0xffffffff
      }
    }
  },
  worklist = {
    next = 0xc1118694,
    prev = 0xc1118694
  }
,
  cpu = 0,
}

c1118680+14 = c1118694:

可知这里的worklist指向header, 所以是空。当前没有未被处理的work_struct.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值