知识大全 Linux线程实现机制分析

Posted

篇首语:少年恃险若平地,独倚长剑凌清秋。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Linux线程实现机制分析相关的知识,希望对你有一定的参考价值。

Linux线程实现机制分析  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  作者 杨沙洲      内容     ·基础知识 线程和进程     ·Linux 内核中的轻量进程实现     ·LinuxThread的线程机制     ·其他的线程实现机制     ·参考资料     ·关于作者     自从多线程编程的概念出现在 Linux 中以来 Linux 多线应用的发展总是与两个问题脱不开干系 兼容性 效率 本文从线程模型入手 通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足 描述了 Linux 社区是如何看待和解决兼容性和效率这两个问题的     一 基础知识 线程和进程     按照教科书上的定义 进程是资源管理的最小单位 线程是程序执行的最小单位 在操作系统设计上 从进程演化出线程 最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开销     无论按照怎样的分法 一个进程至少需要一个线程作为它的指令执行体 进程管理著资源(比如cpu 内存 文件等等) 而将线程分配到某个cpu上执行 一个进程当然可以拥有多个线程 此时 如果进程运行在SMP机器上 它就可以同时使用多个cpu来执行各个线程 达到最大程度的并行 以提高效率 同时 即使是在单cpu的机器上 采用多线程模型来设计程序 正如当年采用多进程模型代替单进程模型一样 使设计更简洁 功能更完备 程序的执行效率也更高 例如采用多个线程响应多个输入 而此时多线程模型所实现的功能实际上也可以用多进程模型来实现 而与后者相比 线程的上下文切换开销就比进程要小多了 从语义上来说 同时响应多个输入这样的功能 实际上就是共享了除cpu以外的所有资源的     针对线程模型的两大意义 分别开发出了核心级线程和用户级线程两种线程模型 分类的标准主要是线程的调度者在核内还是在核外 前者更利于并发使用多处理器的资源 而后者则更多考虑的是上下文切换开销 在目前的商用系统中 通常都将两者结合起来使用 既提供核心线程以满足smp系统的需要 也支持用线程库的方式在用户态实现另一套线程机制 此时一个核心线程同时成为多个用户态线程的调度者 正如很多技术一样 混合 通常都能带来更高的效率 但同时也带来更大的实现难度 出于 简单 的设计思路 Linux从一开始就没有实现混合模型的计划 但它在实现上采用了另一种思路的 混合     在线程机制的具体实现上 可以在操作系统内核上实现线程 也可以在核外实现 后者显然要求核内至少实现了进程 而前者则一般要求在核内同时也支持进程 核心级线程模型显然要求前者的支持 而用户级线程模型则不一定基于后者实现 这种差异 正如前所述 是两种分类方式的标准不同带来的     当核内既支持进程也支持线程时 就可以实现线程 进程的 多对多 模型 即一个进程的某个线程由核内调度 而同时它也可以作为用户级线程池的调度者 选择合适的用户级线程在其空间中运行 这就是前面提到的 混合 线程模型 既可满足多处理机系统的需要 也可以最大限度的减小调度开销 绝大多数商业操作系统(如Digital Unix Solaris Irix)都采用的这种能够完全实现POSIX c标准的线程模型 在核外实现的线程又可以分为 一对一 多对一 两种模型 前者用一个核心进程(也许是轻量进程)对应一个线程 将线程调度等同于进程调度 交给核心完成 而后者则完全在核外实现多线程 调度也在用户态完成 后者就是前面提到的单纯的用户级线程模型的实现方式 显然 这种核外的线程调度器实际上只需要完成线程运行栈的切换 调度开销非常小 但同时因为核心信号(无论是同步的还是异步的)都是以进程为单位的 因而无法定位到线程 所以这种实现方式不能用于多处理器系统 而这个需求正变得越来越大 因此 在现实中 纯用户级线程的实现 除算法研究目的以外 几乎已经消失了     Linux内核只提供了轻量进程的支持 限制了更高效的线程模型的实现 但Linux着重优化了进程的调度开销 一定程度上也弥补了这一缺陷 目前最流行的线程机制LinuxThreads所采用的就是线程 进程 一对一 模型 调度交给核心 而在用户级实现一个包括信号处理在内的线程管理机制 Linux LinuxThreads的运行机制正是本文的描述重点     二 Linux 内核中的轻量进程实现     最初的进程定义都包含程序 资源及其执行三部分 其中程序通常指代码 资源在操作系统层面上通常包括内存资源 IO资源 信号处理等部分 而程序的执行通常理解为执行上下文 包括对cpu的占用 后来发展为线程 在线程概念出现以前 为了减小进程切换的开销 操作系统设计者逐渐修正进程的概念 逐渐允许将进程所占有的资源从其主体剥离出来 允许某些进程共享一部分资源 例如文件 信号 数据内存 甚至代码 这就发展出轻量进程的概念 Linux内核在 x版本就已经实现了轻量进程 应用程序可以通过一个统一的clone()系统调用接口 用不同的参数指定创建轻量进程还是普通进程 在内核中 clone()调用经过参数传递和解释后会调用do_fork() 这个核内函数同时也是fork() vfork()系统调用的最终实现        int do_fork(unsigned long clone_flags unsigned long stack_start   struct pt_regs *regs unsigned long stack_size)     其中的clone_flags取自以下宏的 或 值        #define CSIGNAL x ff  /* signal mask to be sent at exit */  #define CLONE_VM x   /* set if VM shared beeen processes */  #define CLONE_FS     x   /* set if fs info shared beeen processes */  #define CLONE_FILES   x   /* set if open files shared beeen processes */  #define CLONE_SIGHAND x   /* set if signal handlers and blocked signals shared */  #define CLONE_PID x   /* set if pid shared */  #define CLONE_PTRACE x   /* set if we want to let tracing continue on the child too */  #define CLONE_VFORK x   /* set if the parent wants the child to wake it up on mm_release */  #define CLONE_PARENT x   /* set if we want to have the same parent as the cloner */  #define CLONE_THREAD x   /* Same thread group? */  #define CLONE_NEWNS x   /* New namespace group? */  #define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)     在do_fork()中 不同的clone_flags将导致不同的行为 对于LinuxThreads 它使用(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)参数来调用clone()创建 线程 表示共享内存 共享文件系统访问计数 共享文件描述符表 以及共享信号处理方式 本节就针对这几个参数 看看Linux内核是如何实现这些资源的共享的      CLONE_VM     do_fork()需要调用copy_mm()来设置task_struct中的mm和active_mm项 这两个mm_struct数据与进程所关联的内存空间相对应 如果do_fork()时指定了CLONE_VM开关 copy_mm()将把新的task_struct中的mm和active_mm设置成与current的相同 同时提高该mm_struct的使用者数目(mm_struct::mm_users) 也就是说 轻量级进程与父进程共享内存地址空间 由下图示意可以看出mm_struct在进程中的地位        CLONE_FS     task_struct中利用fs(struct fs_struct *)记录了进程所在文件系统的根目录和当前目录信息 do_fork()时调用copy_fs()复制了这个结构 而对于轻量级进程则仅增加fs >count计数 与父进程共享相同的fs_struct 也就是说 轻量级进程没有独立的文件系统相关的信息 进程中任何一个线程改变当前目录 根目录等信息都将直接影响到其他线程      CLONE_FILES     一个进程可能打开了一些文件 在进程结构task_struct中利用files(struct files_struct *)来保存进程打开的文件结构(struct file)信息 do_fork()中调用了copy_files()来处理这个进程属性 轻量级进程与父进程是共享该结构的 copy_files()时仅增加files >count计数 这一共享使得任何线程都能访问进程所维护的打开文件 对它们的操作会直接反映到进程中的其他线程      CLONE_SIGHAND     每一个Linux进程都可以自行定义对信号的处理方式 在task_struct中的sig(struct signal_struct)中使用一个struct k_sigaction结构的数组来保存这个配置信息 do_fork()中的copy_sighand()负责复制该信息 轻量级进程不进行复制 而仅仅增加signal_struct::count计数 与父进程共享该结构 也就是说 子进程与父进程的信号处理方式完全相同 而且可以相互更改     do_fork()中所做的工作很多 在此不详细描述 对于SMP系统 所有的进程fork出来后 都被分配到与父进程相同的cpu上 一直到该进程被调度时才会进行cpu选择     尽管Linux支持轻量级进程 但并不能说它就支持核心级线程 因为Linux的 线程 和 进程 实际上处于一个调度层次 共享一个进程标识符空间 这种限制使得不可能在Linux上实现完全意义上的POSIX线程机制 因此众多的Linux线程库实现尝 cha138/Article/program/Oracle/201311/17455

相关参考

知识大全 Java多线程的相关机制

Java多线程的相关机制  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一线程的基本概念  线程

知识大全 Java多线程处理机制

Java多线程处理机制  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一线程的同步  Java应

知识大全 Java线程通信的机制

Java线程通信的机制  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  &nb

知识大全 解析Java的多线程机制

解析Java的多线程机制  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一进程与应用程序的区别 

知识大全 JAVA多线程中的锁机制

JAVA多线程中的锁机制  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  JAVA中的每个对象都可

知识大全 Java多线程的同步示例及对象锁机制

Java多线程的同步示例及对象锁机制  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!    java

知识大全 超线程多核心下Java多线程编程分析

超线程多核心下Java多线程编程分析  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一Java环

知识大全 超线程多核心下Java多线程编程技术分析[4]

超线程多核心下Java多线程编程技术分析[4]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nb

知识大全 超线程多核心下Java多线程编程技术分析[3]

超线程多核心下Java多线程编程技术分析[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nb

知识大全 超线程多核心下Java多线程编程技术分析[1]

超线程多核心下Java多线程编程技术分析[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nb