主机参考:VPS测评参考推荐/专注分享VPS服务器优惠信息!若您是商家可以在本站进行投稿,查看详情!此外我们还提供软文收录、PayPal代付、广告赞助等服务,查看详情! |
我们发布的部分优惠活动文章可能存在时效性,购买时建议在本站搜索商家名称可查看相关文章充分了解该商家!若非中文页面可使用Edge浏览器同步翻译!PayPal代付/收录合作 |
本教程运行环境:linux7.3系统,Dell G3电脑。
线程通常被定义为一个进程中代码的不同执行路径。有两种类型的线程:“用户级线程”和“内核级线程”。
用户线程是指在没有内核支持的用户程序中实现的线程,它不依赖于操作系统的内核。应用程序进程使用线程库提供的创建、同步、调度和管理线程的功能来控制用户线程。这种线程即使在DOS这样的操作系统中也可以实现,但是线程的调度需要由一个用户程序来完成,有点类似于Windows 3.x的协同多任务处理
另一种需要内核的参与,内核完成线程调度。它依赖于操作系统的内核,由内核的内部需求来创建和撤销。这两种模式各有利弊。
用户不需要额外的内核开销,用户态线程的实现可以定制或修改,以满足特殊应用的要求。但是,当一个线程由于I/O而处于等待状态时,整个进程就会被调度器切换到等待状态,其他线程就没有机会运行了。而内核线程没有限制,有利于多处理器的并发优势,但占用系统开销较多。
Windows和OS/2支持内核线程。支持Linux内核级多线程。
linux中的内核级行
1.内核线程概述
Linux内核可以看作是一个服务进程(管理软硬件资源和响应用户进程的各种进程)。
内核需要多个并行执行流,支持多线程以防止可能的阻塞。
内核是内核的二重身,可以用来处理特定的事情。内核负责内核线程的调度,当一个内核线程处于阻塞状态时,不会影响其他内核线程。
内核是由内核本身直接启动的进程。内核实际上将内核功能委托给一个独立的进程,这个进程与内核中的其他“进程”并行执行。线程通常被称为内核守护进程。在当前内核中,内核线程负责以下工作:
定期将修改后的内存页面与页面源块设备同步,实现文件系统的事务日志。内核线程是由内核创建的,所以内核线程在内核状态下执行,只能访问内核虚拟地址空间,不能访问用户空间。
在linux中,所有的线程都是作为进程实现的,线程的调度算法和数据结构没有单独的定义。一个进程相当于包含一个线程,也就是它本身,多线程。原线程称为主线程,它们共同组成一个线程组。
进程有自己的地址空间,所以每个进程都有自己的页表,而线程没有,所以只能和其他线程共享主线程的地址空间和页表。
2.三种数据结构
每个进程或线程由三个重要的数据结构组成,即struct thread _ info、struct task _ struct和kernel stack。
thread_info对象存储了进程/线程的基本信息,它和进程/线程的内核栈存储在内核空间的2页空间中。thread_info结构存储在地址段的末尾,其余空间用作内核堆栈。使用内核伙伴系统来分配这个空间。
struct thread _ info { int preempt _ count;/* 0 = >可抢占的,& lt0 = & gtbug */struct任务_ struct *任务;/*主任务结构*/_ _ u 32 CPU;/* CPU */};thread_info结构中有一个struct task_struct *task。Task指向线程或进程的task_struct对象。Task _ struct也称为任务描述符:
struct task _ struct { pid _ t pidpid _ t tgidvoid * stackstruct mm_struct *mm、* active _ mm/*文件系统信息*/struct fs _ struct * fs;/*打开文件信息*/struct files _ struct *文件;};# define task _ thread _ info(task)((struct thread _ info *)(task)-& gt;Stack)stack: thread_infomm指向一个进程或线程:对象用于管理进程/线程的页表和虚拟内存区。active_mm:主要用于内核线程访问内核主页面的全局目录。pid:每个task_struct都有一个不同的id,即pidtgid:线程组的前导线程的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 _ un traced,(unsigned long)fn,(unsigned long)arg,NULL,NULL);} PID = kernel _ thread(kthread,NULL,CLONE _ FS | CLONE _ FILES);Kthreadadd本身最终是通过do_fork实现的,可以用来创建用户态进程/线程、内核线程等。通过传入不同的参数。创建kthreadadd时,内核线程的创建就交给它来实现了。
内核的创建分为两个部分:创建和启动。kthread_run作为一个统一的接口,可以同时实现。这两个功能:
#define kthread_run(threadfn,data,namefmt,...)\({ \ struct task _ struct * _ _ k \ = kthread _ create(thread fn,data,namefmt,# _ _ VA _ ARGS _ _);\如果(!IS _ ERR(_ _ k))\ wake _ up _ process(_ _ k);\ _ _ k;\ })# define kthread _ create(thread fn,data,namefmt,arg...)\ kthread _ create _ on _ node(thread fn,data,-1,namefmt,# arg)struct task _ struct * kthread _ create _ on _ node(int(* thread fn)(void *data),void * data,int node,const char namefmt[],...){ DECLARE _ COMPLETION _ on stack(done);结构任务_结构*任务;/* allocate kthread _ create _ info space */struct kthread _ create _ info * create = kmalloc(sizeof(* create),GFP _ kernel);如果(!create)返回ERR _ PTR(-eno mem);创建-& gt;threadfn = threadfn创建-& gt;数据=数据;创建-& gt;node =节点;创建-& gt;done = & amp搞定;/*加入kthread_creta_list列表,等待ktherad_add中断线程创建新线程*/spin _ lock(& kthread _ create _ lock);list _ add _ tail(& amp;创建-& gt;列表& ampkthread _ create _ list);自旋解锁(& ampkthread _ create _ lock);唤醒进程(kthreadd任务);/**在killable状态下等待完成,因为在kthread尝试为*新内核线程分配内存时,我可能会被OOM killer选中。*/if(不太可能(wait _ for _ completion _ killable(& amp;done))) {/**如果我在kthread(或者新的内核线程)*调用complete()之前被SIGKILLed,那么把这个结构的清理工作留给*那个线程。*/if(xchg(& amp;创建-& gt;done,NULL))返回ERR _ PTR(-EINTR);/* * kthread(或新内核线程)将很快调用complete()*。*/等待完成(& amp搞定);}任务=创建-& gt;结果;...kfree(创建);返回任务;}kthread_create_on_node函数:
首先用kmalloc分配kthread_create_info变量create,用函数参数初始化create将create添加到kthread_create_list链表中。然后唤醒kthread的内核线程来创建当前线程,然后用完成来等待内核线程被创建。完成后,创建空间被释放。我们来看看kthread的流程:
int kthread(void * unused){ struct task _ struct * tsk = current;/*为我们的孩子建立一个干净的继承环境。*/set _ task _ comm(tsk & quot;kthreadd & quot);ignore _ signals(tsk);set _ CPU _ allowed _ ptr(tsk,CPU _ all _ mask);set _ MEMS _ allowed(node _ States[N _ MEMORY]);当前->;flags | = PF _ NOFREEZEfor(;;){ set _ current _ state(TASK _ INTERRUPTIBLE);if(list _ empty(& amp;kthread _ create _ list))schedule();__set_current_state(任务_运行);自旋锁。kthread _ create _ lock);而(!list _ empty(amp;kthread _ create _ list)){ struct kthread _ create _ info * create;create = list _ entry(kthread _ create _ list . next,struct kthread_create_info,list);list_del_init。创建-& gt;列表);自旋解锁(& ampkthread _ create _ lock);create_kthread(创建);自旋锁。kthread _ create _ lock);} spin _ unlock(& amp;kthread _ create _ lock);}返回0;} kthreadd利用for(;;)总是驻留在内存中运行:主要进程如下:
检查当kthread_create_list为空时,kthread放弃cpu的执行权。当kthread_create_list不为空时,使用while循环遍历kthread_create_list链表,并在取下一个链表节点后调用create_kthread。创建内核线程静态void create _ kthread(struct kthread _ create _ info * create){ intpid;/*我们需要自己的信号处理器(默认情况下我们不接受信号)。*/pid = kernel_thread(kthread,create,CLONE _ FS | CLONE _ FILES | SIGCHLD);if(PID & lt;0) {/*如果用户被SIGKILLed,我释放结构。*/结构完成*完成= xchg(& amp;创建-& gt;done,NULL);如果(!done){ kfree(create);返回;}创建-& gt;result = ERR _ PTR(PID);完成(完成);}}可以看出,内核线程的创建最终是像kthread一样通过调用kernel_thread来实现的。
static int kthread(void * _ create){..../*如果用户被SIGKILLed,我释放结构。*/done = xchg(& amp;创建-& gt;done,NULL);如果(!done){ kfree(create);do _ exit(-EINTR);}/*好的,告诉用户we & # 39重新生成,等待停止或唤醒*/_ _ set _ current _ state(TASK _ un interruptible);创建-& gt;结果=当前;完成(完成);调度();ret =-EINTR;如果(!test _ bit(KTHREAD _ SHOULD _ STOP & amp;self . flags)){ _ _ kthread _ parkme(& amp;自我);ret = threadfn(数据);}/*我们可以& # 39;不要只是返回,我们必须保存& quot自我& quoton stack */do _ exit(ret);}kthread以struct kthread_create_info类型的create为参数,create有一个创建内核线程的回调函数,函数的参数。在kthread中,完成完成信号量的处理,然后schedule放弃cpu的执行权,等待下一次返回执行回调函数threadfn(data)。
4.内核线程的退出线程一旦启动,就会一直运行,除非线程主动调用do_exit函数,或者其他进程调用kthread_stop函数结束线程的运行。
int kthread _ stop(struct task _ struct * k){ struct kthread * kthread;int rettrace _ sched _ kthread _ stop(k);get _ task _ struct(k);kthread = to _ live _ kthread(k);if(KTHREAD){ set _ bit(KTHREAD _ SHOULD _ STOP,& ampkthread-& gt;旗帜);__kthread_unpark(k,kthread);唤醒进程(k);等待完成。kthread-& gt;已退出);} ret = k-& gt;退出_代码;put _ task _ struct(k);trace _ sched _ kthread _ stop _ ret(ret);返回ret}如果线程函数正在处理一个非常重要的任务,它不会被中断。当然,如果线程函数始终不返回,不检查信号,就永远不会停止。执行kthread_stop时,目标线程一定不能退出,否则会Oops。所以在创建thread_func时,它可以采用以下形式:
thread_func(){ //在此工作//等待退出,同时(!thread _ could _ stop()){ wait();} } exit _ code(){ kthread _ stop(_ task);//给任务发信号通知它可以退出}如果线程在等待满足某个条件后才能继续运行,那么只能在条件满足后调用kthread_stop杀死内核线程。
5.内核线程使用# include " test _ kthread.h & quot# include & ltLinux/delay . h & gt;# include & ltLinux/timer . h & gt;# include & ltLinux/平台_设备. & gt# include & ltLinux/fs . h & gt;# include & ltLinux/module . h & gt;静态结构task _ struct * test _ thread = NULL无符号int time _ conut = 5;int test _ thread _ fun(void * data){ int times = 0;而(!kthread _ should _ stop()){ printk(& quot;\ n printk % u \ r \ n & quot,次);times++;ms LEEP _ interruptible(time _ conut * 1000);} printk(& quot;\n测试线程乐趣退出成功\ r \ n \ n & quot);返回0;} void register _ test _ thread(void){ test _ thread = kthread _ run(test _ thread _ fun,NULL,& quottest _ kthread & quot);if(IS _ ERR(test _ thread)){ printk(KERN _ INFO & quot;创建test_thread失败!\ n & quot);} else { printk(KERN _ INFO & quot;创建测试线程ok!\ n & quot);} } static ssize _ t kthread _ debug _ start(struct device * dev,struct device_attribute *attr,char * buf){ register _ test _ thread();返回0;} static ssize _ t kthread _ debug _ stop(结构设备*dev,结构设备_属性*attr,char *buf){kthread_stop(测试线程);返回0;}静态设备_ATTR(kthread_start,S_IRUSR,kthread_debug_start,NULL);静态设备_ATTR(kthread_stop,S_IRUSR,kthread_debug_stop,NULL);struct attribute * kthread _ group _ info _ attrs[]= { & amp;dev _ attr _ kthread _ start . attr & amp;dev_attr_kthread_stop.attr,NULL,};struct attribute _ group kthread _ group = {。name = & quotkthread & quot,.attrs = kthread_group_info_attrs,};推荐:Linux视频教程
这就是linux拥有内核级线程的细节。请多关注主机参考其他相关文章!
这几篇文章你可能也喜欢:
- 在Linux上操作vi编辑器(Linux上的vi编辑器命令)
- Mondoze:住宅IP/原生IP/IDC IP,VPS低至$8.33/马来西亚服务器/AS152742/11.11促销
- torchbyte 罗马尼亚 VPS 起价为 20 美元/年,AMD Ryzen9+ NVMe 硬盘,免费 DDoS 防护
- zlidc(智联IDC):韩国原生IP云服务器,35.9美元/季度,4核/4G内存/50G SSD/300M优质网络@2.5T月流量
- 椰草云双11活动:香港云服务器81元/年,香港实体服务器199元/月(香港云服务商)
本文由主机参考刊发,转载请注明:linux有内核级线程吗(linux内核线程优先级) https://zhujicankao.com/87540.html
评论前必须登录!
注册