内存


1.  内存映射

cpu有32个引脚可以传递信息,通过32位高低电平将地址信息通过地址总线传到内存,32位地址空间就是从0x00000000 到0xFFFFFFFF。

进程并不能直接访问物理内存,而是访问虚拟内存,操作系统通过也页表将虚拟地址映射为物理地址,C程序中表达的内存地址(&a),都是虚拟内存地址。将不同进程的虚拟内存映射到相同的物理内存,也就实现了共享内存。为了减小页表的大小,一般是按页来记录对应关系,而不是按字节来记录对应关系,一个页大小是4kb。根据页的映射关系和偏移量可以确定字节的对应关系。页编号用16进制地址的前五位表示。多级分页就是将页编号进一步分为两级,根据第一级页表确定第二级页表的位置,二级页表中记录有真正的物理地址和偏移量。

每个进程都运行在虚拟地址空间中。每一个进程拥有一套属于它自己的页表。

2. Linux采用了伙伴算法来解决上述难题

内存组织:

把所有的空闲页按照大小分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024 bit大小的连续页的页块。最大可以申请1024个连续页,对应4MB大小的连续内存。

内存申请:

假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。

内存释放:

页框块在释放时,内核会主动将两个互为伙伴的页框块合并为一个较大的页框块,成功后会试图寻找伙伴并合并为更大的内存块,直至块的大小超过上限或者没有伙伴为止。互为伙伴的两个内存块必须符合以下条件:

1、两个块具有相同的大小;

2、两个块的物理地址连续;

3、第一个快的物理地址是两个块大小的整数倍。

3.大小端

内存高地址:地址越大

内存低地址:地址越小

数据高低字节:左高右低

小端模式:高地址存高字节

大端模式:高地址存低字节

例如:存储0x1234这个数

大小端判断代码

int main(){                                                                                              
  int value=0x00000101;                                                                                                                                                                                        
  char *p=(char *)&value;       //转化为char型指针,会截取value指针的首字节存进p中                                                                                     
  if(*p==0x01){                                                                                                          
    cout<<"small"<

 4.进制

4bit可以表示1位16进制

char a[2]={0X01,0X11}; //char型1字节,共8bit,16进制需要用2位表示。

int i=0X1111 0001;    //int4字节,共32bit,需要用8位16进制表示。

5. 共享内存

共享内存允许两个或更多进程共享一个给定的存储区,因为数据不需要再客户进程和服务进程之间复制。所以这是最快的一种IPC。
使用共享内存时需要注意:多个进程对共享内存的同步访问。
通常用信号量实现对共享内存的同步访问。
共享内存是最快的进程间通信的方案。不管是管道还是消息队列都必须把用户数据拷贝至内核,然后接收方再从内核中拷进来。共要进行两次拷贝,而共享内存一旦映射成功,一个进程向共享内存区写入了数据,其他共享这个内存的所有进程就能立刻看到其中的内容。

应用程序都可以通过Linux的mmap()系统调用(实现)请求这种映射。常用于加载动态库。

6.内存泄漏原因:指针覆盖

指针不能直接进行覆盖,需要先释放老内存内的数据。直接覆盖会成功,但是会导致老数据占用的内存不能被释放,导致内存泄漏。

指针销毁了,并不表示所指的空间也得到了释放 :内存泄露。

7. malloc和new

malloc

不初始化内存,返回类型是 void* 类型,只有一个参数来指定需要的内存大小。malloc开辟的内存需要使用 memset 进行初始化。使用free释放内存。

char *c;
c=(char*)malloc(10);
if(c==NULL)return -1;
memset(c,1,10); 

void *memset(void *s,  int c, size_t n);memset 只能初始化为0/-1,内存值分别是00000,111111,不能初始化为其他值(char型除外)。

new

返回指定类型的指针,并且可以自动计算所需要大小,使用delete释放内存,并执行对象的析构函数。

8. 零拷贝技术

减少拷贝次数的一种方法是调用mmap()来代替read调用,调用mmap(),内核会把数据通过DMA拷贝到内核缓冲区,然后不发往用户空间,而是直接将数据拷贝到socket缓冲区或者其他硬盘缓冲区。

一次read和write操作,会经过四次数据拷贝——从硬盘到内核缓冲区,从内核缓冲区到用户空间,从用户空间到内核缓冲区,从内核缓冲区到socket缓冲区。

使用mmap后需要经过相同过的四次上下文切换和三次数据拷贝,但是mmap调用返回时数据不再从内核拷贝到用户空间,少了一次内存拷贝。零拷贝技术是使用虚拟内存技术实现的物理内存共享机制。

sendfile 实现零拷贝

9.