• Linux程序地址


    目录

    一、定义

    二、问题引出

    三、虚拟地址和物理地址

    (一)问题解释

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

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


    一、定义

    1. #include
    2. #include //getenv的头文件
    3. int un_gval;
    4. int init_gval=2;
    5. int main()
    6. {
    7. printf("代码地址 :%p\n", main);
    8. const char *str = "hello world";
    9. printf("常量地址 :%p\n", str);
    10. printf("已初始化数据地址:%p\n", &init_gval);
    11. printf("未初始化数据地址:%p\n", &un_gval);
    12. char *heap = (char*)malloc(100);
    13. printf("堆区地址 :%p\n", heap);
    14. printf("栈区地址 :%p\n", &str);
    15. return 0;
    16. }

    1. #include
    2. #include //getenv的头文件
    3. int main()
    4. {
    5. const char *str="hello world";
    6. char *heap1 = (char*)malloc(100);
    7. char *heap2 = (char*)malloc(100);
    8. char *heap3 = (char*)malloc(100);
    9. char *heap4 = (char*)malloc(100);
    10. printf("heap1 address:%p\n", heap1);
    11. printf("heap2 address:%p\n", heap2);
    12. printf("heap3 address:%p\n", heap3);
    13. printf("heap4 address:%p\n", heap4);
    14. printf("Stack1 address: %p\n", &str);
    15. printf("Stack2 address: %p\n", &heap1);//变量heap1是在main函数内部定义
    16. printf("Stack3 address: %p\n", &heap2);
    17. printf("Stack4 address: %p\n", &heap3);
    18. return 0;
    19. }

     

    • 栈整体向下增长,但是一旦全部开辟好以后,局部向上使用
    • (例如一个数组int a[10] ,&a[0]<&a[9])

    二、问题引出

    • fork以后的父子进程,输出地址是一致的,但是变量内容不一样
    1. #include
    2. #include
    3. int g_val = 100;
    4. int main()
    5. {
    6. pid_t id = fork();
    7. if(id < 0)
    8. {
    9. perror("fork");
    10. return 0;
    11. }
    12. else if(id == 0)//子进程
    13. {
    14. int cnt = 5;
    15. while(1)
    16. {
    17. printf("child , pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(),getppid(),g_val,&g_val);
    18. sleep(1);
    19. if(cnt == 0)
    20. {
    21. g_val = 200;
    22. printf("child change g_val: 100->200\n");
    23. }
    24. cnt--;
    25. }
    26. }
    27. else
    28. {
    29. //父进程
    30. while(1)
    31. {
    32. printf("father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
    33. sleep(1);
    34. }
    35. }
    36. return 0;
    37. }

    三、虚拟地址和物理地址

    (一)问题解释

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

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

    • 进程地址空间本质上是内存中的一种内核数据结构
    • 在Linux当中进程地址空间具体由结构体mm_struct实现 
    • 进程地址空间是一个虚拟的内存空间,可以看做是一条从 0x000000000xffffffff 的线,这条线上被划分成不同的区域。
    • 每个区域在进程地址空间中都有一定范围的地址划分。在实际运行中,这些虚拟地址会被操作系统内核映射到实际的物理内存地址
    1. struct mm_struct {
    2. struct vm_area_struct * mmap; /* list of VMAs */
    3. struct rb_root mm_rb;
    4. struct vm_area_struct * mmap_cache; /* last find_vma result */
    5. unsigned long (*get_unmapped_area) (struct file *filp,
    6. unsigned long addr, unsigned long len,
    7. unsigned long pgoff, unsigned long flags);
    8. void (*unmap_area) (struct vm_area_struct *area);
    9. unsigned long mmap_base; /* base of mmap area */
    10. unsigned long free_area_cache; /* first hole */
    11. pgd_t * pgd;
    12. atomic_t mm_users; /* How many users with user space? */
    13. atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
    14. int map_count; /* number of VMAs */
    15. struct rw_semaphore mmap_sem;
    16. spinlock_t page_table_lock; /* Protects page tables, mm->rss, mm->anon_rss */
    17. struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung
    18. * together off init_mm.mmlist, and are protected
    19. * by mmlist_lock
    20. */
    21. unsigned long start_code, end_code, start_data, end_data;
    22. unsigned long start_brk, brk, start_stack;
    23. unsigned long arg_start, arg_end, env_start, env_end;
    24. unsigned long rss, anon_rss, total_vm, locked_vm, shared_vm;
    25. unsigned long exec_vm, stack_vm, reserved_vm, def_flags, nr_ptes;
    26. unsigned long saved_auxv[42]; /* for /proc/PID/auxv */
    27. unsigned dumpable:1;
    28. cpumask_t cpu_vm_mask;
    29. /* Architecture-specific MM context */
    30. mm_context_t context;
    31. /* Token based thrashing protection. */
    32. unsigned long swap_token_time;
    33. char recent_pagein;
    34. /* coredumping support */
    35. int core_waiters;
    36. struct completion *core_startup_done, core_done;
    37. /* aio bits */
    38. rwlock_t ioctx_list_lock;
    39. struct kioctx *ioctx_list;
    40. struct kioctx default_kioctx;
    41. unsigned long hiwater_rss; /* High-water RSS usage */
    42. unsigned long hiwater_vm; /* High-water virtual memory usage */
    43. };

    页表中的其他字段:

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

    • 进程以统一的视角看待内存,所以任意一个进程,可以通过地址空间+页表将乱序的内存数据,变成有序
    • 存在虚拟地址空间,可以有效的进行进程访问内存的安全检查
    • 将进程管理和内存管理进行解耦(互不干扰)
    • 通过页表,让进程映射到不同物理内存上,从而实现进程的独立性
  • 相关阅读:
    AI与大数据:智慧城市安全的护航者与变革引擎
    c++的4中类型转换操作符(static_cast,reinterpret_cast,dynamic_cast,const_cast),RTTI
    C++:CRTP(Curiously Recurring Template Pattern 奇异递归模板)
    用哈希简单封装unordered_map和unordered_set
    kibana操作es使用DSL查询
    【保姆级示例向】观察者模式
    模型训练:优化人工智能和机器学习,完善DevOps工具的使用
    命令执行漏洞复现攻击:识别威胁并加强安全
    这波服装设计作品对女性美的诠释,十分到位
    刘二大人 PyTorch深度学习实践 笔记 P1 Overview
  • 原文地址:https://blog.csdn.net/m0_63783532/article/details/133897237