C++学习笔记day24-----UC

内存管理—–堆
上一篇笔记中,讲完了栈段,数据段和代码段,现在补充堆的内容。
在C语言中,通过类似malloc这样的函数,手动向系统要求分配空间的时候,系统是从堆里分配出空间的。
代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
int main(void){
    void *p_num = malloc(1024);
    if(!p_num) return -1;
    printf("pid:%d\n",getpid());
    printf("p_num=%p\n",p_num);
    strcpy(p_num,"hello kitty!");
    getchar();
    free(p_num);
    printf("%s\n",(char *)p_num);
    p_num = NULL;
    return 0;
}
/--------------------------------------------------------
pid:6687
p_num=0x5584b62c3260
/--------------------------------------------------------
5584b5b54000-5584b5b55000 r-xp 00000000 08:01 1051666                    /home/linxin/UC/day05/malloc
5584b5d54000-5584b5d55000 r--p 00000000 08:01 1051666                    /home/linxin/UC/day05/malloc
5584b5d55000-5584b5d56000 rw-p 00001000 08:01 1051666                    /home/linxin/UC/day05/malloc
5584b62c3000-5584b62e4000 rw-p 00000000 00:00 0                          [heap]
7f844e3fd000-7f844e5d3000 r-xp 00000000 08:01 1572948                    /lib/x86_64-linux-gnu/libc-2.26.so
7f844e5d3000-7f844e7d3000 ---p 001d6000 08:01 1572948                    /lib/x86_64-linux-gnu/libc-2.26.so
7f844e7d3000-7f844e7d7000 r--p 001d6000 08:01 1572948                    /lib/x86_64-linux-gnu/libc-2.26.so
7f844e7d7000-7f844e7d9000 rw-p 001da000 08:01 1572948                    /lib/x86_64-linux-gnu/libc-2.26.so
7f844e7d9000-7f844e7dd000 rw-p 00000000 00:00 0
7f844e7dd000-7f844e804000 r-xp 00000000 08:01 1572880                    /lib/x86_64-linux-gnu/ld-2.26.so
7f844e9e2000-7f844e9e4000 rw-p 00000000 00:00 0
7f844ea04000-7f844ea05000 r--p 00027000 08:01 1572880                    /lib/x86_64-linux-gnu/ld-2.26.so
7f844ea05000-7f844ea06000 rw-p 00028000 08:01 1572880                    /lib/x86_64-linux-gnu/ld-2.26.so
7f844ea06000-7f844ea07000 rw-p 00000000 00:00 0
7fff832b3000-7fff832d4000 rw-p 00000000 00:00 0                          [stack]
7fff83325000-7fff83328000 r--p 00000000 00:00 0                          [vvar]
7fff83328000-7fff8332a000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

对比程序输出的malloc返回的地址和进程的映射图,可以得知,这块分配的内存来自于heap,也就是堆。
注意上述程序,是先调用free函数,释放申请的空间,然后通过printf来打印字符串。
首先,这样的做法是错误的。但是,是有可能输出正确的结果的。这是为什么?
需要明确free这个函数的机理。释放,不会改变指针的指向,也不会自动破坏那块空间内的数据。它把那块空间的权限从私有变成了共有。这个时候进程的其他部分也可以操作这块区域。如果,在读取之前没有破坏就是可以输出正确的结果的。

通过mmap将虚拟地址映射到物理地址
系统提供了两个函数,如下:

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能:将一块虚拟地址空间映射到物理地址上(为调用这个函数的进程创建一个新的虚拟地址空间映射)
参数:
addr:需要映射的虚拟地址空间的起始地址。如果为NULL,将由系统为其分配起始地址,这样使用使用很好。
length:从虚拟地址空间的起始地址,连续映射length长度的地址。虚拟地址空间的映射单位是页,一页有4k,所以无论length有多长,最终映射的地址必定是4k的整数倍。
port:决定了了对这个虚拟地址的访问方式
    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.   不可访问
    除了最后一种值,其他的三个值是可以连用的,通过按位或的方式,PS:PROT_EXEC|PROT_READ,表示可读可写。
flags:对映射区域的更新是否显示给其他进程(仅仅指那些和这个进程映射同一虚拟地址空间的进程),是否将更新同步到文件。
    MAP_SHARED:是。
    MAP_PRIVATE:否,也就是私有访问。
    这个参数的上述两个值,二选一,然后可以再和以下的值连用,用按位或的方式:
    MAP_ANONYMOUS:匿名访问,如果用这种方式访问,那么后续的fd参数将会被忽略,建议让fd = -1,offsert = 0

offset:在新版内核种,要求offset的值,必须是页的整数倍。
返回值:
如果分配成功,就会返回一个void指针类型,返回的是系统映射好的虚拟地址空间的首地址
如果分配失败,就会返回一个(void *)-1,并且设置errno的值

int munmap(void *addr, size_t length);
功能:解除虚拟地址空间到物理地址的映射
参数:
addr:mmap函数的返回值
length:映射的虚拟地址空间的长度

应用以下,代码如下:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

int main(void){
    //使用mmap将物理地址映射到进程的虚拟地址
    void *p_mmap = mmap(NULL,128,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    if(p_mmap == MAP_FAILED){
        perror("mapp failed,because of");
        return -1;
    }
    //解除映射
    printf("mmap success...\n");
    strcpy(p_mmap,"hello beijing\n");
    printf("%s\n",(char *)p_mmap);
    printf("pid:%d\n",getpid());
    printf("p_mapp:%p\n",p_mmap);
    printf("&p_mapp:%p\n",&p_mmap);
    getchar();
    munmap(p_mmap,128);
    getchar();
    return 0;
}
/-----------------------------------------------------
mmap success...
hello beijing

pid:7119
p_mapp:0x7f14f87a2000
&p_mapp:0x7ffd7d110f20

通过system call 完成文件的操作
系统提供了一组对文件操作的system call,先介绍open,close,如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags,...);
功能:打开一个文件
参数:
pathname:需要打开文件的路径
flags:设置这个文件的访问方式
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:读写
以上三个标记,三选一。上述选择的标记可以和零个或者多个以下标记连用,用按位或的方式
O_APPEND:以追加的方式打开文件,通过lseek(2)将文件指针的位置设置到文件末尾
O_CREAT:如果文件不存在,创建一个文件。并且会多一个参数mode用于设置文件的权限,权限的计算方法:mode & ~umask
O_EXCL:和O_CREAT连用,如果文件已经存在,报错。open(2)函数会失败。
O_TRUNC:如果文件已经存在其是一个一般文件,打开方式包含写权限,那么这个文件就会被清空
返回值:
成功,一个非负的整数,最小的,没有被使用的文件描述符
失败,返回-1,并设置errno

#include <unistd.h>
int close(int fd);
功能:关闭一个文件
参数:open(2)的返回值

那么什么是文件描述符呢?
文件
在内核中,为每一个进程都创建了一个PCB(进程控制块),在PCB中用一个结构体描述了当前进程使用的资源。其中有一个成员变量是文件类型的指针数组(file* [])。
当进程打开一个文件的时候,会创建一个文件类型(一个结构体类型)的实例,内核会将这个实例的地址存放在PCB用于描述资源的结构体中的指针数组(file* []),而这个数组的下标,就称为文件描述符。
当一个文件关闭的时候,会在数组中将这个文件的地址清零,但是不对文件的实例进行操作。如果数组里有两个元素记录了同一个文件地址,其中一个清零的时候,不影响另一个,系统也没有办法回收文件的实例。
文件地址在数组中清零后,那么数组的这个位置就空出来了,这个时候如果再open(2)一个文件,内核会从数组的0下标位置开始向下寻找一个空的位置用于存放新文件的地址。
这就是为什么open(2)的返回值是一个非负整数,最小的,没有使用的文件描述符。
另外,如果是在bash中启动的进程,那么PCB中的文件指针数组的前三个位置是固定的,分别是:
0:标准输入: STDIN_FILENO 键盘
1:标准输出: STDOUT_FILENO 显示器
2:标准错误输出: STDERR_FILENO 显示器

最后介绍一下一个文件的权限:
权限:
-rw-rw-r– 1 tarena tarena 3299 5月 4 15:00 day05.txt

最前面的占位符,代表文件的类型
-:一般文件
d:代表文件夹文件
c:字符设备文件
b:块设备文件
s:socket文件
l:软链接文件
p:管道文件
-**rw-**rw-r–
属主(u)权限
-rw-**rw-**r–
属组(g)权限
-rw-rw-r–
其他(o)权限

所有(a)

rwxrwxrwx
xxx 特殊含义,0
0777

umask
权限掩码,

etc$umask
0002

etc u m a s k 0033 e t c umask
0033

猜你喜欢

转载自blog.csdn.net/displaymessage/article/details/80199571
今日推荐