zoukankan      html  css  js  c++  java
  • C 高级编程 2 内存管理

    理解malloc的工作原理:
    
    malloc使用一个数据结构(链表)来维护分配的空间。链表的构成:
    分配的空间、上一个空间的地址、下一个空间的地址、以及本空间的信息等。
    对malloc分配的空间不要越界访问,
    因为容易破坏后台的链表维护结构,导致malloc/free/calloc/realloc不正常工作。
    
    定位分配
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <new>
    int main()
    {
        
        
        char a[20];
        int *p=new(a) int;
        
        return 0;
        
    }
    
    
    深入理解LINUX虚拟内存管理
    #include <stdio.h>
    int add(int a, int b)
    {
        
        return a+b;
        
    }
    int man()
    
    {
        
        //int (*fun)(int)=(int(*)(int))add;
        typedef int(*fun)(int);
        fun f=(fun)add;
        int r=fun(20);
        printf("%d
    ",r);
        
    }




    sbrk(0):如果是第一次调用sbrk函数,即内部变量为null,参数值为0,那么返回值为非映射页面的首地址

    参数值非0,那么返回的已映射页的首地址


    5 .函数调用空间的分配与释放与分配 总结: 1.C :函数执行的时候有自己的临时stack c++:函数执行的时候有自己的临时stack+对象stack 2.函数的参数在临时stack, 3.通过积存器返回值 (使用返回值顺数据) 4.通赤参数的返回值.(参数必须是指针) 指针指向的区域必须事先分配 5。如果参数返回指针,参数就是双指针. --------------------------------------------------- int add(int *a,int *b) { return *a+*b; } main() { int a=20; int b=30; int r=add(&a,&b); printf("%d ",r); } --------------------------------------------------------- 5.2. __stdcall __cdecl __fastcall 1.决定函数stack 压stack 参数顺序。 2.决定函数stack清空方式 3.决定了函数的名字转换方式.(函数名重写修改 ,C,C++都不同) 4.__attribute__ 不支持X64 #include <stdio.h> int __attribute__((stdcall)) add( int *a,int *b) { return (*a)+(*b); } main() { int a=20; int b=30; int r=add(&a,&b); printf("%d ",r); } gcc test.c -m32 -S [root@monitor ~]# cat test.s .file "test.c" .text .globl add .type add, @function add: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl (%eax), %edx movl 12(%ebp), %eax movl (%eax), %eax leal (%edx,%eax), %eax popl %ebp ret $8 .size add, .-add .section .rodata .LC0: .string "%d " .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $36, %esp movl $20, -16(%ebp) movl $30, -20(%ebp) leal -20(%ebp), %eax movl %eax, 4(%esp) leal -16(%ebp), %eax movl %eax, (%esp) call add subl $8, %esp movl %eax, -12(%ebp) movl $.LC0, %eax movl -12(%ebp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl -4(%ebp), %ecx leave leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)" .section .note.GNU-stack,"",@progbits [root@monitor ~]# gcc test.c -m64 -S test.c:4: warning: ‘stdcall’ attribute ignored ------------------------------------------------------ #include <stdio.h> int __attribute__((cdecl)) add( int *a,int *b) { return (*a)+(*b); } main() { int a=20; int b=30; int r=add(&a,&b); printf("%d ",r); } [root@monitor ~]# gcc test.c -m32 -S [root@monitor ~]# cat test.s .file "test.c" .text .globl add .type add, @function add: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl (%eax), %edx movl 12(%ebp), %eax movl (%eax), %eax leal (%edx,%eax), %eax popl %ebp ret .size add, .-add .section .rodata .LC0: .string "%d " .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $32, %esp movl $20, 24(%esp) movl $30, 20(%esp) leal 20(%esp), %eax movl %eax, 4(%esp) leal 24(%esp), %eax movl %eax, (%esp) call add movl %eax, 28(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf leave ret .size main, .-main .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)" .section .note.GNU-stack,"",@progbits [root@monitor ~]# gcc test.c -m64 -S test.c:4: warning: ‘cdecl’ attribute ignored ---------------------------------------------------------------- #include <stdio.h> int __attribute__((fastcall)) add( int *a,int *b) { return (*a)+(*b); } main() { int a=20; int b=30; int r=add(&a,&b); printf("%d ",r); } [root@monitor ~]# gcc test.c -m64 -S test.c:4: warning: ‘fastcall’ attribute ignored ---------------------------------------------------------------------- int add( int a ,int b) { return a+b; } int main() { add(10,20); } [root@monitor ~]# vi test1.c int add( int a ,int b) { return a+b; } int main() { add(10,20); } nm: test1.c: File format not recognized [root@monitor ~]# gcc -m32 test.c -o test [root@monitor ~]# nm test 080495ac d _DYNAMIC 08049678 d _GLOBAL_OFFSET_TABLE_ 080484ec R _IO_stdin_used w _Jv_RegisterClasses 0804959c d __CTOR_END__ 08049598 d __CTOR_LIST__ 080495a4 D __DTOR_END__ 080495a0 d __DTOR_LIST__ 08048594 r __FRAME_END__ 080495a8 d __JCR_END__ 080495a8 d __JCR_LIST__ 08049694 A __bss_start 08049690 D __data_start 080484a0 t __do_global_ctors_aux 08048340 t __do_global_dtors_aux 080484f0 R __dso_handle w __gmon_start__ 0804849a T __i686.get_pc_thunk.bx 08049598 d __init_array_end 08049598 d __init_array_start 08048430 T __libc_csu_fini 08048440 T __libc_csu_init U __libc_start_main@@GLIBC_2.0 08049694 A _edata 0804969c A _end 080484cc T _fini 080484e8 R _fp_hw 08048294 T _init 08048310 T _start 080483c4 T add 08049694 b completed.5989 08049690 W data_start 08049698 b dtor_idx.5991 080483a0 t frame_dummy 080483df T main U printf@@GLIBC_2.0 --------------------------------------------------------------------------- [root@monitor ~]# vi test.c int add(int , int); int add(int a,int b) { return a+b; } int main() { int r; r=add(10,20); return 1; } [root@monitor ~]# g++ test.c -o test [root@monitor ~]# nm test 00000000006007a0 d _DYNAMIC 0000000000600968 d _GLOBAL_OFFSET_TABLE_ 0000000000400678 R _IO_stdin_used w _Jv_RegisterClasses 0000000000400554 T _Z3addii 0000000000600780 d __CTOR_END__ 0000000000600778 d __CTOR_LIST__ 0000000000600790 D __DTOR_END__ 0000000000600788 d __DTOR_LIST__ 0000000000400770 r __FRAME_END__ 0000000000600798 d __JCR_END__ 0000000000600798 d __JCR_LIST__ 0000000000600994 A __bss_start 0000000000600990 D __data_start 0000000000400630 t __do_global_ctors_aux 00000000004004c0 t __do_global_dtors_aux 0000000000400680 R __dso_handle w __gmon_start__ U __gxx_personality_v0@@CXXABI_1.3 0000000000600774 d __init_array_end 0000000000600774 d __init_array_start 0000000000400590 T __libc_csu_fini 00000000004005a0 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 0000000000600994 A _edata 00000000006009a8 A _end 0000000000400668 T _fini 0000000000400428 T _init 0000000000400470 T _start 000000000040049c t call_gmon_start 0000000000600998 b completed.6349 0000000000600990 W data_start 00000000006009a0 b dtor_idx.6351 0000000000400530 t frame_dummy 0000000000400569 T main -------------------------------------------------------- 6. far near huge 指针 (windows) near 16 far 32 huge 综合 7.虚拟内存 1.一个程序不能访问另一个程序指向的空间 2.每个程序的开始地址0x80084000 3.程序使用的地址不是物理,而不能逻辑地址 4.辑辑编号是 使用 int 4字节整型地址 (0,4294967296) 每个程序提代了4G的访问能力 5. 针对进程的 ( 虚拟内存<---->物理内存) 这样进程有逻辑上的地址空间,屏蔽了真实内内存使用状况,由MMU后台管理映射 进程只操作虚拟地址,禁止访问物理地址,有助于系统稳定 6.逻辑地址与物理地址关联 有映射才可访问,如果访问没有映射地址,出现段错误 7.虚拟地址在映射的时候有个映射单位 4K(16进制的1000,称为内存页) EG:malloc(1),只少分配4K的内存页, 段错误:无效访问 合法访问:比如malloc分配的空间之外的空间访问,不会出现段错误,但不是合法访问, 可能修改了其他变量值 8.虚拟内存的分配 stack:编绎器自动生成代码维护 heap:地址是否映射,映射的空间是否被管理 1.brk/sbrk 内存映射函数 man 节 关键字 1-8节 1:linux shell命令 ls 2:系统函数 brk 3.标准C文档 fopen 7.系统的编程帮助 man 7 tcp man 7 icmp man 7 udp man 7 socket 分配空间,释放空间: int brk(void *end);分配空间,释放空间 void *sbrk(int size);返回空间地址 应用: 1使用sbrk他配空间 2使用sbrk得到没有映射的虚拟地址 3使用brk他配空间 4使用brk释放空间 sbrk(int size) sbrk与brk后台系统维护一个指针。 指针默认是null 调用sbrk,断定指针是否是0, 如果是0,返回大块空闲空间的首地址并初始化指针,同时把后台指针+size 如果不是0,返回指针,并且把指针位置+SIZE --------------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> main() { int *p1=sbrk(4); int *p2=sbrk(4); int *p3=sbrk(4); int *p4=sbrk(4); int *p5=sbrk(4); printf("%p ",p1); printf("%p ",p2); printf("%p ",p3); printf("%p ",p4); printf("%p ",p4); printf("%d ",getpid()); while(1); } [root@monitor ~]# gcc test.c -m32 -o test [root@monitor ~]# ./test 0x9e03000 0x9e03004 0x9e03008 0x9e0300c 0x9e03010 10148 //分配是有序的 4个字节 [root@monitor 10138]# cd /proc/10148 [root@monitor 10148]# cat maps 00155000-002e5000 r-xp 00000000 ca:01 396343 /lib/libc-2.12.so 002e5000-002e6000 ---p 00190000 ca:01 396343 /lib/libc-2.12.so 002e6000-002e8000 r--p 00190000 ca:01 396343 /lib/libc-2.12.so 002e8000-002e9000 rw-p 00192000 ca:01 396343 /lib/libc-2.12.so 002e9000-002ec000 rw-p 00000000 00:00 0 00ba3000-00bc1000 r-xp 00000000 ca:01 396396 /lib/ld-2.12.so 00bc1000-00bc2000 r--p 0001d000 ca:01 396396 /lib/ld-2.12.so 00bc2000-00bc3000 rw-p 0001e000 ca:01 396396 /lib/ld-2.12.so 00cd7000-00cd8000 r-xp 00000000 00:00 0 [vdso] 08048000-08049000 r-xp 00000000 ca:01 1065171 /root/test 08049000-0804a000 rw-p 00000000 ca:01 1065171 /root/test 09e03000-09e04000 rw-p 00000000 00:00 0 [heap] f775a000-f775b000 rw-p 00000000 00:00 0 f7762000-f7764000 rw-p 00000000 00:00 0 ff9c0000-ff9d5000 rw-p 00000000 00:00 0 [stack] ------------------------------------------------------------------------------------------ #include <stdio.h> #include <stdlib.h> main() { int *p1=sbrk(0);返回没有映射地址,因为0没有分配空间,即逻辑到物理关联没有建立 int *p1=sbrk(4);//返回空闲首地址(位于段) 4K页倍数 系统分配单元,并修改后台指针为+SIZE int *(p1+1023);正常,但不合法 int *(p1+1024);段错误 } ------------------------------------------------------------------------------- 后台维护一个后台变量。初值为NULL #include <stdio.h> #include <stdlib.h> main() { int *p1=sbrk(4); //页首地址 (0-3) int *p2=sbrk(200); //(4,199) int *p3=sbrk(0); //(200) printf("%p ",p1); pirntf("%p ",p2); printf("%p ",p3); printf("%d",getpid()); while(1); } [root@monitor ~]# ./test 0x9b63000 0x9b63004 0x9b630cc 200+4=204 [root@monitor 10211]# cat maps 00ba3000-00bc1000 r-xp 00000000 ca:01 396396 /lib/ld-2.12.so 00bc1000-00bc2000 r--p 0001d000 ca:01 396396 /lib/ld-2.12.so 00bc2000-00bc3000 rw-p 0001e000 ca:01 396396 /lib/ld-2.12.so 00bc5000-00d55000 r-xp 00000000 ca:01 396343 /lib/libc-2.12.so 00d55000-00d56000 ---p 00190000 ca:01 396343 /lib/libc-2.12.so 00d56000-00d58000 r--p 00190000 ca:01 396343 /lib/libc-2.12.so 00d58000-00d59000 rw-p 00192000 ca:01 396343 /lib/libc-2.12.so 00d59000-00d5c000 rw-p 00000000 00:00 0 00d7c000-00d7d000 r-xp 00000000 00:00 0 [vdso] 08048000-08049000 r-xp 00000000 ca:01 1065171 /root/test 08049000-0804a000 rw-p 00000000 ca:01 1065171 /root/test 09b63000-09b64000 rw-p 00000000 00:00 0 [heap] f7706000-f7707000 rw-p 00000000 00:00 0 f770e000-f7710000 rw-p 00000000 00:00 0 ff9d7000-ff9ec000 rw-p 00000000 00:00 0 [stack] ------------------------------------------------------------------------------------------- 释放空间:SIZE为负,释放空间 #include <stdio.h> #include <stdlib.h> main(){ int *p1=sbrk(4); int *p2=sbrk(200); int *p3=sbrk(-4); int *p4=sbrk(-4); int *p5=sbrk(-4); printf("%p ,p1"); printf("%p ",p2); printf("%p ",p3); printf("%p ",p4); printf("%p ",p5); printf("%d ",getpid()); } [root@monitor ~]# ./test 0x98d3000 0x98d3004 0x98d30cc 0x98d30c8 0x98d30c4 10245 ------------------------------------------------------------------------ #include <stdio.h> #include <stdlib.h> main() { //brk绝对移动 int *p=sbrk(0); brk(p+1); // P+1是地址 后台地址 *p=800; brk(p); 移动到P处, 后台地址 *p=99; //Segmentation fault } [root@monitor ~]# ./test Segmentation fault ---------------------------------------- #include <stdio.h> #include <stdlib.h> main() { int *a=malloc(4); *a=9999; printf("%p ",a); while(1); } [root@monitor ~]# ./test 0x209f010 #include <stdio.h> #include <stdlib.h> main() { int *a=(int *)0x209f010; *a=9999; printf("%d ",*a); while(1); } [root@monitor ~]# ./test 0x1cce010 ------------------------------------- [root@monitor ~]# man brk BRK(2) Linux Programmer’s Manual BRK(2) NAME brk, sbrk - change data segment size SYNOPSIS #include <unistd.h> int brk(void *addr); // void *sbrk(intptr_t increment); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): brk(), sbrk(): _BSD_SOURCE || _SVID_SOURCE || _XOPEN_SOURCE >= 500 DESCRIPTION brk() and sbrk() change the location of the program break, which defines the end of the process’s data seg- ment (i.e., the program break is the first location after the end of the uninitialized data segment). Increasing the program break has the effect of allocating memory to the process; decreasing the break deallo- cates memory. brk() sets the end of the data segment to the value specified by addr, when that value is reasonable, the system has enough memory, and the process does not exceed its maximum data size (see setrlimit(2)). sbrk() increments the program’s data space by increment bytes. Calling sbrk() with an increment of 0 can be used to find the current location of the program break. RETURN VALUE On success, brk() returns zero. On error, -1 is returned, and errno is set to ENOMEM. (But see Linux Notes below.) On success, sbrk() returns the previous program break. (If the break was increased, then this value is a pointer to the start of the newly allocated memory). On error, (void *) -1 is returned, and errno is set to ENOMEM. 1. #include <stdio.h> #include <unistd.h> main() { int *p=sbrk(4); //分配4个字节的整数空间 *p=8888; printf("%d ",*p); } [root@monitor ~]# ./test 8888 2. #include <stdio.h> #include <unistd.h> main() { int *p=sbrk(0); *p=8888; } 3. #include <stdio.h> #include <unistd.h> main() { int *p=sbrk(0); //返回没有映射的空间首地址 brk(p+1); // P+1分配空间 *p=8888; //赋值 brk(p); // *p=8888; } ------------------------------------------------------------------------------- http://blog.csdn.net/sgbfblog/article/details/7772153 http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201210975312473/ int brk(void *addr); //后台指针移动 void *sbrk(intptr_t increment); // 后台一个地址值+increment brk() 这个函数的参数是一个地址,假如你已经知道了堆的起始地址,还有堆的大小, 那么你就可以据此修改 brk() 中的地址参数已达到调整堆的目的。 实际上,在应用程序中,基本不直接使用这两个函数,取而代之的是 malloc() 一类函数,这一类库函数的执行效率会更高。 还需要注意一点,当使用 malloc() 分配过大的空间,比如超出 0x20ff8 这个常数(在我的系统(Fedora15)上是这样, 别的系统可能会有变)时,malloc 不再从堆中分配空间,而是使用 mmap() 这个系统调用从映射区寻找可用的内存空间 #include <stdio.h> #include <unistd.h> int main() { /*分配10个字节的空间,返回该空间的首地址*/ void* p = sbrk(12); void* p2 = sbrk(4); void* p3 = sbrk(4); void* p4 = sbrk(4); printf("p=%p ", p); printf("p2=%p ", p2); printf("p3=%p ", p3); printf("p4=%p ", p4); /*用参数为0来获取未分配空间的开始位置*/ void* p0 = sbrk(0); printf("p0=%p ", p0); void* p5 = sbrk(-4); printf("p5=%p ", p5); printf("pid=%d ", getpid()); sleep(10); /*当释放到一个页面的开始位置时,整个页面会被操作系统回收*/ sbrk(-20); while(1); } [root@monitor ~]# gcc test1.c -m32 -o test [root@monitor ~]# ./test p=0x8e2c000 p2=0x8e2c00c p3=0x8e2c010 p4=0x8e2c014 p0=0x8e2c018 p5=0x8e2c018 pid=10679 http://blog.csdn.net/sgbfblog/article/details/7772153 http://www.cnblogs.com/runnyu/tag/apue/ http://bbs.chinaunix.net/thread-4066412-1-1.html 内存分配: 智能指针 stl malloc: new brk/sbrk:结构简单,数据量大 异常处理: int brk(void *) void * sbrk() 如果成功brk返加0,sbrk返加指针 失败brk返回-1, sbrk返回 (void *)-1 #include <string.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> extern int errno; //3种 int main() { void *p=sbrk(1000000*10000); if(p==(void*)-1) { printf("err! "); perror("hello!"); //1种 printf("%m "); //2种 printf("::%s ",strerror(errno)); //3种 } } string:string.h cstring mem: malloc memset memcmp memcpy bzero error: io: time: cast :

    [root@monitor ~]# vi test.c

    
    


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

    
    

    main()
    {
    char *p3=0;
    int *p=sbrk(4);
    int *p2=sbrk(4);
    int *p1=brk(p2);
    int *p4=sbrk(0);
    int *p5=sbrk(4);
    printf("%p ",p);
    printf("%p ",p2);
    printf("%p ",p1);
    printf("%p ",p4);
    printf("%p ",p5);
    *p3=10;

    [root@monitor ~]# ./test
    0x79a000
    0x79a004
    (nil)
    0x79a004
    0x79a004
    Segmentation fault

     
  • 相关阅读:
    wdcp升级php和mysql
    centos安装中文支持(转)
    centos5.5用phpstudy一键安装配置虚拟主机后,yum配置代理服务器squid
    http status 汇总
    关于html标签元素的data-*属性
    [分 享] PHPCMS V9 更换域名,附件地址无法批更新(更换变便)问题>解决方法!!
    svn服务器配置小记
    Camstar Portal modeling user guid --设置本地时间
    msdn webcast 下载地址整理
    mvc 项目下 webservice 程序无法运行
  • 原文地址:https://www.cnblogs.com/zengkefu/p/5510157.html
Copyright © 2011-2022 走看看