精华内容
下载资源
问答
  • 共享内存实现原理
    万次阅读
    2018-09-28 18:02:39

    共享内存的使用实现原理(必考必问,然后共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少?)

    nmap函数要求内核创建一个新额虚拟存储器区域,最好是从地质start开始的一个区域,并将文件描述符fd指定对象的一个连续的片(chunk)映射到这个新的区域。

     SHMMNI为128,表示系统中最多可以有128个共享内存对象。

     

    共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。

    采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

    Linux的2.2.x内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及系统V共享内存。linux发行版本如Redhat 8.0支持mmap()系统调用及系统V共享内存,但还没实现Posix共享内存,本文将主要介绍mmap()系统调用及系统V共享内存API的原理及应用。

    一、内核怎样保证各个进程寻址到同一个共享内存区域的内存页面

    1、page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的。

    2、文件与address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。

    3、进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。

    4、对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区(swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表。
    注:对于映射普通文件情况(非共享映射),缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。如果没有找到,则说明文件数据还没有读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新。

    5、所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。
    注:一个共享内存区域可以看作是特殊文件系统shm中的一个文件,shm的安装点在交换区上。

    上面涉及到了一些数据结构,围绕数据结构理解问题会容易一些。

    二、mmap()及其相关系统调用

    mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

    注:实际上,mmap()系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

    1、mmap()系统调用形式如下:

    void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
    参数fd为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。prot 参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)。flags由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。offset参数一般设为0,表示从文件头开始映射。参数addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。这里不再详细介绍mmap()的参数,读者可参考mmap()手册页获得进一步的信息。

    2、系统调用mmap()用于共享内存的两种方式:

    (1)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用mmap();典型调用代码如下: 

            fd=open(name, flag, mode);
    if(fd<0)
            ...
            
    ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0); 通过mmap()实现共享内存的通信方式有许多特点和要注意的地方,我们将在范例中进行具体说明。 

    (2)使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。
    对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可,参见范例2。

    3、系统调用munmap()

    int munmap( void * addr, size_t len )
    该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。

    4、系统调用msync()

    int msync ( void * addr , size_t len, int flags)
    一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。

    三、mmap()范例

    下面将给出使用mmap()的两个范例:范例1给出两个进程通过映射普通文件实现共享内存通信;范例2给出父子进程通过匿名映射实现共享内存。系统调用mmap()有许多有趣的地方,下面是通过mmap()映射普通文件实现进程间的通信的范例,我们通过该范例来说明mmap()实现共享内存的特点及注意事项。

    范例1:两个进程通过映射普通文件实现共享内存通信

    范例1包含两个子程序:map_normalfile1.c及map_normalfile2.c。编译两个程序,可执行文件分别为map_normalfile1及map_normalfile2。两个程序通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。map_normalfile2试图打开命令行参数指定的一个普通文件,把该文件映射到进程的地址空间,并对映射后的地址空间进行写操作。map_normalfile1把命令行参数指定的文件映射到进程地址空间,然后对映射后的地址空间执行读操作。这样,两个进程通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。

    下面是两个程序代码:

    /*-------------map_normalfile1.c-----------*/
    #include <sys/mman.h>;
    #include <sys/types.h>;
    #include <fcntl.h>;
    #include <unistd.h>;
    typedef struct{
            char name[4];
            int  age;
    }people;

    main(int argc, char** argv) // map a normal file as shared mem:
    {
            int fd,i;
            people *p_map;
            char temp;
            
            fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
            lseek(fd,sizeof(people)*5-1,SEEK_SET);
            write(fd,"",1);
            
            p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
            close( fd );
            temp = 'a';
            for(i=0; i<10; i++)
            {
                    temp += 1;
                    memcpy( ( *(p_map+i) ).name, &temp,2 );
                    ( *(p_map+i) ).age = 20+i;
            }
            printf(" initialize over /n ");
            sleep(10);

            munmap( p_map, sizeof(people)*10 );
            printf( "umap ok /n" );
    }

    /*-------------map_normalfile2.c-----------*/
    #include <sys/mman.h>;
    #include <sys/types.h>;
    #include <fcntl.h>;
    #include <unistd.h>;
    typedef struct{
            char name[4];
            int  age;
    }people;

    main(int argc, char** argv)        // map a normal file as shared mem:
    {
            int fd,i;
            people *p_map;
            fd=open( argv[1],O_CREAT|O_RDWR,00777 );
            p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
            for(i = 0;i<10;i++)
            {
            printf( "name: %s age %d;/n",(*(p_map+i)).name, (*(p_map+i)).age );

            }
            munmap( p_map,sizeof(people)*10 );
    }

    map_normalfile1.c首先定义了一个people数据结构,(在这里采用数据结构的方式是因为,共享内存区的数据往往是有固定格式的,这由通信的各个进程决定,采用结构的方式有普遍代表性)。map_normfile1首先打开或创建一个文件,并把文件的长度设置为5个people结构大小。然后从mmap()的返回地址开始,设置了10个people结构。然后,进程睡眠10秒钟,等待其他进程映射同一个文件,最后解除映射。

    map_normfile2.c只是简单的映射一个文件,并以people数据结构的格式从mmap()返回的地址处读取10个people结构,并输出读取的值,然后解除映射。

    分别把两个程序编译成可执行文件map_normalfile1和map_normalfile2后,在一个终端上先运行./map_normalfile2 /tmp/test_shm,程序输出结果如下:

    initialize over
    umap ok

    在map_normalfile1输出initialize over 之后,输出umap ok之前,在另一个终端上运行map_normalfile2 /tmp/test_shm,将会产生如下输出(为了节省空间,输出结果为稍作整理后的结果):

    name: b        age 20;        name: c        age 21;        name: d        age 22;        name: e        age 23;        name: f        age 24;
    name: g        age 25;        name: h        age 26;        name: I        age 27;        name: j        age 28;        name: k        age 29;

    在map_normalfile1 输出umap ok后,运行map_normalfile2则输出如下结果:

    name: b        age 20;        name: c        age 21;        name: d        age 22;        name: e        age 23;        name: f        age 24;
    name:        age 0;        name:        age 0;        name:        age 0;        name:        age 0;        name:        age 0;

    从程序的运行结果中可以得出的结论

    1、 最终被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小;

    2、 可以用于进程通信的有效地址空间大小大体上受限于被映射文件的大小,但不完全受限于文件大小。打开文件被截短为5个people结构大小,而在map_normalfile1中初始化了10个people数据结构,在恰当时候(map_normalfile1输出initialize over 之后,输出umap ok之前)调用map_normalfile2会发现map_normalfile2将输出全部10个people结构的值,后面将给出详细讨论。
    注:在linux中,内存的保护是以页为基本单位的,即使被映射文件只有一个字节大小,内核也会为映射分配一个页面大小的内存。当被映射文件小于一个页面大小时,进程可以对从mmap()返回地址开始的一个页面大小进行访问,而不会出错;但是,如果对一个页面以外的地址空间进行访问,则导致错误发生,后面将进一步描述。因此,可用于进程间通信的有效地址空间大小不会超过文件大小及一个页面大小的和。

    3、 文件一旦被映射后,调用mmap()的进程对返回地址的访问是对某一内存区域的访问,暂时脱离了磁盘上文件的影响。所有对mmap()返回地址空间的操作只在内存中有意义,只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小。

    范例2:父子进程通过匿名映射实现共享内存

    #include <sys/mman.h>;
    #include <sys/types.h>;
    #include <fcntl.h>;
    #include <unistd.h>;
    typedef struct{
            char name[4];
            int  age;
    }people;
    main(int argc, char** argv)
    {
            int i;
            people *p_map;
            char temp;
            p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
            if(fork() == 0)
            {
                    sleep(2);
                    for(i = 0;i<5;i++)
                            printf("child read: the %d people's age is %d/n",i+1,(*(p_map+i)).age);
                    (*p_map).age = 100;
                    munmap(p_map,sizeof(people)*10); //实际上,进程终止时,会自动解除映射。
                    exit();
            }
            temp = 'a';
            for(i = 0;i<5;i++)
            {
                    temp += 1;
                    memcpy((*(p_map+i)).name, &temp,2);
                    (*(p_map+i)).age=20+i;
            }

            sleep(5);
            printf( "parent read: the first people,s age is %d/n",(*p_map).age );
            printf("umap/n");
            munmap( p_map,sizeof(people)*10 );
            printf( "umap ok/n" );
    }

    考察程序的输出结果,体会父子进程匿名共享内存:

    child read: the 1 people's age is 20
    child read: the 2 people's age is 21
    child read: the 3 people's age is 22
    child read: the 4 people's age is 23
    child read: the 5 people's age is 24

    parent read: the first people,s age is 100
    umap
    umap ok

    四、对mmap()返回地址的访问

    前面对范例运行结构的讨论中已经提到,linux采用的是页式管理机制。对于用mmap()映射普通文件来说,进程会在自己的地址空间新增一块空间,空间大小由mmap()的len参数指定,注意,进程并不一定能够对全部新增空间都能进行有效访问。进程能够访问的有效地址大小取决于文件被映射部分的大小。简单的说,能够容纳文件被映射部分大小的最少页面个数决定了进程从mmap()返回的地址开始,能够有效访问的地址空间大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。可用如下图示说明:





    注意:文件被映射部分而不是整个文件决定了进程能够访问的空间大小,另外,如果指定文件的偏移部分,一定要注意为页面大小的整数倍。下面是对进程映射地址空间的访问范例:

    #include <sys/mman.h>;
    #include <sys/types.h>;
    #include <fcntl.h>;
    #include <unistd.h>;
    typedef struct{
            char name[4];
            int  age;
    }people;

    main(int argc, char** argv)
    {
            int fd,i;
            int pagesize,offset;
            people *p_map;
            
            pagesize = sysconf(_SC_PAGESIZE);
            printf("pagesize is %d/n",pagesize);
            fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
            lseek(fd,pagesize*2-100,SEEK_SET);
            write(fd,"",1);
            offset = 0;        //此处offset = 0编译成版本1;offset = pagesize编译成版本2
            p_map = (people*)mmap(NULL,pagesize*3,PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
            close(fd);
            
            for(i = 1; i<10; i++)
            {
                    (*(p_map+pagesize/sizeof(people)*i-2)).age = 100;
                    printf("access page %d over/n",i);
                    (*(p_map+pagesize/sizeof(people)*i-1)).age = 100;
                    printf("access page %d edge over, now begin to access page %d/n",i, i+1);
                    (*(p_map+pagesize/sizeof(people)*i)).age = 100;
                    printf("access page %d over/n",i+1);
            }
            munmap(p_map,sizeof(people)*10);
    }

    如程序中所注释的那样,把程序编译成两个版本,两个版本主要体现在文件被映射部分的大小不同。文件的大小介于一个页面与两个页面之间(大小为:pagesize*2-99),版本1的被映射部分是整个文件,版本2的文件被映射部分是文件大小减去一个页面后的剩余部分,不到一个页面大小(大小为:pagesize-99)。程序中试图访问每一个页面边界,两个版本都试图在进程空间中映射pagesize*3的字节数。

    版本1的输出结果如下:

    pagesize is 4096
    access page 1 over
    access page 1 edge over, now begin to access page 2
    access page 2 over
    access page 2 over
    access page 2 edge over, now begin to access page 3
    Bus error                //被映射文件在进程空间中覆盖了两个页面,此时,进程试图访问第三个页面

    版本2的输出结果如下:

    pagesize is 4096
    access page 1 over
    access page 1 edge over, now begin to access page 2
    Bus error                //被映射文件在进程空间中覆盖了一个页面,此时,进程试图访问第二个页面

    结论:采用系统调用mmap()实现进程间通信是很方便的,在应用层上接口非常简洁。内部实现机制区涉及到了linux存储管理以及文件系统等方面的内容,可以参考一下相关重要数据结构来加深理解。在本专题的后面部分,将介绍系统v共享内存的实现。

    在共享内存(上)中,主要围绕着系统调用mmap()进行讨论的,本部分将讨论系统V共享内存,并通过实验结果对比来阐述两者的异同。系统V共享内存指的是把所有共享数据放在共享内存区域(IPC shared memory region),任何想要访问该数据的进程都必须在本进程的地址空间新增一块内存区域,用来映射存放共享数据的物理内存页面。

    系统调用mmap()通过映射一个普通文件实现共享内存。系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shmid_kernel结构联系起来的),后面还将阐述。

    1、系统V共享内存原理

    进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统V共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构注同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。

    注:每一个共享内存区都有一个控制结构struct shmid_kernel,shmid_kernel是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁,定义如下:

    struct shmid_kernel /* private to the kernel */
    {        
            struct kern_ipc_perm        shm_perm;
            struct file *                shm_file;
            int                        id;
            unsigned long                shm_nattch;
            unsigned long                shm_segsz;
            time_t                        shm_atim;
            time_t                        shm_dtim;
            time_t                        shm_ctim;
            pid_t                        shm_cprid;
            pid_t                        shm_lprid;
    };

    该结构中最重要的一个域应该是shm_file,它存储了将被映射文件的地址。每个共享内存区对象都对应特殊文件系统shm中的一个文件,一般情况下,特殊文件系统shm中的文件是不能用read()、write()等方法访问的,当采取共享内存的方式把其中的文件映射到进程地址空间后,可直接采用访问内存的方式对其访问。

    这里我们采用[1]中的图表给出与系统V共享内存相关数据结构:





    正如消息队列和信号灯一样,内核通过数据结构struct ipc_ids shm_ids维护系统中的所有共享内存区域。上图中的shm_ids.entries变量指向一个ipc_id结构数组,而每个ipc_id结构数组中有个指向kern_ipc_perm结构的指针。到这里读者应该很熟悉了,对于系统V共享内存区来说,kern_ipc_perm的宿主是shmid_kernel结构,shmid_kernel是用来描述一个共享内存区域的,这样内核就能够控制系统中所有的共享区域。同时,在shmid_kernel结构的file类型指针shm_file指向文件系统shm中相应的文件,这样,共享内存区域就与shm文件系统中的文件对应起来。

    在创建了一个共享内存区域后,还要将它映射到进程地址空间,系统调用shmat()完成此项功能。由于在调用shmget()时,已经创建了文件系统shm中的一个同名文件与共享内存区域相对应,因此,调用shmat()的过程相当于映射文件系统shm中的同名文件过程,原理与mmap()大同小异。

    2、系统V共享内存API

    对于系统V共享内存,主要有以下几个API:shmget()、shmat()、shmdt()及shmctl()。

    #include <sys/ipc.h>;
    #include <sys/shm.h>;

    shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。shmat()把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区域进行访问操作。shmdt()调用用来解除进程对共享内存区域的映射。shmctl实现对共享内存区域的控制操作。这里我们不对这些系统调用作具体的介绍,读者可参考相应的手册页面,后面的范例中将给出它们的调用方法。

    注:shmget的内部实现包含了许多重要的系统V共享内存机制;shmat在把共享内存区域映射到进程空间时,并不真正改变进程的页表。当进程第一次访问内存映射区域访问时,会因为没有物理页表的分配而导致一个缺页异常,然后内核再根据相应的存储管理机制为共享内存映射区域分配相应的页表。

    3、系统V共享内存限制

    在/proc/sys/kernel/目录下,记录着系统V共享内存的一下限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做。

    在[2]中,给出了这些限制的测试方法,不再赘述。

    4、系统V共享内存范例

    本部分将给出系统V共享内存API的使用方法,并对比分析系统V共享内存机制与mmap()映射普通文件实现共享内存之间的差异,首先给出两个进程通过系统V共享内存通信的范例:

    /***** testwrite.c *******/
    #include <sys/ipc.h>;
    #include <sys/shm.h>;
    #include <sys/types.h>;
    #include <unistd.h>;
    typedef struct{
            char name[4];
            int age;
    } people;
    main(int argc, char** argv)
    {
            int shm_id,i;
            key_t key;
            char temp;
            people *p_map;
            char* name = "/dev/shm/myshm2";
            key = ftok(name,0);
            if(key==-1)
                    perror("ftok error");
            shm_id=shmget(key,4096,IPC_CREAT);        
            if(shm_id==-1)
            {
                    perror("shmget error");
                    return;
            }
            p_map=(people*)shmat(shm_id,NULL,0);
            temp='a';
            for(i = 0;i<10;i++)
            {
                    temp+=1;
                    memcpy((*(p_map+i)).name,&temp,1);
                    (*(p_map+i)).age=20+i;
            }
            if(shmdt(p_map)==-1)
                    perror(" detach error ");
    }
    /********** testread.c ************/
    #include <sys/ipc.h>;
    #include <sys/shm.h>;
    #include <sys/types.h>;
    #include <unistd.h>;
    typedef struct{
            char name[4];
            int age;
    } people;
    main(int argc, char** argv)
    {
            int shm_id,i;
            key_t key;
            people *p_map;
            char* name = "/dev/shm/myshm2";
            key = ftok(name,0);
            if(key == -1)
                    perror("ftok error");
            shm_id = shmget(key,4096,IPC_CREAT);        
            if(shm_id == -1)
            {
                    perror("shmget error");
                    return;
            }
            p_map = (people*)shmat(shm_id,NULL,0);
            for(i = 0;i<10;i++)
            {
            printf( "name:%s/n",(*(p_map+i)).name );
            printf( "age %d/n",(*(p_map+i)).age );
            }
            if(shmdt(p_map) == -1)
                    perror(" detach error ");
    }

    testwrite.c创建一个系统V共享内存区,并在其中写入格式化数据;testread.c访问同一个系统V共享内存区,读出其中的格式化数据。分别把两个程序编译为testwrite及testread,先后执行./testwrite及./testread 则./testread输出结果如下:

    name: b        age 20;        name: c        age 21;        name: d        age 22;        name: e        age 23;        name: f        age 24;
    name: g        age 25;        name: h        age 26;        name: I        age 27;        name: j        age 28;        name: k        age 29;

    通过对试验结果分析,对比系统V与mmap()映射普通文件实现共享内存通信,可以得出如下结论:

    1、 系统V共享内存中的数据,从来不写入到实际磁盘文件中去;而通过mmap()映射普通文件实现的共享内存通信可以指定何时将数据写入磁盘文件中。注:前面讲到,系统V共享内存机制实际是通过映射特殊文件系统shm中的文件实现的,文件系统shm的安装点在交换分区上,系统重新引导后,所有的内容都丢失。

    2、 系统V共享内存是随内核持续的,即使所有访问共享内存的进程都已经正常终止,共享内存区仍然存在(除非显式删除共享内存),在内核重新引导之前,对该共享内存区域的任何改写操作都将一直保留。

    3、 通过调用mmap()映射普通文件进行进程间通信时,一定要注意考虑进程何时终止对通信的影响。而通过系统V共享内存实现通信的进程则不然。注:这里没有给出shmctl的使用范例,原理与消息队列大同小异。

    结论:

    共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制。共享内存可以通过mmap()映射普通文件(特殊情况下还可以采用匿名映射)机制实现,也可以通过系统V共享内存机制实现。应用接口和原理很简单,内部机制复杂。为了实现更安全通信,往往还与信号灯等同步机制共同使用。

    共享内存涉及到了存储管理以及文件系统等方面的知识,深入理解其内部机制有一定的难度,关键还要紧紧抓住内核使用的重要数据结构。系统V共享内存是以文件的形式组织在特殊文件系统shm中的。通过shmget可以创建或获得共享内存的标识符。取得共享内存标识符后,要通过shmat将这个内存区映射到本进程的虚拟地址空间。

    --------------------- 本文来自 hai0808 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/liu0808/article/details/52967167?utm_source=copy

    更多相关内容
  • DDR4内存原理图及PCB

    2021-07-13 07:24:15
    AD官方出的的参考pcb,DDR4内存条,画的很漂亮。
  • 电脑DDR4内存条电路设计资料 包含原理图及PCB
  • 10分钟看懂内存的实现原理

    千次阅读 多人点赞 2019-11-06 13:57:46
    内存最小单位是一些类似于二极管这样的东西,它能存储一个电状态,高或低,可表示1或0; -这些单元经过组织起来保存数据,组织的方法是8个编成一个字节,4个字节一个字,每组数据都可以读写; -这些单元按照顺序排放...

     

    内存最小单位是一些类似于二极管这样的东西,它能存储一个电状态,高或低,可表示1或0;
    -这些单元经过组织起来保存数据,组织的方法是8个编成一个字节,4个字节一个字,每组数据都可以读写;
    -这些单元按照顺序排放后用地址编号,按照地址可访问其中的任一个字、字节;
    -这些电路访问时由两组数据连线:地址线和数据线,比如都是32位的,地址线描述要访问的具体单元,数据线存放要给这个单元赋值的数据(写访问)或读出的数据(读访问);
    -这些单元上电时才能保持状态,所以内存你一掉电(关机),其中的数据就丢失了。

     

    硬盘存储器中,所存储二进制信息的两种状态是用什么表示的?

    用两个不同方向的磁化,来表示数码0和1。
    磁性材料均匀地涂覆在圆形的铝合金载体上就成为硬磁盘盘体,当磁头上的写线圈中通过一定方向的脉冲电流时,磁头铁芯内就产生一定方向的磁通。在磁头缝隙处则会产生很强的磁场并形成一个闭合回路,磁头下方硬磁盘盘体上一个很小区域被磁化形成一个磁化元(即记录单元)。若在磁头的写线圈中通过相反方向的脉冲电流,该磁化元则向相反方向磁化。如果某一个方向的脉冲电流将磁头下方硬磁盘的极小区域磁化为信息"1"的话(写入"1"),那么相反方向的脉冲电流就会将磁头下方硬磁盘的极小区域磁化为信息"0"(写入"0")。也就是说,硬盘中的"0"和"1",是用盘体上磁化元的不同磁化方向来表示的。

     

    本文讲内存的实现,从底层的二极管到内存的电路结构,本章逻辑线路为:电路-->二极管-->逻辑门-->组合逻辑单元j和存储单元-->内存

    我们知道计算机本质是在做0和1的二进制运算,那么内存从抽象意义上看也是保存着0和1,那么问题是怎么保存这些0和1呢?下面就说说内存的硬件实现哈。

    电路

    先从一个简单电路看起,

    image

    二极管

    然而我们要控制二进制,在计算机里我们用两种互补的二极管(CMOS即n-MOS管和p-MOS管)实现这个开关,这两种原理如下。

    image

    n-MOS管,栅极端(Gate)通电后,则电路联通,栅极断电则电路断开

    image

    p-MOS管反过来,栅极端(Gate)通电后,则电路断开,栅极断电则电路联通

    提示:小圆圈表示“非”,即取反的意思,若输入为1,流经小圆圈后则变为0

    逻辑门

    通过这些二极管的组合,我们可以实现计算机里的“与”、“或”、“非”逻辑电路。

    非门(NOT):

    image

    对应真值表:

    InOut
    01
    10

    或门(OR):

    image

    真值表:

    ABC
    000
    011
    101
    111

    与门(AND):

    image

     

    真值表:

    ABC
    000
    010
    100
    111

    既然知道逻辑门的二极管实现后则可以用符号替代这复杂的底层实现。

    image

    看完了逻辑门,紧接着看计算机内的逻辑单元。逻辑单元可以分为两类:

    1. 一类不可存储信息的(组合逻辑单元)
    2. 另一类可存储信息(存储单元)。我们分别介绍和内存相关的几个结构。

    组合逻辑单元

    不可储存信息的结构:用于计算或控制二进制

    1. 译码器:依据输入端的二进制,在输出端选中某一条电路

    image

    如图,如果A=0,B=0,则选中最上面的那条线

    1. 多路复用器:根据控制信号,只选择输入端的某根电路,把这根线输出到输出端

    image

    如图,若S1=0,S2=0,则A线路被选中,A的值即是输出值。

    存储单元

    可存储信息的结构:可用于保存二进制

    1. 门控D锁存器

       

      image

    • WE=0时(不可读写),S和R必为1,则电路稳定,out端可保持原来的值
    • WE=1时(可读写),则根据D的值,out端变为对应的值。然后恢复WE=0则保存起来
    1. 寄存器:
      一个门控D锁存器只能保存一个bit,则组合起来就能保存n个bit,这就是寄存器

    image

    介绍完这些组成结构后,我们终于可以讲解内存啦!

    内存

    先介绍两个概念:

    1. 寻址空间:即能保存多少个内存地址,我们通常意义上的4G内存,就表示计算机能保存2的32次方个地址!能保存这么多地址,也就能找到这些地址上的二进制信息

    2. 寻址能力:每个地址里具体存多少个bit,由于历史原因,绝大部分计算机都是8bit的寻址能力。

    那么我们来看一个超简单的内存哈。这个内存是2的2次方的寻址空间,即只能查找4个地址。寻址能力是3个bit,即每个地址只能存3个bit。

    image

    如图

    1. 内存的寻址由一个2个输入的译码器实现。译码器根据A1和A2的输入,选中对应的一条线,即找到地址对应的存储空间。
    2. 内存存储信息的能力具体实现是由门控D锁存器保存bit信息。
    3. 读取内存的输出由多路复用器实现。根据寻址时译码器选中的某一个线路,每一列的多路复用器只能输出那个线路的对应的那个门控D锁存器保存的bit。则最终的三个bit都是由同一个线路对应的三个门控D锁存器的输出。

    这就是内存的实现原理了哈。



     

    展开全文
  • DDR3 内存原理

    热门讨论 2012-03-04 15:58:42
    这是DDR3内存条详细的pdf原理图资料,是UDIMM(无缓冲双通道内存模块),240个引脚。
  • DDR4内存条PCB.rar

    2019-11-24 23:37:49
    单条DDR4 DIMM条的电路图和PCB,用来查看DDR4原理和布线,用来验证DDR4的走线,是学习DDR4原理布线的重要参考资料
  • 内存屏障底层原理

    千次阅读 2019-05-23 08:44:16
    主题是什么? 我写这个系列的博客主要目的是解析Disruptor是...这篇文章提及到了内存屏障,我想弄清楚它们到底是什么,以及它们是如何应用于实践中的。 什么是内存屏障? 它是一个CPU指令。没错,又一次,我们在讨论...

    主题是什么?

    我写这个系列的博客主要目的是解析Disruptor是如何工作的,并深入了解下为什么这样工作。理论上,我应该从可能准备使用disruptor的开发人员的角度来写,以便在代码和技术论文Disruptor-1.0.pdf之间搭建一座桥梁。这篇文章提及到了内存屏障,我想弄清楚它们到底是什么,以及它们是如何应用于实践中的。

    什么是内存屏障?

    它是一个CPU指令。没错,又一次,我们在讨论CPU级别的东西,以便获得我们想要的性能(Martin著名的Mechanical Sympathy理论)。基本上,它是这样一条指令: a)确保一些特定操作执行的顺序; b)影响一些数据的可见性(可能是某些指令执行后的结果)。

    编译器和CPU可以在保证输出结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。正如去拉斯维加斯旅途中各个站点的先后顺序在你心中都一清二楚。

     

    内存屏障另一个作用是强制更新一次不同CPU的缓存。例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个cpu核心或者哪颗CPU执行的。

    和Java有什么关系?

    现在我知道你在想什么——这不是汇编程序。它是Java。

    这里有个神奇咒语叫volatile(我觉得这个词在Java规范中从未被解释清楚)。如果你的字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。

     

    这意味着如果你对一个volatile字段进行写操作,你必须知道:

    1、一旦你完成写入,任何访问这个字段的线程将会得到最新的值。

    2、在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。

    举个例子呗!

    很高兴你这样说了。又是时候让我来画几个甜甜圈了。

    RingBuffer的指针(cursor)(译注:指向队尾元素)属于一个神奇的volatile变量,同时也是我们能够不用锁操作就能实现Disruptor的原因之一。

     

    生产者将会取得下一个Entry(或者是一批),并可对它(们)作任意改动, 把它(们)更新为任何想要的值。如你所知,在所有改动都完成后,生产者对ring buffer调用commit方法来更新序列号(译注:把cursor更新为该Entry的序列号)。对volatile字段(cursor)的写操作创建了一个内存屏障,这个屏障将刷新所有缓存里的值(或者至少相应地使得缓存失效)。

    这时候,消费者们能获得最新的序列号码(8),并且因为内存屏障保证了它之前执行的指令的顺序,消费者们可以确信生产者对7号Entry所作的改动已经可用。

    那么消费者那边会发生什么?

    消费者中的序列号是volatile类型的,会被若干个外部对象读取——其他的下游消费者可能在跟踪这个消费者。ProducerBarrier RingBuffer(取决于你看的是旧的还是新的代码)跟踪它以确保环没有出现重叠(wrap)的情况(译注:为了防止下游的消费者和上游的消费者对同一个Entry竞争消费,导致在环形队列中互相覆盖数据,下游消费者要对上游消费者的消费情况进行跟踪)。

    所以,如果你的下游消费者(C2)看见前一个消费者(C1)在消费号码为12的Entry,当C2的读取也到了12,它在更新序列号前将可以获得C1对该Entry的所作的更新。

    基本来说就是,C1更新序列号前对ring buffer的所有操作(如上图黑色所示),必须先发生,待C2拿到C1更新过的序列号之后,C2才可以为所欲为(如上图蓝色所示)。

    对性能的影响

    内存屏障作为另一个CPU级的指令,没有锁那样大的开销。内核并没有在多个线程间干涉和调度。但凡事都是有代价的。内存屏障的确是有开销的——编译器/cpu不能重排序指令,导致不可以尽可能地高效利用CPU,另外刷新缓存亦会有开销。所以不要以为用volatile代替锁操作就一点事都没。

    你会注意到Disruptor的实现对序列号的读写频率尽量降到最低。对volatile字段的每次读或写都是相对高成本的操作。但是,也应该认识到在批量的情况下可以获得很好的表现。如果你知道不应对序列号频繁读写,那么很合理的想到,先获得一整批Entries,并在更新序列号前处理它们。这个技巧对生产者和消费者都适用。

    long nextSequence = sequence + 1;
    while (running){
        try{
                final long availableSequence = consumerBarrier.waitFor(nextSequence);
                while (nextSequence <= availableSequence){
                    entry = consumerBarrier.getEntry(nextSequence);
                    handler.onAvailable(entry);
                    nextSequence++;
                }
                sequence = entry.getSequence();
                ......
            }catch (final Exception ex){
                exceptionHandler.handle(ex, entry);
                sequence = entry.getSequence();
                nextSequence = entry.getSequence() + 1;
            }
        }    
    

    (你会注意到,这是个旧式的代码和命名习惯,因为这是摘自我以前的博客文章,我认为如果直接转换为新式的代码和命名习惯会让人有点混乱)

    在上面的代码中,我们在消费者处理entries的循环中用一个局部变量(nextSequence)来递增。这表明我们想尽可能地减少对volatile类型的序列号的进行读写。

    总结

    内存屏障是CPU指令,它允许你对数据什么时候对其他进程可见作出假设。在Java里,你使用volatile关键字来实现内存屏障。使用volatile意味着你不用被迫选择加锁,并且还能让你获得性能的提升。

    但是,你需要对你的设计进行一些更细致的思考,特别是你对volatile字段的使用有多频繁,以及对它们的读写有多频繁。

    PS:上文中讲到的Disruptor中使用的New World Order是一种完全不同于我目前为止所发表的博文中的命名习惯。我想下一篇文章会对旧式的和新式的命名习惯做一个对照。

    原文地址:http://ifeve.com/disruptor-memory-barriers/
    展开全文
  • 内存工作原理

    千次阅读 2015-11-29 14:18:51
    现代的PC(包括NB)都是以存储器为核心的多总线结构,即CPU只通过存储总线与主存储器交换信息(先在Cache里找数据,如果找不到,再去主存找)。输入输出设备通过I/O总线直接与主存储器...内部存储器简称内存,也可称
    现代的PC(包括NB)都是以存储器为核心的多总线结构,即CPU只通过存储总线与主存储器交换信息(先在Cache里找数据,如果找不到,再去主存找)。输入输出设备通过I/O总线直接与主存储器交换信息。在I/O设备和主存储器之间配置专用的I/O处理器。CPU不直接参与I/O设备与主存储器之间的信息传送。
        存储器分为内部存储器和外部存储器(或者叫主存储器和辅助存储器)。内部存储器简称内存,也可称为主存。从广义上讲,只要是PC内部的易失性存储器都可以看作是内存,如显存,二级缓存等等。外部存储器也称为外存,主要由一些非易失性存储器构成,比如硬盘、光盘、U盘、存储卡等等。
    内存作为数据的临时仓库,起着承上启下的作用,一方面要从外存中读取执行程序和需要的数据,另一方面还要为CPU服务,进行读写操作。所以主存储器快慢直接影响着PC的速度。下面我就从内存的原理开始谈起。
        一、原理篇
        内存工作原理
        1.内存寻址
        首先,内存从CPU获得查找某个数据的指令,然后再找出存取资料的位置时(这个动作称为“寻址”),它先定出横坐标(也就是“列地址”)再定出纵坐标(也就是“行地址”),这就好像在地图上画个十字标记一样,非常准确地定出这个地方。对于电脑系统而言,找出这个地方时还必须确定是否位置正确,因此电脑还必须判读该地址的信号,横坐标有横坐标的信号(也就是RAS信号,Row Address Strobe)纵坐标有纵坐标的信号(也就是CAS信号,Column Address Strobe),最后再进行读或写的动作。因此,内存在读写时至少必须有五个步骤:分别是画个十字(内有定地址两个操作以及判读地址两个信号,共四个操作)以及或读或写的操作,才能完成内存的存取操作。
        2.内存传输
        为了储存资料,或者是从内存内部读取资料,CPU都会为这些读取或写入的资料编上地址(也就是我们所说的十字寻址方式),这个时候,CPU会通过地址总线(Address Bus)将地址送到内存,然后数据总线(Data Bus)就会把对应的正确数据送往微处理器,传回去给CPU使用。
        3.存取时间
        所谓存取时间,指的是CPU读或写内存内资料的过程时间,也称为总线循环(bus cycle)。以读取为例,从CPU发出指令给内存时,便会要求内存取用特定地址的特定资料,内存响应CPU后便会将CPU所需要的资料送给CPU,一直到CPU收到数据为止,便成为一个读取的流程。因此,这整个过程简单地说便是CPU给出读取指令,内存回复指令,并丢出资料给CPU的过程。我们常说的6ns(纳秒,秒-9)就是指上述的过程所花费的时间,而ns便是计算运算过程的时间单位。我们平时习惯用存取时间的倒数来表示速度,比如6ns的内存实际频率为1/6ns=166MHz(如果是DDR就标DDR333,DDR2就标DDR2 667)。
        4.内存延迟
        内存的延迟时间(也就是所谓的潜伏期,从FSB到DRAM)等于下列时间的综合:FSB同主板芯片组之间的延迟时间(±1个时钟周期),芯片组同DRAM之间的延迟时间(±1个时钟周期),RAS到CAS延迟时间:RAS(2-3个时钟周期,用于决定正确的行地址),CAS延迟时间 (2-3时钟周期,用于决定正确的列地址),另外还需要1个时钟周期来传送数据,数据从DRAM输出缓存通过芯片组到CPU的延迟时间(±2个时钟周期)。一般的说明内存延迟涉及四个参数CAS(Column Address Strobe 行地址控制器)延迟,RAS(Row Address Strobe列地址控制器)-to-CAS延迟,RAS Precharge(RAS预冲电压)延迟,Act-to-Precharge(相对于时钟下沿的数据读取时间)延迟。其中CAS延迟比较重要,它反映了内存从接受指令到完成传输结果的过程中的延迟。大家平时见到的数据3—3—3—6中,第一参数就是CAS延迟(CL=3)。当然,延迟越小速度越快。
        二、外观篇
        由于笔记本的空间设计要求,笔记本内存比台式机内存条要窄,通常采用SO-DIMM模组规范,布线也比较紧凑,针脚也为标准的200Pin。我们经常看到的内存上,一般的元件有内存颗粒、电路板、SPD芯片、排阻(终结电阻)和针脚。下面我来分别介绍一下。
        1.颗粒
        内存颗粒就是大家平时见到内存上一个个的集成电路块。颗粒是内存的主要组成部分,颗粒性能可以说很大程度上决定了内存的性能,常见的颗粒有以下一些参数。
        A.厂商
        市场上生产内存颗粒的厂商主要有Hynix(现代电子),Samsung Electronics(***电子),Micro(美光),Infineon(英飞凌),Kingmax(胜创)等等。不过需要注意的一点是,“内存颗粒”和“内存条”是完全不同的两回事。能够生产内存颗粒的厂商全球没几个,而有了内存颗粒后内存条的生产就要简单得多,生产者自然要多得多。充斥市场的杂牌内存条与品牌内存条有着根本的区别,它们在成本上也有很多不同。Kingston、Kingmax、金邦等大的品牌内存条采用的都是符合Intel规定的6层PCB板和现代、***等内存大厂的内存颗粒,按照严格的工艺进行生产;而那些杂牌内存条虽然号称“***”、“现代”,其实就是一些小厂和作坊,他们拿来大厂内存颗粒的切割角料,焊到劣质的PCB板上就下了线,品质完全没有保证,而且经常与一些大的经销商结成联盟来生产和销售,价格波动也更容易受到渠道因素的影响。
        B.内存芯片类型
        内存芯片类型分SDRAM,DDR SDRAM,DDRⅡ SDRAM、SDRAM、DDR SDRAM和DDR SDRAM同出一门,都属SDRAM系,因此三者的颗粒在外观上不容易分辨,。但是由于采用的物理技术不同,三者在电路,延迟,带宽上还是有很大区别的,区分三者一般都是看颗粒的参数或者针脚和缺口位置,后面我会重点讲DDR和DDRⅡ技术。
        C.内存工艺和工作电压
        SDRAM内存工艺主要以CMOS为主,内存的工作电压和内存的芯片类型有很大关系,在JEDEC(Joint Electron Device Engineering Council 电子元件工业联合会)的规范中,SDRAM的工作电压是3.3V,DDR是2.5V,DDRⅡ是1.8V。
        D.芯片密度
        位宽及刷新速度芯片的密度一般都会用bit为单位进行表示(1B=8bit),比如16Mbit是16Mbit÷ 8bit=2MB也就是单颗芯片是2MB的。还有一个参数就是位宽,SDRAM系的位宽是64bit,采用多少个颗粒(一般为偶数)组成64bit也是不一样的。比如一个芯片是4bit的,那么要用16个同样的芯片才能组成64bits的,如果芯片是16bit那么只须4个就可以了。举个例子,256MB的内存可以用512bits÷8×4颗=256MB,4颗×16bit=64bit来组成,一般表示为512Mbits×16bit或64MB×16bit。刷新速度,内存条是由电子存储单元组成的,刷新过程对以列方式排列在芯片上的存储单元进行充电。刷新率是指被刷新的列的数目。两个常用的刷新率是2K和4K。2K模式能够在一定的时间内刷新较多的存储单元并且所用时间较短,因此2K所用的电量要大于4K。4K模式利用较慢的时间刷新较少的存储单元,然而它使用的电量较少。一些特殊设计的SDRAM具有自动刷新功能,它可自动刷新而不借助CPU或外部刷新电路。建立在DRAM内部的自动刷新,减少了电量消耗,被普遍应用于笔记本电脑。
        E.Bank
        内存的Bank一般分为物理Bank和逻辑Bank。物理Bank体现在SDRAM内存模组上,"Bank 数"表示该内存的物理存储体的数量。(等同于"行"/Row)。逻辑Bank表示一个SDRAM设备内部的逻辑存储库的数量。(现在通常是4个bank)。此外,对于主板,它还表示DIMM连接插槽或插槽组,例如Bank 0 或 Bank A。这里的Bank是内存插槽的计算单位,它是电脑系统与内存之间数据总线的基本工作单位。只有插满一个BANK,电脑才可以正常开机。举个例子,1个SDRAM线槽一个Bank为64bit,而老早以前的EDO内存是32bit的,必须要安装两根内存才能正常工作。主板上的Bank编号从Bank 0开始,必须插满Bank 0才能开机,Bank 1以后的插槽留给日后升级扩充内存用。
        F.电气接口类型
        般的电气接口类型与内存类型对应,如SDRAM是SSTL_3(3.3V)、DDR是 SSTL_2(2.5V)、DDRⅡ是SSTL_18(1.8V)。
        G.内存的封装
        现在比较普遍的封装形式有两种BGA和TSOP两种,BGA封装分FBGA,μBGA,TinyBGA(KingMAX)等等,TSOP分TSOPⅠ和TSOPⅡ。BGA封装具有芯片面积小的特点,可以减少PCB板的面积,发热量也比较小,但是需要专用的焊接设备,无法手工焊接。另外一般BGA封装的芯片,需要多层PCB板布线,这就对成本提出了要求。此外,BGA封装还拥有芯片安装容易、电气性能更好、信号传输延迟低、允许高频运作、散热性卓越等许多优点,它成为DDRⅡ官方选择也在情理之中。而TSOP相对来说工艺比较成熟,成本低,缺点是频率提升比较困难,体积较大,发热量也比BGA大。
        H.速度及延迟
        一般内存的速度都会用频率表示。比如大家常常看到的SDRAM 133、DDR 266、DDRⅡ 533其实物理工作频率都是133MHz,只是采用了不同的技术,理论上相当于2倍或4倍的速率运行,还有一种表示速度方法是用脉冲周期来表示速度,一般是纳秒级的。比如1/133MHz=7ns,说明该内存的脉冲周期是7ns。内存延迟我前面说过了,参数一般为4个,也有用3个的,数字越小表示延迟越小,速度越快。

        I.工作温度
        工作温度:工业常温(-40 - 85度);扩展温度(-25 - 85度)
        2.电路板
        电路板也称PCB版,是印刷电路板电子板卡的基础,由若干层导体和绝缘体组成的平板。电路图纸上的线路都蚀刻在其上,然后焊接上电子元件。由于所有的内存元件都焊在电路版上,因此电路板的布线是决定内存稳定性的重要方面,跟据Intel的规范,DDR内存必须使用6层PCB版才能保证内存的电气化功能和运行的稳定性。所以建议大家购买大厂的产品,不要使用来历不明的山寨货。
        3.SPD及SPD芯片
        SPD(Serial Presence Detect)— 串行存在侦测,SPD是一颗8针的EEPROM(Electrically Erasable Programmable ROM 电子可擦写程序式只读内存), 容量为256字节~2KB,里面主要保存了该内存的相关资料,如容量、芯片厂商、内存模组厂商、工作速度、是否具备ECC校验等。SPD的内容一般由内存模组制造商写入。支持SPD的主板在启动时自动检测SPD中的资料,并以此设定内存的工作参数。当开机时PC的BIOS将自动读取SPD中记录的信息,如果没有SPD,就容易出现死机或致命错误的现象。建议大家购买有SPD芯片的内存。

        4.排阻

        排阻,也称终结电阻(终结器)是DDR内存中比较重要的硬件。DDR内存对工作环境提出很高的要求,如果先前发出的信号不能被电路终端完全吸收掉而在电路上形成反射现象,就会对后面信号的影响从而造成运算出错。因此目前支持DDR主板都是通过采用终结电阻来解决这个问题。 由于每根数据线至少需要一个终结电阻,这意味着每块DDR主板需要大量的终结电阻,这也无形中增加了主板的生产成本,而且由于不同的内存模组对终结电阻的要求不可能完全一样,也造成了所谓的“内存兼容性问题”。由于DDR II内部集成了终结器,这个问题上得到了比较完美的解决。

        5.针脚(Pin)

        Pin-针状引脚,是内存金手指上的金属接触点。由于不同的内存的针脚不同,所以针脚也是从外观区分各种内存的主要方法。内存针脚分为正反两面,例如笔记本DDR内存是200Pin,那么正反两面的针脚就各为200÷2=100个。此外,有些大厂的金手指使用技术先进的电镀金制作工艺,镀金层色泽纯正,有效提高抗氧化性。保证了内存工作的稳定性

    三、技术篇

        1.DDR
    DDR技术

        DDR
    技术

        DDR SDRAM
    是双倍数据速率(Double Data RateSDRAM的缩写。从名称上可以看出,这种内存在技术上,与SDRAM有着密不可分的关系。事实上,DDR内存就是SDRAM内存的加强版。DDR运用了更先进的同步电路,使指定地址、数据的输送和输出主要步骤既独立执行,又保持与CPU完全同步;DDR使用了DLL(Delay Locked Loop,延时锁定回路提供一个数据滤波信号)技术,当数据有效时,存储控制器可使用这个数据滤波信号来精确定位数据,每16次输出一次,并重新同步来自不同存储器模块的数据。DDL本质上不需要提高时钟频率就能加倍提高SDRAM的速度,它允许在时钟脉冲的上升沿和下降沿读出数据,理论上使用原来的工作的频率可以产生2倍的带宽。同速率的DDR内存与SDR内存相比,性能要超出一倍,可以简单理解为133MHZ DDR="266MHZ" SDR。从外形体积上DDRSDRAM相比差别并不大,他们具有同样的尺寸和同样的针脚距离。DDR内存采用的是支持2.5V电压的SSTL2标准,而不是SDRAM使用的3.3V电压的LVTTL标准。但是DDR存在自身的局限性DDR只是在SDRAM基础上作简单改良,并行技术与生俱来的易受干扰特性并没有得到丝毫改善,尤其随着工作频率的提高和数据传输速度加快,总线间的信号干扰将造成系统不稳定的灾难性后果;反过来,信号干扰也制约着内存频率的提升——当发展到DDR400规范时,芯片核心的工作频率达到200MHz,这个数字已经非常接近DDR的速度极限,只有那些品质优秀的颗粒才能够稳定工作于200MHz之上,所以DDR标准就成了一种进一步提高内存速度的解决方法。

        DDR 
    技术

        DDR
    相对于DDR有三大技术革新,4位预取(DDR2位)、Posted CAS、整合终结器(ODT)、FBGA/CSP封装。要解释预取的概念,我们必须从内存的频率说起。大家通常说的内存频率其实是一个笼统的说法,内存频率实际上应细分为数据频率、时钟频率和DRAM核心频率三种。数据频率指的是内存模组与系统交换数据的频率;时钟频率则是指内存与系统协调一致的频率;而DRAM核心频率指的是DRAM内部组件的工作频率,它只与内存自身有关而不受任何外部因素影响。对SDRAM来说,这三者在数字上是完全等同的,也就是数据频率=时钟频率=核心频率;而DDR技术却不是如此,它要在一个时钟周期内传输两次数据,数据频率就等于时钟频率的两倍,但核心频率还是与时钟频率相等。由于数据传输频率翻倍(传输的数据量也翻倍),而内部核心的频率并没有改变,这意味着DDR芯片核心必须在一个周期中供给双倍的数据量才行,实现这一任务的就是所谓的两位预取(2bit Prefect)技术;DDR采用的4位预取。这项技术的原理是将DRAM存储矩阵的位宽增加一(两)倍,这样在一个时钟周期内就可以传输双(四)倍的数据,这些数据接着被转化为宽度为1/214)的两道数据流、分别从每个时钟周期的上升沿和下降沿传送出去。Posted CASDDR通过引入Posted CAS功能来解决带宽利用变低的问题,所谓Posted CAS,指的是将CAS(读/写命令)提前几个周期、直接插到RAS信号后面的一个时钟周期,这样CAS命令可以在随后的几个周期内都能保持有效,但读/写操作并没有因此提前、总的延迟时间没有改变。这样做的好处在于可以彻底避免信号冲突、提高内存使用效率,但它只有在读写极其频繁的环境下得到体现,若是普通应用,Posted CAS功能反而会增加读取延迟、令系统性能下降,因此我们可以根据需要、通过BIOSPosted CAS功能开启或关闭(关闭状态下DDR的工作模式就与DDR完全相同)。  芯片整合终结器,提高了内存工作的稳定性,增强的内存的兼容性。FBGA封装和CSP封装,封装虽然无法直接决定内存的性能,但它对内存的稳定工作至关重要。 FBGA封装是DDR的官方选择,FBGA属于BGA体系(Ball Grid Array,球栅阵列封装),前面已经讲过了。CSP封装最大的特点在于封装面积与芯片面积异常接近,两者比值仅有1.141,它也是目前最接近11理想状况的芯片封装技术。这样在同样一条模组中就可以容纳下更多数量的内存芯片,有利于提升模组的总容量。 

        2.
    双通道内存控制器技术

        所谓双通道
    DDR,简单来说,就是芯片组可以在两个不同的数据通道上分别寻址、读取数据。这两个相互独立工作的内存通道是依附于两个独立并行工作的,位宽为64-bit的内存控制器下,因此使普通的DDR内存可以达到128-bit的位宽,如果是DDR333的话,双通道技术可以使其达到DDR667的效果,内存带宽陡增一倍。双通道DDR有两个64bit内存控制器,双64bit内存体系所提供的带宽等同于一个128bit内存体系所提供的带宽,但是二者所达到效果却是不同的。双通道体系包含了两个独立的、具备互补性的智能内存控制器,两个内存控制器都能够在彼此间零等待时间的情况下同时运作。例如,当控制器B准备进行下一次存取内存的时候,控制器 A就在读/写主内存,反之亦然。两个内存控制器的这种互补天性可以让有效等待时间缩减50%,双通道技术使内存的带宽翻了一翻。双通道DDR的两个内存控制器在功能上是完全一样的,并且两个控制器的时序参数都是可以单独编程设定的。这样的灵活性可以让用户使用三条不同构造、容量、速度的DIMM内存条,此时双通道DDR简单地调整到最低的密度来实现128bit带宽,允许不同密度/等待时间特性的DIMM内存条可以可靠地共同运作。双通道DDR技术带来的性能提升是明显的,DDR266能够提供2.1GB/s的带宽,而双通道DDR266则能提供4.2GB/s的带宽。以此类推,双通道DDR333DDR400能够达到5.4GB/s6.4GB/s

        3.CPU
    集成内存控制器技术

        这是
    AMD公司提高CPU与内存性能的一项技术,这项技术是一种将北桥的内存控制器集成到CPU的一种技术,这种技术的使用使得原来,CPU-北桥-内存三方传输数据的过程直接简化成CPU与内存之间的单项传输技术,并且降低了它的延迟潜伏期,提高了内存工作效率。这么做得的目的是为了解放系统的北桥,众所周知,显卡也是通过北桥向CPU传输数据的,虽然说早在GeForce256时代就有了GPU的说法,但是随着现在游戏的进步,画面的华丽,不少数据还是需要CPU来做辅助处理的。这些数据传输到CPU必然要经过系统的北桥,由于AMD64系统将内存控制集成到主般中来了,所以压力减小的北桥便可以更好地为显卡服务。另外,缺少了中间环节,内存和CPU之间的数据交换显得更为流畅。但是这项技术也有缺点,当新的内存技术出现时,必须要更换CPU才能支持。这在无形间增加了成本。

        4.
    其他技术

        A.ECC
    内存

        全称
    Error Checkingand Correcting。它也是在原来的数据位上外加位来实现的。如8位数据,则需1位用于Parity检验,5位用于ECC,这额外的5位是用来重建错误的数据的。当数据的位数增加一倍,Parity也增加一倍,而ECC只需增加一位,当数据为64位时所用的ECCParity位数相同(都为。在那些Parity只能检测到错误的地方,ECC可以纠正绝大多数错误。若工作正常时,你不会发觉你的数据出过错,只有经过内存的纠错后,计算机的操作指令才可以继续执行。当然在纠错时系统的性能有着明显降低,不过这种纠错对服务器等应用而言是十分重要的,ECC内存的价格比普通内存要昂贵许多。

        B.
    UnBuffered Memory 内存

        (
    UnBuffered Memory,(不)带有缓存的内存条。缓存能够二次推动信号穿过内存芯片,而且使内存条上能够放置更多的内存芯片。带缓存的内存条和不带缓存的内存条不能混用。电脑的内存控制器结构,决定了该电脑上带缓存的内存还是上不带缓存的内存。

    展开全文
  • 共享内存(shm)学习笔记

    千次阅读 2019-08-11 15:21:10
    共享内存 共享内存是系统创建的特殊地址空间,允许不相关的多个进程使用这个内存空间,即多个进程能够使用同一块内存中的数据。 共享内存与其他进程通信方式相比较,不需要复制数据,直接读写内存,是一种效率...
  • 计算机原理(3)-内存工作原理

    千次阅读 2018-01-19 22:21:48
    CPU和内存是计算机中最重要的两个组件,前面已经知道了CPU是如何工作的,上一篇也介绍了内存采用的DRAM的存储原理。CPU工作需要知道指令或数据的内存地址,那么这样一个地址是如何和内存这样一个硬件联系起来的呢?...
  • linux共享内存原理剖析

    千次阅读 2017-05-08 16:41:14
    linux共享内存原理剖析共享内存区域是被多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。共享内存是...
  • dpdk大页内存原理

    千次阅读 2019-08-16 23:56:26
    在分析dpdk大页内存的源码之前,有必要对linux内存管理的原理以及大页内存原理有个了解,缺少这些底层基础知识,分析dpdk大页内存的源码将举步维艰。这篇文章详细介绍下linux内存管理以及大页内存的方方面面,为...
  • 内存数据库原理及最佳实践,讲述内存数据库的原理以及实例
  • 渗透测试——内存攻击原理

    千次阅读 2019-02-13 22:34:05
    什么是内存攻击 内存攻击指的是攻击者利用软件安全漏洞,构造恶意输入导致软件在处理输入数据时出现非预期错误,将输入数据写入内存中的某些特定敏感位置,从而劫持软件控制流,转而执行外部输入的指令代码,造成...
  • C++内存池的简单原理及实现

    千次阅读 2019-05-05 18:11:03
    为什么要用内存池 C++程序默认的内存管理(new,delete,malloc,free)会频繁地在堆上分配和释放内存,导致性能的损失,产生大量的内存碎片,降低内存的...内存原理 内存池的思想是,在真正使用内存之前,预先...
  • 文档是本科时候的一个课程设计,里面详细介绍了EM4100 ID卡的读写原理以及电路设计,文末附了PCB图以及原理电路图,并且有示波器波形仿真分析。
  • 内存:存储数据和程序。这类存储器通常是易失性的;输入输出模块:在计算机和外部环境之间移动数据;系统总线:在处理器、内存和输入输出模块间提供通信的设施。 1.2定义处理器寄存器的两种主要类别 答:用户可见...
  • Netty内存原理分析

    万次阅读 多人点赞 2015-04-29 22:13:08
    为了避免频繁的内存分配给系统带来负担以及GC对系统性能带来波动,Netty4提出了全新的内存管理,使用了全新的内存池来管理内存的分配和回收。内存池这块的代码晦涩难懂,而且几乎没有注释,所以阅读起来比较费力,...
  • 共享内存原理

    千次阅读 2015-04-08 22:54:12
    共享内存区域是被多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。共享内存是进程间共享数据的一种最...
  • Redis的内存淘汰算法和原理是什么

    千次阅读 2022-04-01 11:06:53
    Redis的内存淘汰算法和原理是什么 Redis里面的内存淘汰策略,是指当内存的使用率达到了maxmemory的上限的时候,它的一种内存释放的一个行为.Redis里面提供了很多种内存的淘汰算法归纳起来主要有4种 1.随机,随机移除...
  • 内存基本存储原理: cpu先要从RAM中获取数据,需要首先给地址总线传递数据的定位地址,在若干个时钟周期之后,数据总线就会把数据传送给CPU.当地址解码器收到地址总线送来的地址数据后,它就会根据这个地址数据找到相...
  • 介绍内存与CPU的连接
  • 文章目录1. 常见内存泄漏1.1 “单例模式” 造成的内存泄漏1.2 “静态实例... LeakCanary使用与原理解析2.1 LeakCanary使用方法2.2 LeakCanary原理解析  在《Android性能优化(1):常见内存泄漏与优化(一)》和《An...
  • 《操作系统:精髓与设计原理(第6版)(英文版)》是讲解操作系统的经典教材,全书不仅系统地讲述了操作系统的基本概念、原理和方法,而且以当代最流行的操作系统——Windows Vista、UNIx和Linux为例,全面清楚地展现了...
  • 操作系统精髓与设计原理 第9版总结

    千次阅读 2019-04-02 23:04:43
    计算机4个主要部件:CPU 内存(主存) I/O 系统总线 MAR:存储器地址寄存器 MBR:存储器缓冲寄存器 计算机其实所做的事就是:取指令和执行指令 取到的指令被放到:指令寄存器(IR) 指令也分为四类: 举...
  • 深入理解内存原理简介

    万次阅读 多人点赞 2016-12-27 23:21:04
    取指令 从内存中取出当前指令,并生成下一条指令在内存中的地址。  分析指令 指令取出后,控制器还必须具有两种分析的功能。一是对指令进行译码或测试,并产生相应的操作控制信号,以便启动规定的动作。比如一次...
  • 揭秘Java虚拟机:JVM设计原理与实现

    千次阅读 2018-12-25 08:57:33
    《揭秘Java虚拟机:JVM设计原理与实现》从源码角度解读HotSpot的内部实现机制,本书主要包含三大部分——JVM数据结构设计与实现、执行引擎机制及内存分配模型。 数据结构部分包括Java字节码文件格式、常量池解析、...
  • ThreadLocal原理内存泄露预防

    万次阅读 多人点赞 2018-05-24 13:38:21
    原理 为什么key使用弱引用 内存泄露 线程池 参阅:http://www.importnew.com/22039.html 前言 ThreadLocal提供了线程独有的局部变量,可以在整个线程存活的过程中随时取用,极大地方便了一些逻辑的实现。...
  • 背景众所周知,内存的高低是评判一款app的性能优劣的重要的指标之一。如何更简单的帮助开发者分析、暴露且解决内存泄漏问题,几乎是每一个平台或框架、开发者亟需的一个的"标配"...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 438,646
精华内容 175,458
关键字:

内存设计原理

友情链接: DiMir_2.rar