http://blog.csdn.net/dyllove98/article/details/8917197

Linux对于内存的管理涉及到非常多的方面,这篇文章首先从对进程虚拟地址空间的管理说起。(所依据的代码是2.6.32.60)

无论是内核线程还是用户进程,对于内核来说,无非都是 task_struct这个数据结构的一个实例而已,task_struct被称为进程描述符(process descriptor),因为它记录了这个进程所有的context。其中有一个被称为'内存描述符‘(memory descriptor)的数据结构 mm_struct,抽象并描述了Linux视角下管理进程地址空间的所有信息。
mm_struct定义在include/linux/mm_types.h中,其中的域抽象了进程的地址空间,如下图所示:
 
 
 
每个进程都有自己独立的mm_struct,使得每个进程都有一个抽象的平坦的独立的32或64位地址空间,各个进程都在各自的地址空间中相同的地址内存存放不同的数据而且互不干扰。如果进程之间共享相同的地址空间,则被称为线程
其中[start_code,end_code)表示代码段的地址空间范围。
[start_data,end_start)表示数据段的地址空间范围。
[start_brk,brk)分别表示heap段的起始空间和当前的heap指针。
[start_stack,end_stack)表示stack段的地址空间范围。
mmap_base表示memory mapping段的起始地址。 那为什么mmap段没有结束的地址呢?
bbs段是用来干什么的呢?bbs表示的所有没有初始化的全局变量,这样只需要将它们匿名映射为‘零页’,而不用在程序load过程中从磁盘文件显示的mapping,这样既减少了elf二进制文件的大小,也提高了程序加载的效率。 在mm_struct中为什么没有bbs段的地址空间表示呢?
 
除此之外,mm_struct还定义了几个重要的域:
  1. 215 atomic_t mm_users; /* How many users with user space? */
  2. 216 atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */

这两个counter乍看好像差不多,那Linux使用中有什么区别呢?看代码就是最好的解释了。

  1. 681static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
  2. 682{
  3. 683 struct mm_struct * mm, *oldmm;
  4. 684 int retval;
  5.  
  6. 692 tsk->mm = NULL;
  7. 693 tsk->active_mm = NULL;
  8. 694
  9. 695 /*
  10. 696 * Are we cloning a kernel thread?
  11. 697 *
  12. 698 * We need to steal a active VM for that..
  13. 699 */
  14. 700 oldmm = current->mm;
  15. 701 if (!oldmm)
  16. 702 return 0;
  17. 703
  18. 704 if (clone_flags & CLONE_VM) {
  19. 705 atomic_inc(&oldmm->mm_users);
  20. 706 mm = oldmm;
  21. 707 goto good_mm;
  22. 708 }

无论我们在调用fork,vfork,clone的时候最终会调用do_fork函数,区别在于vfork和clone会给copy_mm传入一个CLONE_VM的flag,这个标识表示父子进程都运行在同样一个‘虚拟地址空间’上面(在Linux称之为lightweight process或者线程),当然也就共享同样的物理地址空间(Page Frames)。

copy_mm函数中,如果创建线程中有CLONE_VM标识,则表示父子进程共享地址空间和同一个内存描述符,并且只需要将mm_users值+1,也就是说mm_users表示正在引用该地址空间的thread数目,是一个thread level的counter。

mm_count呢?mm_count的理解有点复杂。

对Linux来说,用户进程和内核线程(kernel thread)都是task_struct的实例,唯一的区别是kernel thread是没有进程地址空间的,内核线程也没有mm描述符的,所以内核线程的tsk->mm域是空(NULL)。内核scheduler在进程context switching的时候,会根据tsk->mm判断即将调度的进程是用户进程还是内核线程。但是虽然thread thread不用访问用户进程地址空间,但是仍然需要page table来访问kernel自己的空间。但是幸运的是,对于任何用户进程来说,他们的内核空间都是100%相同的,所以内核可以’borrow'上一个被调用的用户进程的mm中的页表来访问内核地址,这个mm就记录在active_mm。

简而言之就是,对于kernel thread,tsk->mm == NULL表示自己内核线程的身份,而tsk->active_mm是借用上一个用户进程的mm,用mm的page table来访问内核空间。对于用户进程,tsk->mm == tsk->active_mm。

为了支持这个特别,mm_struct里面引入了另外一个counter,mm_count。刚才说过mm_users表示这个进程地址空间被多少线程共享或者引用,而mm_count则表示这个地址空间被内核线程引用的次数+1。

比如一个进程A有3个线程,那么这个A的mm_struct的mm_users值为3,但是mm_count为1,所以mm_count是process level的counter。维护2个counter有何用处呢?考虑这样的scenario,内核调度完A以后,切换到内核内核线程B,B ’borrow' A的mm描述符以访问内核空间,这时mm_count变成了2,同时另外一个cpu core调度了A并且进程A exit,这个时候mm_users变为了0,mm_count变为了1,但是内核不会因为mm_users==0而销毁这个mm_struct,内核只会当mm_count==0的时候才会释放mm_struct,因为这个时候既没有用户进程使用这个地址空间,也没有内核线程引用这个地址空间。

We'll try to explain the difference between the use of mm_users and mm_count with an example. Consider a memory descriptor shared by two lightweight processes. Normally, its mm_users field stores the value 2, while its mm_count field stores the value 1 (both owner processes count as one).

If the memory descriptor is temporarily lent to a kernel thread (see the next section), the kernel increases the mm_count field. In this way, even if both lightweight processes die and the mm_users field becomes zero, the memory descriptor is not released until the kernel thread finishes using it because the mm_count field remains greater than zero.

linux内核线程,进程,线程的更多相关文章

  1. 24小时学通Linux内核之进程

    都说这个主题不错,连我自己都觉得有点过大了,不过我想我还是得坚持下去,努力在有限的时间里学习到Linux内核的奥秘,也希望大家多指点,让我更有进步.今天讲的全是进程,这点在大二的时候就困惑了我,结果那 ...

  2. 深入Linux内核架构——进程管理和调度(上)

    如果系统只有一个处理器,那么给定时刻只有一个程序可以运行.在多处理器系统中,真正并行运行的进程数目取决于物理CPU的数目.内核和处理器建立了多任务的错觉,是通过以很短的间隔在系统运行的应用程序之间不停 ...

  3. (转)Linux内核之进程和系统调用

    Linux内核之进程和系统调用 什么是系统调用 在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的.功能十分强大的一系列的函数.这些系统调用是在内核中实现的,再通过一定 ...

  4. Linux内核之进程地址空间

    Linux内核之进程地址空间 内核中的函数以相当直接了当的方式获得动态内存: __get_free_pages 或 alloc_pages从分区页框分配器中获得页框; kmem_cache_alloc ...

  5. linux内核中创建线程方法

    1.头文件 #include <linux/sched.h> //wake_up_process() #include <linux/kthread.h> //kthread_ ...

  6. linux内核中创建线程方法【转】

    本文转载自:https://www.cnblogs.com/Ph-one/p/6077787.html 1.头文件 #include <linux/sched.h> //wake_up_p ...

  7. Linux内核分析--进程创建,执行,切换

    学号:351 原创作品转载请注明出处本实验来源 https://github.com/mengning/linuxkernel/ 实验要求 从整理上理解进程创建.可执行文件的加载和进程执行进程切换,重 ...

  8. Linux内核分析——进程的切换和系统的一般执行过程

    进程的切换和系统的一般执行过程 一.进程切换的关键代码switch_to分析 (一)进程调度与进程调度的时机分析 1.不同类型的进程有不同的调度需求 第一种分类: (1)I/O-bound:频繁进行I ...

  9. 深入理解Linux内核-进程

    1.进程的静态特性 进程:程序执行时的一个实例 进程描述符(task_struct): 进程的基本信息(thread_info).指向内存区描述符的指针(mm_struct).进程相关的tty(tty ...

  10. 20135239 益西拉姆 linux内核分析 进程的切换和系统的一般执行过程

    week 8 进程的切换和系统的一般执行过程 [ 20135239 原文请转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...

随机推荐

  1. Linux c 屏蔽信号、切换信号

    信号导致的问题 不是任何信号我们都需要的,如果遇到我们不想处理的信号,我们怎么避免这个信号? 1.      信号屏蔽 intsigprocmask(int how,//操作方式 SIG_BLOCK屏 ...

  2. linux用户管理中两个重要的“父子”配置文件

    在Linux中主要通过用户配置文件来查看和修改用户信息,因此下面我们将介绍两个重要的用户配置文件,让你能够更好的hold住你的用户. 一:父文件/etc/passwd 1.查看配置文件/etc/pas ...

  3. (转)深入浅出K-Means算法

    原文地址:http://www.csdn.net/article/2012-07-03/2807073-k-means 摘要:在数据挖掘中,K-Means算法是一种 cluster analysis ...

  4. [Node.js]31. Level 7: Redis coming for Node.js, Simple Redis Commands

    Let's start practicing using the redis key-value store from our node application. First require the  ...

  5. (LeetCode 21)Merge Two Sorted Lists

    Merge two sorted linked lists and return it as a new list. The new list should be made by splicing t ...

  6. MFC 小知识总结二

    1 UpdateData 不能及时更新控件内容 原因: UpdateData是通过数据交换实现改变控件显示的.改变了之后必需要调用一次OnPaint才干实现刷新. 因此,若是在一个函数里重复多次调用有 ...

  7. IO介绍

    IO在计算机中指Input/Output,也就是输入和输出.由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘.网络等,就需要IO接口. 比如你打开 ...

  8. Ubuntu apt-get方式安装Subversion

    按照官方文档 http://subversion.apache.org/packages.html 使用apt-get安装方式: 1.安装Subversion: sudo apt-get instal ...

  9. 原创Oracle数据泵导出/导入(expdp/impdp)

    //创建目录 create Or Replace directory dpdata1 as 'd:\test\dump'; //赋予读写权限 grant read,write on directory ...

  10. 〖Linux〗Ubuntu13.10中使用虚拟机对MTK手机进行线刷

    最近一个同学把一台MTK手机刷坏了,在我的笔记本电脑上没有WindowsXp操作系统: 而在MTK线刷过程中,最好的刷机系统便是WindowsXP3,于是有了想在Linux中直接开启XP虚拟机来刷机的 ...