Linux에는 커널 수준 스레드가 있고 Linux는 커널 수준 멀티스레딩을 지원합니다. Linux 커널은 서비스 프로세스(소프트웨어 및 하드웨어 리소스 관리, 사용자 프로세스의 다양한 프로세스에 응답)로 간주할 수 있습니다. 커널은 여러 실행 스트림을 병렬로 필요로 하며 차단 가능성을 방지하기 위해 멀티스레딩을 지원합니다. 커널 스레드는 특정 작업을 처리하는 데 사용할 수 있는 커널의 복제본입니다. 커널은 커널 스레드의 예약을 담당합니다. 하나의 커널 스레드가 차단되면 다른 커널 스레드에 영향을 주지 않습니다.
이 튜토리얼의 운영 환경: linux7.3 시스템, Dell G3 컴퓨터.
스레드는 일반적으로 프로세스 내에서 코드의 다양한 실행 경로로 정의됩니다. 구현 측면에서 스레드에는 "사용자 수준 스레드"와 "커널 수준 스레드"라는 두 가지 유형이 있습니다.
사용자 스레드는 커널 지원 없이 사용자 프로그램에서 구현되는 스레드를 의미합니다. 애플리케이션 프로세스는 스레드 라이브러리를 사용하여 사용자 스레드를 제어하기 위한 스레드 생성, 동기화, 예약 및 관리 기능을 제공합니다. . 이러한 종류의 스레드는 DOS와 같은 운영 체제에서도 구현될 수 있지만 스레드 예약은 사용자 프로그램에 의해 완료되어야 하며 이는 Windows 3.x의 협력적 멀티태스킹과 다소 유사합니다.
다른 하나는 스레드 스케줄링을 완료하는 커널의 참여가 필요합니다. 이는 운영 체제 코어에 의존하며 커널의 내부 요구에 따라 생성 및 제거됩니다. 두 모델 모두 고유한 장점과 단점이 있습니다.
사용자 스레드는 추가 커널 오버헤드가 필요하지 않으며 사용자 모드 스레드의 구현은 특수 응용 프로그램의 요구 사항에 맞게 사용자 정의하거나 수정할 수 있지만 스레드가 I/O로 인해 대기 상태에 있는 경우 전체 프로세스가 프로그램이 대기 상태로 전환되면 다른 스레드는 실행될 기회가 없지만 커널 스레드에는 제한이 없으므로 다중 프로세서의 동시성을 활용하는 데 도움이 되지만 시스템 비용이 더 많이 소요됩니다. .
Windows NT 및 OS/2는 커널 스레드를 지원합니다. Linux는 커널 수준 멀티스레딩을 지원합니다.
Linux의 커널 수준 스레드
1. 커널 스레드 개요
Linux 커널은 서비스 프로세스(소프트웨어 및 하드웨어 리소스 관리, 사용자 프로세스의 다양한 프로세스에 응답)로 간주될 수 있습니다.
커널 필요 다중 실행 스트림은 병렬이며 가능한 차단을 방지하기 위해 다중 스레딩이 지원됩니다.
커널 스레드는 특정 작업을 처리하는 데 사용할 수 있는 커널의 복제본입니다. 커널은 커널 스레드 중 하나가 차단되어도 다른 커널 스레드에 영향을 주지 않습니다.
커널 스레드는 커널 자체에서 직접 시작되는 프로세스입니다. 커널 스레드는 실제로 커널 기능을 독립적인 프로세스에 위임하여 실행합니다. 이 프로세스는 커널의 다른 "프로세스"와 병렬로 실행됩니다. 커널 스레드는 종종 커널 데몬이라고 불립니다. 현재 커널에서 커널 스레드는 다음 작업을 담당합니다.
커널 스레드는 다음에 의해 생성됩니다. 커널이므로 커널 스레드 커널 모드에서 실행할 때 커널 가상 주소 공간에만 액세스할 수 있고 사용자 공간에는 액세스할 수 없습니다.
리눅스에서는 모든 스레드가 프로세스로 구현되며 스레드에 대해 정의된 별도의 스케줄링 알고리즘 및 데이터 구조가 없습니다. 프로세스는 하나의 스레드, 즉 여러 스레드를 포함하는 것과 동일합니다. , 그리고 그들은 함께 스레드 그룹을 형성합니다.
프로세스에는 자체 주소 공간이 있으므로 각 프로세스에는 자체 페이지 테이블이 있지만 스레드는 그렇지 않습니다. 메인 스레드의 주소 공간과 페이지 테이블만 다른 스레드와 공유할 수 있습니다.
2. 구조
각 프로세스 또는 스레드는 세 가지 중요한 데이터 구조, 즉 struct thread_info, struct task_struct 및 커널 스택으로 구성됩니다.
thread_info 객체는 프로세스/스레드의 기본 정보를 저장하며, 프로세스/스레드의 커널 스택은 커널 공간의 페이지 길이의 두 배에 해당하는 공간에 저장됩니다. thread_info 구조는 주소 세그먼트 끝에 저장되고 나머지 공간은 커널 스택으로 사용됩니다. 커널은 버디 시스템을 사용하여 이 공간을 할당합니다.
struct thread_info {
int preempt_count; /* 0 => preemptable, bug */
struct task_struct *task; /* main task structure */
__u32 cpu; /* cpu */};
thread_info 구조에는 task_struct *task가 있습니다. task는 스레드 또는 프로세스의 task_struct 개체를 가리킵니다. task_struct는 작업 설명자라고도 합니다.
struct task_struct {
pid_t pid;
pid_t tgid;
void *stack;
struct mm_struct *mm, *active_mm;
/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;};#define task_thread_info(task) ((struct thread_info *)(task)->stack)
linux系统上虚拟地址空间分为两个部分:供用户态程序访问的虚拟地址空间和供内核访问的内核空间。每当内核执行上下文切换时,虚拟地址空间的用户层部分都会切换,以便匹配运行的进程,内核空间的部分是不会切换的。
3.内核线程创建
在内核版本linux-3.x以后,内核线程的创建被延后执行,并且交给名为kthreadd 2号线程执行创建过程,但是kthreadd本身是怎么创建的呢?过程如下:
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);
}
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadadd本身最终是通过do_fork实现的,do_fork通过传入不同的参数,可以分别用于创建用户态进程/线程,内核线程等。当kthreadadd被创建以后,内核线程的创建交给它实现。
内核线程的创建分为创建和启动两个部分,kthread_run作为统一的接口,可以同时实现,这两个功能:
#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; \
})
#define kthread_create(threadfn, data, namefmt, arg...) \
kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)
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;
/*分配kthread_create_info空间*/
struct kthread_create_info *create = kmalloc(sizeof(*create),
GFP_KERNEL);
if (!create)
return ERR_PTR(-ENOMEM);
create->threadfn = threadfn;
create->data = data;
create->node = node;
create->done = &done;
/*加入到kthread_creta_list列表中,等待ktherad_add中断线程去创建改线程*/
spin_lock(&kthread_create_lock);
list_add_tail(&create->list, &kthread_create_list);
spin_unlock(&kthread_create_lock);
wake_up_process(kthreadd_task);
/*
* 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))) {
/*
* 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);
}
task = create->result;
.
.
.
kfree(create);
return task;
}
kthread_create_on_node函数中:
下面来看下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();
__set_current_state(TASK_RUNNING);
spin_lock(&kthread_create_lock);
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);
spin_unlock(&kthread_create_lock);
create_kthread(create);
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
kthreadd利用for(;;)一直驻留在内存中运行:主要过程如下:
static void create_kthread(struct kthread_create_info *create)
{
int pid;
/* We want our own signal handler (we take no signals by default). */
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
if (pid done, NULL);
if (!done) {
kfree(create);
return;
}
create->result = ERR_PTR(pid);
complete(done);
}
}
可以看到内核线程的创建最终还是和kthreadd一样,调用kernel_thread实现。
static int kthread(void *_create)
{
.
.
.
.
/* If user was SIGKILLed, I release the structure. */
done = xchg(&create->done, NULL);
if (!done) {
kfree(create);
do_exit(-EINTR);
}
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_UNINTERRUPTIBLE);
create->result = current;
complete(done);
schedule();
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
__kthread_parkme(&self);
ret = threadfn(data);
}
/* we can't just return, we must preserve "self" on stack */
do_exit(ret);
}
kthread以struct kthread_create_info 类型的create为参数,create中带有创建内核线程的回调函数,以及函数的参数。kthread中,完成completion信号量的处理,然后schedule让出cpu的执行权,等待下次返回 时,执行回调函数threadfn(data)。
线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。
int kthread_stop(struct task_struct *k)
{
struct kthread *kthread;
int ret;
trace_sched_kthread_stop(k);
get_task_struct(k);
kthread = to_live_kthread(k);
if (kthread) {
set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
__kthread_unpark(k, kthread);
wake_up_process(k);
wait_for_completion(&kthread->exited);
}
ret = k->exit_code;
put_task_struct(k);
trace_sched_kthread_stop_ret(ret);
return ret;
}
如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止。在执行kthread_stop的时候,目标线程必须没有退出,否则会Oops。所以在创建thread_func时,可以采用以下形式:
thread_func()
{
// do your work here
// wait to exit
while(!thread_could_stop())
{
wait();
}
}
exit_code()
{
kthread_stop(_task); //发信号给task,通知其可以退出了
}
如果线程中在等待某个条件满足才能继续运行,所以只有满足了条件以后,才能调用kthread_stop杀掉内核线程。
#include "test_kthread.h"
#include <linux>
#include <linux>
#include <linux>
#include <linux>
#include <linux>
static struct task_struct *test_thread = NULL;
unsigned int time_conut = 5;
int test_thread_fun(void *data)
{
int times = 0;
while(!kthread_should_stop())
{
printk("\n printk %u\r\n", times);
times++;
msleep_interruptible(time_conut*1000);
}
printk("\n test_thread_fun exit success\r\n\n");
return 0;
}
void register_test_thread(void)
{
test_thread = kthread_run(test_thread_fun , NULL, "test_kthread" );
if (IS_ERR(test_thread)){
printk(KERN_INFO "create test_thread failed!\n");
}
else {
printk(KERN_INFO "create test_thread ok!\n");
}
}
static ssize_t kthread_debug_start(struct device *dev, struct device_attribute *attr, char *buf)
{
register_test_thread();
return 0;
}
static ssize_t kthread_debug_stop(struct device *dev, struct device_attribute *attr, char *buf)
{
kthread_stop(test_thread);
return 0;
}
static DEVICE_ATTR(kthread_start, S_IRUSR, kthread_debug_start,NULL);
static DEVICE_ATTR(kthread_stop, S_IRUSR, kthread_debug_stop,NULL);
struct attribute * kthread_group_info_attrs[] =
{
&dev_attr_kthread_start.attr,
&dev_attr_kthread_stop.attr,
NULL,
};
struct attribute_group kthread_group =
{
.name = "kthread",
.attrs = kthread_group_info_attrs,
};</linux></linux></linux></linux></linux>
相关推荐:《Linux视频教程》
위 내용은 Linux에는 커널 수준 스레드가 있습니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!