Kernel Threads (커널 스레드)
커널 스레드(kthread)는 유저 주소 공간 없이 커널 문맥에서 동작하는 핵심 실행 단위입니다. 이 문서는 kthreadd(PID 2) 기반 생성 흐름, kthread_create/run/stop/park 제어 API, kthread_worker 패턴, per-CPU 및 smpboot 스레드 설계, freezer/CPU hotplug 대응, ksoftirqd/kworker/kswapd 운영 특성까지 실전 관점으로 상세히 설명합니다.
핵심 요약
- 커널 스레드 —
mm == NULL인task_struct. 유저 주소 공간 없이 커널 코드만 실행합니다. - kthreadd (PID 2) — 모든 커널 스레드의 부모. 생성 요청을 직렬화하여 처리합니다.
- kthread_create / kthread_run — 커널 스레드 생성 API.
kthread_run은 생성 + 즉시 깨우기를 합칩니다. - kthread_should_stop — 스레드 함수 내 메인 루프의 종료 조건.
kthread_stop()이 호출되면 true를 반환합니다. - kthread_worker — workqueue 대안으로, 전용 스레드에서 작업을 순차 실행하는 프레임워크입니다.
단계별 이해
- 커널 스레드 식별 —
ps aux에서 [대괄호]로 표시되는 프로세스가 커널 스레드입니다.ps -eo pid,ppid,comm | grep "\\["로 확인하면 대부분 PPID가 2(kthreadd)입니다. - 기본 패턴 이해 — 커널 스레드는
while (!kthread_should_stop()) { 작업; sleep; }패턴으로 동작합니다.외부에서
kthread_stop()을 호출하면 루프를 빠져나와 종료합니다. - 생성 흐름 —
kthread_create()→ kthreadd가kernel_clone()으로 실제 태스크 생성 →wake_up_process()로 실행 시작.kthread_run()은 이 세 단계를 하나로 합친 매크로입니다. - 실습 — 아래 기본 커널 스레드 모듈 예제로 직접 로드/언로드하며 동작을 확인하세요.
dmesg -w로 커널 로그를 실시간 모니터링하면 스레드 동작을 관찰할 수 있습니다.
개요 (Overview)
리눅스에서 커널 스레드(kernel thread, kthread)는 유저 공간 주소 공간 없이 커널 모드에서만 동작하는 태스크입니다. 일반 유저 프로세스와 동일하게 task_struct로 표현되지만, 핵심적인 차이가 있습니다:
| 속성 | 유저 프로세스 / 스레드 | 커널 스레드 |
|---|---|---|
task_struct.mm |
유효한 mm_struct 포인터 |
NULL |
task_struct.active_mm |
== mm |
이전 태스크의 mm을 빌림 (Lazy TLB) |
task_struct.flags |
PF_KTHREAD 없음 |
PF_KTHREAD 설정됨 |
| 주소 공간 | 유저 + 커널 | 커널만 |
ps 표시 |
일반 이름 | [대괄호] 표기 (예: [kswapd0]) |
| 부모 프로세스 | 다양 (fork한 프로세스) | kthreadd (PID 2) |
| 시그널 처리 | 유저 시그널 핸들러 | 대부분 시그널 차단 (커널 내부 제어) |
/* include/linux/sched.h - 커널 스레드 판별 */
static inline bool is_kthread(struct task_struct *p)
{
return p->flags & PF_KTHREAD;
}
/* context_switch()에서의 Lazy TLB */
if (!next->mm) {
/* 커널 스레드: 이전 태스크의 mm을 빌림 */
next->active_mm = prev->active_mm;
/* → TLB flush 불필요, 성능 향상 */
}
Lazy TLB: 커널 스레드는 유저 주소 공간에 접근하지 않으므로, 컨텍스트 스위칭 시 페이지 테이블을 전환할 필요가 없습니다. 이전 태스크의 mm을 빌려 사용하여 불필요한 TLB flush를 방지합니다.
kthreadd (PID 2) — 커널 스레드 관리자
kthreadd는 부팅 초기에 rest_init()에서 생성되는 커널 스레드로, PID 2를 부여받습니다. 이후 생성되는 모든 커널 스레드의 부모 역할을 합니다.
/* init/main.c - rest_init() */
static void rest_init(void)
{
/* PID 1: kernel_init → init/systemd */
kernel_thread(kernel_init, NULL, CLONE_FS);
/* PID 2: kthreadd — 커널 스레드 관리자 */
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
complete(&kthreadd_done); /* kernel_init이 대기 중 */
cpu_startup_entry(CPUHP_ONLINE); /* idle 스레드가 됨 */
}
kthreadd 메인 루프
kthreadd()는 무한 루프에서 kthread_create_list를 감시하며, 새 커널 스레드 생성 요청을 처리합니다:
/* kernel/kthread.c - kthreadd() 메인 루프 (간략화) */
int kthreadd(void *unused)
{
struct task_struct *tsk = current;
/* 모든 시그널 차단 */
set_cpus_allowed_ptr(tsk, housekeeping_cpumask(HK_TYPE_KTHREAD));
ignore_signals(tsk);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&kthread_create_list))
schedule(); /* 요청 없으면 슬립 */
__set_current_state(TASK_RUNNING);
while (!list_empty(&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);
/* 실제 커널 스레드 생성 */
create_kthread(create);
}
}
return 0;
}
직렬화: 커널 스레드 생성이 kthreadd를 거치는 이유는 kernel_clone() 호출 시의 환경(파일 디스크립터, 네임스페이스 등)을 깨끗하게 유지하기 위함입니다. 임의의 컨텍스트에서 직접 kernel_clone()을 호출하면 예기치 않은 리소스가 상속될 수 있습니다.
생성 API 심화
kthread_create()
kthread_create()는 커널 스레드를 생성하되, TASK_UNINTERRUPTIBLE 상태로 두어 아직 실행하지 않습니다. 호출자가 명시적으로 wake_up_process()를 호출해야 스레드가 실행됩니다.
/* include/linux/kthread.h */
struct task_struct *kthread_create(
int (*threadfn)(void *data), /* 스레드 함수 */
void *data, /* 함수에 전달할 인자 */
const char *namefmt, ... /* 스레드 이름 (printf 형식) */
);
/* 사용 예시 */
struct task_struct *kth;
kth = kthread_create(my_thread_fn, my_data, "my-kthread/%d", cpu);
if (IS_ERR(kth)) {
pr_err("kthread_create failed: %ld\\n", PTR_ERR(kth));
return PTR_ERR(kth);
}
/* CPU 바인딩 등 설정 후 시작 */
kthread_bind(kth, cpu);
wake_up_process(kth);
kthread_run()
kthread_run()은 kthread_create() + wake_up_process()를 합친 편의 매크로입니다:
/* include/linux/kthread.h */
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, \
## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
/* 가장 흔한 사용법 */
struct task_struct *kth;
kth = kthread_run(my_thread_fn, NULL, "my-kthread");
if (IS_ERR(kth))
return PTR_ERR(kth);
kthread_create_on_node() / kthread_create_on_cpu()
NUMA 노드 또는 특정 CPU에 바인딩된 커널 스레드를 생성합니다:
/* NUMA 노드 지정 생성 */
struct task_struct *kthread_create_on_node(
int (*threadfn)(void *data),
void *data, int node,
const char *namefmt, ...
);
/* 특정 CPU 바인딩 생성 — per-CPU kthread에 사용 */
struct task_struct *kthread_create_on_cpu(
int (*threadfn)(void *data),
void *data, unsigned int cpu,
const char *namefmt
);
/* → 자동으로 kthread_bind(task, cpu) + 이름에 CPU 번호 부여 */
내부 생성 흐름
/* kernel/kthread.c - create_kthread() 내부 (간략화) */
static void create_kthread(struct kthread_create_info *create)
{
int pid;
/* kernel_clone()으로 새 태스크 생성
* → 새 태스크는 kthread() 함수에서 시작 */
pid = kernel_thread(kthread, create, create->full_name,
CLONE_FS | CLONE_FILES | SIGCHLD);
if (pid < 0) {
create->result = ERR_PTR(pid);
complete(&create->done);
}
}
/* 새로 생성된 커널 스레드의 시작점 */
static int kthread(void *_create)
{
struct kthread_create_info *create = _create;
int (*threadfn)(void *data) = create->threadfn;
void *data = create->data;
int ret;
/* 호출자에게 task_struct 포인터 전달 */
create->result = current;
complete(&create->done);
/* wake_up_process()가 호출될 때까지 대기 */
schedule_preempt_disabled();
/* 스레드 함수 실행 */
ret = threadfn(data);
kthread_exit(ret);
}
커널 스레드 함수 패턴
기본 kthread_should_stop() 루프
가장 기본적인 커널 스레드 패턴입니다:
static int my_thread_fn(void *data)
{
while (!kthread_should_stop()) {
/* 작업 수행 */
do_my_work(data);
/* 일정 시간 슬립 */
msleep_interruptible(1000);
}
return 0;
}
조건부 슬립 패턴 (set_current_state + schedule)
이벤트가 발생할 때만 깨어나는 효율적인 패턴입니다:
static int event_thread_fn(void *data)
{
struct my_device *dev = data;
while (!kthread_should_stop()) {
/* ① 먼저 상태를 INTERRUPTIBLE로 설정 */
set_current_state(TASK_INTERRUPTIBLE);
/* ② 조건 확인 — 작업이 없으면 슬립 */
if (!has_pending_work(dev)) {
schedule(); /* CPU 양보, 깨워질 때까지 대기 */
continue;
}
/* ③ 작업이 있으면 RUNNING으로 복원 후 처리 */
__set_current_state(TASK_RUNNING);
process_pending_work(dev);
}
__set_current_state(TASK_RUNNING);
return 0;
}
/* 다른 곳에서 작업을 추가하고 스레드를 깨움 */
add_work(dev, new_work);
wake_up_process(thread_task);
순서 주의: set_current_state(TASK_INTERRUPTIBLE)를 조건 확인 전에 호출해야 합니다. 순서를 바꾸면 조건 확인 후 ~ schedule() 사이에 이벤트가 발생해도 놓치는 wakeup-miss 레이스가 발생합니다.
wait_event_interruptible 패턴
위의 조건부 슬립을 더 안전하게 래핑한 API입니다:
static DECLARE_WAIT_QUEUE_HEAD(my_wq);
static bool work_pending = false;
static int waiter_thread_fn(void *data)
{
while (!kthread_should_stop()) {
/* 조건이 true가 되거나 kthread_stop()이 호출될 때까지 대기 */
wait_event_interruptible(my_wq,
work_pending || kthread_should_stop());
if (kthread_should_stop())
break;
work_pending = false;
do_work();
}
return 0;
}
/* 깨우기 */
work_pending = true;
wake_up_interruptible(&my_wq);
종료 API
kthread_stop()
kthread_stop()은 대상 커널 스레드에 종료 요청을 보내고, 스레드가 실제로 종료할 때까지 대기합니다:
/* kernel/kthread.c - kthread_stop() (간략화) */
int kthread_stop(struct task_struct *k)
{
get_task_struct(k); /* 참조 카운트 증가 */
/* KTHREAD_SHOULD_STOP 플래그 설정 */
kthread_set_bit(KTHREAD_SHOULD_STOP, k);
/* 스레드가 슬립 중이면 깨움 */
wake_up_process(k);
/* 스레드가 종료할 때까지 대기 */
wait_for_completion(&kthread->exited);
ret = kthread->result;
put_task_struct(k); /* 참조 카운트 감소 */
return ret; /* 스레드 함수의 반환값 */
}
교착 위험: kthread_stop()은 스레드 종료를 동기적으로 대기합니다. 스레드 함수가 kthread_should_stop()을 확인하지 않거나, 영구적으로 블록된 상태라면 kthread_stop()도 영원히 블록됩니다.
kthread_park() / kthread_unpark()
커널 스레드를 일시 중지/재개하는 API입니다. CPU 핫플러그 시 per-CPU 스레드를 관리하는 데 주로 사용됩니다:
/* 스레드 일시 중지 */
kthread_park(kth);
/* → 스레드 내부에서 kthread_should_park()가 true 반환
* → 스레드는 kthread_parkme()에서 대기 상태로 진입 */
/* 스레드 재개 */
kthread_unpark(kth);
/* → KTHREAD_SHOULD_PARK 해제, 스레드 깨움 */
/* 스레드 함수 내에서 park 지원하기 */
static int parkable_thread_fn(void *data)
{
while (!kthread_should_stop()) {
/* park 요청이 있으면 대기 */
if (kthread_should_park())
kthread_parkme();
do_work();
schedule();
}
return 0;
}
커널 스레드 상태 전환
커널 스레드는 생성부터 종료까지 여러 상태를 거칩니다. 다음 다이어그램은 주요 상태 전환을 보여줍니다:
| 상태 | task_struct.state | 설명 |
|---|---|---|
| CREATED | TASK_UNINTERRUPTIBLE | kthread_create() 직후, wake_up_process() 대기 중 |
| RUNNING | TASK_RUNNING | CPU에서 실행 중이거나 runqueue에서 대기 |
| INTERRUPTIBLE | TASK_INTERRUPTIBLE | schedule()로 자발적 양보, 이벤트/시그널로 깨울 수 있음 |
| UNINTERRUPTIBLE | TASK_UNINTERRUPTIBLE | I/O 완료 대기, 시그널로 깨울 수 없음 (D 상태) |
| PARKED | TASK_PARKED | kthread_park()로 일시 중지, CPU 핫플러그 시 사용 |
| STOPPED | TASK_DEAD | 스레드 함수 종료, do_exit() 호출됨 |
ps -eo pid,stat,comm | grep "\[.*\]"로 커널 스레드의 상태를 확인할 수 있습니다. S는 INTERRUPTIBLE, D는 UNINTERRUPTIBLE, R는 RUNNING입니다.
Per-CPU 커널 스레드
많은 커널 서브시스템이 각 CPU마다 전용 커널 스레드를 실행합니다. 대표적으로 ksoftirqd/N, migration/N, kworker/N:* 등이 있습니다.
smpboot 프레임워크
smpboot 프레임워크는 per-CPU 커널 스레드를 체계적으로 관리합니다. CPU 핫플러그(online/offline) 이벤트에 자동으로 대응하여 스레드를 생성/정지/재개합니다:
/* include/linux/smpboot.h */
struct smp_hotplug_thread {
struct task_struct __percpu **store; /* per-CPU 태스크 포인터 */
struct list_head list; /* 등록 리스트 */
int (*thread_should_run)(unsigned int cpu);
void (*thread_fn)(unsigned int cpu);
void (*create)(unsigned int cpu);
void (*setup)(unsigned int cpu);
void (*cleanup)(unsigned int cpu, bool online);
void (*park)(unsigned int cpu);
void (*unpark)(unsigned int cpu);
const char *thread_comm; /* "ksoftirqd/%u" */
};
/* 등록 — 모든 online CPU에 대해 스레드 자동 생성 */
int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread);
/* 예: ksoftirqd 등록 (kernel/softirq.c) */
static struct smp_hotplug_thread softirq_threads = {
.store = &ksoftirqd,
.thread_should_run = ksoftirqd_should_run,
.thread_fn = run_ksoftirqd,
.thread_comm = "ksoftirqd/%u",
};
smpboot_thread_fn()이 per-CPU 스레드의 메인 루프를 실행합니다:
/* kernel/smpboot.c - smpboot_thread_fn() 핵심 (간략화) */
static int smpboot_thread_fn(void *data)
{
struct smp_hotplug_thread *ht = data;
unsigned int cpu = smp_processor_id();
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_park()) {
__set_current_state(TASK_RUNNING);
kthread_parkme(); /* CPU offline 시 park */
continue;
}
if (!ht->thread_should_run(cpu)) {
schedule(); /* 할 일 없으면 슬립 */
} else {
__set_current_state(TASK_RUNNING);
ht->thread_fn(cpu); /* 실제 작업 수행 */
}
if (kthread_should_stop())
break;
}
return 0;
}
smpboot 프레임워크 아키텍처
smpboot은 CPU 핫플러그 이벤트와 per-CPU 스레드를 자동으로 연동합니다:
smpboot_register_percpu_thread()만 호출하면, 커널이 CPU 핫플러그 이벤트에 자동으로 대응하여 스레드를 생성/park/unpark/정리합니다.
kthread_worker API
kthread_worker는 workqueue의 경량 대안입니다. 전용 커널 스레드에서 작업(kthread_work)을 순차적으로 실행합니다. workqueue의 복잡한 동시성 관리가 필요 없고, 특정 스레드에서 작업을 보장해야 할 때 유용합니다.
/* include/linux/kthread.h */
struct kthread_worker {
unsigned int flags;
struct list_head work_list; /* 대기 중인 작업 리스트 */
struct list_head delayed_work_list;
struct task_struct *task; /* 워커 스레드 */
struct kthread_work *current_work; /* 현재 실행 중인 작업 */
};
struct kthread_work {
struct list_head node;
kthread_work_func_t func; /* 작업 함수 */
struct kthread_worker *worker;
};
/* 주요 API */
struct kthread_worker *kthread_create_worker(unsigned int flags,
const char *namefmt, ...);
void kthread_destroy_worker(struct kthread_worker *worker);
bool kthread_queue_work(struct kthread_worker *worker,
struct kthread_work *work);
void kthread_flush_work(struct kthread_work *work);
void kthread_flush_worker(struct kthread_worker *worker);
| 비교 항목 | workqueue (CMWQ) | kthread_worker |
|---|---|---|
| 스레드 관리 | 커널이 worker pool 자동 관리 | 전용 스레드 1개 직접 생성 |
| 동시 실행 | 여러 work가 동시 실행 가능 | 항상 순차 실행 (직렬화 보장) |
| 사용 사례 | 일반적인 비동기 작업 | 특정 스레드 바인딩, RT 우선순위 필요 |
| 복잡도 | 높음 (풀 관리, concurrency 제어) | 낮음 (단순 큐 + 단일 스레드) |
| 오버헤드 | 공유 풀 사용으로 효율적 | 전용 스레드로 리소스 점유 |
성능 특성 비교
실제 측정 결과로 kthread_worker와 workqueue의 성능 차이를 확인할 수 있습니다:
| 측정 항목 | workqueue (CMWQ) | kthread_worker | 비고 |
|---|---|---|---|
| 작업 제출 지연 | ~2μs | ~1.5μs | kthread_worker가 15-20% 빠름 (단순 큐 구조) |
| 작업 실행 시작 지연 | 5-50μs (가변) | 3-8μs (안정) | 전용 스레드가 더 예측 가능 |
| 처리량 (짧은 작업) | ~800K ops/sec | ~650K ops/sec | workqueue가 병렬 실행으로 유리 |
| 처리량 (긴 작업) | ~200K ops/sec | ~180K ops/sec | 큰 차이 없음 |
| 메모리 오버헤드 | pool 공유 | +8KB/worker | 전용 스택 공간 필요 |
| CPU 사용률 (idle) | ~0.01% | ~0.02% | 전용 스레드 유지 비용 |
| RT 우선순위 지원 | 제한적 | 완전 지원 | kthread_worker가 RT 적합 |
측정 환경: Intel Xeon Gold 6248R, 작업당 평균 50μs 실행 시간, 1000회 반복 측정
kthread_worker 사용 시나리오
다음 경우에 workqueue 대신 kthread_worker를 사용하는 것이 적합합니다:
- Real-time 우선순위 필요 — 작업이 RT 스케줄링 클래스(SCHED_FIFO/RR)로 실행되어야 할 때
- CPU affinity 고정 — 특정 CPU에 바인딩하여 캐시 지역성을 극대화할 때
- 순차 실행 보장 — 작업 간 순서가 중요하고 동시 실행을 허용하지 않을 때
- 단순성 우선 — CMWQ의 복잡한 동시성 관리가 과도할 때 (드라이버 초기화 등)
- 블록킹 작업 — 작업이 오래 블록될 수 있고, 다른 작업에 영향을 주지 않아야 할 때
/* kthread_worker 사용 예시 */
static struct kthread_worker *my_worker;
static struct kthread_work my_work;
static void my_work_func(struct kthread_work *work)
{
pr_info("work executed on thread: %s\\n", current->comm);
/* 실제 작업 수행 */
}
/* 초기화 */
my_worker = kthread_create_worker(0, "my-worker");
kthread_init_work(&my_work, my_work_func);
/* 작업 큐잉 */
kthread_queue_work(my_worker, &my_work);
/* 정리 */
kthread_flush_worker(my_worker); /* 대기 중인 작업 모두 완료 대기 */
kthread_destroy_worker(my_worker);
주요 커널 스레드 종합
부팅 직후 ps -eo pid,ppid,cls,ni,comm | head -30을 실행하면 다양한 커널 스레드를 확인할 수 있습니다. 주요 커널 스레드의 역할을 정리합니다:
| 이름 | Per-CPU | 역할 | 관련 문서 |
|---|---|---|---|
[kthreadd] |
PID 2. 모든 커널 스레드의 부모, 생성 요청 처리 | 본 문서 | |
[ksoftirqd/N] |
O | CPU N의 softirq 처리 (부하 시 스레드 컨텍스트로 지연 실행) | Bottom Half |
[kworker/N:M] |
O | CPU N에 바인딩된 workqueue worker (M은 워커 번호) | Workqueue |
[kworker/u*:M] |
unbound workqueue worker (특정 CPU에 바인딩되지 않음) | Workqueue | |
[migration/N] |
O | CPU N의 태스크 마이그레이션 처리 (RT 최고 우선순위) | 스케줄러 |
[rcu_gp] |
RCU grace period 관리 | RCU | |
[rcu_preempt] |
PREEMPT_RCU grace period 관리 (CONFIG_PREEMPT_RCU) | RCU | |
[kswapd0] |
NUMA 노드별 페이지 회수 (메모리 부족 시 활성화) | 메모리 심화 | |
[kcompactd0] |
메모리 compaction (단편화 해소) | 메모리 심화 | |
[khugepaged] |
Transparent Huge Page 합병 | 메모리 심화 | |
[writeback] |
dirty 페이지 → 디스크 기록 | Page Cache | |
[kblockd] |
블록 I/O 요청 처리 | Block I/O | |
[jbd2/sdXN-8] |
ext4 저널링 (JBD2 트랜잭션 커밋) | ext4 | |
[irq/N-name] |
Threaded IRQ 핸들러 (하드웨어 인터럽트 처리 스레드) | 인터럽트 | |
[cpuhp/N] |
O | CPU 핫플러그 관리 | - |
[idle] |
O | CPU가 할 일이 없을 때 실행되는 idle 스레드 (PID 0) | - |
[kworker] 이름 해석: kworker/0:1은 CPU 0에 바인딩된 워커 1번, kworker/u16:2는 unbound 워커 풀(16개 CPU)의 워커 2번입니다. kworker/0:1H의 H는 high-priority를 의미합니다.
Real-Time 커널 및 스케줄링 클래스
커널 스레드는 기본적으로 CFS(COMPLETELY_FAIR_SCHEDULER)에 의해 스케줄링되지만, 실시간 요구사항이 있는 작업에는 실시간 스케줄링 클래스를 적용할 수 있습니다.
스케줄링 클래스 적용
SCHED_FIFO와 SCHED_RR은 실시간 스케줄링 정책으로, 커널 스레드에 적용하면 우선순위에 따른 선점 스케줄링이 가능합니다:
/* 커널 스레드에 실시간 우선순위 설정 */
struct sched_param param;
param.sched_priority = 50; /* 1~99, 높은 값이 높은 우선순위 */
/* SCHED_FIFO: 선점 가능하면 즉시 실행, 타임 슬라이스 없음 */
sched_setscheduler(kth, SCHED_FIFO, ¶m);
/* SCHED_RR: 타임 슬라이스 기반 라운드 로빈 */
sched_setscheduler(kth, SCHED_RR, ¶m);
/* SCHED_NORMAL로 복원 */
sched_setscheduler(kth, SCHED_NORMAL, NULL);
migration 스레드 (RT 최고 우선순위)
CPU 간 태스크 마이그레이션을 담당하는 migration/N 스레드는 실시간 최고 우선순위(SCHED_FIFO, 우선순위 99)로 실행됩니다:
# migration 스레드의 스케줄링 정보 확인
chrt -p $(pgrep -f "migration/0")
# pid 57의 스케줄링 정책:
# policy: SCHED_FIFO priority: 99
RT 커널 (PREEMPT_RT): PREEMPT_RT 패치 적용 시 더 많은 커널 코드가 선점 가능해져 실시간 응답성이 향상됩니다. 이때 ksoftirqd, workqueue 등의 커널 스레드들도 인터럽트 컨텍스트에서 스레드 컨텍스트로 이전되어 더 부드러운 스케줄링이 가능해집니다.
cgroup과 커널 스레드
커널 스레드는 기본적으로 모든 cgroup에 속하지 않지만, 특정 조건에서 cgroup 제어를 받을 수 있습니다.
커널 스레드의 cgroup 규칙
| 속성 | 설명 |
|---|---|
threaded 모드 |
cgoup v2의 threaded 옵션 사용 시 커널 스레드를 cgroup에 포함 가능 |
| CPU cgroup | cpu 컨트롤러가 적용되면 CPU 시간 할당 제어 가능 |
| cpuset cgroup | 커널 스레드가 특정 CPU에서만 실행되도록 제한 가능 |
| 기본 동작 | 대부분의 커널 스레드는 cgroup 제한 없이 시스템 전역으로 실행 |
/* 커널 스레드를 특정 cgroup에 추가 (cgoup v2 threaded 모드) */
/* 주의: 모든 커널 스레드가 이 기능을 지원하는 것은 아님 */
int kthread_attach_group(struct task_struct *kth, struct cgroup *cgrp)
{
int ret;
get_task_struct(kth);
cgroup_attach_task(cgrp, kth, false);
put_task_struct(kth);
return ret;
}
cgroup 제한: 모든 커널 스레드가 cgroup 이동에 반응하는 것은 아닙니다. migration/N, ksoftirqd/N 등의 중요 커널 스레드는 시스템 안정성을 위해 cgroup 제어가 의도적으로 무시됩니다.
BH Workqueue
최근 커널에서는 tasklet 대체를 위해 BH (Bottom-Half) Workqueue 도입과 전환이 진행 중입니다. BH workqueue는 workqueue 프레임워크를 활용하면서 softirq 컨텍스트에서 작업을 실행합니다.
BH Workqueue vs Tasklet
| 특성 | Tasklet (Legacy) | BH Workqueue |
|---|---|---|
| 컨텍스트 | softirq (BH) | softirq (BH) |
| 동시성 | 동일 softirq 타입만 직렬화 | workqueue pool 기반 동시성 제어 |
| flush/cancel | 제한적 | 완전한 flush/cancel 지원 |
| melting | tasklet_schedule() | queue_work_on() |
| 추적성 | 제한적 | ftrace, perf 지원 |
| 상태 | deprecated | 전환 진행 중 |
/* BH Workqueue 사용 예시 (커널 설정/버전 확인 필요) */
#include <linux/workqueue.h>
static void bh_work_handler(struct work_struct *work)
{
pr_info("BH work executed in softirq context\\n");
/* 실제 작업 — atomic 컨텍스트에서 실행됨 */
}
static void bh_work_handler_highpri(struct work_struct *work)
{
pr_info("High priority BH work\\n");
}
/* 시스템 제공 BH workqueue 사용 */
static struct work_struct my_bh_work;
static int __init bh_init(void)
{
INIT_WORK(&my_bh_work, bh_work_handler);
/* 일반 BH workqueue에 큐잉 */
queue_work(system_bh_wq, &my_bh_work);
/* 고우선순위 BH workqueue */
INIT_WORK(&highpri_work, bh_work_handler_highpri);
queue_work(system_bh_highpri_wq, &highpri_work);
return 0;
}
커스텀 BH Workqueue 생성
/* 커스텀 BH workqueue 생성 */
struct workqueue_struct *my_bh_wq;
my_bh_wq = alloc_workqueue("my-bh-wq",
WQ_UNBOUND | WQ_BH, /* BH 플래그 필수 */
0); /* max_active */
/* 사용 후 파괴 */
destroy_workqueue(my_bh_wq);
tasklet → BH workqueue 마이그레이션: 신규 코드에서는 tasklet 신규 도입을 피하고 BH workqueue 또는 threaded IRQ/workqueue 패턴을 우선 검토하세요. 기존 tasklet 코드는 서브시스템별 일정에 따라 점진적으로 전환되고 있으므로, 적용 전 대상 커널 브랜치의 지원 상태를 확인해야 합니다.
성능 튜닝 및 모니터링
커널 스레드 성능 튜닝
대용량 시스템에서는 커널 스레드의 동작을 튜닝하여 성능을 최적화할 수 있습니다:
/* CPU affinity 설정 — 특정 CPU에서만 실행되도록 제한 */
cpumask_t mask;
cpumask_clear(&mask);
cpumask_set_cpu(0, &mask); /* CPU 0에만 바인딩 */
set_cpus_allowed_ptr(current, &mask);
/*nice 값 설정 — 스케줄링 우선순위 조절 */
set_user_nice(current, -10); /* 더 높은 우선순위 */
/* 커널 스레드 시작 지연 — 시스템 부하 상태 확인 후 */
if (system_state == SYSTEM_RUNNING)
kth = kthread_run(fn, data, "delayed-kthread");
else
schedule_delayed_work(&delayed_init, msecs_to_jiffies(5000));
커널 스레드 통계 확인
# 커널 스레드별 CPU 사용량 확인
ps -eo pid,ppid,comm,%cpu,%mem,psr | grep -E "\\[" | sort -k4 -rn | head -20
# 특정 커널 스레드의 상세 통계
cat /proc/<pid>/sched
# se.exec_start : 123456789.123456
# se.vruntime : 1234.567890
# se.sum_exec_runtime : 5678.901234
# se.nr_switches : 12345
# se.nr_voluntary_switches: 12000
# se.nr_involuntary_switches: 345
# se.wait_start : 0.000000
# se.sleep_start : 123456789.123456
# se.sleep_max : 1234.567890
# se.iowait_sum : 123.456789
# se.iowait_max : 12.345678
# se.nr_migrations : 1234
# kworker 스레드별 CPU 사용량 상세
cat /proc/interrupts | head -5
cat /proc/softirqs
# workqueue 통계 (Linux 4.11+)
cat /sys/kernel/debug/workqueue
perf 도구 활용
# 특정 커널 스레드 프로파일링
perf record -p <pid> -g -- sleep 10
perf report
# 커널 스레드 생성/종료 이벤트 추적
perf record -e sched:sched_kthread_stop -e sched:sched_kthread_start -a -- sleep 30
perf script
# workqueue 작업 실행 추적
perf record -e workqueue:workqueue_execute_start -e workqueue:workqueue_execute_end -a
perf report
커널 스레드 디버깅
커널 스레드가 응답하지 않거나 예상대로 동작하지 않을 때 사용할 수 있는 디버깅 기법들입니다:
Hung Task 탐지
# Hung task 감지 활성화 (기본 120초)
echo 120 > /proc/sys/kernel/hung_task_timeout_secs
echo 1 > /proc/sys/kernel/hung_task_warnings
# D 상태(UNINTERRUPTIBLE)에 장시간 머무는 스레드 확인
ps -eo pid,stat,wchan:30,comm | grep -E '^[[:space:]]*[0-9]+[[:space:]]+D'
# 특정 스레드의 대기 이유 확인
cat /proc/<pid>/stack
# [<0>] __schedule+0x2e0/0x970
# [<0>] schedule+0x46/0xb0
# [<0>] io_schedule+0x16/0x40
# [<0>] wait_on_page_bit+0x15e/0x250
# → 페이지 I/O 대기 중
ftrace로 스레드 추적
# 특정 커널 스레드의 함수 호출 추적
cd /sys/kernel/debug/tracing
echo 0 > tracing_on
echo function_graph > current_tracer
echo 1 > options/funcgraph-proc
echo kswapd0 > set_ftrace_pid # 또는 PID 번호
echo 1 > tracing_on
sleep 5
echo 0 > tracing_on
cat trace | head -100
# kthread 생성/종료 이벤트 추적
echo 1 > events/sched/sched_process_fork/enable
echo 1 > events/sched/sched_process_exit/enable
echo 1 > tracing_on
cat trace_pipe
데드락 탐지
# lockdep으로 락 의존성 문제 감지 (CONFIG_PROVE_LOCKING 필요)
dmesg | grep -i "possible circular locking"
# 모든 스레드의 락 정보 확인
cat /proc/lock_stat
# 특정 스레드가 대기 중인 락 확인
cat /proc/<pid>/wchan # 대기 중인 커널 함수
# SysRq로 전체 스레드 스택 덤프 (긴급 상황)
echo t > /proc/sysrq-trigger
dmesg | tail -200
디버깅 팁
| 문제 증상 | 확인 방법 | 해결 방향 |
|---|---|---|
| 스레드가 생성되지 않음 | dmesg | grep kthread | kthreadd 상태 확인, OOM 확인 |
| 스레드가 종료되지 않음 | cat /proc/<pid>/stack | kthread_should_stop() 확인 누락 의심 |
| CPU 100% 사용 | perf top -p <pid> | 무한 루프, schedule() 누락 |
| D 상태에 장시간 | /proc/<pid>/stack | I/O 대기, mutex 대기 확인 |
| Park 후 unpark 안됨 | ps에서 STAT=P 확인 | CPU 핫플러그 상태 확인 |
panic_print 파라미터를 설정하면 유용한 정보를 출력할 수 있습니다. echo 0x1ff > /proc/sys/kernel/panic_print로 모든 정보(태스크 리스트, 메모리 정보, 타이머 등)를 출력합니다.
커널 스레드 시각화
커널 스레드 상태 시각화
# 모든 커널 스레드의 상태 시각화 (심플)
for pid in $(ls /proc/ | grep -E '^[0-9]+$'); do
if grep -q '\[.*\]' /proc/$pid/comm 2>/dev/null; then
state=$(awk '{print $3}' /proc/$pid/stat)
comm=$(cat /proc/$pid/comm)
printf "%-30s %-5s %s\n" "$comm" "$pid" "$state"
done
# kernel thread 상태 그래프 (Python 스크립트 예시)
#!/usr/bin/env python3
import os
threads = []
for pid in os.listdir('/proc'):
if not pid.isdigit(): continue
try:
with open(f'/proc/{pid}/comm') as f:
comm = f.read().strip()
if comm.startswith('[') and comm.endswith(']'):
with open(f'/proc/{pid}/stat') as s:
state = s.read().split()[2]
threads.append((comm, pid, state))
except: pass
# 상태별 분류
states = {}
for t in threads:
states.setdefault(t[2], []).append(t)
for state, lst in sorted(states.items()):
print(f"# {state}: {len(lst)} threads")
for comm, pid, _ in lst[:5]:
print(f" {comm} (pid={pid})")
Flame Graph로 커널 스레드 분석
# 커널 스레드 스택 flamegraph 생성
perf record -F 99 -a -g -- sleep 30
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > kthread.svg
# 특정 커널 스레드만 프로파일링
perf record -F 99 -p $(pgrep -f kworker/0:1) -g -- sleep 10
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > kworker0.svg
내부 구현 상세
to_kthread() / struct kthread
각 커널 스레드에는 struct kthread가 부속되며, task_struct.set_child_tid 포인터를 통해 접근합니다:
/* kernel/kthread.c */
struct kthread {
unsigned long flags; /* KTHREAD_SHOULD_STOP 등 */
unsigned int cpu; /* per-CPU kthread의 대상 CPU */
int result; /* 스레드 함수 반환값 */
int (*threadfn)(void *);
void *data;
struct completion parked;
struct completion exited;
char *full_name;
};
/* 플래그 비트 */
enum KTHREAD_BITS {
KTHREAD_IS_PER_CPU = 0, /* per-CPU 스레드 여부 */
KTHREAD_SHOULD_STOP = 1, /* kthread_stop() 요청됨 */
KTHREAD_SHOULD_PARK = 2, /* kthread_park() 요청됨 */
KTHREAD_IS_PARKED = 3, /* 현재 park 상태 */
};
/* task_struct에서 kthread 구조체 접근 */
static inline struct kthread *to_kthread(struct task_struct *k)
{
WARN_ON(!(k->flags & PF_KTHREAD));
return k->worker_private;
}
kernel_thread() → kernel_clone()
kernel_thread()는 kernel_clone()의 래퍼로, 커널 모드에서 새 태스크를 생성합니다:
/* kernel/fork.c */
pid_t kernel_thread(int (*fn)(void *), void *arg,
const char *name, unsigned long flags)
{
struct kernel_clone_args args = {
.flags = ((lower_32_bits(flags) | CLONE_VM | CLONE_UNTRACED)
& ~CSIGNAL),
.exit_signal = (lower_32_bits(flags) & CSIGNAL),
.fn = fn,
.fn_arg = arg,
.name = name,
.kthread = 1, /* PF_KTHREAD 설정 */
};
return kernel_clone(&args);
}
/* kernel_clone()은 copy_process()를 호출하여:
* 1. task_struct 할당
* 2. PF_KTHREAD 플래그 설정
* 3. mm = NULL (유저 주소 공간 없음)
* 4. 커널 스택 할당
* 5. 스케줄러에 등록
*/
동기화 및 슬립 패턴
completion 기반 패턴
커널 스레드의 시작/종료를 동기화할 때 completion을 사용합니다:
static DECLARE_COMPLETION(thread_started);
static int my_thread_fn(void *data)
{
/* 초기화 완료 시그널 */
complete(&thread_started);
while (!kthread_should_stop()) {
do_work();
msleep(100);
}
return 0;
}
/* 생성 측 */
kth = kthread_run(my_thread_fn, data, "my-thread");
wait_for_completion(&thread_started);
/* 이 시점에서 스레드 초기화가 확실히 완료됨 */
Freezable 커널 스레드
시스템 절전(suspend) 시 커널 스레드도 얼릴 수 있습니다. I/O를 수행하는 커널 스레드는 suspend 전에 정지해야 데이터 손상을 방지할 수 있습니다:
static int freezable_thread_fn(void *data)
{
/* 이 스레드는 freezable — suspend 시 동결됨 */
set_freezable();
while (!kthread_should_stop()) {
/* try_to_freeze()가 suspend 시 스레드를 정지시킴 */
try_to_freeze();
do_io_work();
/* freezable 슬립 — suspend 도중에 깨어나지 않음 */
wait_event_freezable(my_wq, has_work || kthread_should_stop());
}
return 0;
}
/* wait_event_freezable은 내부적으로:
* 1. freezing(current)이면 try_to_freeze() 호출
* 2. 조건 확인
* 3. 슬립
* 을 안전하게 조합합니다 */
디버깅 (Debugging)
커널 스레드 식별하기
# 모든 커널 스레드 나열 (대괄호 표기)
ps -eo pid,ppid,cls,ni,psr,comm | awk '$2==2 || $1==2'
# 특정 커널 스레드의 상세 정보
cat /proc/<pid>/status
# Name: kswapd0
# State: S (sleeping)
# Tgid: <pid>
# VmSize: (없음 — 커널 스레드는 유저 메모리 없음)
# 커널 스레드의 CPU affinity 확인
taskset -p <pid>
# 커널 스레드 스택 트레이스
cat /proc/<pid>/stack
디버깅 도구
# ftrace로 kthread 생성 추적
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_kthread_work_execute_start/enable
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_kthread_work_queue_work/enable
cat /sys/kernel/debug/tracing/trace
# SysRq-T: 모든 태스크의 스택 덤프 (커널 스레드 포함)
echo t > /proc/sysrq-trigger
dmesg | less
# 특정 커널 스레드의 스케줄링 통계
cat /proc/<pid>/sched
# perf로 커널 스레드 프로파일링
perf top -t <pid>
/proc/<pid>/stack: CONFIG_STACKTRACE가 활성화된 커널에서 커널 스레드의 현재 호출 스택을 확인할 수 있습니다. 스레드가 어디서 슬립하고 있는지 파악하는 데 매우 유용합니다.
코드 예제 (Code Examples)
기본 커널 스레드 모듈
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched.h>
static struct task_struct *my_thread;
static int thread_func(void *data)
{
int count = 0;
pr_info("kthread started: pid=%d, comm=%s\\n",
current->pid, current->comm);
while (!kthread_should_stop()) {
pr_info("kthread iteration %d (cpu=%d)\\n",
count++, smp_processor_id());
msleep(2000);
}
pr_info("kthread stopping, count=%d\\n", count);
return count;
}
static int __init my_init(void)
{
pr_info("creating kernel thread\\n");
my_thread = kthread_run(thread_func, NULL, "example_kthread");
if (IS_ERR(my_thread)) {
pr_err("kthread_run failed: %ld\\n", PTR_ERR(my_thread));
return PTR_ERR(my_thread);
}
pr_info("kthread created: pid=%d\\n", my_thread->pid);
return 0;
}
static void __exit my_exit(void)
{
int ret;
if (my_thread) {
ret = kthread_stop(my_thread);
pr_info("kthread stopped, return value: %d\\n", ret);
}
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Basic Kernel Thread Example");
Per-CPU 커널 스레드
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/cpumask.h>
static struct task_struct * __percpu *per_cpu_threads;
static int per_cpu_fn(void *data)
{
unsigned int cpu = (unsigned long)data;
pr_info("per-cpu kthread on CPU %u started (pid=%d)\\n",
cpu, current->pid);
while (!kthread_should_stop()) {
if (kthread_should_park())
kthread_parkme();
pr_info("CPU %u: doing work\\n", cpu);
msleep(5000);
}
return 0;
}
static int __init percpu_init(void)
{
unsigned int cpu;
per_cpu_threads = alloc_percpu(struct task_struct *);
if (!per_cpu_threads)
return -ENOMEM;
for_each_online_cpu(cpu) {
struct task_struct *t;
t = kthread_create_on_cpu(per_cpu_fn,
(void *)(unsigned long)cpu,
cpu, "my_pcpu/%u");
if (IS_ERR(t)) {
pr_err("failed on CPU %u\\n", cpu);
continue;
}
*per_cpu_ptr(per_cpu_threads, cpu) = t;
wake_up_process(t);
}
return 0;
}
static void __exit percpu_exit(void)
{
unsigned int cpu;
for_each_online_cpu(cpu) {
struct task_struct *t = *per_cpu_ptr(per_cpu_threads, cpu);
if (t)
kthread_stop(t);
}
free_percpu(per_cpu_threads);
}
module_init(percpu_init);
module_exit(percpu_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Per-CPU Kernel Thread Example");
kthread_worker 사용 예제
#include <linux/module.h>
#include <linux/kthread.h>
static struct kthread_worker *worker;
static struct kthread_work my_work;
static struct kthread_delayed_work my_delayed_work;
static void work_func(struct kthread_work *work)
{
pr_info("work executed: thread=%s, cpu=%d\\n",
current->comm, smp_processor_id());
}
static void delayed_work_func(struct kthread_work *work)
{
pr_info("delayed work executed\\n");
/* 자기 자신을 다시 큐잉 (주기적 실행) */
kthread_queue_delayed_work(worker, &my_delayed_work,
msecs_to_jiffies(3000));
}
static int __init worker_init(void)
{
/* 워커 생성 (전용 스레드 자동 생성) */
worker = kthread_create_worker(0, "my_kworker");
if (IS_ERR(worker))
return PTR_ERR(worker);
/* 즉시 실행 작업 */
kthread_init_work(&my_work, work_func);
kthread_queue_work(worker, &my_work);
/* 지연 실행 작업 (3초 후) */
kthread_init_delayed_work(&my_delayed_work, delayed_work_func);
kthread_queue_delayed_work(worker, &my_delayed_work,
msecs_to_jiffies(3000));
return 0;
}
static void __exit worker_exit(void)
{
kthread_cancel_delayed_work_sync(&my_delayed_work);
kthread_destroy_worker(worker);
}
module_init(worker_init);
module_exit(worker_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("kthread_worker Example");
Freezable 커널 스레드
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/wait.h>
static struct task_struct *freeze_thread;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static bool do_work = false;
static int freezable_fn(void *data)
{
set_freezable();
while (!kthread_should_stop()) {
/* suspend 시 자동 동결, resume 시 자동 해동 */
wait_event_freezable(wq,
do_work || kthread_should_stop());
if (kthread_should_stop())
break;
do_work = false;
pr_info("freezable thread: doing I/O work\\n");
}
return 0;
}
static int __init freeze_init(void)
{
freeze_thread = kthread_run(freezable_fn, NULL, "freezable_kth");
return IS_ERR(freeze_thread) ? PTR_ERR(freeze_thread) : 0;
}
static void __exit freeze_exit(void)
{
kthread_stop(freeze_thread);
}
module_init(freeze_init);
module_exit(freeze_exit);
MODULE_LICENSE("GPL");
주의사항 및 버그 패턴
kthread_stop() 없이 모듈 언로드
/* 잘못된 예 — 스레드 실행 중 모듈 언로드 */
static void __exit bad_exit(void)
{
/* kthread_stop() 호출 안 함!
* → 스레드가 계속 실행되지만 코드 영역이 해제됨
* → 커널 패닉 (use-after-free) */
pr_info("goodbye\\n");
}
/* 올바른 예 */
static void __exit good_exit(void)
{
if (my_thread) {
kthread_stop(my_thread); /* 반드시 종료 대기 */
my_thread = NULL;
}
}
커널 스레드에서 유저 공간 접근
/* 잘못된 예 — 커널 스레드에서 유저 메모리 접근 */
static int bad_thread_fn(void *data)
{
char __user *ubuf = (char __user *)data;
char kbuf[64];
/* mm == NULL이므로 copy_from_user()가 실패하거나 Oops 발생!
* 커널 스레드는 유저 주소 공간이 없습니다 */
copy_from_user(kbuf, ubuf, 64); /* WRONG */
return 0;
}
/* 올바른 방법: 데이터를 커널 메모리에 복사해서 전달 */
char *kdata = kmalloc(64, GFP_KERNEL);
copy_from_user(kdata, ubuf, 64); /* 유저 컨텍스트에서 미리 복사 */
kth = kthread_run(my_fn, kdata, "my-kthread"); /* 커널 메모리 전달 */
무한 루프 / CPU 100% 점유
/* 잘못된 예 — 슬립 없는 폴링 루프 */
static int hog_thread_fn(void *data)
{
while (!kthread_should_stop()) {
/* CPU를 100% 점유! 다른 태스크 기아 발생 */
do_polling();
}
return 0;
}
/* 올바른 예 — schedule() 또는 cond_resched() 삽입 */
static int good_thread_fn(void *data)
{
while (!kthread_should_stop()) {
if (!has_work()) {
usleep_range(1000, 2000); /* 1~2ms 슬립 */
continue;
}
do_work();
cond_resched(); /* 선점 포인트 */
}
return 0;
}
kthread_create 에러 미처리
/* 잘못된 예 */
my_thread = kthread_run(fn, data, "my-kth");
wake_up_process(my_thread); /* IS_ERR일 때 크래시! */
/* 올바른 예 */
my_thread = kthread_run(fn, data, "my-kth");
if (IS_ERR(my_thread)) {
pr_err("failed: %ld\\n", PTR_ERR(my_thread));
my_thread = NULL; /* exit에서 NULL 체크 가능하도록 */
return PTR_ERR(my_thread);
}
모듈 언로드 안전: module_exit에서는 반드시 (1) kthread_stop()으로 스레드 종료를 기다리고, (2) 스레드가 사용한 리소스를 해제하세요. 스레드가 실행 중인 상태에서 모듈이 언로드되면 코드 영역이 해제되어 커널 패닉이 발생합니다.
API 레퍼런스 요약
| 함수 / 매크로 | 설명 | 헤더 |
|---|---|---|
kthread_create(fn, data, fmt, ...) |
커널 스레드 생성 (미실행 상태) | linux/kthread.h |
kthread_run(fn, data, fmt, ...) |
커널 스레드 생성 + 즉시 실행 | linux/kthread.h |
kthread_create_on_node(fn, data, node, fmt, ...) |
NUMA 노드 지정 생성 | linux/kthread.h |
kthread_create_on_cpu(fn, data, cpu, fmt) |
CPU 바인딩 생성 (per-CPU) | linux/kthread.h |
kthread_bind(task, cpu) |
미실행 스레드를 특정 CPU에 바인딩 | linux/kthread.h |
kthread_should_stop() |
종료 요청 확인 (루프 조건) | linux/kthread.h |
kthread_stop(task) |
스레드 종료 요청 + 종료 대기 | linux/kthread.h |
kthread_should_park() |
일시 중지 요청 확인 | linux/kthread.h |
kthread_park(task) |
스레드 일시 중지 | linux/kthread.h |
kthread_unpark(task) |
스레드 재개 | linux/kthread.h |
kthread_parkme() |
현재 스레드를 park 상태로 전환 | linux/kthread.h |
kthread_create_worker(flags, fmt, ...) |
kthread_worker + 전용 스레드 생성 | linux/kthread.h |
kthread_queue_work(worker, work) |
작업을 워커 큐에 추가 | linux/kthread.h |
kthread_flush_work(work) |
특정 작업 완료 대기 | linux/kthread.h |
kthread_destroy_worker(worker) |
워커 + 전용 스레드 정리 | linux/kthread.h |
smpboot_register_percpu_thread(ht) |
per-CPU 스레드 프레임워크 등록 | linux/smpboot.h |
set_freezable() |
현재 스레드를 freezable로 마킹 | linux/freezer.h |
wait_event_freezable(wq, cond) |
freeze 안전한 조건부 대기 | linux/freezer.h |
관련 문서
Kernel Threads와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.