Rumah >Operasi dan penyelenggaraan >operasi dan penyelenggaraan linux >Adakah Linux mempunyai benang peringkat kernel?

Adakah Linux mempunyai benang peringkat kernel?

青灯夜游
青灯夜游asal
2022-11-11 14:20:322348semak imbas

Linux mempunyai benang peringkat kernel, dan Linux menyokong berbilang benang peringkat kernel. Kernel Linux boleh dianggap sebagai proses perkhidmatan (menguruskan sumber perisian dan perkakasan, bertindak balas kepada pelbagai proses proses pengguna, kernel memerlukan berbilang aliran pelaksanaan secara selari, dan menyokong multi-threading untuk mengelakkan kemungkinan sekatan); Benang kernel ialah klon kernel yang boleh digunakan untuk mengendalikan perkara tertentu Kernel bertanggungjawab untuk penjadualan benang kernel Apabila satu benang kernel disekat, ia tidak menjejaskan benang kernel lain.

Adakah Linux mempunyai benang peringkat kernel?

Persekitaran pengendalian tutorial ini: sistem linux7.3, komputer Dell G3.

Benang biasanya ditakrifkan sebagai laluan pelaksanaan kod yang berbeza dalam proses. Dari segi pelaksanaan, terdapat dua jenis benang: "benang peringkat pengguna" dan "benang peringkat kernel".

Urutan pengguna merujuk kepada utas yang dilaksanakan dalam program pengguna tanpa sokongan kernel. Proses aplikasi menggunakan pustaka utas untuk menyediakan fungsi untuk mencipta, menyegerak, menjadualkan dan mengurus utas untuk mengawal urutan pengguna. Benang jenis ini malah boleh dilaksanakan dalam sistem pengendalian seperti DOS, tetapi penjadualan benang perlu dilengkapkan oleh program pengguna, yang agak serupa dengan multitasking koperasi Windows 3.x.

Yang lain memerlukan penyertaan kernel, yang melengkapkan penjadualan benang. Ia bergantung pada teras sistem pengendalian dan dicipta dan dimusnahkan oleh keperluan dalaman kernel Kedua-dua model mempunyai kelebihan dan kekurangan mereka sendiri.

Urut pengguna tidak memerlukan overhed kernel tambahan, dan pelaksanaan utas mod pengguna boleh disesuaikan atau diubah suai untuk memenuhi keperluan aplikasi khas Walau bagaimanapun, apabila utas berada dalam keadaan menunggu disebabkan oleh I/O , keseluruhan proses Ia akan ditukar kepada keadaan menunggu oleh penjadual, dan utas lain tidak akan mendapat peluang untuk dijalankan sementara utas kernel tidak mempunyai sekatan, yang kondusif untuk mengambil kesempatan daripada kesesuaian berbilang pemproses, tetapi; ia memerlukan lebih banyak perbelanjaan sistem.

Windows NT dan OS/2 menyokong utas kernel. Linux menyokong multithreading peringkat kernel.

Benang peringkat kernel dalam linux

Ikhtisar benang kernel

Inti Linux boleh dianggap sebagai proses perkhidmatan (menguruskan sumber perisian dan perkakasan dan bertindak balas kepada pelbagai proses proses pengguna)

Inti memerlukan berbilang aliran pelaksanaan secara selari untuk mengelakkan kemungkinan sekatan, ia menyokong multi-threading.

Benang kernel ialah klon kernel yang boleh digunakan untuk mengendalikan perkara tertentu Kernel bertanggungjawab untuk penjadualan benang kernel Apabila satu benang kernel disekat, ia tidak menjejaskan benang kernel lain .

Benang kernel ialah proses yang dimulakan terus oleh kernel itu sendiri. Benang kernel sebenarnya mewakilkan fungsi kernel kepada proses bebas untuk pelaksanaan, yang dilaksanakan selari dengan "proses" lain dalam kernel. Benang kernel sering dipanggil daemon kernel. Dalam kernel semasa, utas kernel bertanggungjawab untuk kerja berikut:

  • Segerakkan halaman memori yang diubah suai secara berkala dengan peranti blok sumber halaman
  • Laksanakan log transaksi sistem fail

Benang kernel dicipta oleh kernel, jadi thread kernel dilaksanakan dalam mod kernel dan hanya boleh mengakses ruang alamat maya kernel dan tidak boleh mengakses ruang pengguna.

Di Linux, semua urutan dilaksanakan sebagai proses, dan tidak ada algoritma penjadualan dan struktur data yang ditakrifkan untuk utas Satu proses adalah bersamaan dengan mengandungi satu utas, iaitu ia sendiri, berbilang benang dan asal benang dipanggil benang utama, bersama-sama mereka membentuk kumpulan benang.

Proses mempunyai ruang alamatnya sendiri, jadi setiap proses mempunyai jadual halaman sendiri, tetapi utas itu tidak boleh berkongsi ruang alamat dan jadual halaman utas utama dengan utas lain

2. Tiga struktur data

Setiap proses atau thread terdiri daripada tiga struktur data penting, iaitu struct thread_info, struct task_struct dan kernel stack.

Objek thread_info menyimpan maklumat asas proses/benang Ia dan tindanan kernel proses/benang disimpan dalam ruang dua kali panjang halaman dalam ruang kernel. Struktur thread_info disimpan di hujung segmen alamat, dan ruang yang tinggal digunakan sebagai timbunan kernel. Kernel menggunakan sistem buddy untuk memperuntukkan ruang ini.

Adakah Linux mempunyai benang peringkat kernel?
struct thread_info {

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

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

Terdapat struct task_struct *tugas dalam struktur thread_info dan tugasan menunjuk ke utas atau proses objek_struktur tugas, task_struct juga dipanggil deskriptor tugas:

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视频教程

Atas ialah kandungan terperinci Adakah Linux mempunyai benang peringkat kernel?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:Apakah pelayan vnc dalam linuxArtikel seterusnya:Apakah pelayan vnc dalam linux