struct workqueue_struct { ... /* hot fields used during command issue, aligned to cacheline */ unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */ struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */ struct pool_workqueue __rcu *numa_pwq_tbl[]; /* FR: unbound pwqs indexed by node */ };
最终都指向了同一个worker_pool结构体。
2. 下面对pool_workqueue结构体进行介绍,pool_workqueue结构体定
义如下:
/* * The per-pool workqueue. While queued, the lower WORK_STRUCT_FLAG_BITS * of work_struct->data are used for flags and the remaining high bits * point to the pwq; thus, pwqs need to be aligned at two's power of the * number of flag bits. */ struct pool_workqueue { struct worker_pool *pool; /* I: the associated pool */ struct workqueue_struct *wq; /* I: the owning workqueue */ ... } __aligned(1 << WORK_STRUCT_FLAG_BITS);
3. worker_pool用来表示工作者线程池,我们在内核文档中已经可以
看到每个CPU包含两个线程池,因此可以找到下面的代码:
/* the per-cpu worker pools */ static DEFINE_PER_CPU_SHARED_ALIGNED(struct worker_pool [NR_STD_WORKER_POOLS], cpu_worker_pools);
/* PL: hash of all unbound pools keyed by pool->attrs */ static DEFINE_HASHTABLE(unbound_pool_hash, UNBOUND_POOL_HASH_ORDER);
/* I: attributes used when instantiating standard unbound pools on demand */ static struct workqueue_attrs *unbound_std_wq_attrs[NR_STD_WORKER_POOLS];
在workqueue.c的create_worker函数中可以发现下面的代码:
worker->task = kthread_create_on_node(worker_thread, worker, pool->node, "kworker/%s", id_buf);
5. 下面将分析worker_thread函数:
if (unlikely(worker->flags & WORKER_DIE)) { spin_unlock_irq(&pool->lock); WARN_ON_ONCE(!list_empty(&worker->entry)); worker->task->flags &= ~PF_WQ_WORKER; return 0; }
worker_leave_idle(worker); recheck: /* no more worker necessary? */ if (!need_more_worker(pool)) goto sleep;
/* do we need to manage? */ if (unlikely(!may_start_working(pool)) && manage_workers(worker)) goto recheck;
if (!mutex_trylock(&pool->manager_arb)) return ret; /* * With manager arbitration won, manager_mutex would be free in * most cases. trylock first without dropping @pool->lock. */ if (unlikely(!mutex_trylock(&pool->manager_mutex))) { spin_unlock_irq(&pool->lock); mutex_lock(&pool->manager_mutex); spin_lock_irq(&pool->lock); ret = true; }
/* * Destroy and then create so that may_start_working() is true * on return. */ ret |= maybe_destroy_workers(pool);
while (too_many_workers(pool)) {
/* * nr_idle and idle_list may disagree if idle rebinding is in * progress. Never return %true if idle_list is empty. */ if (list_empty(&pool->idle_list)) return false; return nr_idle > 2 && (nr_idle - 2) * MAX_IDLE_WORKERS_RATIO >= nr_busy;
worker = list_entry(pool->idle_list.prev, struct worker, entry); expires = worker->last_active + IDLE_WORKER_TIMEOUT; if (time_before(jiffies, expires)) { mod_timer(&pool->idle_timer, expires); break; } destroy_worker(worker); ret = true;
ret |= maybe_create_worker(pool);
if (!need_to_create_worker(pool)) return false;
return need_more_worker(pool) && !may_start_working(pool);
return !list_empty(&pool->worklist) && __need_more_worker(pool);
return !atomic_read(&pool->nr_running);
/* if we don't make progress in MAYDAY_INITIAL_TIMEOUT, call for help */ mod_timer(&pool->mayday_timer, jiffies + MAYDAY_INITIAL_TIMEOUT);
setup_timer(&pool->mayday_timer, pool_mayday_timeout, (unsigned long)pool);
while (true) { struct worker *worker; worker = create_worker(pool);
start_worker(worker);
5.2
do { struct work_struct *work = list_first_entry(&pool->worklist, struct work_struct, entry);
if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) { /* optimization path, not strictly necessary */ process_one_work(worker, work);
worker->current_func = work->func; ... worker->current_func(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(pool));
6. 工作者线程在什么时候被调度?
schedule_work | \--> queue_work | \--> queue_work_on | \--> __queue_work | \--> insert_work | \--> wake_up_worker
if (__need_more_worker(pool)) wake_up_worker(pool);