momo zone

调核人的blog

内核代码中关于线程的一些简单认识

这里所谓的线程是指的内核线程,并不是用户线程。后者由上层的线程库调用系统调用clone创建的。

内核线程一般并不被用户程序直接使用,而是在内核空间中并发执行某些代码而由内核自己设置的一个“进程”。(比如驱动程序中同步设备与缓存) 在linux中线程和进程都用thread_info数据结构(具体和体系结构相关)进行描述,都参与调度,其实对内核而言根本就不区分所谓进程和线程,它们都是可调度的执行流,比如在设备驱动中可以通过与nice相关的函数设置新创建的内核线程的优先级。 在用户态执行fork时如果指定了CLONE_VM标志,那么父子进程共享父进程的上下文,即mm_struct,此时就把它们都称作线程。

内核提供一个方便创建内核线程的函数叫kernel_thread ,原型: int kernel_thread(int (*fn)(void *),void *arg ,unsigned long flag) 他会先进行一些其他数据或结构的维护,然后最终还要调用do_fork:

int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.si = (unsigned long) fn;
regs.di = (unsigned long) arg;
#ifdef CONFIG_X86_32
regs.ds = __USER_DS;
regs.es = __USER_DS;
regs.fs = __KERNEL_PERCPU;
regs.gs = __KERNEL_STACK_CANARY;
#else
regs.ss = __KERNEL_DS;
#endif
regs.orig_ax = -1;
regs.ip = (unsigned long) kernel_thread_helper;
regs.cs = __KERNEL_CS | get_kernel_rpl();
regs.flags = X86_EFLAGS_IF | 0x2;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int  xds;
int  xes;
int  xfs;
int  xgs;
long orig_eax;
long eip;
int  xcs;
long eflags;
long esp;
int  xss;
};

如果没有这个标志而只有CLONE_FS或者CLONE_FILES标志,那么创建的子进程只共享父进程的fs_struct和files_struct结构,此时还把它们叫做进程。所以在linux上线程本质是一个共享资源的手段,对内核而言根本不区分它们。 mm_struct用于描述用户态进程上下文,所以内核线程都没有这个数据结构(其实并不是没有,而是将它指向另一个用户进程的mm_struct)。当调度内核线程时,它将借用上一个用户进程的mm_struct(内核线程的active_mm指向上一个用户进程的mm_struct),而所有用户进程页表的高端都是内核页表,描述内核空间,由此内核线程就可以在内核空间内运行了。 总结一下 :

内核线程

内核线程只运行在内核态,不受用户态上下文的拖累。

Ø         处理器竞争:可以在全系统范围内竞争处理器资源;

Ø         使用资源:唯一使用的资源是内核栈和上下文切换时保持寄存器的空间(看一下struct pt_regs的设置)

Ø         调度:调度的开销可能和进程自身差不多昂贵

Ø         同步效率:资源的同步和数据共享比整个进程的数据同步和共享要低一些。

—————————————————————————————————-

顺便再说一下用户线程,这类线程首先由应用程序调用线程库为出发点。线程库调用系统调用clone。也就是说一个用户线程会对应内核的一个进程(当然资源是共享的),创建流程和内核进程类似。 LinuxThreads是用户空间的线程库,所采用的是线程-进程1对1模型(即一个用户线程(注意这里的用户线程也不是教科书上的用户线程)对应一个轻量级进程,而一个轻量级进程对应一个特定的内核线程),将线程的调度等同于进程的调度,调度交由内核完成,而线程的创建、同步、销毁由核外线程库完成(LinuxThreads已绑定到GLIBC中发行)。在LinuxThreads中,由专门的一个管理线程处理所有的线程管理工作。 最后,不要将教科书上的线程模型和linux中线程的实现搞混,这两者名字差不多,但概念相差较远(原因是理论的那套太复杂,linux没有实现)。貌似windows实现的进程比较接近教科书… 没研究了

再总结一下:

轻量级进程

轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联。内核线程只能由内核管理并像普通进程一样被调度。

轻量级进程由clone()系统调用创建,参数是CLONE_VM,即与父进程是共享进程地址空间和系统资源。

与普通进程区别:LWP只有一个最小的执行上下文和调度程序所需的统计信息。

Ø         处理器竞争:因与特定内核线程关联,因此可以在全系统范围内竞争处理器资源

Ø         使用资源:与父进程共享进程地址空间

Ø         调度:像普通进程一样调度

关于linux线程实现一个更具体的介绍可以看看这个 http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/

如果搞不清哪些所谓的XX线程就看看这个吧,应该是权威的说明:http://linas.org/linux/threads-faq.html

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: