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.
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:
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.
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)
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视频教程》
Atas ialah kandungan terperinci Adakah Linux mempunyai benang peringkat kernel?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!