1 sbrk()/brk()
sbrk()/brk()底层都维护了一个位置
sbrk(increment)
当 increment 为正时,位置向后移动increment字节,同时返回移动之前的位置,相当于分配内存。
当 increment 为负时,位置向前移动increment字节,相当于释放内存,其返回值没有实际意义。
当 increment 为零时,不移动位置,只返回当前位置。
sbrk.c
#include <stdio.h>
#include <unistd.h>
int main(){
void* p1 = sbrk(4);//分配4字节内存
void* p2 = sbrk(4);//映射了一个内存页
void* p3 = sbrk(4);
void* p4 = sbrk(4);
printf("p1=%p,p2=%p,p3=%p,p4=%p\n",p1,p2,p3,p4);
sbrk(-4);//释放4字节
sbrk(-8);
void* cur = sbrk(0);//返回当前位置
printf("cur=%p\n",cur);
printf("pid=%d\n",getpid());
sbrk(4093);//4093+4 = 4097字节,超了
printf("超了\n"); sleep(15);
sbrk(-1);//回到一个内存页
printf("恢复到了一个内存页\n");
while(1);
}
sbrk()便于申请内存,但释放内存很不方便,brk()则相反,brk()参数是新的位置。
brk.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
void* p = sbrk(0);
int r = brk(p+4);//分配4个字节
if(r==-1)
perror("brk");
brk(p+8);//再次分配4个字节
brk(p+4);//释放4个字节
int* pi = p;
*pi = 100;
char* str = sbrk(0);//取当前位置
brk(str+10);//分配n字节
strcpy(str,"abcde");
printf("str=%s,*pi=%d\n",str,*pi);
brk(p);
}//练习:用sbrk()申请内存 brk()释放内存,实现
//存入一个int,一个double,一个字符串并打印
sbrk_brk.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
void* p = sbrk(4);
int* pi = p;
*pi = 100;
double* pd = sbrk(sizeof(double));
*pd = 3.14;
char* st = sbrk(10);
strcpy(st,"abcde");
printf("*pi=%d,*pd=%lf,st=%s\n",*pi,*pd,st);
brk(st);//释放字符串
brk(p);
}
2 mmap() / munmap()
很多的权限、选项、设置都可以用位或连接。
读 1 0 0
写 0 1 0
读写 1 1 0 (位或)
mmap.c
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
int main(){
void* p = mmap(0,//首地址,0代表内核指定
4,//分配4字节,但映射4096字节
PROT_READ|PROT_WRITE,//权限
MAP_PRIVATE|MAP_ANONYMOUS,//私有匿名 针对
0,0);//物理内存
if(p == MAP_FAILED){
perror("mmap");
}
int* pi = p; *pi = 100;
char* str = p+10; strcpy(str,"abcde");
char* ch = p;
int i;
for(i=0;i<20;i++){
printf("%d\n",ch[i]);
}
munmap(p,4);//解除映射
}
/*在这个程序中,打印的第一列是100,可以说明该计算机的存储格式是小端格式,100的十六进制是0x64,低位的存储在低地址,所以是小端。*/
练习:
使用mmap()/munmap()函数实现分配内存,存储某个员工的信息。员工是一个结构,成员包括id,name,salary。
memp.c
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
struct emp{
int id;
char name[20];
double salary;
};
int main(){
void* p = mmap(0,//首地址,0代表内核指定
sizeof(struct emp),
PROT_READ|PROT_WRITE,//权限
MAP_PRIVATE|MAP_ANONYMOUS,//私有匿名 针对
0,0);//文件描述符 //物理内存
if(p == MAP_FAILED)
perror("mmap"),exit(-1);//看成一个语句
struct emp* em = p;
em->id = 100; //(*em).id = 100;em[0].id=100
strcpy(em->name,"zhangfei");
em->salary = 12000.0;
printf("id=%d,name=%s,sal=%lf\n",em->id,
em->name,em->salary);
munmap(p,4);//解除映射
}
3 系统调用
用户空间的代码不能直接访问内核空间,因此内核空间提供了一系列的函数实现用户空间进入内核空间的接口(桥梁),这一系列的函数统称为 系统调用(System Call)。
程序由用户层进入内核层时,会把用户层的状态先封存起来,然后到内核层运行代码,运行结束以后,从内核层出来到用户层时,再把数据加载回来。因此,频繁的系统调用效率较低。
time 命令可以测试程序在用户层和内核层的运行时间。