首頁  >  文章  >  運維  >  linux有核心級線程麼

linux有核心級線程麼

青灯夜游
青灯夜游原創
2022-11-11 14:20:322309瀏覽

linux有核心級線程,linux支援核心級的多線程。 Linux核心可以看作服務進程(管理軟硬體資源,回應用戶進程的各種進程);核心需要多個執行流並行,為了防止可能的阻塞,支援多執行緒。內核線程就是內核的分身,可以用來處理一件特定事情,內核線程的調度由內核負責,一個內核線程的處於阻塞狀態時不會影響其他的內核線程。

linux有核心級線程麼

本教學操作環境:linux7.3系統、Dell G3電腦。

執行緒通常被定義為一個行程中程式碼的不同執行路線。從實作方式上劃分,執行緒有兩種類型:「使用者級執行緒」和「核心級執行緒」。

使用者執行緒指不需要核心支援而在使用者程式中實現的線程,其不依賴作業系統核心,應用程式利用線程庫提供創建、同步、調度和管理線程的函數來控制使用者線程。這種執行緒甚至在像 DOS 這樣的作業系統中也可實現,但執行緒的調度需要使用者程式完成,這有些類似 Windows 3.x 的協作式多任務。

另外一種則需要核心的參與,由核心完成執行緒的調度。其依賴作業系統核心,由核心的內部需求進行創建和撤銷,這兩種模型各有其好處和缺點。

使用者執行緒不需要額外的核心開支,且使用者狀態執行緒的實作方式可以被定製或修改以適應特殊應用的要求,但是當一個執行緒因I/O 而處於等待狀態時,整個行程就會被調度程式切換為等待狀態,其他執行緒無法運作的機會;而核心執行緒則沒有各個限制,有利於發揮多處理器的並發優勢,但卻佔用了更多的系統開支。

Windows NT和OS/2支援核心執行緒。 Linux 支援核心級的多執行緒。

linux中的核心級線

#1.核心執行緒概述

#Linux核心可以看作服務進程(管理軟硬體資源,響應用戶進程的各種進程)

核心需要多個執行流並行,為了防止可能的阻塞,支援多執行緒。

內核執行緒就是核心的分身,可以用來處理一件特定事情,核心執行緒的調度由核心負責,一個核心執行緒的處於阻塞狀態時不影響其他的核心執行緒。

核心執行緒是直接由核心本身啟動的行程。核心執行緒實際上是將核心函數委託給獨立的進程執行,它與核心中的其他「進程」並行執行。核心線程經常被稱之為核心守護程式。在目前的核心中,核心執行緒就負責下面的工作:

  • 週期性地將修改的記憶體頁與頁來源區塊裝置同步
  • 實作檔案系統的交易日誌

核心執行緒由核心創建,所以核心執行緒在核心態執行,只能存取核心虛擬位址空間,不能存取使用者空間。

在linux所有的線程都當作進程來實現,也沒有單獨為線程定義調度演算法以及資料結構,一個進程相當於包含一個線程,就是自身,多線程,原本的線程稱為主線程,他們一起構成線程組。

行程擁有自己的位址空間,所以每個行程都有自己的頁表,而執行緒卻沒有,只能和其它執行緒共享主執行緒的位址空間和頁表

#2.三個資料結構

每個行程或執行緒由三個重要的資料結構,分別是struct thread_info, struct task_struct 和核心堆疊。

thread_info物件存放的進程/執行緒的基本訊息,它和進程/執行緒的核心堆疊存放在核心空間裡的一段2倍頁長空間中。其中thread_info結構存放在位址段的末尾,其餘空間作為核心堆疊。核心使用夥伴系統分配這段空間。

linux有核心級線程麼
struct thread_info {

	int			preempt_count;	/* 0 => preemptable,  bug */

	struct task_struct	*task;		/* main task structure */
	__u32			cpu;		/* cpu */};

thread_info結構體中有一個struct 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)
  • stack:是指向进程或者线程的thread_info
  • mm:对象用来管理该进程/线程的页表以及虚拟内存区
  • active_mm:主要用于内核线程访问主内核页全局目录
  • pid:每个task_struct都会有一个不同的id,就是pid
  • tgid:线程组领头线程的PID,就是主线程的pid

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函数中:

  • 首先利用kmalloc分配kthread_create_info变量create,利用函数参数初始化create
  • 将create加入kthread_create_list链表中,然后唤醒kthreadd内核线程创建当前线程
  • 唤醒kthreadd后,利用completion等待内核线程创建完成,completion完成后,释放create空间

下面来看下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(;;)一直驻留在内存中运行:主要过程如下:

  • 检查kthread_create_list为空时,kthreadd让出cpu的执行权
  • kthread_create_list不为空时,利用while循环遍历kthread_create_list链表
  • 每取下一个链表节点后调用create_kthread,创建内核线程
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)。

4.内核线程的退出

线程一旦启动起来后,会一直运行,除非该线程主动调用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杀掉内核线程。

5.内核线程使用

#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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn