进程地址空间

前置知识:进程=内存可执行程序+PCB(linux中为 task_struct的结构体)

1.每个进程运行后,都会有一个进程地址空间存在(mm_struct)

(虚拟地址)

虚拟地址与真实物理内存地址之间存在一个映射表,称为页表

        fork()创建子进程后,子进程会以父进程为模板,进行pcb拷贝(对子进程特征参数会进行适当改变,如pid 优先级等)。页表也会进行拷贝,当子进程进行变量修改时,会发生写时拷贝,页表中父子进程同一变量映射的真实物理地址会发生改变,因此会导致父子进程中同一变量 同一地址(int i = 0;&i得到的是虚拟地址)却显示不同的值。

2.页表

        进程pcb:task_struct结构体中 存在mm_struct*指针,而struct mmmm_struct结构体中存储着栈区、堆区、静态区等各个虚拟地址的区间范围(亦即“每个进程运行后,都会有一个进程地址空间存在”的含义)。

        虚拟地址存在的含义:1.有效进行进程访问内存的安全检查!!(访问字段决定虚拟地址是否应用与实际物理地址的映射关系)2. 页表的存在得以让进程与内存分配与程序执行之间进行解耦(进程只管根据虚拟地址做自己的事情,当有需要时os进行分配空间建立虚拟地址与实际内存地址之间的映射关系,亦即pcb根本不用关注实际地址空间只管做自己的事情即可)

398 struct mm_struct {
  399   struct vm_area_struct * mmap;   /* list of VMAs */    //虚拟地址空间结构体,双向链表包含红黑树节点访问到不能访问的区域。
  400   struct rb_root mm_rb;                                 //红黑树的根节点
  401   struct vm_area_struct * mmap_cache; /* last find_vma result */  //mmap的高速缓冲器,指的是mmap最后指向的一个虚拟地址区间
  402 #ifdef CONFIG_MMU                                                                                                              
  403   unsigned long (*get_unmapped_area) (struct file *filp,
  404         unsigned long addr, unsigned long len,
  405         unsigned long pgoff, unsigned long flags);
  406   void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
  407 #endif
  408   unsigned long mmap_base;    /* base of mmap area */                                       //mmap区域的基地址
  409   unsigned long mmap_legacy_base;         /* base of mmap area in bottom-up allocations */  //自底向上的配置
  410   unsigned long task_size;    /* size of task vm space */                                   //进程的虚拟地址空间大小
  411   unsigned long cached_hole_size;   /* if non-zero, the largest hole below free_area_cache */ //缓冲器的最大的大小
  412   unsigned long free_area_cache;    /* first hole of size cached_hole_size or larger */       //不受约束的空间大小
  413   unsigned long highest_vm_end;   /* highest vma end address */                             //虚拟地址空间最大结尾地址
  414   pgd_t * pgd;                                                                              //页表的全局目录
  415   atomic_t mm_users;      /* How many users with user space? */                             //有多少用户
  416   atomic_t mm_count;      /* How many references to "struct mm_struct" (users count as 1) */ //有多少用户引用mm_struct
  417   atomic_long_t nr_ptes;  /* Page table pages */                                       //页表
  418   int map_count;        /* number of VMAs */                                               //虚拟地址空间的个数
  419 
  420   spinlock_t page_table_lock;   /* Protects page tables and some counters */               //保护页表和用户
  421   struct rw_semaphore mmap_sem;                                                            //读写信号
  422 
  423   struct list_head mmlist;    /* List of maybe swapped mm's.  These are globally strung
  424              * together off init_mm.mmlist, and are protected
  425              * by mmlist_lock
  426              */
  427 
  428 
  429   unsigned long hiwater_rss;  /* High-watermark of RSS usage */                     //标志
  430   unsigned long hiwater_vm; /* High-water virtual memory usage */
  431 
  432   unsigned long total_vm;   /* Total pages mapped */
  433   unsigned long locked_vm;  /* Pages that have PG_mlocked set */
  434   unsigned long pinned_vm;  /* Refcount permanently increased */
  435   unsigned long shared_vm;  /* Shared pages (files) */
  436   unsigned long exec_vm;    /* VM_EXEC & ~VM_WRITE */                                                                          
  437   unsigned long stack_vm;   /* VM_GROWSUP/DOWN */
  438   unsigned long def_flags;
  439   unsigned long start_code, end_code, start_data, end_data;               //开始代码段,结束代码。开始数据,结束数据
  440   unsigned long start_brk, brk, start_stack;                              //堆的开始和结束。
  441   unsigned long arg_start, arg_end, env_start, env_end;                   //参数的起始和结束,环境变量的起始和终点
  442 
  443   unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
        ...
};

3.写时拷贝(变量改变时才重新分配内存空间,不改变就不动)

        第二部分提到,子进程的task_struct(pcb)会拷贝父进程的pcb,因此子进程一个变量相应的虚拟地址与父进程中虚拟地址一致,当我们试图修改该变量时,如果子进程页表对应的实际地址不进行修改,而更改了此变量对应实际地址的值,会导致父进程的该变量内容被动修改,这会违反进程独立性原则(进程之间不能相互影响,不然复杂情况下,进程被随意修改,难以追溯),我们不能让子进程影响父进程,因此我们需要重新赋予 子进程页表中该变量对应的实际内存。

        那么写时拷贝如何实现呢?

                系统在创建子进程进行拷贝父进程pcb之前,会将pcb中的内容改成只读,此时,子进程pcb拷贝完成时权限也是只读,当变量尽相改变时就会报错,在系统底层,我们就知道了报错的地方是需要改变的,此时将权限改成读写,进行更改就可以了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值