转自:https://www.cnblogs.com/arnoldlu/p/8336998.html

关键词:kthread、irq、ksoftirqd、kworker、workqueues

在使用ps查看线程的时候,会有不少[...]名称的线程,这些有别于其它线程,都是内核线程。

其中多数内核线程从名称看,就知道其主要功能。

比如给中断线程化使用的irq内核线程,软中断使用的内核线程ksoftirqd,以及work使用的kworker内核线程。

本文首先概览一下Linux都有哪些内核线程,然后分析创建内核线程的API。

在介绍内核线程和普通线程都有哪些区别?

最后介绍主要内核线程(irq/ksoftirqd/kworker/)的创建过程及其作用。

1. ps下初步认识Linux内核线程

在ps -a会显示如下,可以看出内核线程都用[...]标注。

并且pid=1的init进程是所有用户空间进程的父进程;pid=2的kthreadd内核线程是所有内核线程的父线程。

内核线程分为几大类:softirq、kworker、irq及其他。

PID   USER     TIME   COMMAND
1 0 0:01 {linuxrc} init
2 0 0:00 [kthreadd]
3 0 0:00 [ksoftirqd/0]
4 0 0:00 [kworker/0:0]
5 0 0:00 [kworker/0:0H]
6 0 0:00 [kworker/u8:0]
7 0 0:00 [rcu_sched]
8 0 0:00 [rcu_bh]
9 0 0:00 [migration/0]
10 0 0:00 [migration/1]
11 0 0:00 [ksoftirqd/1]
12 0 0:00 [kworker/1:0]
13 0 0:00 [kworker/1:0H]
14 0 0:00 [migration/2]
15 0 0:00 [ksoftirqd/2]
16 0 0:00 [kworker/2:0]
17 0 0:00 [kworker/2:0H]
18 0 0:00 [migration/3]
19 0 0:00 [ksoftirqd/3]
20 0 0:00 [kworker/3:0]
21 0 0:00 [kworker/3:0H]
22 0 0:00 [khelper]
23 0 0:00 [kdevtmpfs]
24 0 0:00 [perf]
25 0 0:00 [kworker/u8:1]
279 0 0:00 [khungtaskd]
280 0 0:00 [writeback]
281 0 0:00 [kintegrityd]
282 0 0:00 [kworker/0:1]
284 0 0:00 [bioset]
286 0 0:00 [kblockd]
294 0 0:00 [ata_sff]
408 0 0:00 [rpciod]
409 0 0:00 [kworker/2:1]
410 0 0:00 [kworker/1:1]
412 0 0:00 [kswapd0]
416 0 0:00 [fsnotify_mark]
429 0 0:00 [nfsiod]
449 0 0:00 [kworker/3:1]
527 0 0:00 [kpsmoused]
537 0 0:00 [kworker/1:2]
613 0 0:00 [deferwq]

2. kthreadd以及创建内核线程API

2.1 kthreadd:kthreadd内核线程的创建

内核其他线程的创立,要基于kthreadd。kthreadd线程是其他线程的父线程。

start_kernel-->rest_init如下:

static noinline void __init_refok rest_init(void)
{
int pid; rcu_scheduler_starting();
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS);--------------------------------创建第一个用户空间线程init
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);---------------创建第一个内核线程kthreadd
rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);--------------------kthreadd_task指向kthreadd的task_strcut结构体
rcu_read_unlock();
complete(&kthreadd_done);--------------------------------------------------在init进程kernel_init-->kernel_init_freeable中等待kthreadd_done释放 /*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);
}

kernel_init在kthreadd之前启动,但是kernel_init的很多任务需要基于kthreadd。所以在kernel_init的开头等待reset_init的kthreadd_done完成量。

因为kernel_init-->kernel_init_freeable-->do_basic_setup-->do_initcalls中很多初始化需要kthread_create支援。

kernel_init-->kernel_init_freeable:
static noinline void __init kernel_init_freeable(void)
{
/*
* Wait until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done);-------------------等待kthreadd_done完成量
...

do_basic_setup();---------------------------------------很多初始化需要kthread_create支持

...
}

内核中有一个线程kthreadd_task负责创建其他内核线程,这个线程的函数为kthreadd()。

int kthreadd(void *unused)
{
struct task_struct *tsk = current; /* Setup a clean context for our children to inherit. */
set_task_comm(tsk, "kthreadd");
ignore_signals(tsk);
set_cpus_allowed_ptr(tsk, cpu_all_mask);
set_mems_allowed(node_states[N_MEMORY]); current->flags |= PF_NOFREEZE; for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&kthread_create_list))
schedule();----------------------------------------------如果kthread_create_list为空,让出CPU,进入休眠状态。在kthread_create_on_node()中会将要创建进程节点加入到kthread_create_list中,然后唤醒此进程。
__set_current_state(TASK_RUNNING); spin_lock(&kthread_create_lock);
while (!list_empty(&kthread_create_list)) {------------------只要kthread_create_list不为空,遍历kthread_create_list链表
struct kthread_create_info *create; create = list_entry(kthread_create_list.next,
struct kthread_create_info, list);
list_del_init(&create->list);----------------------------从kthread_create_list中摘除当前create
spin_unlock(&kthread_create_lock); create_kthread(create);----------------------------------创建线程 spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
} return 0;
} static void create_kthread(struct kthread_create_info *create)
{
int pid; #ifdef CONFIG_NUMA
current->pref_node_fork = create->node;
#endif
/* We want our own signal handler (we take no signals by default). */
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);----调用do_fork()创建线程
if (pid < 0) {
/* If user was SIGKILLed, I release the structure. */
struct completion *done = xchg(&create->done, NULL); if (!done) {
kfree(create);
return;
}
create->result = ERR_PTR(pid);
complete(done);--------------------------------------------------------触发complete事件
}
} pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
(unsigned long)arg, NULL, NULL);
}

2.2 创建内核线程接口:kthread_create等

kthread_create()是最常见的创建内核线程的接口。

kthread_create_on_cpu()相对于kthread_create多了个cpu,但都基于kthread_create_on_node()。

kthread_run基于kthreadd_create,所以这些函数都基于kthread_create_on_node。

#define kthread_create(threadfn, data, namefmt, arg...) \kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)

struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
void *data,
unsigned int cpu,
const char *namefmt); /**
* kthread_run - create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).
*/
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
=kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \--------------------------如果kthread_create()正确创建了一个进程,调用wake_up_process()唤醒它。
wake_up_process(__k); \
__k; \
})

kthread_create_on_node()负责创建一个线程,填充一个kthread_create_info结构体;然后将此结构体作为一个节点插入kthread_create_list队尾。

然后唤醒kthreadd_task进行处理,创建线程。

struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
void *data, int node,
const char namefmt[],
...)
{
DECLARE_COMPLETION_ONSTACK(done);
struct task_struct *task;
struct kthread_create_info *create = kmalloc(sizeof(*create),
GFP_KERNEL);---------------------------------创建插入kthread_create_list的节点。 if (!create)
return ERR_PTR(-ENOMEM);
create->threadfn = threadfn;
create->data = data;
create->node = node;
create->done = &done; spin_lock(&kthread_create_lock);
list_add_tail(&create->list, &kthread_create_list);-------------------将填充的节点插入kthread_create_list中。
spin_unlock(&kthread_create_lock); wake_up_process(kthreadd_task);---------------------------------------唤醒kthread_task处理kthread_create_list链表,创建相应的线程。
/*
* Wait for completion in killable state, for I might be chosen by
* the OOM killer while kthreadd is trying to allocate memory for
* new kernel thread.
*/
if (unlikely(wait_for_completion_killable(&done))) {------------------等待complete事件触发,在create_kthread()中触发。
/*
* If I was SIGKILLed before kthreadd (or new kernel thread)
* calls complete(), leave the cleanup of this structure to
* that thread.
*/
if (xchg(&create->done, NULL))
return ERR_PTR(-EINTR);
/*
* kthreadd (or new kernel thread) will call complete()
* shortly.
*/
wait_for_completion(&done);---------------------------------------等待complete事件触发。
}
task = create->result;------------------------------------------------创建的结果为task_struct结构体。
if (!IS_ERR(task)) {
static const struct sched_param param = { .sched_priority = 0 };
va_list args; va_start(args, namefmt);
vsnprintf(task->comm, sizeof(task->comm), namefmt, args);---------配置进程名称。
va_end(args);
/*
* root may have changed our (kthreadd's) priority or CPU mask.
* The kernel thread should not inherit these properties.
*/
sched_setscheduler_nocheck(task, SCHED_NORMAL, &param);-----------设置进程调度策略为NORMAL,优先级为0。
set_cpus_allowed_ptr(task, cpu_all_mask);
}
kfree(create);--------------------------------------------------------释放kthread_create_info。
return task;
}

3. 内核线程和普通线程的区别

内核线程没有地址空间,所以task_struct->mm指针为NULL。内核线程没有用户上下文。

内核线程只工作在内核空间,不会切换至用户空间。但内核线程同样是可调度且可抢占的。

普通线程即可工作在内核空间,也可工作在用户空间。

内核线程只能访问3GB以上地址,而普通线程可访问所有4GB地址空间。

4. irq、softirq、woker内核线程

irq、softirq、worker都可能创建对应的内核线程,有线程就有优先级。

下面从优先来来看看它们的重要性。

可以看出中断内核线程优先级很高,为49,并且使用了实时调度策略。softirq和worker都是普通内核线程。

  prio policy
irq 49 SCHED_FIFO
softirq 120 SCHED_NORMAL
worker 120 SCHED_NORMAL
init 120 SCHED_NORMAL
kthreadd 120 SCHED_NORMAL
cfinteractive 0 SCHED_FIFO

其它特殊内核线程init优先级为120,kthreadd优先级为120.

cfinteractive优先级最高,主要处理CPU Frequency负载更新。

4.1 irq/xx-xx:创建处理线程化中断的线程

request_threaded_irq-->__setup_irq,可见如果设置了thread_fn,并且不允许中断嵌套,则创建一个类似"irq/中断号-终端名称"的线程。

线程函数是irq_thread,

/*
* Internal function to register an irqaction - typically used to
* allocate special interrupts that are part of the architecture.
*/
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
...
if (new->thread_fn && !nested) {
struct task_struct *t;
static const struct sched_param param = {
.sched_priority = MAX_USER_RT_PRIO/2,
}; t = kthread_create(irq_thread, new, "irq/%d-%s", irq,----------------在irq_thread中调用irq_thread_fn,进而调用action->thread_fn,request_threaded_irq参数thread_fn。
new->name);
...
}
...
}

request_irq是对request_threaded_irq的封装,创建中断线程的工作交给__setup_irq()

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

更详细信息参考:《Linux中断管理 (1)Linux中断管理机制》中关于request_irq()介绍。

4.2 ksoftirqd/xx:创建处理软中断线程

软中断线程通过smpboot_register_percpu_thread注册softirq_threads创建。

static struct smp_hotplug_thread softirq_threads = {
.store = &ksoftirqd,
.thread_should_run = ksoftirqd_should_run,
.thread_fn = run_ksoftirqd,
.thread_comm = "ksoftirqd/%u",
}; static __init int spawn_ksoftirqd(void)
{
register_cpu_notifier(&cpu_nfb); BUG_ON(smpboot_register_percpu_thread(&softirq_threads)); return 0;
}

smpboot_register_percpu_thread-->__smpboot_create_thread,最终也还是调用kthread_create_on_cpu,创建了类似"ksoftirqd/xx"的内核线程,xx为cpuid号。

从ps -a中可以看出创建的结果如下,可以看出每个CPU创建了一个ksoftirqd内核线程。

    3 0          0:03 [ksoftirqd/0]
11 0 0:03 [ksoftirqd/1]
15 0 0:00 [ksoftirqd/2]
19 0 0:00 [ksoftirqd/3]

更详细信息参考:  《Linux中断管理 (2)软中断和tasklet

4.3 kworker:创建work的工作线程

kwoker线程是处理work的工作线程,详细参考《Linux中断管理 (3)workqueue工作队列》。

每个CPU都会创建自己的workqueue,用以集中处理内核kworker。

workquuue就是把一些任务(work)推迟到一个或一组内核线程中去执行,那个内核线程被称为worker_thread。

首先看看创建结果,可以看出在init_workqueues中创建了绑定CPU0的两个kworker,分别是nice=0和nice=-20。

apply_workqueue_attrs创建unbund worker,即kworker/u8:0。

然后在每个CPU_UP_PREPARE回调中创建两个不同nice的kworker。所以四个CPU一共9个内核线程。

PID   USER     TIME   COMMAND
1 0 0:01 {linuxrc} init
2 0 0:00 [kthreadd]
3 0 0:00 [ksoftirqd/0]
4 0 0:00 [kworker/0:0]
5 0 0:00 [kworker/0:0H]---------------init_workqueues-->create_worker
6 0 0:00 [kworker/u8:0]---------------apply_workqueue_attrs-->alloc_unbound_pwq-->create_worker
7 0 0:00 [rcu_sched]
8 0 0:00 [rcu_bh]
9 0 0:00 [migration/0]
10 0 0:00 [migration/1]
11 0 0:00 [ksoftirqd/1]
12 0 0:00 [kworker/1:0]---------------workqueue_cpu_up_callback-->create_worker
13 0 0:00 [kworker/1:0H]
14 0 0:00 [migration/2]
15 0 0:00 [ksoftirqd/2]
16 0 0:00 [kworker/2:0]
17 0 0:00 [kworker/2:0H]--------------workqueue_cpu_up_callback-->create_worker
18 0 0:00 [migration/3]
19 0 0:00 [ksoftirqd/3]
20 0 0:00 [kworker/3:0]
21 0 0:00 [kworker/3:0H]--------------workqueue_cpu_up_callback-->create_worker
22 0 0:00 [khelper]
23 0 0:00 [kdevtmpfs]
24 0 0:00 [perf]
25 0 0:00 [kworker/u8:1]--------------worker_thread-->create_worker
279 0 0:00 [khungtaskd]
280 0 0:00 [writeback]
281 0 0:00 [kintegrityd]
282 0 0:00 [kworker/0:1]---------------worker_thread-->create_worker
284 0 0:00 [bioset]
286 0 0:00 [kblockd]
294 0 0:00 [ata_sff]
408 0 0:00 [rpciod]
409 0 0:00 [kworker/2:1]---------------worker_thread-->create_worker
410 0 0:00 [kworker/1:1]---------------worker_thread-->create_worker
412 0 0:00 [kswapd0]
416 0 0:00 [fsnotify_mark]
429 0 0:00 [nfsiod]
449 0 0:00 [kworker/3:1]---------------worker_thread-->create_worker
527 0 0:00 [kpsmoused]
537 0 0:00 [kworker/1:2]---------------worker_thread-->create_worker
613 0 0:00 [deferwq]

init_workqueues-->create_worker-->kthread_create_on_node,创建"kworker/xx:xxH"内核线程。

static int __init init_workqueues(void)
{
int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
int i, cpu;
...
/* create the initial worker */
for_each_online_cpu(cpu) {---------------------------------遍历CPU[0~3]
struct worker_pool *pool; for_each_cpu_worker_pool(pool, cpu) {------------------NR_STD_WORKER_POOLS=2,所以每个CPU有两个pool
pool->flags &= ~POOL_DISASSOCIATED;
BUG_ON(!create_worker(pool));
}
}
...
system_wq = alloc_workqueue("events", 0, 0);
system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0);
system_long_wq = alloc_workqueue("events_long", 0, 0);
system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND,
WQ_UNBOUND_MAX_ACTIVE);
system_freezable_wq = alloc_workqueue("events_freezable",
WQ_FREEZABLE, 0);
system_power_efficient_wq = alloc_workqueue("events_power_efficient",
WQ_POWER_EFFICIENT, 0);
system_freezable_power_efficient_wq = alloc_workqueue("events_freezable_power_efficient",
WQ_FREEZABLE | WQ_POWER_EFFICIENT,
0);
BUG_ON(!system_wq || !system_highpri_wq || !system_long_wq ||
!system_unbound_wq || !system_freezable_wq ||
!system_power_efficient_wq ||
!system_freezable_power_efficient_wq);
return 0;
}

create_worker()函数创建工作线程。

static struct worker *create_worker(struct worker_pool *pool)
{
...
if (pool->cpu >= 0)
snprintf(id_buf, sizeof(id_buf), "%d:%d%s", pool->cpu, id,-------------cpuid和id,区分cpu和cpu内kworker。
pool->attrs->nice < 0 ? "H" : "");
else
snprintf(id_buf, sizeof(id_buf), "u%d:%d", pool->id, id);--------------u表示不指定cpu。 worker->task = kthread_create_on_node(worker_thread, worker, pool->node,
"kworker/%s", id_buf);
...
}

更详细信息参考:《Linux中断管理 (3)workqueue工作队列》、《Linux workqueue工作原理》、《Concurrency Managed Workqueue之(一):workqueue的基本概念

5. 其他内核线程

rcu_sched、rcu_bh

migration

khelper

kdevtmpfs

perf

writeback

kintegrityd

bioset

kblockd

ata_sff

rpciod

kswapd

nfsiod

kpsmpused

deferwq

联系方式:arnoldlu@qq.com

Linux进程管理 (篇外)内核线程简要介绍【转】的更多相关文章

  1. Linux进程管理 (篇外)内核线程简要介绍

    关键词:kthread.irq.ksoftirqd.kworker.workqueues 在使用ps查看线程的时候,会有不少[...]名称的线程,这些有别于其它线程,都是内核线程. 其中多数内核线程从 ...

  2. Linux进程管理(三、 线程)

    // ---- refer glibc, pthread_create.c ----// int __pthread_create_2_0 (newthread, attr, start_routin ...

  3. Linux进程管理专题

    Linux进程管理 (1)进程的诞生介绍了如何表示进程?进程的生命周期.进程的创建等等? Linux支持多种调度器(deadline/realtime/cfs/idle),其中CFS调度器最常见.Li ...

  4. Linux进程管理 (1)进程的诞生

    专题:Linux进程管理专题 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 L ...

  5. Linux进程管理 (2)CFS调度器

    关键词: 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 Linux进程管理 ( ...

  6. Linux进程管理子系统分析【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51298732 Linux进程管理: 进程与程序: 程序:存放在磁盘上的一系列代码 ...

  7. Linux进程管理知识整理

    Linux进程管理知识整理 1.进程有哪些状态?什么是进程的可中断等待状态?进程退出后为什么要等待调度器删除其task_struct结构?进程的退出状态有哪些? TASK_RUNNING(可运行状态) ...

  8. Linux 进程管理剖析--转

    地址:http://www.ibm.com/developerworks/cn/linux/l-linux-process-management/index.html Linux 是一种动态系统,能够 ...

  9. Linux性能及调优指南(翻译)之Linux进程管理

    本文为IBM RedBook的Linux Performanceand Tuning Guidelines的1.1节的翻译原文地址:http://www.redbooks.ibm.com/redpap ...

随机推荐

  1. Shell命令-用户用户组管理之userdel、groupadd

    文件及内容处理 - userdel.groupadd 1. userdel:删除用户 userdel命令的功能说明 userdel 命令用于删除用户帐号.userdel 可删除用户帐号与相关的文件.若 ...

  2. VMware workstation安装centos7,无ifconfig命令

    一.centos7默认未启动ifconfig,vi /etc/sysconfig/network-scripts/ifcfg-ens33,改成ONBOOT=yes: systemctl restart ...

  3. Xshell使用教程

    Xshell 是一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议.Xshell 通过互联网到远程主机的安全连接以及它创新性的设 ...

  4. Django cache (缓存)

    五种配置 1.开发调试 # 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backend ...

  5. react中使用动画

    1. css原生动画的使用 import React, { useState } from "react" import "./index.css" funct ...

  6. Linux学习笔记-第17天 有点赶

    第十章开始进度感觉有些快,该加把劲儿了

  7. session未释放

    客户反映一个关于session的bug,说有了1,2天以后,就无法登陆了. 我们这边试了好几天,都没有出现类似问题,后来没办法,只能远程开会,7点左右和他们通话,偶然发现他们居然直接关remote d ...

  8. POJ2976Dropping tests(分数规划)

    传送门 题目大意:n个二元组a[i],b[i],去掉k个,求sigma a[i]/ sigma b[i]的最大值 代码: #include<iostream> #include<cs ...

  9. Paper | Multi-scale Dense Networks for Resource Efficient Image Classification

    目录 故事背景 方法 两种加速策略 网络设计 网络优化 失败设计 回头品味 实验 数据集和数据处理 结果 第二次阅读 本文不是第一个提出early exit思想的 写作流畅 网络回顾 其他 发表在IC ...

  10. leetcode 410. 分割数组的最大值(二分法)

    1. 题目描述 给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组.设计一个算法使得这 m 个子数组各自和的最大值最小. 注意: 数组长度 n 满足以下条件: 1 ≤ n ...