关键词: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
: {linuxrc} init
: [kthreadd]
: [ksoftirqd/]
: [kworker/:]
: [kworker/:0H]
: [kworker/u8:]
: [rcu_sched]
: [rcu_bh]
: [migration/]
: [migration/]
: [ksoftirqd/]
: [kworker/:]
: [kworker/:0H]
: [migration/]
: [ksoftirqd/]
: [kworker/:]
: [kworker/:0H]
: [migration/]
: [ksoftirqd/]
: [kworker/:]
: [kworker/:0H]
: [khelper]
: [kdevtmpfs]
: [perf]
: [kworker/u8:]
: [khungtaskd]
: [writeback]
: [kintegrityd]
: [kworker/:]
: [bioset]
: [kblockd]
: [ata_sff]
: [rpciod]
: [kworker/:]
: [kworker/:]
: [kswapd0]
: [fsnotify_mark]
: [nfsiod]
: [kworker/:]
: [kpsmoused]
: [kworker/:]
: [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 ;
} 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 < ) {
/* 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, -, 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 = };
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/,
}; 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 ;
}

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

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

               : [ksoftirqd/]
: [ksoftirqd/]
: [ksoftirqd/]
: [ksoftirqd/]

更详细信息参考:  《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
: {linuxrc} init
: [kthreadd]
: [ksoftirqd/]
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
: [rcu_sched]
: [rcu_bh]
: [migration/]
: [migration/]
: [ksoftirqd/]
12 0 0:00 [kworker/1:0]---------------workqueue_cpu_up_callback-->create_worker
13 0 0:00 [kworker/1:0H]
: [migration/]
: [ksoftirqd/]
16 0 0:00 [kworker/2:0]
17 0 0:00 [kworker/2:0H]--------------workqueue_cpu_up_callback-->create_worker
: [migration/]
: [ksoftirqd/]
20 0 0:00 [kworker/3:0]
21 0 0:00 [kworker/3:0H]--------------workqueue_cpu_up_callback-->create_worker
: [khelper]
: [kdevtmpfs]
: [perf]
25 0 0:00 [kworker/u8:1]--------------worker_thread-->create_worker
: [khungtaskd]
: [writeback]
: [kintegrityd]
282 0 0:00 [kworker/0:1]---------------worker_thread-->create_worker
: [bioset]
: [kblockd]
: [ata_sff]
: [rpciod]
409 0 0:00 [kworker/2:1]---------------worker_thread-->create_worker
410 0 0:00 [kworker/1:1]---------------worker_thread-->create_worker
: [kswapd0]
: [fsnotify_mark]
: [nfsiod]
449 0 0:00 [kworker/3:1]---------------worker_thread-->create_worker
: [kpsmoused]
537 0 0:00 [kworker/1:2]---------------worker_thread-->create_worker
: [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] = { , 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", , );
system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, );
system_long_wq = alloc_workqueue("events_long", , );
system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND,
WQ_UNBOUND_MAX_ACTIVE);
system_freezable_wq = alloc_workqueue("events_freezable",
WQ_FREEZABLE, );
system_power_efficient_wq = alloc_workqueue("events_power_efficient",
WQ_POWER_EFFICIENT, );
system_freezable_power_efficient_wq = alloc_workqueue("events_freezable_power_efficient",
WQ_FREEZABLE | WQ_POWER_EFFICIENT,
);
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 ;
}

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

static struct worker *create_worker(struct worker_pool *pool)
{
...
if (pool->cpu >= )
snprintf(id_buf, sizeof(id_buf), "%d:%d%s", pool->cpu, id,-------------cpuid和id,区分cpu和cpu内kworker。
pool->attrs->nice < ? "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

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

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

    转自:https://www.cnblogs.com/arnoldlu/p/8336998.html 关键词:kthread.irq.ksoftirqd.kworker.workqueues 在使用p ...

  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. 二路归并算法的java实现

    “归并”的含义是将两个或者两个以上的有序表组合成一个新的有序表. 假设待排序表含有n个元素,则可以看成是n个有序的子表,每个子表的长度为1,然后两两归并,得到(n/2)或者(n/2+1)个长度为2或1 ...

  2. Android绘制优化(二)布局优化

    前言 我们知道一个界面的测量和绘制是通过递归来完成的,减少布局的层数就会减少测量和绘制的时间,从而性能就会得到提升.当然这只是布局优化的一方面,那么如何来进行布局的分析和优化呢?本篇文章会给你一个满意 ...

  3. 全方位理解Android权限之底层实现概览

    0000 这个阶段搞了很多和Android文件权限相关的问题,虽然一知半解,但也算是对Android权限机制有一些自己的理解.遂将这些内容整理出来.因为权限这部分涉及到的内容很多,故将知识分为几块内容 ...

  4. ALTER SYSTEM ARCHIVELOG CURRENT挂起案例

    最近两天,一台ORACLE数据库的作业执行delete_ob_get_epps.sh脚本清理过期备份时,执行下面SQL语句就会被阻塞,在监控工具DPA里面部分截图如下(图片分开截断) sql 'alt ...

  5. SQL Server -- 回忆笔记(二):增删改查,修改表结构,约束,关键字使用,函数,多表联合查询

    SQL Server知识点回忆篇(二):增删改查,修改表结构,约束,关键字使用,函数,多表联合查询 1. insert 如果sql server设置的排序规则不是简体中文,必须在简体中文字符串前加N, ...

  6. SQL SERVER 查询与整理索引碎片

    重建索引 use DATABASE_NAME; ) ) DECLARE @fillfactor INT DECLARE TableCursor CURSOR FOR SELECT OBJECT_SCH ...

  7. Java中的生产消费者问题

    package day190109; import java.util.LinkedList; import java.util.Queue; import java.util.Random; pub ...

  8. python拟合数据,并通过拟合的曲线去预测新值的方法

    from scipy import interpolate import matplotlib.pyplot as plt import numpy as np def f(x): x_points ...

  9. June 5. 2018 Week 23rd Tuesday

    Learn to let go and be clear of where you really want to head for. 学会放手,同时也要弄清楚自己的真正所爱. From Kissing ...

  10. Cesium实现文字、点、多段线、多边形的实时绘制

    背景知识 点.线.面以及文字的实时绘制是GIS很重要的一个功能,是用户对感兴趣区域标注的业务需要.同时Cesium提供了点.线(多段线).面及文字(label)绘制的接口,绘制方式总共有两种,一种是通 ...