linux有內(nèi)核級線程么

linux有內(nèi)核級線程,linux支持內(nèi)核級的多線程。Linux內(nèi)核可以看作服務(wù)進程(管理軟硬件資源,響應(yīng)用戶進程的各種進程);內(nèi)核需要多個執(zhí)行流并行,為了防止可能的阻塞,支持多線程。內(nèi)核線程就是內(nèi)核的一個分身,可以用以處理一件特定事情,內(nèi)核線程的調(diào)度由內(nèi)核負責,一個內(nèi)核線程的處于阻塞狀態(tài)時不影響其他的內(nèi)核線程。

linux有內(nèi)核級線程么

本教程操作環(huán)境:linux7.3系統(tǒng)、Dell G3電腦。

線程通常被定義為一個進程中代碼的不同執(zhí)行路線。從實現(xiàn)方式上劃分,線程有兩種類型:“用戶級線程”和“內(nèi)核級線程”。

用戶線程指不需要內(nèi)核支持而在用戶程序中實現(xiàn)的線程,其不依賴于操作系統(tǒng)核心,應(yīng)用進程利用線程庫提供創(chuàng)建、同步、調(diào)度和管理線程的函數(shù)來控制用戶線程。這種線程甚至在象 DOS 這樣的操作系統(tǒng)中也可實現(xiàn),但線程的調(diào)度需要用戶程序完成,這有些類似 Windows 3.x 的協(xié)作式多任務(wù)。

另外一種則需要內(nèi)核的參與,由內(nèi)核完成線程的調(diào)度。其依賴于操作系統(tǒng)核心,由內(nèi)核的內(nèi)部需求進行創(chuàng)建和撤銷,這兩種模型各有其好處和缺點。

用戶線程不需要額外的內(nèi)核開支,并且用戶態(tài)線程的實現(xiàn)方式可以被定制或修改以適應(yīng)特殊應(yīng)用的要求,但是當一個線程因 I/O 而處于等待狀態(tài)時,整個進程就會被調(diào)度程序切換為等待狀態(tài),其他線程得不到運行的機會;而內(nèi)核線程則沒有各個限制,有利于發(fā)揮多處理器的并發(fā)優(yōu)勢,但卻占用了更多的系統(tǒng)開支。

Windows NT和OS/2支持內(nèi)核線程。Linux 支持內(nèi)核級的多線程。

linux中的內(nèi)核級線

1.內(nèi)核線程概述

Linux內(nèi)核可以看作服務(wù)進程(管理軟硬件資源,響應(yīng)用戶進程的各種進程)

內(nèi)核需要多個執(zhí)行流并行,為了防止可能的阻塞,支持多線程。

內(nèi)核線程就是內(nèi)核的一個分身,可以用以處理一件特定事情,內(nèi)核線程的調(diào)度由內(nèi)核負責,一個內(nèi)核線程的處于阻塞狀態(tài)時不影響其他的內(nèi)核線程。

內(nèi)核線程是直接由內(nèi)核本身啟動的進程。內(nèi)核線程實際上是將內(nèi)核函數(shù)委托給獨立的進程執(zhí)行,它與內(nèi)核中的其他“進程”并行執(zhí)行。內(nèi)核線程經(jīng)常被稱之為內(nèi)核守護進程。當前的內(nèi)核中,內(nèi)核線程就負責下面的工作:

  • 周期性地將修改的內(nèi)存頁與頁來源塊設(shè)備同步
  • 實現(xiàn)文件系統(tǒng)的事務(wù)日志

內(nèi)核線程由內(nèi)核創(chuàng)建,所以內(nèi)核線程在內(nèi)核態(tài)執(zhí)行,只能訪問內(nèi)核虛擬地址空間,不能訪問用戶空間。

在linux所有的線程都當作進程來實現(xiàn),也沒有單獨為線程定義調(diào)度算法以及數(shù)據(jù)結(jié)構(gòu),一個進程相當于包含一個線程,就是自身,多線程,原本的線程稱為主線程,他們一起構(gòu)成線程組。

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

2.三個數(shù)據(jù)結(jié)構(gòu)

每個進程或線程由三個重要的數(shù)據(jù)結(jié)構(gòu),分別是struct thread_info, struct task_struct 和內(nèi)核棧。

thread_info對象存放的進程/線程的基本信息,它和進程/線程的內(nèi)核棧存放在內(nèi)核空間里的一段2倍頁長空間中。其中thread_info結(jié)構(gòu)存放在地址段的末尾,其余空間作為內(nèi)核棧。內(nèi)核使用伙伴系統(tǒng)分配這段空間。

linux有內(nèi)核級線程么
struct thread_info {  	int			preempt_count;	/* 0 => preemptable, <0 => bug */  	struct task_struct	*task;		/* main task structure */ 	__u32			cpu;		/* cpu */};

thread_info結(jié)構(gòu)體中有一個struct task_struct *task,task指向該線程或者進程的task_struct對象,task_struct也叫做任務(wù)描述符:

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:對象用來管理該進程/線程的頁表以及虛擬內(nèi)存區(qū)
  • active_mm:主要用于內(nèi)核線程訪問主內(nèi)核頁全局目錄
  • pid:每個task_struct都會有一個不同的id,就是pid
  • tgid:線程組領(lǐng)頭線程的PID,就是主線程的pid

linux系統(tǒng)上虛擬地址空間分為兩個部分:供用戶態(tài)程序訪問的虛擬地址空間和供內(nèi)核訪問的內(nèi)核空間。每當內(nèi)核執(zhí)行上下文切換時,虛擬地址空間的用戶層部分都會切換,以便匹配運行的進程,內(nèi)核空間的部分是不會切換的。

3.內(nèi)核線程創(chuàng)建

在內(nèi)核版本linux-3.x以后,內(nèi)核線程的創(chuàng)建被延后執(zhí)行,并且交給名為kthreadd 2號線程執(zhí)行創(chuàng)建過程,但是kthreadd本身是怎么創(chuàng)建的呢?過程如下:

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實現(xiàn)的,do_fork通過傳入不同的參數(shù),可以分別用于創(chuàng)建用戶態(tài)進程/線程,內(nèi)核線程等。當kthreadadd被創(chuàng)建以后,內(nèi)核線程的創(chuàng)建交給它實現(xiàn)。

內(nèi)核線程的創(chuàng)建分為創(chuàng)建和啟動兩個部分,kthread_run作為統(tǒng)一的接口,可以同時實現(xiàn),這兩個功能:

#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中斷線程去創(chuàng)建改線程*/ 	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函數(shù)中:

  • 首先利用kmalloc分配kthread_create_info變量create,利用函數(shù)參數(shù)初始化create
  • 將create加入kthread_create_list鏈表中,然后喚醒kthreadd內(nèi)核線程創(chuàng)建當前線程
  • 喚醒kthreadd后,利用completion等待內(nèi)核線程創(chuàng)建完成,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(;;)一直駐留在內(nèi)存中運行:主要過程如下:

  • 檢查kthread_create_list為空時,kthreadd讓出cpu的執(zhí)行權(quán)
  • kthread_create_list不為空時,利用while循環(huán)遍歷kthread_create_list鏈表
  • 每取下一個鏈表節(jié)點后調(diào)用create_kthread,創(chuàng)建內(nèi)核線程
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 < 0) { 		/* 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); 	} }

可以看到內(nèi)核線程的創(chuàng)建最終還是和kthreadd一樣,調(diào)用kernel_thread實現(xiàn)。

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為參數(shù),create中帶有創(chuàng)建內(nèi)核線程的回調(diào)函數(shù),以及函數(shù)的參數(shù)。kthread中,完成completion信號量的處理,然后schedule讓出cpu的執(zhí)行權(quán),等待下次返回 時,執(zhí)行回調(diào)函數(shù)threadfn(data)。

4.內(nèi)核線程的退出

線程一旦啟動起來后,會一直運行,除非該線程主動調(diào)用do_exit函數(shù),或者其他的進程調(diào)用kthread_stop函數(shù),結(jié)束線程的運行。

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; }

如果線程函數(shù)正在處理一個非常重要的任務(wù),它不會被中斷的。當然如果線程函數(shù)永遠不返回并且不檢查信號,它將永遠都不會停止。在執(zhí)行kthread_stop的時候,目標線程必須沒有退出,否則會Oops。所以在創(chuàng)建thread_func時,可以采用以下形式:

thread_func() {     // do your work here     // wait to exit     while(!thread_could_stop())     {            wait();     } }  exit_code() {      kthread_stop(_task);   //發(fā)信號給task,通知其可以退出了 }

如果線程中在等待某個條件滿足才能繼續(xù)運行,所以只有滿足了條件以后,才能調(diào)用kthread_stop殺掉內(nèi)核線程。

5.內(nèi)核線程使用

#include "test_kthread.h" #include <linux/delay.h> #include <linux/timer.h>  #include <linux/platform_device.h> #include <linux/fs.h> #include <linux/module.h>  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 %urn", times); 		times++; 		msleep_interruptible(time_conut*1000); 	}  	printk("n   test_thread_fun exit successrnn");  	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, };

相關(guān)推薦:《Linux視頻教程

以上就是

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊7 分享