走入 Linux 的殿堂现已有一年有余了,在这里我想将 Linux 的各种完成机制剖析一遍,一方面临自己来说也是温故而知新,另一方面,促进我们的沟通,最好能够给我们一些抛砖引玉的启迪。我是硬件身世,搞硬件现已好多年了,从是专门软件开发也挨近两年了,在这一段时刻内我越发以为软硬件协同规划是未来开展的干流,软硬件的边界越来越含糊,软硬件的规划思维是相通的,完成办法是各异的,完成的成果上当然也存在较大不同,因而,很有必要做好软硬件的协同规划。本着这样的主意,我想将我所知道的 Linux 剖析一遍,特别是一些我以为精华和重要的机制,别的在评论过程中,我会刺进一些其他的 OS 完成机制,进行比照剖析,我把这一类 blog 文章划归为“ Linux 机制剖析”,期望我们支撑。
什么是 workqueue ?
Linux 中的 Workqueue 机制便是为了简化内核线程的创立。经过调用 workqueue 的接口就能创立内核线程。而且能够依据当时体系 CPU 的个数创立线程的数量,使得线程处理的业务能够并行化。
workqueue 是内核中完成简略而有用的机制,他明显简化了内核 daemon 的创立,便利了用户的编程,
Workqueue 机制的完成
Workqueue 机制中界说了两个重要的数据结构,剖析如下:
1、cpu_workqueue_struct 结构。该结构将 CPU 和内核线程进行了绑定。在创立 workqueue 的过程中, Linux 依据当时体系 CPU 的个数创立 cpu_workqueue_struct 。在该结构首要保护了一个使命行列,以及内核线程需求睡觉的等候行列,别的还保护了一个使命上下文,即 task_struct 。
2、work_struct 结构是对使命的笼统。在该结构中需求保护详细的使命办法,需求处理的数据,以及使命处理的时刻。该结构界说如下:
struct work_struct {
unsigned long pending;
struct list_head entry; /* 将使命挂载到 queue 的挂载点 */
void (*func)(void *); /* 使命办法 */
void *data; /* 使命处理的数据 */
void *wq_data; /* work 的属主 */
strut timer_list timer; /* 使命延时处理定时器 */
};
当用户调用 workqueue 的初始化接口 create_workqueue 或许 create_singlethread_workqueue 对 workqueue 行列进行初始化时,内核就开端为用户分配一个 workqueue 目标,而且将其链到一个大局的 workqueue 行列中。然后 Linux 依据当时 CPU 的状况,为 workqueue 目标分配与 CPU 个数相同的 cpu_workqueue_struct 目标,每个 cpu_workqueue_struct 目标都会存在一条使命行列。紧接着, Linux 为每个 cpu_workqueue_struct 目标分配一个内核 thread ,即内核 daemon 去处理每个行列中的使命。至此,用户调用初始化接口将 workqueue 初始化结束,回来 workqueue 的指针。
在初始化 workqueue 过程中,内核需求初始化内核线程,注册的内核线程作业比较简略,便是不断的扫描对应 cpu_workqueue_struct 中的使命行列,从中获取一个有用使命,然后履行该使命。所以假如使命行列为空,那么内核 daemon 就在 cpu_workqueue_struct 中的等候行列上睡觉,直到有人唤醒 daemon 去处理使命行列。
Workqueue 初始化结束之后,将使命运转的上下文环境构建起来了,可是详细还没有可履行的使命,所以,需求界说详细的 work_struct 目标。然后将 work_struct 加入到使命行列中, Linux 会唤醒 daemon 去处理使命。
上述描绘的 workqueue 内核完成原理能够描绘如下:
在 Workqueue 机制中,供给了一个体系默许的 workqueue 行列—— keventd_wq ,这个行列是 Linux体系在初始化的时分就创立的。用户能够直接初始化一个 work_struct 目标,然后在该行列中进行调度,运用愈加便利。
Workqueue 编程接口
序号 |
接口函数 |
阐明 |
1 |
create_workqueue |
用于创立一个 workqueue 行列,为体系中的每个 CPU 都创立一个内核线程。输入参数: @name : workqueue 的称号 |
2 |
create_singlethread_workqueue |
用于创立 workqueue ,只创立一个内核线程。输入参数: @name : workqueue 称号 |
3 |
destroy_workqueue |
开释 workqueue 行列。输入参数: @ workqueue_struct :需求开释的workqueue 行列指针 |
4 |
schedule_work |
调度履行一个详细的使命,履行的使命将会被挂入 Linux 体系供给的 workqueue ——keventd_wq 输入参数: @ work_struct :详细使命目标指针 |
5 |
schedule_delayed_work |
推迟必定时刻去履行一个详细的使命,功能与schedule_work 相似,多了一个推迟时刻,输入参数: @work_struct :详细使命目标指针 @delay :推迟时刻 |