Linux程序地址

目录

一、定义

二、问题引出

三、虚拟地址和物理地址

(一)问题解释

(二)什么是进程地址空间

(三)为什么要有进程地址空间


一、定义

cpp 复制代码
#include <stdio.h>    
#include <stdlib.h>//getenv的头文件    
    
int un_gval;    
int init_gval=2;    
int main()    
{    
    printf("代码地址        :%p\n", main);    
    const char *str = "hello world";    
    printf("常量地址        :%p\n", str);    
    printf("已初始化数据地址:%p\n", &init_gval);    
    printf("未初始化数据地址:%p\n", &un_gval);                                                                                                  
    
    char *heap = (char*)malloc(100);    
    printf("堆区地址        :%p\n", heap);    
    
    printf("栈区地址        :%p\n", &str);    
    return 0;
} 
cpp 复制代码
#include <stdio.h>    
#include <stdlib.h>//getenv的头文件        
int main()    
{    
    const char *str="hello world";    
    char *heap1 = (char*)malloc(100);    
    char *heap2 = (char*)malloc(100);    
    char *heap3 = (char*)malloc(100);    
    char *heap4 = (char*)malloc(100);    
    
    printf("heap1 address:%p\n", heap1);                                                                                                        
    printf("heap2 address:%p\n", heap2);    
    printf("heap3 address:%p\n", heap3);    
    printf("heap4 address:%p\n", heap4);    
    
    printf("Stack1 address: %p\n", &str);    
    printf("Stack2 address: %p\n", &heap1);//变量heap1是在main函数内部定义    
    printf("Stack3 address: %p\n", &heap2);    
    printf("Stack4 address: %p\n", &heap3);    
    return 0;    
} 
  • 栈整体向下增长,但是一旦全部开辟好以后,局部向上使用
  • (例如一个数组int a[10] ,&a[0]<&a[9])

二、问题引出

  • fork以后的父子进程,输出地址是一致的,但是变量内容不一样
cpp 复制代码
  #include<stdio.h>
  #include<unistd.h>
  
  int g_val = 100;
  int main()
  {
      pid_t id = fork();
      if(id < 0)
      {
          perror("fork");
          return 0;
      }
      else if(id == 0)//子进程
      {
          int cnt = 5;
          while(1)
          {
              printf("child , pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(),getppid(),g_val,&g_val);
              sleep(1);
              if(cnt == 0)
              {
                  g_val = 200;
                  printf("child change g_val: 100->200\n");
              }                                                                                                      
              cnt--;
          }
      }
      else
      {
          //父进程
          while(1)
          {
              printf("father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
              sleep(1);
          }
  
      }
      return 0;
  }

三、虚拟地址和物理地址

(一)问题解释

  • 每一个进程运行之后,都会有一个进程地址空间的存在(在系统层面都要有自己的页表映射结构)

(二)什么是进程地址空间

  • 进程地址空间本质上是内存中的一种内核数据结构
  • 在Linux当中进程地址空间具体由结构体mm_struct实现
  • 进程地址空间是一个虚拟的内存空间,可以看做是一条从 0x000000000xffffffff 的线,这条线上被划分成不同的区域。
  • 每个区域在进程地址空间中都有一定范围的地址划分。在实际运行中,这些虚拟地址会被操作系统内核映射到实际的物理内存地址上
cpp 复制代码
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);
	void (*unmap_area) (struct vm_area_struct *area);
	unsigned long mmap_base;		/* base of mmap area */
	unsigned long free_area_cache;		/* first hole */
	pgd_t * pgd;
	atomic_t mm_users;			/* How many users with user space? */
	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, mm->rss, mm->anon_rss */

	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
						 */

	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 rss, anon_rss, total_vm, locked_vm, shared_vm;
	unsigned long exec_vm, stack_vm, reserved_vm, def_flags, nr_ptes;

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

	unsigned dumpable:1;
	cpumask_t cpu_vm_mask;

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

	/* Token based thrashing protection. */
	unsigned long swap_token_time;
	char recent_pagein;

	/* coredumping support */
	int core_waiters;
	struct completion *core_startup_done, core_done;

	/* aio bits */
	rwlock_t		ioctx_list_lock;
	struct kioctx		*ioctx_list;

	struct kioctx		default_kioctx;

	unsigned long hiwater_rss;	/* High-water RSS usage */
	unsigned long hiwater_vm;	/* High-water virtual memory usage */
};

页表中的其他字段:

(三)为什么要有进程地址空间

  • 进程以统一的视角看待内存,所以任意一个进程,可以通过地址空间+页表将乱序的内存数据,变成有序
  • 存在虚拟地址空间,可以有效的进行进程访问内存的安全检查
  • 将进程管理和内存管理进行解耦(互不干扰)
  • 通过页表,让进程映射到不同物理内存上,从而实现进程的独立性
相关推荐
LKAI.21 分钟前
搭建Elastic search群集
linux·运维·elasticsearch·搜索引擎
正在走向自律1 小时前
阿里云ESC服务器一次性全部迁移到另一个ESC
服务器·阿里云·云计算
gywl2 小时前
openEuler VM虚拟机操作(期末考试)
linux·服务器·网络·windows·http·centos
青木沐2 小时前
Jenkins介绍
运维·jenkins
WTT00112 小时前
2024楚慧杯WP
大数据·运维·网络·安全·web安全·ctf
苹果醋33 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
了一li3 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
日记跟新中3 小时前
Ubuntu20.04 修改root密码
linux·运维·服务器
唐小旭3 小时前
服务器建立-错误:pyenv环境建立后python版本不对
运维·服务器·python