C 语言部分,就一再的讲内存管理,参看:C语言再学习 -- 再论内存管理 UNIX、Linux 部分还是要讲,足见其重要。
-A | -B --format = {sysv | berkeley}选择输出样式(默认为berkeley)
-o | -d | -x --radix = {8 | 10 | 16}以八进制,十进制或十六进制显示数字
-t -- totals 显示总大小(仅限伯克利)
-- common 显示* COM *符号的总大小
--target = 设置二进制文件格式
@ 从读取选项
-h --help显示此信息
-v --version显示程序的版本
# size
text data bss dec hex filename
1621 272 8 1901 76d a.out
int num = 10;
使此变量以其初值存放在初始化数据段中。
long sum[100];
使此变量存放在非初始化数据段中。
int main (void)
{
return 0;
}
# size
text data bss dec hex filename
1056 252 8 1316 524 a.out
const int i = 10;
const static j = 12;
int main (void)
{
const int n = 8;
int a = 9; //变量 a 在栈区;字面值常量 9 在代码段
a = 10; //可执行指令,在代码段
const static int num = 10;
return 0;
}
# size
text data bss dec hex filename
1100 252 8 1360 550 a.out
static int n = 10;
int num = 10;
int main (void)
{
static int i = 8;
return 0;
}
# size
text data bss dec hex filename
1056 264 8 1328 530 a.out
static int n;
int num;
int main (void)
{
static int i;
return 0;
}
# size
text data bss dec hex filename
1056 252 20 1328 530 a.out
#include
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
三个函数返回:若成功则为非空指针,若出错则为NULL
void free(void *ptr);
void * memset (void * p,int c,int n) ;
该函数可以将指定的内存空间按字节单位置为指定的字符 c。其中,p 为要清零的内存空间的首地址,c 为要设定的值,n 为被操作的内存空间的字节长度。如果要用memset清 0,变量 c 实参要为 0。
#include
#include
#include
int main (void)
{
int *p = (int *)malloc (sizeof (int));
if (NULL == p)
perror ("fail to malloc"), exit (1);
printf ("%d\n", *p);
memset (p, 0, sizeof (int));
printf ("%d\n", *p);
*p = 2;
printf ("%d\n", *p);
free (p);
p = NULL;
return 0;
}
输出结果:
0
0
2
int *p = (int *)calloc (SIZE, sizeof (int));
等同于
int *p = (int *)malloc (SIZE * sizeof (int));
#include
#include
#define SIZE 5
int main (void)
{
int i = 0;
int *p = (int *)calloc (SIZE, sizeof (int));
if (NULL == p)
perror ("fail to calloc"), exit (1);
for (i = 0; i < SIZE; i++)
p[i] = i;
for (i = 0; i < SIZE; i++)
printf ("p[%d] = %d\n", i, p[i]);
free (p);
p = NULL;
return 0;
}
输出结果:
p[0] = 0
p[1] = 1
p[2] = 2
p[3] = 3
p[4] = 4
#include
#include
int main (void)
{
int i = 0;
int* pn = (int*)malloc(5*sizeof(int));
printf ("malloc %p\n",pn);
for(i = 0;i < 5; i++)
pn[i]=i;
pn = (int*)realloc(pn, 10*sizeof(int));
printf("realloc %p\n",pn);
for(i = 5;i < 10; i++)
pn[i]=i;
for(i=0;i<10;i++)
printf("%d ",pn[i]);
printf ("\n");
free(pn);
return 0;
}
输出结果:
malloc 0x8d59008
realloc 0x8d59008
0 1 2 3 4 5 6 7 8 9
int* p = new int;
delete p;
int* p = new int;
int* p = new int ();
int* p = new int (100);
int* p = new int[4] {10, 20, 30, 40};
delete[] p;
某些 C++ 实现,用 new 操作符动态分配数组时,会在数组首元素前面多分配 4 或 8 个字节,用以存放数组的长度。int* p = new int;
delete p;
delete p; // 核心转储
int* p = new int;
delete p;
p = NULL;
delete p; // 什么也不做
char* p = (char*)malloc (0xFFFFFFFF);
if (p == NULL) {
cerr << "内存分配失败!" << endl;
exit (-1);
}
try {
char* p = new char[0xFFFFFFFF];
}
catch (bad_alloc& ex) {
cerr << "内存分配失败!" << endl;
exit (-1);
}
#include
#include
using namespace std;
struct Student
{
string name;
int age;
};
int main (void)
{
int *p1 = new int;
*p1 = 1234;
++*p1;
cout << *p1 << endl;
delete p1;
p1 = new int ();
cout << *p1 << endl;
delete p1;
p1 = new int (1234);
cout << *p1 << endl;
delete p1;
p1 = new int[4] {1, 2, 3, 4};
for (size_t i = 0; i < 4; i++)
cout << p1[i] << ' ';
cout << endl;
delete[] p1;
try {
p1 = new int [3];
delete[] p1;
p1 = NULL;
}
catch (exception& ex) {
cout << ex.what() << endl;
perror ("fail to new");
return -1;
}
int (*p2)[4] = new int[3][4];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 4; j++)
p2[i][j] = (i + 1) * 10 + j + 1;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++)
cout << p2[i][j] << ' ';
cout << endl;
}
delete[] p2;
int (*p3)[4][5] = new int[3][4][5];
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 4; ++j)
for (int k = 0; k < 5; ++k)
p3[i][j][k] = (i+1)*100+(j+1)*10+k+1;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 5; ++k)
cout << p3[i][j][k] << ' ';
cout << endl;
}
cout << endl;
}
delete[] p3;
string *p4 = new string;
cout << '[' << *p4 << ']' << endl;
delete p4;
p4 = new string ("string");
cout << *p4 << endl;
delete p4;
p4 = new string[3] {"beijing", "shanghai", "shenzhen"};
for (int i = 0; i < 3; i++)
cout << p4[i] << ' ';
cout << endl;
delete[] p4;
Student *p5 = new Student;
p5->name = "mayun";
p5->age = 56;
cout << p5->name << "," << p5->age << endl;
delete p5;
return 0;
}
输出结果:
1235
0
1234
1 2 3 4
11 12 13 14
21 22 23 24
31 32 33 34
111 112 113 114 115
121 122 123 124 125
131 132 133 134 135
141 142 143 144 145
211 212 213 214 215
221 222 223 224 225
231 232 233 234 235
241 242 243 244 245
311 312 313 314 315
321 322 323 324 325
331 332 333 334 335
341 342 343 344 345
[]
string
beijing shanghai shenzhen
mayun,56
#include
int getpagesize(void);
#include
#include
int main (void)
{
printf ("page size = %d Byte\n", getpagesize ());
return 0;
}
输出结果:
page size = 4096 Byte
#include
void *sbrk(intptr_t increment);
//sbrk函数的使用
#include
#include
#include
int main(void)
{
//申请4个字节的动态内存
void* p1 = sbrk(4);
void* p2 = sbrk(4);
void* p3 = sbrk(4);
printf("p1 = %p,p2 = %p,p3 = %p\n",p1,p2,p3);
printf("------------------\n");
//获取内存块当前的末尾位置+1
void* cur = sbrk(0);
printf("cur = %p\n",cur);//p3+4
//释放4个字节的内存空间
void* p4 = sbrk(-4);
printf("p4 = %p\n",p4);//p3+4
//获取内存块的当前位置
cur = sbrk(0);
printf("cur = %p\n",cur);//p3
printf("--------------\n");
//再次释放4个字节的内存
p4 = sbrk(-4);
printf("p4 = %p\n",p4);//p3
cur = sbrk(0);
printf("cur = %p\n",cur);//p2
printf("--------------\n");
//目前就剩下4个字节
printf("当前进程PID:%d\n",getpid());
printf("目前进程拥有4个字节\n");
getchar();
sbrk(4093);
printf("申请了4093个字节的内存,恰好超过了1个内存页\n");
getchar();
sbrk(-1);
printf("释放了1个字节的内存,回到了1个内存页的范围\n");
getchar();
return 0;
}
输出结果:
p1 = 0x8141000,p2 = 0x8141004,p3 = 0x8141008
------------------
cur = 0x814100c
p4 = 0x814100c
cur = 0x8141008
--------------
p4 = 0x8141008
cur = 0x8141004
--------------
当前进程PID:4787
目前进程拥有4个字节
1
申请了4093个字节的内存,恰好超过了1个内存页
释放了1个字节的内存,回到了1个内存页的范围
2
#include
int brk(void *addr);
#include
#include
#include
int main (void)
{
//获取一个有效位置
void *p = sbrk (0);
printf ("p = %p\n", p);
//使用 brk 函数申请 4 个字节内存
int res = brk (p + 4);
if (-1 == res)
perror ("fail to brk"), exit (1);
void *cur = sbrk (0);
printf ("cur = %p\n", cur);
//申请 4 个字节
brk (p + 8);
cur = sbrk (0);
printf ("cur = %p\n", cur);
//释放了 4 个字节
brk (p + 4);
cur = sbrk (0);
printf ("cur = %p\n", cur);
//释放了所有字节
brk (p);
cur = sbrk (0);
printf ("cur = %p\n", cur);
return 0;
}
输出结果:
p = 0x98cc000
cur = 0x98cc004
cur = 0x98cc008
cur = 0x98cc004
cur = 0x98cc000
//使用sbrk函数和brk函数操作内存
#include
#include
#include
int main()
{
void* cur=sbrk(0); //动态分配内存 和malloc一样的用法
printf("cur=%p\n",cur);
int* pi=(int*)sbrk(sizeof(int));
*pi=100;
double* pd=(double*)sbrk(sizeof(double));
*pd=3.1415926;
char* pc=(char*)sbrk(sizeof(char));
strcpy(pc,"hello");
printf("*pi=%d,*pd=%lf,pc=%s\n",*pi,*pd,pc);
//释放所有的动态内存
brk(pi);//申请开始的
cur=sbrk(0);
printf("cur=%p\n",cur);
return 0;
}
输出结果:
cur=0x9b4c000
*pi=100,*pd=3.141593,pc=hello
cur=0x9b4c000
DESCRIPTION
brk() and sbrk() change the location of the program break, which defines the end of the process's data segment (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 allo‐
cating memory to the process; decreasing the break deallocates 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 cur‐
rent location of the program break.
#include
#include
#include
int main (void)
{
void *cur = sbrk (0);
printf ("cur = %p\n", cur);
void *ptr = malloc (100);
cur = sbrk (0);
printf ("cur = %p\n", cur);
printf ("ptr = %p\n", ptr);
free (ptr);
// ptr = NULL;
cur = sbrk (0);
printf ("cur = %p\n", cur);
printf ("ptr = %p\n", ptr);
return 0;
}
输出结果:
cur = 0x9d9b000
cur = 0x9dbc000
ptr = 0x9d9b008
cur = 0x9dbc000
ptr = 0x9d9b008
#include
#include
#include
int main()
{
void *cur = sbrk (0);
printf ("cur=%p\n", cur);
int* pi=(int*)malloc(4);
printf("pi=%p\n",pi);
//故意越界使用一下内存
*(pi+10000)=250;
printf("越界存放的数据是:%d\n",*(pi+10000));//250
//故意超出33个内存页的范围
*(pi+1025*33)=250;
//*(pi+33789)=250;
printf("越界存放的数据是:%d\n",*(pi+1025*33));//int类型 加1等于4个字节
//十六进制的21000就是33个内存页也就是十六进制的1000就是1个内存页
while(1);
return 0;
}
输出结果:
cur=0x8966000
pi=0x8966008
越界存放的数据是:250
段错误 (核心已转储)
#dmesg
[131882.277528] test[5977]: segfault at 94ee08c ip 08048476 sp bf8dfcd0 error 6 in test[8048000+1000]
错误代码: error 6
#define ENXIO 6 /* No such device or address */
即,没有驱动或地址。
#include
#include
#include
int main()
{
void *cur = sbrk (0);
printf ("cur = %p\n", cur);
//获取当前进程的进程号
printf("进程号是:%d\n",getpid());
int* pi=(int*)malloc(4);
printf("pi=%p\n",pi);
//故意越界使用一下内存
*(pi+10000)=250;
printf("越界存放的数据是:%d\n",*(pi+10000));//250
//故意超出33个内存页的范围
*(pi+33789)=250;
printf("越界存放的数据是:%d\n",*(pi+33789));//int类型 加1等于4个字节
//十六进制的21000就是33个内存页也就是十六进制的1000就是1个内存页
while (1);
return 0;
}
输出结果:
cur = 0x9e6c000
进程号是:6393
pi=0x9e6c008
越界存放的数据是:250
越界存放的数据是:250
//在另一个终端查看:
# cat /proc/6393/maps
08048000-08049000 r-xp 00000000 08:01 2102158 /home/tarena/project/c_test/a.out
08049000-0804a000 r--p 00000000 08:01 2102158 /home/tarena/project/c_test/a.out
0804a000-0804b000 rw-p 00001000 08:01 2102158 /home/tarena/project/c_test/a.out
09e6c000-09e8d000 rw-p 00000000 00:00 0 [heap]
b755c000-b755d000 rw-p 00000000 00:00 0
b755d000-b76fc000 r-xp 00000000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so
b76fc000-b76fe000 r--p 0019f000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so
b76fe000-b76ff000 rw-p 001a1000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so
b76ff000-b7702000 rw-p 00000000 00:00 0
b7713000-b7716000 rw-p 00000000 00:00 0
b7716000-b7717000 r-xp 00000000 00:00 0 [vdso]
b7717000-b7737000 r-xp 00000000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so
b7737000-b7738000 r--p 0001f000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so
b7738000-b7739000 rw-p 00020000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so
bfe48000-bfe69000 rw-p 00000000 00:00 0 [stack]
#include
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
#include
int munmap(void *addr, size_t length);
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int fd;
struct stat sb;
fd = open("/etc/passwd", O_RDONLY); /*打开/etc/passwd */
char *start = (char*)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if(start == MAP_FAILED) /* 判断是否映射成功 */
perror ("mmap"), exit (1);
printf("%s", start);
munmap(start, sb.st_size); /* 解除映射 */
close (fd);
return 0;
}
输出结果:
同 cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
....
# time ./a.out
real 0m0.004s
user 0m0.000s
sys 0m0.000s
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column
设置返回值的输出位置.默认 为40.
-e expr
指定一个表达式,用来控制如何跟踪.格式如下:
[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:
-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none.
注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
-e trace=set
只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file
只跟踪有关文件操作的系统调用.
-e trace=process
只跟踪有关进程控制的系统调用.
-e trace=network
跟踪与网络有关的所有系统调用.
-e strace=signal
跟踪所有与系统信号有关的 系统调用
-e trace=ipc
跟踪所有与进程通讯有关的系统调用
-e abbrev=set
设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set
将指 定的系统调用的参数以十六进制显示.
-e signal=set
指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set
输出从指定文件中读出 的数据.例如:
-e read=3,5
-e write=set
输出写入到指定文件中的数据.
-o filename
将strace的输出写入文件filename
-p pid
跟踪指定的进程pid.
-s strsize
指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username
以username 的UID和GID执行被跟踪的命令