Linux02进程内存管理

1.进程地址空间

   1.1程序的结构与进程的结构

       [root@localhost demo]# size test



  text   data    bss    dec    hex filename

    1193    492     16   1701    6a5 test

        一个可执行程序包含三个部分:

        代码段:主要存放指令,操作以及只读的常量数据例如字符串常量。

        数据段:全局或者静态的已经初始化的变量

        BSS段:全局或者静态的未初始化的变量

        执行一个程序要运行,需要将程序加载到内存(磁盘有专门的读写驱动,但是速度比较慢,内存可有cpu直接访问读写,比磁盘速度要快)

   

名称速度
CPU寄存器1ns
cache1ns~10ns
DRAM100ns
磁盘10ss
      进程:进程是操作系统最小的资源管理单元。一个程序运行,必然要为其创建一个进程有生命周期的。一个进程是执行的程序段。

                其资源分为内核资源和用户资源。内核资源是PCB---进程控制块,也就是一个结构体,负责管理进程所有资源。

                mm_struct指向该进程相关的内存资源:从低到高地址分为:代码段->数据段->BSS段->堆段->栈段

                在执行程序时,系统首先在内核空间创建一个进程,为这个进程申请一个进程控制块PCB(task_struct),用于管理整个进城的所有资源。

     代码段,数据段,BSS段,直接从磁盘拷贝到当前内存空间,大小相等。

     动态的空间:堆和栈空间,以及mmap段(主要是映射其他库的相关信息)

                命令:pmap 进程号或者cat /proc/进程号/maps

                       [root@localhost proc]# cat 31281/maps
00400000-004d4000 r-xp 00000000 08:02 390951                             /bin/bash
006d3000-006dd000 rw-p 000d3000 08:02 390951                             /bin/bash
006dd000-006e2000 rw-p 00000000 00:00 0 
008dc000-008e5000 rw-p 000dc000 08:02 390951                             /bin/bash
00b45000-00ba8000 rw-p 00000000 00:00 0                                  [heap]
32c0c00000-32c0c20000 r-xp 00000000 08:02 781828                         /lib64/ld-2.12.so
32c0e1f000-32c0e20000 r--p 0001f000 08:02 781828                         /lib64/ld-2.12.so
32c0e20000-32c0e21000 rw-p 00020000 08:02 781828                         /lib64/ld-2.12.so
32c0e21000-32c0e22000 rw-p 00000000 00:00 0 
32c1000000-32c1002000 r-xp 00000000 08:02 782740                         /lib64/libdl-2.12.so
32c1002000-32c1202000 ---p 00002000 08:02 782740                         /lib64/libdl-2.12.so
32c1202000-32c1203000 r--p 00002000 08:02 782740                         /lib64/libdl-2.12.so
32c1203000-32c1204000 rw-p 00003000 08:02 782740                         /lib64/libdl-2.12.so
32c1400000-32c158b000 r-xp 00000000 08:02 782725                         /lib64/libc-2.12.so
32c158b000-32c178a000 ---p 0018b000 08:02 782725                         /lib64/libc-2.12.so
32c178a000-32c178e000 r--p 0018a000 08:02 782725                         /lib64/libc-2.12.so
32c178e000-32c178f000 rw-p 0018e000 08:02 782725                         /lib64/libc-2.12.so
32c178f000-32c1794000 rw-p 00000000 00:00 0 
3e6a000000-3e6a01d000 r-xp 00000000 08:02 781958                         /lib64/libtinfo.so.5.7
3e6a01d000-3e6a21c000 ---p 0001d000 08:02 781958                         /lib64/libtinfo.so.5.7
3e6a21c000-3e6a220000 rw-p 0001c000 08:02 781958                         /lib64/libtinfo.so.5.7
3e6a220000-3e6a221000 rw-p 00000000 00:00 0 
7f2a4f326000-7f2a551b7000 r--p 00000000 08:02 914111                     /usr/lib/locale/locale-archive
7f2a551b7000-7f2a551c3000 r-xp 00000000 08:02 781857                     /lib64/libnss_files-2.12.so
7f2a551c3000-7f2a553c3000 ---p 0000c000 08:02 781857                     /lib64/libnss_files-2.12.so
7f2a553c3000-7f2a553c4000 r--p 0000c000 08:02 781857                     /lib64/libnss_files-2.12.so
7f2a553c4000-7f2a553c5000 rw-p 0000d000 08:02 781857                     /lib64/libnss_files-2.12.so
7f2a553c5000-7f2a553c8000 rw-p 00000000 00:00 0 
7f2a553cc000-7f2a553ce000 rw-p 00000000 00:00 0 
7f2a553ce000-7f2a553d5000 r--s 00000000 08:02 914369                     /usr/lib64/gconv/gconv-modules.cache
7f2a553d5000-7f2a553d6000 rw-p 00000000 00:00 0 
7fffc0a71000-7fffc0a86000 rw-p 00000000 00:00 0                          [stack]

7fffc0b0a000-7fffc0b0b000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

                 所看到的地址不是物理地址,都是虚拟地址。出于对资源的保护,一个程序并不需要立即将所有的资源全部加载到内存,而是实际上可采用写时申请的方法。而且保护系统,很多用户非法访问不会造成内核的崩溃。

     1.2虚拟地址空间的申请

              32位平台下,一个进程拥有4G的虚拟地址,这个地址如何来分配和使用呢

大笑

  •  代码段,数据段,BSS段,这三个部分直接从磁盘拷贝到内存,起始地址在当前的32位平台下的地址为0x08048000.
  •  堆栈。是动态变化
  • mmap映射的文件
  • 栈段
  • 高地址1G空间,仅供内核映射处理,用户空间不能直接处理
    内核空间1G
    gap间隙为了安全
    高地址向低地址增长
    map映射区域
     
    gap间隙为了安全
    BSS 
    数据段 
    代码段 

  • 堆和栈的起始地址是随机产生的,其目的是为了避免安全漏洞。但是可以指定在堆中申请空间的起始地址(brk/sbrk函数);
    #include<stdio.h>
    #include<stdlib.h>

    #include<stdio.h>
    #include<stdlib.h>


    int main(int argc,char*argv[])
    {
      brk(0x09050000);
      printf("brk=%p\n",sbrk(0));
      char *ptr=malloc(1024);
      printf("new=%p\n",ptr);
      return 1;
    }
    结果:
  • [root@localhost demo]# ./t
    brk=0x9050000
    new=0x9050010
  • 系统执行一个过程,到底怎么来加载这些空间的?可以用strace工具来查看。
  •  strace ./t
    execve("./t", ["./t"], [/* 25 vars */]) = 0
    brk(0)                                  = 0x7e0000//起始地址
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bba000
    access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
    open("/etc/ld.so.cache", O_RDONLY)      = 3
    fstat(3, {st_mode=S_IFREG|0644, st_size=52315, ...}) = 0
    mmap(NULL, 52315, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb578bad000
    close(3)                                = 0
    open("/lib64/libc.so.6", O_RDONLY)      = 3
    read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356A\3012\0\0\0"..., 832) = 832
    fstat(3, {st_mode=S_IFREG|0755, st_size=1926800, ...}) = 0
    mmap(0x32c1400000, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x32c1400000
    mprotect(0x32c158b000, 2093056, PROT_NONE) = 0
    mmap(0x32c178a000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x32c178a000
    mmap(0x32c178f000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x32c178f000
    close(3)                                = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bac000
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bab000
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578baa000
    arch_prctl(ARCH_SET_FS, 0x7fb578bab700) = 0
    mprotect(0x32c178a000, 16384, PROT_READ) = 0
    mprotect(0x32c0e1f000, 4096, PROT_READ) = 0
    munmap(0x7fb578bad000, 52315)           = 0
    brk(0x9050000)                          = 0x9050000
    fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bb9000
    write(1, "brk=0x9050000\n", 14brk=0x9050000
    )         = 14
    brk(0x9071000)                          = 0x9071000
    write(1, "new=0x9050010\n", 14new=0x9050010
    )         = 14
    exit_group(1)                           = ?
  • 堆空间的起始地址是随机的,可设置为不随机。大小也可以设置成固定大小
    代码段,数据段,BSS段的地址在编译链接时固定
    在BSS结束和堆起始地址有空隙,这个空隙是随机的
    brk函数仅仅是调整在堆中申请空间的起始值。使用以下命令可以指定堆的起始地址固定
    sudo sysctl -w kernel/randomize_va_space=0


  • 系统默认为每个进程分配的堆空间大小是固定的。使用 sbrk(0)得到的是我们堆空间的结束
    值。第一次使用 malloc 申请资源返回的地址接近于堆空间的起始值。全用 brk(addr)改变的是新申请数据
    的堆空间起始值。
  • 在真正编程中,很少全用 brk/sbrk,使用 malloc函数来新申请堆空间,效率更高。部分时间使
    用 mmap来映射 mmap区,
    栈:栈从高地址向低地址增长,栈的起始值也是随机的。栈中主要存放的是局部变量,新调用子函
    数时函数的参数及返回值。由 OS自动管理。

1.3内存空间的申请和释放
  • 代码段中:由只读数据和代码组成: const,字符串常量,这些内容的空间在编译链接时申请好
    且指定存储地址。
     
  • 数据段 BSS 段申请:定义的全局的或者是静态的变量。已经初始化的在数据段,未初始化的
    BSS段中。因此也是在编译链接时已经申请且指定的地址。
    以上空间的申请在运行时候就加载到内存中,直到程序的结束,不在发生变化,除了exec函数执行
  • 堆:由程序员自己动态管理。
    动态申请,堆的起始位置由brk函数指定,但是具体编程中一般不会自己使用它,而是使用malloc函数。内核对内存的管理是页式管理,因此在 malloc申请空间时,使用链式结构来管理已经
    申请的堆空间。

  • 栈:由os管理
  • mmap的库以及相关文件
    将一个文件的内容全部或者部分的映射到虚拟地址空间中,后面释放时操作这段内存空间可以被同
    步到磁盘文件的操作,效率比较高。共享映射,其它进程如果也映射这个文件,可以立即看到这个更改,但并不是立即更新磁盘文件的内容,如果要更新到磁盘,必须使用 msync以及 munmap时。 

2.常见内存错误以及valgrind使用

         代码段:只读数据。因此对这一部分的数据,试图写只读数据。这个在编译的时候基本可以检测。

数据段/BSS段:未初始化直接访问。即使没有显式初始化,仍然会初始化为 0。
栈空间数据:
(1)局部变量,未初始化这类变量会给随机的初值,出现异常情况更诡异。
(2) 栈溢出,在栈中申请过大的局部变量。
堆空间数据:
内存泄漏,( 1)申请未释放( 2)申请后,双重释放。
对于所有的地址空间:
( 1)野指针的问题。未初始化指针。去访问这个指针指向的空间。
( 2)越界访问,例如一个数组 a[10],试图访问 a[10]以及以后。
( 3)非法的越权访问。例如 mmap的空间只读,但试图写。
( 4)空间不在控制范围仍然去访问空间,例如返回局部变量地址,且后面访问这个空间。
使用工具,来检测常见的内存错误。 valgrind工具。
1041 gcc -o valgrind_example01 valgrind_example01.c -g
1042 valgrind --tool=memcheck --show-reachable=yes --read-var-info=yes --verbose --time-stamp=yes --
leak-check=full --log-file=mycode.log ./valgrind_example01
1043 less mycode.log
如果要使用图形化的工具,要安装 QT,这个工具名字叫 valkyrie。
         

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385261.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络层

1. 简单解释一些ARP协议的工作过程

1018 锤子剪刀布 (20 分)

大家应该都会玩“锤子剪刀布”的游戏&#xff1a;两人同时给出手势&#xff0c;胜负规则如图所示&#xff1a; 现给出两人的交锋记录&#xff0c;请统计双方的胜、平、负次数&#xff0c;并且给出双方分别出什么手势的胜算最大。 输入格式&#xff1a; 输入第 1 行给出正整数 N…

【C++基础】常见面试问题(二)

1. 指针和引用的区别 指针保存的是所指对象的地址&#xff0c;引用是所指对象的别名&#xff0c;指针需要通过解引用间接访问&#xff0c;而引用是直接访问指针可以改变地址&#xff0c;从而改变所指的对象&#xff0c;而引用必须从一而终&#xff1b;引用在定义的时候必须初始…

static关键字用法

static修饰局部变量 静态局部变量存储在全局静态区生存期为整个程序生命周期&#xff0c;但是其作用域仍与自动变量相同&#xff0c;只能在定义该变量的函数内使用该变量。退出该函数后&#xff0c;尽管该变量还继续存在&#xff0c;但不能使用它。静态局部变量若在声明时未赋以…

1039 到底买不买 (20 分)

小红想买些珠子做一串自己喜欢的珠串。卖珠子的摊主有很多串五颜六色的珠串&#xff0c;但是不肯把任何一串拆散了卖。于是小红要你帮忙判断一下&#xff0c;某串珠子里是否包含了全部自己想要的珠子&#xff1f;如果是&#xff0c;那么告诉她有多少多余的珠子&#xff1b;如果…

存储

一、多重继承&#xff08;无虚函数覆盖&#xff09; 下面&#xff0c;再让我们来看看多重继承中的情况&#xff0c;假设有下面这样一个类的继承关系。注意&#xff1a;子类并没有覆盖父类的函数。 class Base1 { public: virtual void f() { cout << "Base1::f&quo…

BTree和B+Tree详解

B 树是为了磁盘或其它存储设备而设计的一种多叉&#xff08;下面你会看到&#xff0c;相对于二叉&#xff0c;B树每个内结点有多个分支&#xff0c;即多叉&#xff09;平衡查找树。 B 树又叫平衡多路查找树。一棵m阶的B 树 (m叉树)的特性如下&#xff1a; 树中每个结点最多含…

【1】MySQL的四种事务隔离级别

二、事务的并发问题 1、脏读&#xff1a;事务A读取了事务B更新的数据&#xff0c;然后B回滚操作&#xff0c;那么A读取到的数据是脏数据 2、不可重复读&#xff1a;事务 A 多次读取同一数据&#xff0c;事务 B 在事务A多次读取的过程中&#xff0c;对数据作了更新并提交&#x…

eventfd(一)

函数原型&#xff1a; 创建的时候可以传入一个计数器的初始值initval。 第二个参数flags在linux 2.6.26之前的版本是没有使用的&#xff0c;必须初始化为0&#xff0c;在2.6.27之后的版本flag才被使用。 #include <sys/eventfd.h> int eventfd(unsigned int initval, in…

gettimeofday

作用&#xff1a;需要打印代码执行到某处的时间&#xff0c;或者需要计算程序执行的时间差&#xff08;精确到微妙级&#xff09;。这时会用到gettimeofday函数&#xff0c;它可以返回自1970-01-01 00:00:00到现在经历的秒数。 #include <sys/time.h> int gettimeofday(…

1070 结绳 (25 分

给定一段一段的绳子&#xff0c;你需要把它们串成一条绳。每次串连的时候&#xff0c;是把两段绳子对折&#xff0c;再如下图所示套接在一起。这样得到的绳子又被当成是另一段绳子&#xff0c;可以再次对折去跟另一段绳子串连。每次串连后&#xff0c;原来两段绳子的长度就会减…

1072 开学寄语 (20 分)

下图是上海某校的新学期开学寄语&#xff1a;天将降大任于斯人也&#xff0c;必先删其微博&#xff0c;卸其 QQ&#xff0c;封其电脑&#xff0c;夺其手机&#xff0c;收其 ipad&#xff0c;断其 wifi&#xff0c;使其百无聊赖&#xff0c;然后&#xff0c;净面、理发、整衣&am…

1076 Wifi密码 (15 分)

下面是微博上流传的一张照片&#xff1a;“各位亲爱的同学们&#xff0c;鉴于大家有时需要使用 wifi&#xff0c;又怕耽误亲们的学习&#xff0c;现将 wifi 密码设置为下列数学题答案&#xff1a;A-1&#xff1b;B-2&#xff1b;C-3&#xff1b;D-4&#xff1b;请同学们自己作答…

c++如何防止一个类被其他类继承?

如何在防止一个类被其他的类继承呢&#xff1f; 如果是仅仅为了达到这个目的可以直接把这个类的构造函数设置成私有的&#xff0c;这样就杜绝了其他类的继承。也相当于毁掉了这个类&#xff08;无法再创造出自己的对象&#xff09;。 那么怎么样既要保证这个类的完整性&#…

C++中构造函数和析构函数可以抛出异常吗?

不建议在构造函数中抛出异常。当构造函数中抛出异常时&#xff0c;析构函数将不会被执行&#xff0c;需要手动释放内存。析构函数不应该抛出异常。当析构函数中有一些可能发生的异常时&#xff0c;这时候要把可能发生的异常完全封装在析构函数内部&#xff0c;决不能让它抛出到…

1086 就不告诉你 (15 分)

做作业的时候&#xff0c;邻座的小盆友问你&#xff1a;“五乘以七等于多少&#xff1f;”你应该不失礼貌地围笑着告诉他&#xff1a;“五十三。”本题就要求你&#xff0c;对任何一对给定的正整数&#xff0c;倒着输出它们的乘积。 输入格式&#xff1a; 输入在第一行给出两个…

多线程顺序交替打印ABCD

题目&#xff1a;按照 ABCD的顺序交替打印。 1. 测试代码&#xff1a; #include <iostream> #include <unistd.h> #include <stdlib.h> #include <pthread.h> using namespace std;struct {int t;pthread_mutex_t mutex;pthread_cond_t cond; } tes…

1092 最好吃的月饼 (20 分

月饼是久负盛名的中国传统糕点之一&#xff0c;自唐朝以来&#xff0c;已经发展出几百品种。 若想评比出一种“最好吃”的月饼&#xff0c;那势必在吃货界引发一场腥风血雨…… 在这里我们用数字说话&#xff0c;给出全国各地各种月饼的销量&#xff0c;要求你从中找出销量冠军…

1094 谷歌的招聘 (20 分)

2004 年 7 月&#xff0c;谷歌在硅谷的 101 号公路边竖立了一块巨大的广告牌&#xff08;如下图&#xff09;用于招聘。内容超级简单&#xff0c;就是一个以 .com 结尾的网址&#xff0c;而前面的网址是一个 10 位素数&#xff0c;这个素数是自然常数 e 中最早出现的 10 位连续…