您的位置:首页 > 编程语言

C 高级编程 2 内存管理

2016-05-19 21:09 337 查看
理解malloc的工作原理:

malloc使用一个数据结构(链表)来维护分配的空间。链表的构成:
分配的空间、上一个空间的地址、下一个空间的地址、以及本空间的信息等。
对malloc分配的空间不要越界访问,
因为容易破坏后台的链表维护结构,导致malloc/free/calloc/realloc不正常工作。

定位分配
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<new>
intmain()
{

chara[20];
int*p=new(a)int;

return0;

}

深入理解LINUX虚拟内存管理
#include<stdio.h>
intadd(inta,intb)
{

returna+b;

}
intman()

{

//int(*fun)(int)=(int(*)(int))add;
typedefint(*fun)(int);
funf=(fun)add;
intr=fun(20);
printf("%d\n",r);

}






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

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

5.函数调用空间的分配与释放与分配
总结:
1.C:函数执行的时候有自己的临时stack
c++:函数执行的时候有自己的临时stack+对象stack
2.函数的参数在临时stack,
3.通过积存器返回值(使用返回值顺数据)
4.通赤参数的返回值.(参数必须是指针)
指针指向的区域必须事先分配
5。如果参数返回指针,参数就是双指针.

---------------------------------------------------
intadd(int*a,int*b)
{return*a+*b;
}
main()
{
inta=20;
intb=30;
intr=add(&a,&b);
printf("%d\n",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()
{

inta=20;
intb=30;
intr=add(&a,&b);
printf("%d\n",r);

}

gcctest.c-m32-S

[root@monitor~]#cattest.s
.file"test.c"
.text
.globladd
.typeadd,@function
add:
pushl%ebp
movl%esp,%ebp
movl8(%ebp),%eax
movl(%eax),%edx
movl12(%ebp),%eax
movl(%eax),%eax
leal(%edx,%eax),%eax
popl%ebp
ret$8
.sizeadd,.-add
.section.rodata
.LC0:
.string"%d\n"
.text
.globlmain
.typemain,@function
main:
leal4(%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)
calladd
subl$8,%esp
movl%eax,-12(%ebp)
movl$.LC0,%eax
movl-12(%ebp),%edx
movl%edx,4(%esp)
movl%eax,(%esp)
callprintf
movl-4(%ebp),%ecx
leave
leal-4(%ecx),%esp
ret
.sizemain,.-main
.ident"GCC:(GNU)4.4.720120313(RedHat4.4.7-16)"
.section.note.GNU-stack,"",@progbits

[root@monitor~]#gcctest.c-m64-S
test.c:4:warning:‘stdcall’attributeignored

------------------------------------------------------

#include<stdio.h>
int__attribute__((cdecl))add(int*a,int*b)
{
return(*a)+(*b);

}
main()
{

inta=20;
intb=30;
intr=add(&a,&b);
printf("%d\n",r);

}

[root@monitor~]#gcctest.c-m32-S
[root@monitor~]#cattest.s
.file"test.c"
.text
.globladd
.typeadd,@function
add:
pushl%ebp
movl%esp,%ebp
movl8(%ebp),%eax
movl(%eax),%edx
movl12(%ebp),%eax
movl(%eax),%eax
leal(%edx,%eax),%eax
popl%ebp
ret
.sizeadd,.-add
.section.rodata
.LC0:
.string"%d\n"
.text
.globlmain
.typemain,@function
main:
pushl%ebp
movl%esp,%ebp
andl$-16,%esp
subl$32,%esp
movl$20,24(%esp)
movl$30,20(%esp)
leal20(%esp),%eax
movl%eax,4(%esp)
leal24(%esp),%eax
movl%eax,(%esp)
calladd
movl%eax,28(%esp)
movl$.LC0,%eax
movl28(%esp),%edx
movl%edx,4(%esp)
movl%eax,(%esp)
callprintf
leave
ret
.sizemain,.-main
.ident"GCC:(GNU)4.4.720120313(RedHat4.4.7-16)"
.section.note.GNU-stack,"",@progbits

[root@monitor~]#gcctest.c-m64-S
test.c:4:warning:‘cdecl’attributeignored
----------------------------------------------------------------
#include<stdio.h>
int__attribute__((fastcall))add(int*a,int*b)
{
return(*a)+(*b);

}
main()
{

inta=20;
intb=30;
intr=add(&a,&b);
printf("%d\n",r);

}

[root@monitor~]#gcctest.c-m64-S
test.c:4:warning:‘fastcall’attributeignored

----------------------------------------------------------------------
intadd(inta,intb)
{

returna+b;
}

intmain()
{

add(10,20);
}

[root@monitor~]#vitest1.c

intadd(inta,intb)
{

returna+b;
}

intmain()
{

add(10,20);
}

nm:test1.c:Fileformatnotrecognized
[root@monitor~]#gcc-m32test.c-otest
[root@monitor~]#nmtest
080495acd_DYNAMIC
08049678d_GLOBAL_OFFSET_TABLE_
080484ecR_IO_stdin_used
w_Jv_RegisterClasses
0804959cd__CTOR_END__
08049598d__CTOR_LIST__
080495a4D__DTOR_END__
080495a0d__DTOR_LIST__
08048594r__FRAME_END__
080495a8d__JCR_END__
080495a8d__JCR_LIST__
08049694A__bss_start
08049690D__data_start
080484a0t__do_global_ctors_aux
08048340t__do_global_dtors_aux
080484f0R__dso_handle
w__gmon_start__
0804849aT__i686.get_pc_thunk.bx
08049598d__init_array_end
08049598d__init_array_start
08048430T__libc_csu_fini
08048440T__libc_csu_init
U__libc_start_main@@GLIBC_2.0
08049694A_edata
0804969cA_end
080484ccT_fini
080484e8R_fp_hw
08048294T_init
08048310T_start
080483c4Tadd
08049694bcompleted.5989
08049690Wdata_start
08049698bdtor_idx.5991
080483a0tframe_dummy
080483dfTmain
Uprintf@@GLIBC_2.0
---------------------------------------------------------------------------
[root@monitor~]#vitest.c

intadd(int,int);
intadd(inta,intb)
{
returna+b;
}

intmain()
{intr;
r=add(10,20);
return1;
}

[root@monitor~]#g++test.c-otest
[root@monitor~]#nmtest
00000000006007a0d_DYNAMIC
0000000000600968d_GLOBAL_OFFSET_TABLE_
0000000000400678R_IO_stdin_used
w_Jv_RegisterClasses
0000000000400554T_Z3addii
0000000000600780d__CTOR_END__
0000000000600778d__CTOR_LIST__
0000000000600790D__DTOR_END__
0000000000600788d__DTOR_LIST__
0000000000400770r__FRAME_END__
0000000000600798d__JCR_END__
0000000000600798d__JCR_LIST__
0000000000600994A__bss_start
0000000000600990D__data_start
0000000000400630t__do_global_ctors_aux
00000000004004c0t__do_global_dtors_aux
0000000000400680R__dso_handle
w__gmon_start__
U__gxx_personality_v0@@CXXABI_1.3
0000000000600774d__init_array_end
0000000000600774d__init_array_start
0000000000400590T__libc_csu_fini
00000000004005a0T__libc_csu_init
U__libc_start_main@@GLIBC_2.2.5
0000000000600994A_edata
00000000006009a8A_end
0000000000400668T_fini
0000000000400428T_init
0000000000400470T_start
000000000040049ctcall_gmon_start
0000000000600998bcompleted.6349
0000000000600990Wdata_start
00000000006009a0bdtor_idx.6351
0000000000400530tframe_dummy
0000000000400569Tmain

--------------------------------------------------------

6.farnearhuge指针(windows)
near16
far32
huge综合

7.虚拟内存
1.一个程序不能访问另一个程序指向的空间
2.每个程序的开始地址0x80084000
3.程序使用的地址不是物理,而不能逻辑地址
4.辑辑编号是使用int4字节整型地址
(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:linuxshell命令ls
2:系统函数brk
3.标准C文档fopen
7.系统的编程帮助
man7tcp
man7icmp
man7udp
man7socket

分配空间,释放空间:
intbrk(void*end);分配空间,释放空间
void*sbrk(intsize);返回空间地址

应用:
1使用sbrk他配空间
2使用sbrk得到没有映射的虚拟地址
3使用brk他配空间
4使用brk释放空间

sbrk(intsize)

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\n",p1);
printf("%p\n",p2);
printf("%p\n",p3);
printf("%p\n",p4);
printf("%p\n",p4);
printf("%d\n",getpid());
while(1);
}

[root@monitor~]#gcctest.c-m32-otest
[root@monitor~]#./test
0x9e03000
0x9e03004
0x9e03008
0x9e0300c
0x9e03010
10148
//分配是有序的4个字节

[root@monitor10138]#cd/proc/10148
[root@monitor10148]#catmaps
00155000-002e5000r-xp00000000ca:01396343/lib/libc-2.12.so
002e5000-002e6000---p00190000ca:01396343/lib/libc-2.12.so
002e6000-002e8000r--p00190000ca:01396343/lib/libc-2.12.so
002e8000-002e9000rw-p00192000ca:01396343/lib/libc-2.12.so
002e9000-002ec000rw-p0000000000:000
00ba3000-00bc1000r-xp00000000ca:01396396/lib/ld-2.12.so
00bc1000-00bc2000r--p0001d000ca:01396396/lib/ld-2.12.so
00bc2000-00bc3000rw-p0001e000ca:01396396/lib/ld-2.12.so
00cd7000-00cd8000r-xp0000000000:000[vdso]
08048000-08049000r-xp00000000ca:011065171/root/test
08049000-0804a000rw-p00000000ca:011065171/root/test

09e03000-09e04000rw-p0000000000:000[heap]
f775a000-f775b000rw-p0000000000:000
f7762000-f7764000rw-p0000000000:000
ff9c0000-ff9d5000rw-p0000000000:000[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\n",p1);
pirntf("%p\n",p2);
printf("%p\n",p3);
printf("%d",getpid());
while(1);

}

[root@monitor~]#./test
0x9b63000
0x9b63004
0x9b630cc200+4=204

[root@monitor10211]#catmaps
00ba3000-00bc1000r-xp00000000ca:01396396/lib/ld-2.12.so
00bc1000-00bc2000r--p0001d000ca:01396396/lib/ld-2.12.so
00bc2000-00bc3000rw-p0001e000ca:01396396/lib/ld-2.12.so
00bc5000-00d55000r-xp00000000ca:01396343/lib/libc-2.12.so
00d55000-00d56000---p00190000ca:01396343/lib/libc-2.12.so
00d56000-00d58000r--p00190000ca:01396343/lib/libc-2.12.so
00d58000-00d59000rw-p00192000ca:01396343/lib/libc-2.12.so
00d59000-00d5c000rw-p0000000000:000
00d7c000-00d7d000r-xp0000000000:000[vdso]
08048000-08049000r-xp00000000ca:011065171/root/test
08049000-0804a000rw-p00000000ca:011065171/root/test

09b63000-09b64000rw-p0000000000:000[heap]
f7706000-f7707000rw-p0000000000:000
f770e000-f7710000rw-p0000000000:000

ff9d7000-ff9ec000rw-p0000000000:000[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\n,p1");
printf("%p\n",p2);
printf("%p\n",p3);
printf("%p\n",p4);
printf("%p\n",p5);
printf("%d\n",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;//Segmentationfault

}

[root@monitor~]#./test
Segmentationfault

----------------------------------------
#include<stdio.h>
#include<stdlib.h>
main()
{

int*a=malloc(4);
*a=9999;
printf("%p\n",a);
while(1);

}

[root@monitor~]#./test
0x209f010

#include<stdio.h>
#include<stdlib.h>
main()
{

int*a=(int*)0x209f010;
*a=9999;
printf("%d\n",*a);
while(1);

}

[root@monitor~]#./test
0x1cce010
-------------------------------------

[root@monitor~]#manbrk
BRK(2)LinuxProgrammer’sManualBRK(2)

NAME
brk,sbrk-changedatasegmentsize

SYNOPSIS
#include<unistd.h>

intbrk(void*addr);//

void*sbrk(intptr_tincrement);

FeatureTestMacroRequirementsforglibc(seefeature_test_macros(7)):

brk(),sbrk():_BSD_SOURCE||_SVID_SOURCE||_XOPEN_SOURCE>=500

DESCRIPTION
brk()andsbrk()changethelocationoftheprogrambreak,whichdefinestheendoftheprocess’sdataseg-
ment(i.e.,theprogrambreakisthefirstlocationaftertheendoftheuninitializeddatasegment).
Increasingtheprogrambreakhastheeffectofallocatingmemorytotheprocess;decreasingthebreakdeallo-
catesmemory.

brk()setstheendofthedatasegmenttothevaluespecifiedbyaddr,whenthatvalueisreasonable,the
systemhasenoughmemory,andtheprocessdoesnotexceeditsmaximumdatasize(seesetrlimit(2)).

sbrk()incrementstheprogram’sdataspacebyincrementbytes.Callingsbrk()withanincrementof0canbe
usedtofindthecurrentlocationoftheprogrambreak.

RETURNVALUE
Onsuccess,brk()returnszero.Onerror,-1isreturned,anderrnoissettoENOMEM.(ButseeLinuxNotes
below.)

Onsuccess,sbrk()returnsthepreviousprogrambreak.(Ifthebreakwasincreased,thenthisvalueisa
pointertothestartofthenewlyallocatedmemory).Onerror,(void*)-1isreturned,anderrnoissetto
ENOMEM.

1.#include<stdio.h>
#include<unistd.h>
main()
{

int*p=sbrk(4);//分配4个字节的整数空间
*p=8888;
printf("%d\n",*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/7772153http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201210975312473/

intbrk(void*addr);//后台指针移动
void*sbrk(intptr_tincrement);//后台一个地址值+increment
brk()这个函数的参数是一个地址,假如你已经知道了堆的起始地址,还有堆的大小,
那么你就可以据此修改brk()中的地址参数已达到调整堆的目的。
实际上,在应用程序中,基本不直接使用这两个函数,取而代之的是malloc()一类函数,这一类库函数的执行效率会更高。
还需要注意一点,当使用malloc()分配过大的空间,比如超出0x20ff8这个常数(在我的系统(Fedora15)上是这样,
别的系统可能会有变)时,malloc不再从堆中分配空间,而是使用mmap()这个系统调用从映射区寻找可用的内存空间

#include<stdio.h>
#include<unistd.h>
intmain()
{
/*分配10个字节的空间,返回该空间的首地址*/
void*p=sbrk(12);
void*p2=sbrk(4);
void*p3=sbrk(4);
void*p4=sbrk(4);
printf("p=%p\n",p);
printf("p2=%p\n",p2);
printf("p3=%p\n",p3);
printf("p4=%p\n",p4);
/*用参数为0来获取未分配空间的开始位置*/
void*p0=sbrk(0);
printf("p0=%p\n",p0);
void*p5=sbrk(-4);
printf("p5=%p\n",p5);
printf("pid=%d\n",getpid());
sleep(10);
/*当释放到一个页面的开始位置时,整个页面会被操作系统回收*/
sbrk(-20);
while(1);
}
[root@monitor~]#gcctest1.c-m32-otest
[root@monitor~]#./test
p=0x8e2c000
p2=0x8e2c00c
p3=0x8e2c010
p4=0x8e2c014
p0=0x8e2c018
p5=0x8e2c018
pid=10679
http://blog.csdn.net/sgbfblog/article/details/7772153http://www.cnblogs.com/runnyu/tag/apue/http://bbs.chinaunix.net/thread-4066412-1-1.html
内存分配:

智能指针
stl
malloc:
new
brk/sbrk:结构简单,数据量大

异常处理:
intbrk(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>
externinterrno;//3种

intmain()
{

void*p=sbrk(1000000*10000);
if(p==(void*)-1)
{printf("err!\n");
perror("hello!");//1种
printf("%m\n");//2种
printf("::%s\n",strerror(errno));//3种

}

}

string:string.hcstring
mem:mallocmemsetmemcmpmemcpybzero
error:
io:
time:
cast:

[root@monitor~]#vitest.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\n",p);
printf("%p\n",p2);
printf("%p\n",p1);
printf("%p\n",p4);
printf("%p\n",p5);
*p3=10;

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


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: