【操作系统】LinuxKernel-进程地址空间 认知框架构建(task_struct,mm_struct,vm_area_struct)

进程地址空间

概念

在 Linux 系统中,采用了虚拟内存管理技术,事实上大多数现在操作系统都是如此!在 Linux 系统中,每一个进程都在自己独立的地址空间中运行

虚拟地址会通过硬件 MMU(内存管理单元)映射到实际的物理地址空间中,建立虚拟地址到物理地址的映射关系后,对虚拟地址的读写操作实际上就是对物理地址的读写操作,MMU 会将物理地址“翻译”为对应的物理地址,其关系如下所示:img

那么,为什么要引入虚拟地址呢?引入虚拟地址有什么优点呢?

点这里看,这里解释的非常好了

struct task_struct (进程的PCB)

太长了,就不放在这里了

几种状态:

/* Used in tsk->state: */
#define TASK_RUNNING            0x0000//进程要么正在执行,要么准备执行,内核中有一个队列,里面都是等待执行的进程。
#define TASK_INTERRUPTIBLE      0x0001 //可中断的睡眠,可以通过一个信号唤醒
#define TASK_UNINTERRUPTIBLE        0x0002//不可中断睡眠,不可以通过信号进行唤醒
#define __TASK_STOPPED          0x0004 //进程停止执行,在进程接收到 SIGTTIN、SIGSTOP、SIGTSTP、STGTTOU信号后会进入此状态
#define __TASK_TRACED           0x0008//进程被追踪,被 debugger 等进程监视。
/* Used in tsk->exit_state: */
#define EXIT_DEAD           0x0010//僵尸状态的进程,表示进程被终止,但是父进程还没有获取它的终止信息,比如进程有没有执行完等信息。 
#define EXIT_ZOMBIE         0x0020//进程的最终状态,进程死亡
#define EXIT_TRACE          (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED         0x0040//停放状态
#define TASK_DEAD           0x0080//死亡,最终状态
#define TASK_WAKEKILL           0x0100//唤醒并杀死的进程
#define TASK_WAKING         0x0200//唤醒进程
#define TASK_NOLOAD         0x0400//空载状态
#define TASK_NEW            0x0800//新的状态
#define TASK_STATE_MAX          0x1000//任务状态的最大值

还有好多为了方便设置state的宏 也就是奇技淫巧 我只是为了建立框架 暂时不看了

进程调度信息img

调度策略

img

标识符

img

IPC进程通信信息

为了使进程能在同一项任务上协调工作,进程之间必须能进行通信即交流数据。 Linux 支持多种不同形式的通信机制。它支持典型的UNIX 通信机制(IPC Mechanisms): 信号(Signals)、管道(Pipes),也支持System V / Posix 通信机制:共享内存(Shared Memory)、 信号量和消息队列(Message Queues)

img

进程链接信息(Links)

因为一个进程能创建几个子进程,而子进程之间有兄 弟关系,在task_struct 结构中有几个域来表示这种关系

ps:除了初始化进程init,其他进程都有一个父进程(Parent Process) 。可以通过fork()或clone()系统调用来创建子进程,除了进程标识符(PID) 等必要的信息外,子进程的task_struct 结构中的绝大部分的信息都是从父进程中拷贝 。系统有必要记录这种“亲属”关系,使进程之间的协作更加方便img

时间和定时器信息

一个进程从创建到终止叫做该进程的生存期(lifetime)。进程在其生存期内使用CPU 的时间,内核都要进行记录,以便进行统计、计费等有关操作。进程耗费CPU 的时间由两部 分组成:一是在用户模式(或称为用户态)下耗费的时间、一是在系统模式(或称为系统态) 下耗费的时间。每个时钟滴答,也就是每个时钟中断,内核都要更新当前进程耗费CPU 的时 间信息img

文件系统信息

fs_struct 中描述了两个VFS 索引节点(VFS inode),这两个索引节点叫做root 和pwd,分别指向进程的可执行映像所对应的根目录(Home Directory)和当前目录或工作目录。 file_struct 结构用来记录了进程打开的文件的描述符(Descriptor)img

虚拟内存信息

除了内核线程,每个进程都有自己的地址空间img

页面管理信息

当物理内存不足时,Linux 内存管理子系统需要把内存中的部分页面交换到外存,其交 换是以页为单位的img

SMP信息img

处理器相关的环境(上下文)信息

和“处理器”相关的环境信息。进程作为一个执行环境的综合, 当系统调度某个进程执行,即为该进程建立完整的环境时,处理器(Processor)的寄存器、 堆栈等是必不可少的。因为不同的处理器对内部寄存器和堆栈的定义不尽相同,所以叫做“和 处理器相关的环境”,也叫做“处理机状态”。当进程暂时停止运行时,处理机状态必须保 存在进程的 thread_struct 结构(多线程的话每个线程都有一份)中,当进程被调度重新运行时再从中恢复这些环境,也就是恢 复这些寄存器和堆栈的值img

struct mm_struct (进程的虚拟地址空间)

一个进程的虚拟地址空间主要由两个数据结来描述。一个是最高层次的:mm_struct,一个是较高层次的:vm_area_structs。最高层次的mm_struct结构描述了一个进程的整个虚拟地址空间。较高层次的结构vm_area_truct描述了虚拟地址空间的一个区间(简称虚拟区)每个进程只有一个mm_struct结构,在每个进程的task_struct结构中,有一个指向该进程的结构。可以说,mm_struct结构是对整个用户空间的描述

[

](https://blog.csdn.net/qq_26768741/article/details/54375524)

struct mm_struct {

    //指向线性区对象的链表头
    struct vm_area_struct * mmap;       /* list of VMAs */
    //指向线性区对象的红黑树
    struct rb_root mm_rb;
    //指向最近找到的虚拟区间
    struct vm_area_struct * mmap_cache; /* last find_vma result */

    //用来在进程地址空间中搜索有效的进程地址空间的函数
    unsigned long (*get_unmapped_area) (struct file *filp,
                unsigned long addr, unsigned long len,
                unsigned long pgoff, unsigned long flags);

       unsigned long (*get_unmapped_exec_area) (struct file *filp,
                unsigned long addr, unsigned long len,
                unsigned long pgoff, unsigned long flags);

    //释放线性区时调用的方法,          
    void (*unmap_area) (struct mm_struct *mm, unsigned long addr);

    //标识第一个分配文件内存映射的线性地址
    unsigned long mmap_base;        /* base of mmap area */


    unsigned long task_size;        /* size of task vm space */
    /*
     * RHEL6 special for bug 790921: this same variable can mean
     * two different things. If sysctl_unmap_area_factor is zero,
     * this means the largest hole below free_area_cache. If the
     * sysctl is set to a positive value, this variable is used
     * to count how much memory has been munmapped from this process
     * since the last time free_area_cache was reset back to mmap_base.
     * This is ugly, but necessary to preserve kABI.
     */
    unsigned long cached_hole_size;

    //内核进程搜索进程地址空间中线性地址的空间空间
    unsigned long free_area_cache;      /* first hole of size cached_hole_size or larger */

    //指向页表的目录
    pgd_t * pgd;

    //共享进程时的个数
    atomic_t mm_users;          /* How many users with user space? */

    //内存描述符的主使用计数器,采用引用计数的原理,当为0时代表无用户再次使用
    atomic_t mm_count;          /* How many references to "struct mm_struct" (users count as 1) */

    //线性区的个数
    int map_count;              /* number of VMAs */

    struct rw_semaphore mmap_sem;

    //保护任务页表和引用计数的锁
    spinlock_t page_table_lock;     /* Protects page tables and some counters */

    //mm_struct结构,第一个成员就是初始化的mm_struct结构,
    struct list_head mmlist;        /* List of maybe swapped mm's.  These are globally strung
                         * together off init_mm.mmlist, and are protected
                         * by mmlist_lock
                         */

    /* Special counters, in some configurations protected by the
     * page_table_lock, in other configurations by being atomic.
     */

    mm_counter_t _file_rss;
    mm_counter_t _anon_rss;
    mm_counter_t _swap_usage;

    //进程拥有的最大页表数目
    unsigned long hiwater_rss;  /* High-watermark of RSS usage *///进程线性区的最大页表数目
    unsigned long hiwater_vm;   /* High-water virtual memory usage */

    //进程地址空间的大小,锁住无法换页的个数,共享文件内存映射的页数,可执行内存映射中的页数
    unsigned long total_vm, locked_vm, shared_vm, exec_vm;
    //用户态堆栈的页数,
    unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
    //维护代码段和数据段
    unsigned long start_code, end_code, start_data, end_data;
    //维护堆和栈
    unsigned long start_brk, brk, start_stack;
    //维护命令行参数,命令行参数的起始地址和最后地址,以及环境变量的起始地址和最后地址
    unsigned long arg_start, arg_end, env_start, env_end;

    unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */

    struct linux_binfmt *binfmt;

    cpumask_t cpu_vm_mask;

    /* Architecture-specific MM context */
    mm_context_t context;

    /* Swap token stuff */
    /*
     * Last value of global fault stamp as seen by this process.
     * In other words, this value gives an indication of how long
     * it has been since this task got the token.
     * Look at mm/thrash.c
     */
    unsigned int faultstamp;
    unsigned int token_priority;
    unsigned int last_interval;

    //线性区的默认访问标志
    unsigned long flags; /* Must use atomic bitops to access the bits */

    struct core_state *core_state; /* coredumping support */
#ifdef CONFIG_AIO
    spinlock_t      ioctx_lock;
    struct hlist_head   ioctx_list;
#endif
#ifdef CONFIG_MM_OWNER
    /*
     * "owner" points to a task that is regarded as the canonical
     * user/owner of this mm. All of the following must be true in
     * order for it to be changed:
     *
     * current == mm->owner
     * current->mm != mm
     * new_owner->mm == mm
     * new_owner->alloc_lock is held
     */
    struct task_struct *owner;
#endif

#ifdef CONFIG_PROC_FS
    /* store ref to file /proc/<pid>/exe symlink points to */
    struct file *exe_file;
    unsigned long num_exe_file_vmas;
#endif
#ifdef CONFIG_MMU_NOTIFIER
    struct mmu_notifier_mm *mmu_notifier_mm;
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
    pgtable_t pmd_huge_pte; /* protected by page_table_lock */
#endif
    /* reserved for Red Hat */
#ifdef __GENKSYMS__
    unsigned long rh_reserved[2];
#else
    /* How many tasks sharing this mm are OOM_DISABLE */
    union {
        unsigned long rh_reserved_aux;
        atomic_t oom_disable_count;
    };

    /* base of lib map area (ASCII armour) */
    unsigned long shlib_base;
#endif
};

struct vm_area_struct (进程虚拟地址空间的区间)

struct vm_area_struct {
	/* The first cache line has the info for VMA tree walking. 
	第一个缓存行具有VMA树移动的信息*/
 
	unsigned long vm_start;		/* Our start address within vm_mm. */
	unsigned long vm_end;		/* The first byte after our end address within vm_mm. */
 
	/* linked list of VM areas per task, sorted by address
	每个任务的VM区域的链接列表,按地址排序*/
	struct vm_area_struct *vm_next, *vm_prev;
 
	struct rb_node vm_rb;
 
	/*
	 此VMA左侧最大的可用内存间隙(以字节为单位)。 
	 在此VMA和vma-> vm_prev之间,
	 或者在VMA rbtree中我们下面的一个VMA与其->vm_prev之间。 
	 这有助于get_unmapped_area找到合适大小的空闲区域。
	 */
	unsigned long rb_subtree_gap;
 
	/* Second cache line starts here. 
	第二个缓存行从这里开始*/
 
	struct mm_struct *vm_mm;	/* 我们所属的address space*/
	pgprot_t vm_page_prot;		/* 此VMA的访问权限 */
	unsigned long vm_flags;		/* Flags, see mm.h. */
 
	/*
	 对于具有地址空间(address apace)和后备存储(backing store)的区域,
	 链接到address_space->i_mmap间隔树,或者链接到address_space-> i_mmap_nonlinear列表中的vma。
	 */
	union {
		struct {
			struct rb_node rb;
			unsigned long rb_subtree_last;
		} linear;
		struct list_head nonlinear;
	} shared;
 
	/*
	 在其中一个文件页面的COW之后,文件的MAP_PRIVATE vma可以在i_mmap树和anon_vma列表中。
	 MAP_SHARED vma只能位于i_mmap树中。 
	 匿名MAP_PRIVATE,堆栈或brk vma(带有NULL文件)只能位于anon_vma列表中。
	 */
	struct list_head anon_vma_chain; /* Serialized by mmap_sem & * page_table_lock
										由mmap_sem和* page_table_lock序列化*/
	struct anon_vma *anon_vma;	/* Serialized by page_table_lock 由page_table_lock序列化*/
 
	/* 用于处理此结构体的函数指针 */
	const struct vm_operations_struct *vm_ops;
 
	/* 后备存储(backing store)的信息: */
	unsigned long vm_pgoff;		/* 以PAGE_SIZE为单位的偏移量(在vm_file中),*不是* PAGE_CACHE_SIZE*/
	struct file * vm_file;		/* 我们映射到文件(可以为NULL)*/
	void * vm_private_data;		/* 是vm_pte(共享内存) */
 
#ifndef CONFIG_MMU
	struct vm_region *vm_region;	/* NOMMU映射区域 */
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* 针对VMA的NUMA政策 */
#endif
};

从mm_struct 到vm_area_struct到linear address space

img

这里有张更直观的图

img

进程退出

exit_mm()—>mmput() 减少mm_user个数—>如果mm_user==0(没有fork的子进程,也没有线程共享他的资源) —> free_mm() —> kmem_cache_free() —>把mm_struct还给slab,可以直接被复用,省得再开辟很浪费资源

(而且内核线程在运行的时候还会借用其他进程的mm_struct,所以内核线程又叫Anonymous user)

关于mm_user mm_count的CONFUSION

(1) mm_user指的就是所有共享此mm_struct描述的进程地址空间的线程数量,即:一个(进程中)线程组中的线程个数。当本进程中的线程退出时,mm_user减1,但只有当所有共享此进程空间的线程退出时,会对mm_count减1,否则不减

(2)mm_count指的就是对mm_struct本身此结构体的引用次数。不管本进程中有多少线程,在没有其他进程或线程引用的情况下,此mm_count为1,因为本进程中的所有线程共享一个进程地址空间,就是创建线程,fork()时,直接将主线程task_struct中的mm域直接给了被fork出来的task_struct的mm域。当有其他进程或线程(除本进程中的线程)引用此mm_struct时,则mm_count加1

(3)当本进程中的线程退出时,mm_user会减1,如果mm_user减到0了,则会对mm_count减1,如果此时mm_count也为0了,说明该进程空间么有任何使用者了,则会归还此进程地址空间占的内存给系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiujiaHuan13

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值