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,一经查实,立即删除!

相关文章

epoll

开发高性能网络程序时&#xff0c;windows开发者们言必称iocp&#xff0c;linux开发者们则言必称epoll。大家都明白epoll是一种IO多路复用技术&#xff0c;可以非常高效的处理数以百万计的socket句柄&#xff0c;比起以前的select和poll效率高大发了。我们用起epoll来都感觉挺爽…

剑指offer目录

序号题目1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

基于升序链表的定时器

#ifndef LST_TIMER#define LST_TIMER#include <time.h>#define BUFFER_SIZE 64class util_timer;//用户数据结构&#xff1a;客户端地址、客户端的socket、socket文件描述符、读缓存和定时器struct client_data{sockaddr_in address;int sockfd;char buf[ BUFFER_SIZE ];…

SIGCHLD信号使用和注意事项

1.SIGCHLD简介 SIGCHILD是指在一个进程终止或者停止时&#xff0c;将SIGCHILD信号发送给其父进程&#xff0c;按照系统默认将忽略此信号&#xff0c;如果父进程希望被告知其子系统的这种状态&#xff0c;则应捕捉此信号。注意&#xff1a;SIGCLD信号与其长得非常相似。SIGCLD是…

08-图7 公路村村通 (30 分

现有村落间道路的统计数据表中&#xff0c;列出了有可能建设成标准公路的若干条道路的成本&#xff0c;求使每个村落都有公路连通所需要的最低成本。 输入格式: 输入数据包括城镇数目正整数N&#xff08;≤&#xff09;和候选道路数目M&#xff08;≤&#xff09;&#xff1b;随…

【Leetcode】33. 搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如&#xff0c;数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 搜索一个给定的目标值&#xff0c;如果数组中存在这个目标值&#xff0c;则返回它的索引&#xff0c;否则返回 -1 。 你可以假设数组中不存在重…

08-图9 关键活动 (30 分

假定一个工程项目由一组子任务构成&#xff0c;子任务之间有的可以并行执行&#xff0c;有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。 比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成…

【Leetocde | 10 】54. 螺旋矩阵

解题代码&#xff1a; class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {if (matrix.empty() || matrix[0].empty()) return {};int m matrix.size(), n matrix[0].size();vector<int> res;int up 0, down m …

09-排序1 排序 (25 分)

给定N个&#xff08;长整型范围内的&#xff09;整数&#xff0c;要求输出从小到大排序后的结果。 本题旨在测试各种不同的排序算法在各种数据情况下的表现。各组测试数据特点如下&#xff1a; 数据1&#xff1a;只有1个元素&#xff1b; 数据2&#xff1a;11个不相同的整数…

网络层

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

【Leetocde | 24 】152. 乘积最大子序列

这道题最直接的方法就是用DP来做&#xff0c;而且要用两个dp数组&#xff0c;其中f[i]表示子数组[0, i]范围内并且一定包含nums[i]数字的最大子数组乘积&#xff0c;g[i]表示子数组[0, i]范围内并且一定包含nums[i]数字的最小子数组乘积&#xff0c;初始化时f[0]和g[0]都初始化…

【Leetcode | 1】3. 无重复字符的最长子串

这里我们可以建立一个HashMap&#xff0c;建立每个字符和其最后出现位置之间的映射&#xff0c;然后我们需要定义两个变量res和left&#xff0c;其中res用来记录最长无重复子串的长度&#xff0c;left指向该无重复子串左边的起始位置的前一个&#xff0c;由于是前一个&#xff…

【Leetcode | 】93. 复原IP地址

class Solution { public:vector<string> strs;//用于存放临时的四个段vector<string> result;//存放结果void dfs(string &s, int beginIndex, int step) {if (step 4 && beginIndex s.size()) //搜索成功{string temRec strs[0] "." …

海量数据(一)

1. 有1亿个浮点数&#xff0c;如果找出期中最大的10000个&#xff1f; 最容易想到的方法是将数据全部排序&#xff0c;然后在排序后的集合中进行查找&#xff0c;最快的排序算法的时间复杂度一般为O&#xff08;nlogn&#xff09;&#xff0c;如快速排序。但是在32位的机器上&a…

1018 锤子剪刀布 (20 分)

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

1019 数字黑洞 (20 分)

给定任一个各位数字不完全相同的 4 位正整数&#xff0c;如果我们先把 4 个数字按非递增排序&#xff0c;再按非递减排序&#xff0c;然后用第 1 个数字减第 2 个数字&#xff0c;将得到一个新的数字。一直重复这样做&#xff0c;我们很快会停在有“数字黑洞”之称的 6174&…

61. 旋转链表

给定一个链表&#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: 1->2->3->4->5->NULL, k 2 输出: 4->5->1->2->3->NULL 解释: 向右旋转 1 步: 5->1->2->3->4->NULL…

1020 月饼 (25 分)

月饼是中国人在中秋佳节时吃的一种传统食品&#xff0c;不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量&#xff0c;请你计算可以获得的最大收益是多少。 注意&#xff1a;销售时允许取出一部分库存。样例给出的情形是这样的&#x…

2. 二叉树的深度

题目描述 输入一棵二叉树&#xff0c;求该树的深度。从根结点到叶结点依次经过的结点&#xff08;含根、叶结点&#xff09;形成树的一条路径&#xff0c;最长路径的长度为树的深度。 /* struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;TreeNode(in…

1021 个位数统计 (15 分

给定一个 k 位整数 1 (0, ,, d​k−1​​>0)&#xff0c;请编写程序统计每种不同的个位数字出现的次数。例如&#xff1a;给定 0&#xff0c;则有 2 个 0&#xff0c;3 个 1&#xff0c;和 1 个 3。 输入格式&#xff1a; 每个输入包含 1 个测试用例&#xff0c;即一个不超过…