Linux内核分析之进程管理-08

韩乔落

第8章 内核线程

基于 Linux 6.12.38 源码分析


8.1 内核线程概述

8.1.1 特点

内核线程是运行在内核空间的特殊进程:

  • 没有用户地址空间mm 为 NULL
  • 运行在内核态:只能在内核空间执行
  • 不访问用户空间:不能直接访问用户内存
  • 直接访问内核数据:可以使用所有内核函数和数据结构

8.1.2 与用户进程对比

特性 用户进程 内核线程
地址空间 独立的用户空间 mm = NULL
运行级别 可运行在用户态和内核态 仅内核态
用户栈 + 内核栈 仅内核栈
创建方式 fork/clone kthread_create
数据访问 需要系统调用访问内核数据 直接访问内核数据

8.2 内核线程创建 API

8.2.1 核心函数

位置: kernel/kthread.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
* 创建内核线程
* @threadfn: 线程函数
* @data: 传递给线程函数的数据
* @namefmt: 线程名称格式化字符串
*/
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[],
...)
{
struct task_struct *task;
va_list args;

// 格式化线程名
va_start(args, namefmt);
task = __kthread_create_on_node(threadfn, data, NUMA_NO_NODE,
namefmt, args);
va_end(args);

return task;
}
EXPORT_SYMBOL(kthread_create);

/*
* 创建并唤醒内核线程
*/
struct task_struct *kthread_run(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...)
{
struct task_struct *task;
va_list args;

va_start(args, namefmt);
task = __kthread_create_on_node(threadfn, data, NUMA_NO_NODE,
namefmt, args);
va_end(args);

if (IS_ERR(task))
return task;

// 唤醒线程
wake_up_process(task);

return task;
}
EXPORT_SYMBOL(kthread_run);

8.2.2 停止内核线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
* 停止内核线程
* @k: 要停止的内核线程任务
*
* 返回: 线程函数的返回值
*/
int kthread_stop(struct task_struct *k)
{
struct kthread *kthread = to_kthread(k);

// 设置停止标志
set_kthread_stop(k);

// 唤醒线程 (如果正在睡眠)
wake_up_process(k);

// 等待线程退出
wait_for_completion(&kthread->exited);

return kthread->result;
}
EXPORT_SYMBOL(kthread_stop);

/*
* 检查是否应该停止
*/
bool kthread_should_stop(void)
{
return test_bit(KTHREAD_SHOULD_STOP, &current->flags);
}
EXPORT_SYMBOL(kthread_should_stop);

8.3 内核线程实现

8.3.1 kthread_create_on_node()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// kernel/kthread.c
struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
void *data, int node,
const char namefmt[],
va_list args)
{
struct kthread_create_info *create;
struct task_struct *task;
unsigned long flags = 0;

// 分配创建信息结构
create = kmalloc(sizeof(*create), GFP_KERNEL);
if (!create)
return ERR_PTR(-ENOMEM);

// 设置创建参数
create->threadfn = threadfn;
create->data = data;
create->done = &done;

// 格式化名称
create->full_name = kvasprintf(GFP_KERNEL, namefmt, args);
if (!create->full_name) {
kfree(create);
return ERR_PTR(-ENOMEM);
}

// 创建内核进程
task = create->result;
if (!IS_ERR(task)) {
static const struct sched_param param = { .sched_priority = 0 };
va_list args;

// 设置调度策略
va_start(args, namefmt);
task = kernel_thread(kthread, create, namefmt, args);
va_end(args);
}

if (IS_ERR(task)) {
kfree(create->full_name);
kfree(create);
}

return task;
}

8.3.2 kthread() 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// kernel/kthread.c
static int kthread(void *_create)
{
struct kthread_create_info *create = _create;
int (*threadfn)(void *data) = create->threadfn;
void *data = create->data;
struct kthread *self;
struct completion *done;
int ret;

// 设置当前任务为内核线程
self = to_kthread(current);

// 初始化 kthread 结构
init_completion(&self->parked);
init_completion(&self->exited);

// 通知创建完成
complete(create->done);

// 执行线程函数
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &current->flags)) {
__set_current_state(TASK_RUNNING);
ret = threadfn(data);
}

// 线程退出处理
kthread_complete(self);
return ret;
}

8.4 常见内核线程

8.4.1 ksoftirqd

功能: 处理软中断

位置: kernel/softirqd.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* 软中断守护进程
*/
static int ksoftirqd(void *__data)
{
// 设置优先级
set_user_nice(current, 0);

while (!kthread_should_stop()) {
// 如果有待处理的软中断,继续处理
if (local_softirq_pending()) {
__do_softirq();
// 避免饥饿
cond_resched();
} else {
schedule();
}
}

return 0;
}

// 每个 CPU 一个
static struct smp_hotplug_thread ksoftirqd_threads[] = {
{ .name = "ksoftirqd/%u", }
};

8.4.2 kworker

功能: 执行工作队列任务

位置: kernel/workqueue.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 工作队列线程
*/
static int worker_thread(void *__worker)
{
struct worker *worker = __worker;
struct worker_pool *pool = worker->pool;

// 设置名称
snprintf(current->comm, sizeof(current->comm), "kworker/%u:%s",
pool->cpu, pool->name);

while (!kthread_should_stop()) {
// 处理工作项
process_one_work(worker);

// 进入空闲状态
if (list_empty(&worker->worklist))
sleep();
}

return 0;
}

8.4.3 kswapd

功能: 页面回收

位置: mm/vmscan.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 页面回收守护进程
*/
static int kswapd(void *p)
{
struct task_struct *tsk = current;

// 设置优先级 (nice 值)
set_user_nice(tsk, PRIO_TO_NICE(tsk->static_prio));

while (!kthread_should_stop()) {
// 检查是否需要回收
if (nr_free_pages() < pages_min) {
// 执行页面回收
reclaim_pages();
}

// 等待唤醒
schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
}

return 0;
}

8.4.4 migration

功能: 负载均衡,迁移任务到其他 CPU

位置: kernel/sched/topology.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* 迁移线程
*/
static int migration_thread(void *data)
{
while (!kthread_should_stop()) {
// 执行负载均衡
run_load_balance();

// 定期执行
schedule_timeout_interruptible(HZ);
}

return 0;
}

8.5 内核线程的内存管理

8.5.1 active_mm 机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 内核线程没有 mm,使用 active_mm 借用
struct task_struct {
struct mm_struct *mm; // 进程地址空间
struct mm_struct *active_mm; // 活跃地址空间
};

/*
* 进入 lazy TLB 模式 (借用地址空间)
*/
static inline void enter_lazy_tlb(struct mm_struct *mm)
{
if (this_cpu_read(cpu_tlbstate.loaded_mm) != mm) {
// 借用 prev 的 mm
current->active_mm = prev->active_mm;
}
}

8.5.2 内核线程分配内存

1
2
3
4
5
6
7
8
// 内核线程使用 GFP_KERNEL 分配内存
void *data = kmalloc(size, GFP_KERNEL);

// 或者使用 vmalloc
void *data = vmalloc(size);

// 如果在原子上下文,使用 GFP_ATOMIC
void *data = kmalloc(size, GFP_ATOMIC);

8.6 内核线程调度

8.6.1 调度策略

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* 内核线程可以设置调度策略
*/
struct task_struct *task;
struct sched_param param = {
.sched_priority = 0, // 实时优先级 (0-99)
};

// 设置为实时 FIFO 调度
sched_setscheduler(task, SCHED_FIFO, &param);

// 或设置为普通调度
sched_setscheduler(task, SCHED_NORMAL, &param);

8.6.2 绑定到特定 CPU

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* 将内核线程绑定到特定 CPU
*/
int kthread_bind(struct task_struct *p, unsigned int cpu)
{
// 设置 CPU 亲和性
set_cpus_allowed_ptr(p, cpumask_of(cpu));

// 迁移到目标 CPU
migrate_thread(p, cpu);

return 0;
}

8.7 内核线程与信号

8.7.1 信号处理

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* 内核线程可以忽略大部分信号
*/
static void ignore_signals(struct task_struct *task)
{
int i;

siginitsetinv(&task->blocked, 0);

// 阻塞所有信号
for (i = 0; i < _NSIG; i++)
sigaddset(&task->blocked, i);
}

8.8 内核线程监控

8.8.1 查看内核线程

1
2
3
4
5
6
7
8
# 查看所有内核线程
ps -ef | grep '\['

# 查看特定内核线程
ps -eo pid,ppid,comm | grep kworker

# 查看内核线程栈
cat /proc/1234/stack

8.8.2 常见内核线程列表

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ps -ef | grep '\['
root 2 0 kthreadd
root 9 2 [ksoftirqd/0]
root 10 2 [rcu_sched]
root 11 2 [rcuc/0]
root 12 2 [rcu_sched]
root 13 2 [rcuc/1]
root 14 2 [rcu_sched]
root 15 2 [rcuc/2]
root 16 2 [migration/0]
root 17 2 [watchdog/0]
root 18 2 [kworker/0:0H]
...

8.9 本章小结

本章介绍了 Linux 内核线程机制:

  1. 特点:没有用户地址空间,仅运行在内核态
  2. 创建 API:kthread_create、kthread_run
  3. 停止 API:kthread_stop、kthread_should_stop
  4. 实现:通过 kthread() 统一入口执行线程函数
  5. 常见线程:ksoftirqd、kworker、kswapd、migration 等
  6. 内存管理:使用 active_mm 借用机制
  7. 调度绑定:可以设置调度策略和 CPU 亲和性
  8. 监控:通过 ps 和 /proc 查看内核线程

下一章将介绍进程关系与组管理。

  • Title: Linux内核分析之进程管理-08
  • Author: 韩乔落
  • Created at : 2026-01-14 19:20:22
  • Updated at : 2026-01-19 13:40:54
  • Link: https://jelasin.github.io/2026/01/14/Linux内核分析之进程管理-08/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments