今天总结 linux的内存管理、进程的映射以及介绍两个函数 mmap();和malloc()函数
首先linux系统中,有物理地址、虚拟地址、虚拟内存这样的说法。
物理地址:物理内存上的地址。
虚拟地址:物理地址的映射。
虚拟内存:每个程序运行时都有自己的虚拟内存,32位机器一共4G的虚拟内存,其中0~(3G-1)叫做用户态,(3G-1)~4G叫做内核态。
在用户态中又分为: 代码段、数据段、栈段、堆(栈和堆中间为动态库,下面又有证明)。
下面我们来看当程序运行时,我们平时写的代码、声明的变量等都在那些段里。
补充两点:一、什么叫进程? 答:程序运行起来就是进程。
二、操作系统会管理每一个进程,通过一个id来管理,这个id叫做pid,通过函数getpid()来获得。然后通过查看/grop/id号码/maps 可以查看进程中用户态的分配的状态。
下面写一个简单的函数如下:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5 int main(){
6 int id=getpid();
7 int a=10;
8 int *p1=(int *)malloc(1024);
9 int *p2=(int *)malloc(1024);
10 int *p3=(int *)malloc(1024*4*35);
11 printf("pid=%d\n",id);
12 printf("a的地址=%p\n",&a);
13 printf("p1里的地址=%p\n",p1);
14 printf("p2里的地址=%p\n",p2);
15 printf("p3里的地址=%p\n",p3);
16 getchar();
17 return 0;
18 }
运行结果如下:
tarena@ubuntu:~/LIANXI/10.12$ ./a.out
pid=19468
a的地址=0x7fff794913c8
p1里的地址=0xa14010
p2里的地址=0xa14420
p3里的地址=0x7f5d46ba5010
采用 cat /proc/19468/maps得到如下结果:
tarena@ubuntu:~/LIANXI/10.12$ cat /proc/19468/maps
00400000-00401000 r-xp 00000000 08:01 1982110 /home/tarena/LIANXI/10.12/a.out
00600000-00601000 r--p 00000000 08:01 1982110 /home/tarena/LIANXI/10.12/a.out
00601000-00602000 rw-p 00001000 08:01 1982110 /home/tarena/LIANXI/10.12/a.out
00a14000-00a35000 rw-p 00000000 00:00 0 [heap]
7f5d465fe000-7f5d467b3000 r-xp 00000000 08:01 397377 /lib/x86_64-linux-gnu/libc-2.15.so
7f5d467b3000-7f5d469b3000 ---p 001b5000 08:01 397377 /lib/x86_64-linux-gnu/libc-2.15.so
7f5d469b3000-7f5d469b7000 r--p 001b5000 08:01 397377 /lib/x86_64-linux-gnu/libc-2.15.so
7f5d469b7000-7f5d469b9000 rw-p 001b9000 08:01 397377 /lib/x86_64-linux-gnu/libc-2.15.so
7f5d469b9000-7f5d469be000 rw-p 00000000 00:00 0
7f5d469be000-7f5d469e0000 r-xp 00000000 08:01 397357 /lib/x86_64-linux-gnu/ld-2.15.so
7f5d46ba5000-7f5d46bcc000 rw-p 00000000 00:00 0
7f5d46bdc000-7f5d46be0000 rw-p 00000000 00:00 0
7f5d46be0000-7f5d46be1000 r--p 00022000 08:01 397357 /lib/x86_64-linux-gnu/ld-2.15.so
7f5d46be1000-7f5d46be3000 rw-p 00023000 08:01 397357 /lib/x86_64-linux-gnu/ld-2.15.so
7fff79473000-7fff79494000 rw-p 00000000 00:00 0 [stack]
7fff795fe000-7fff79600000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
运行结果中a的地址在 stack中,红色的部分。
p1,p2的地址在 heap 中,蓝色的部分。
p3的地址在黄色的部分中。
以上这些地址都是虚拟地址。
我们把a的地址相减等于十进制的33;
p1和p2的地址相减等于十进制的33;
p3的地址相减等于十进制的39;
注:实际物理地址中以页为单位来映射,一页等于4K。
所以:
当使用malloc向系统发出请求,需要一块1024字节(1k)的空间的时候,系统会直接分配33页的空间。
当第二次向系统发出请求,又需要一块1024字节的空间的时候,系统会在第一次分配的33页中来分配。
当第三次想系统发出请求,又需要35页的时候,系统分配了39页,多出来的根据ubuntu的系统位数不同,用途不同。我的是64位的,暂时想不出他们的用途分别是 什么。。。有知道的欢迎指导! 32位的机器是多出两页,分别是与动态库的隔膜和存放一些信息的。
前面第一次想系统发出请求,系统直接给了33页(你只需要一页的1/4),这个就叫做malloc的缓冲机制。
下面介绍mmap()函数
它的形式参数有:
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能:在当前进程的虚拟地址里创建一个映射(与物理地址的映射)
参数:
addr:一般直接NULL
lehgth:指定映射区域的长度
prot: 对这块地址的操作选择 选择多个时用按位或 |
PROT_EXEC Pages may be executed.//可执行的
PROT_READ Pages may be read.//可读
PROT_WRITE Pages may be written.//可写
PROT_NONE Pages may not be accessed.//不能被访问的
flags:
MAP_SHARED 对映射区域的更新可以被其他映射同一块区域的进程看到,对内存的更新同步到文件
MAP_PRIVATE 对映射区域的更新不能被其他映射到同一区域的进程看到,不会同步 到文件
MAP_ANONYMOUS 不支持对任何文件的映射 fd 和 offset 被忽略
fd:-1
offset:0
返回值:指向所映射的地址的指针void 型。
函数如下:
1 #include<stdio.h>
5 #include<string.h>
6 #include <sys/mman.h>
7 int main(){
8 int prot=PROT_READ | PROT_WRITE;
9 int flags=MAP_PRIVATE | MAP_ANONYMOUS;
10 int fd=-1;
11 void *p=mmap(NULL,1024,prot,flags,fd,0);
12 strcpy(p,"hello world!");
13 printf("%s\n",(char *)p);
14 munmap(p,1024);
15 p=NULL;
17 return 0;
18 }
运行结果如下:
tarena@ubuntu:~/LIANXI/10.12$ gcc main.c
tarena@ubuntu:~/LIANXI/10.12$ ./a.out
hello world!
可以看出我们直接对p所指向的空间进行了操作。
前面介绍的malloc函数时向系统请求一块存储区,他的实质就是mmap()函数,系统在malloc()函数中调用了mmap()函数。