linux内核线程,进程,线程

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还定义了几个重要的域:

 215        atomic_t mm_users;                      /* How many users with user space? */
 216        atomic_t mm_count;                      /* How many references to "struct mm_struct" (users count as 1) */

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

 681static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
 682{
 683        struct mm_struct * mm, *oldmm;
 684        int retval;

 692        tsk->mm = NULL;
 693        tsk->active_mm = NULL;
 694
 695        /*
 696         * Are we cloning a kernel thread?
 697         *
 698         * We need to steal a active VM for that..
 699         */
 700        oldmm = current->mm;
 701        if (!oldmm)
 702                return 0;
 703
 704        if (clone_flags & CLONE_VM) {
 705                atomic_inc(&oldmm->mm_users);
 706                mm = oldmm;
 707                goto good_mm;
 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.

时间: 2024-10-26 15:43:26

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

Linux内核分析——进程的描述和进程的创建

Linux内核分析——进程的描述和进程的创建 20135111李光豫 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验内容 阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235: 分析fork函数对应的内核处理过程sys_clone,理解创建一个新进

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

如果系统只有一个处理器,那么给定时刻只有一个程序可以运行.在多处理器系统中,真正并行运行的进程数目取决于物理CPU的数目.内核和处理器建立了多任务的错觉,是通过以很短的间隔在系统运行的应用程序之间不停切换做到的.由此,以下两个问题必须由内核解决:除非明确要求,否则应用程序不能彼此干扰:CPU时间必须在各种应用程序之间尽可能公平共享(一些程序可能比其他程序更重要).本篇博文主要涉及内核共享CPU时间的方法以及如何在进程之间切换(内核为各进程分配时间,保证切换之后从上次撤销其资源时执行环境完全相同)

Linux内核学习-进程

先说几个术语: 一.Linux进程的五个段 下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的.重点:代码段.数据段.堆栈段,这是一个概念堆.栈.全局区.常量区,这是另一个概念1)代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像.代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作--它是不可写的.代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.这部分

linux内核中的线程创建接口

介绍一些内核给我们提供的用于在内核中创建线程的内核接口函数. 其实在linux内核中,无论是进程还是线程,都是用struct task_struct结构体来表示的. 用于创建一个内核线程的函数: struct task_struct * kthread_create( int (*threadfn)(void *data),                                           void *data,                                 

深入理解Linux内核-进程

1.进程的静态特性 进程:程序执行时的一个实例 进程描述符(task_struct): 进程的基本信息(thread_info).指向内存区描述符的指针(mm_struct).进程相关的tty(tty_struct).当前目录(fs_struct).指向 文件描述符的指针(files_struct).所接收的信号(signal_struct) 进程状态:1.可运行状态(TASK_RUNNING):正在运行或者准备执行 2.可中断的等待状态(TASK_INTERRUPTIBLE):进程挂起,产生硬

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

进程的切换和系统的一般执行过程 一.进程切换的关键代码switch_to分析 (一)进程调度与进程调度的时机分析 1.不同类型的进程有不同的调度需求 第一种分类: (1)I/O-bound:频繁进行I/O,花费很多时间等待I/O操作的完成. (2)CPU-bound:计算密集型,需要大量CPU时间进行计算. 第二种分类: (1)批处理进程:不必交互.很快响应. (2)实时进程:要求响应时间短. (3)交互式进程(shell). 2.调度策略:是一组规则,它们决定什么时候以怎样的方式选择一个新进程

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

week 8 进程的切换和系统的一般执行过程 [ 20135239 原文请转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] 一.进程调度与进程调度的时机分析 操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已.对于理解操作系统的工作机制,反而是进程的调度时机与进程的切换机制更为关键. 不同类型的进程有不同的调度需求 第一

Linux内核开发 — 进程控制

本章主要是以代码的角度分析进程的定义.状态.数据结构等概念. 进程的定义 进程是一段运行的程序,他是一个动态的可执行实体.而程序是代码和数据的集合,代码是一个静态的实体,程序是可以供多个进程使用,比如相同的应用程序可以在不同的计算机上运行而产生多个进程. 进程四要素 进程四要素主要是针对代码中对线程.进程的区别而言: l  有一段程序供其执行 l  有进程专用的内核堆栈空间 l  有内核控制块(有一个task_struct 数据结构),拥有内核控制块,才能被内核调度 l  有独立的用户空间  

20135239益西拉姆 Linux内核分析 进程的描述和进程的创建

[益西拉姆 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] 第六周 进程的描述和进程的创建 一. 进程的描述 进程控制块PCB——task_struct 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct数据结构很庞大 Linux进程的状态与操作系统原理中的描述的进程状态似乎有所不同,比如就绪状态和运行状态都是

Linux内核之进程管理

引言: 在Linux的内核的五大组成模块中,进程管理模块时非常重要的一部分,它虽然不像内存管理.虚拟文件系统等模块那样复杂,也不像进程间通信模块那样条理化,但作为五大内核模块之一,进程管理对我们理解内核的运作.对于我们以后的编程非常重要.同时,作为五大组成模块中的核心模块,它与其他四个模块都有联系.下面就对进程模块进行想写的介绍,首先要了解进程及其相关的概念.其次介绍进程的创建.切换.撤销等基本操作.除此之外,还给出了Linux内核是如何对进程进行调度管理的.      一.进程及其相关概念 进