扩展阅读
  • linux c/c++ IP字符串转换成可比较大小的数字
  • 在win分区上安装linux和独立分区安装linux有什么区别?可以同时安装吗?(两个linux系统)
  • linux哪个版本好?linux操作系统版本详细介绍及选择方案推荐
  • 在虚拟机上安装的linux上,能像真的linux系统一样开发linux程序么?
  • secureCRT下Linux终端汉字乱码解决方法
  • 我重装window后,把linux的引导区覆盖了,进不了linux怎么办?急啊,望热心的人帮助 (现在有linux的盘)
  • Linux c字符串中不可打印字符转换成16进制
  • 红旗Linux主机可以通过127.0.0.1访问,但如何是连网的Win2000机器通过Linux的IP去访问Linux
  • Linux常用命令介绍:更改所属用户群组或档案属性
  • 安装vmware软件,不用再安装linux系统,就可以模拟linux系统了,然后可以在其上学习一下LINUX下的基本操作 了?
  • linux命令大全详细分类介绍及常用linux命令文档手册下载
  • 我重装window后,把linux的引导区覆盖了,进不了linux怎么办?急啊,望热心的人帮助 (现在没有linux的盘,只有DOS启动盘)
  • Linux Kernel 'sctp_v6_xmit()'函数信息泄露漏洞
  • 如何让win2000和linux共存。我装好WIN2000,再装LINUX7.0,但LILO只能找到LINUX,不能引导WIN2000
  • linux c下利用srand和rand函数生成随机字符串
  • 在windows中的VMware装了个linux,主板有两个串口,能做windows和linux的串口通信测试么,怎么测试这两个串口在linux是有效
  • Docker官方镜像将会使用Alpine Linux替换Ubuntu
  • 我们网站的服务器从windows2000迁往linux,ASP程序继续使用,可是我连LINUX的皮毛都不了解,大家告诉我LINUX下怎么建网站??
  • Linux下c基于openssl生成MD5的函数
  • 中文Linux与西文Linus分别哪一个版是权威?I认为是:中科软的白旗Linux与西文的绿帽子Linux!大家的看法呢?
  • Linux/CentOS下的CST和UTC时间的区别以及不一致的解决方法
  • Windows2000和Linux双操作系统,Linux系统有问题,我直接把Linux分区删除后,Windows2000进不去了,怎么办???
  •  
    当前位置:  操作系统>Linux

    linux下进程间通信:共享内存原理及具体用法举例(基于c/c++语言)

     
        发布时间:2014-4-8  


        本文导语:  linux下进程间通信:共享内存原理及具体用法举例(基于c/c++语言) 共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针...

    linux进程间通信:共享内存原理及具体用法举例(基于c/c++语言)

    共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。共享内存允许两个或多个进程进程共享同一块内存(这块内存会映射到各个进程自己独立的地址空间)从而使得这些进程可以相互通信

     在GNU/Linux中所有的进程都有唯一的虚拟地址空间,而共享内存应用编程接口API允许一个进程使用公共内存区段。但是对内存的共享访问其复杂度也相应增加。共享内存的优点是简易性。

     使用消息队列时,一个进程要向队列中写入消息,这要引起从用户地址空间向内核地址空间的一次复制 同样一个进程进行消息读取时也要进行一次复制。共享内存的优点是完全省去了这些操作。 共享内存会映射到进程的虚拟地址空间,进程对其可以直接访问,避免了数据的复制过程。  因此,共享内存是GNU/Linux现在可用的最快速的IPC机制。 进程退出时会自动和已经挂接的共享内存区段分离,但是仍建议当进程不再使用共享区段时 调用shmdt来卸载区段。

     注意,当一个进程分支出父进程和子进程时,父进程先前创建的所有共享内存区段都会被子进程继承如果区段已经做了删除标记(在前面以IPC——RMID指令调用shmctl),而当前挂接数已经变为0 这个区段就会被移除。

     linux下共享内存基本原理

    使用共享内存的目的:

    共享内存共享内存是进程间通信中最简单的方式之一。

    共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。

    当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

    使用共享内存的流程

    1.进程必须首先分配它。

    2.随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。

    3.当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块。

    共享内存分配:

    int segment_id = shmget (shm_key, int size , shmflag );

    1.进程通过调用shmget(Shared Memory GET,获取共享内存)来分配一个共享内存块。 该函数的第一个参数是一个用来标识共享内存块的键值。

    彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。

    不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突

    用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。

    2.该函数的第二个参数指定了所申请的内存块的大小。

    因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍。

    3.第三个参数是一组标志,通过特定常量的按位或操作来shmget。这些特定常量包括:

    IPC_CREAT:

     这个标志表示应创建一个新的共享内存块。通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。

    IPC_EXCL:

     这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。

     也就是说,这个标志将使线程获得一个“独有”的共享内存块。如果没有指定这个标志而系统中存在一个具有相通键值的共享内存块,

     shmget会返回这个已经建立的共享内存块,而不是重新创建一个。

    模式标志:

     这个值由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。其中表示执行权限的位将被忽略。

     指明访问权限的一个简单办法是利用<sys/stat.h>中指定,并且在手册页第二节stat条目中说明了的常量指定。

     例如,

      S_IRUSR和S_IWUSR分别指定了该内存块属主的读写权限,

      S_IROTH和S_IWOTH则指定了其它用户的读写权限。

     绑定和脱离:

    pst= shmat(iShm_id, NULL, 0)

    一个进程获取对一块共享内存的访问,这个进程必须先调用 shmat(SHared Memory Attach,绑定到共享内存)。

    将 shmget 返回的共享内存标识符 SHMID 传递给这个函数作为第一个参数。

    第二个参数是一个指针,指向您希望用于映射该共享内存块的进程内存地址;如果您指定NULL则Linux会自动选择一个合适的地址用于映射。

    第三个参数是一个标志位,包含了以下选项:   

     SHM_RND

      表示第二个参数指定的地址应被向下靠拢到内存页面大小的整数倍。

      如果您不指定这个标志,您将不得不在调用shmat的时候手工将共享内存块的大小按页面大小对齐。

     SHM_RDONLY

      表示这个内存块将仅允许读取操作而禁止写入。 如果这个函数调用成功则会返回绑定的共享内存块对应的地址。

      通过 fork 函数创建的子进程同时继承这些共享内存块;

      如果需要,它们可以主动脱离这些共享内存块。

      当一个进程不再使用一个共享内存块的时候应通过调用 shmdt(Shared Memory Detach,脱离共享内存块)

      函数与该共享内存块脱离。将由 shmat 函数返回的地址传递给这个函数。

      如果当释放这个内存块的进程是最后一个使用该内存块的进程,则这个内存块将被删除。

      对 exit 或任何exec族函数的调用都会自动使进程脱离共享内存块。

     控制和释放共享内存块:

    shmctl(iShm_id,IPC_RMID,0)<0

    调用 shmctl("Shared Memory Control",控制共享内存)函数会返回一个共享内存块的相关复杂度 iis7站长之家。同时 shmctl 允许程序修改这些信息。

    该函数的第一个参数是一个共享内存块标识。要获取一个共享内存块的相关信息,则为该函数传递 IPC_STAT 作为第二个参数,同时传递一个指向一个

    struct shmid_ds 对象的指针作为第三个参数。

    要删除一个共享内存块,则应将 IPC_RMID 作为第二个参数,而将 NULL 作为第三个参数。当最后一个绑定该共享内存块的进程与其脱离时,该共享内存块将被删除。

    应当在结束使用每个共享内存块的时候都使用 shmctl 进行释放,以防止超过系统所允许的共享内存块的总数限制。调用 exit 和 exec 会使进程脱离共享内存块,但不会删除这个内存块。 要查看其它有关共享内存块的操作的描述,请参考shmctl函数的手册页。

    共享内存的总体大小是有限制的,这个大小通过SHMMAX参数来定义(以字节为单位),

    您可以通过执行以下命令来确定 SHMMAX 的值:

    cat /proc/sys/kernel/shmmax

    修改共享内存:

    设置 SHMMAX

    # >echo "2147483648" > /proc/sys/kernel/shmmax

    您还可以使用 sysctl 命令来更改 SHMMAX 的值:

    # sysctl -w kernel.shmmax=2147483648

    最后,通过将该内核参数插入到 /etc/sysctl.conf 启动文件中,您可以使这种更改永久有效:

    # echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf

     共享内存相关系统函数简介

      shmget(  )  创建一个新的共享内存区段

                 取得一个共享内存区段的描述符

      shmctl(  )  取得一个共享内存区段的信息

                 为一个共享内存区段设置特定的信息

                 移除一个共享内存区段

      shmat(  )   挂接一个共享内存区段

      shmdt(  )   于一个共享内存区段的分离

      共享内存举例用法实例

     实例代码1

    //创建一个共享内存区段,并显示其相关信息,然后删除该内存共享区
    #include <stdio.h>
    #include <unistd.h>  //getpagesize(  )
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #define MY_SHM_ID 67483
    int main(  )
        {
            //获得系统中页面的大小
            printf( "page size=%d/n",getpagesize(  ) );
            //创建一个共享内存区段
            int shmid,ret;
            shmid=shmget( MY_SHM_ID,4096,0666|IPC_CREAT );
            //创建了一个4KB大小共享内存区段。指定的大小必须是当前系统架构
            //中页面大小的整数倍
            if( shmid>0 )
                printf( "Create a shared memory segment %d/n",shmid );
            //获得一个内存区段的信息
            struct shmid_ds shmds;
            //shmid=shmget( MY_SHM_ID,0,0 );//示例怎样获得一个共享内存的标识符
            ret=shmctl( shmid,IPC_STAT,&shmds );
            if( ret==0 )
                {
                    printf( "Size of memory segment is %d/n",shmds.shm_segsz );
                    printf( "Numbre of attaches %d/n",( int )shmds.shm_nattch );
                }
            else
                {
                    printf( "shmctl(  ) call failed/n" );
                }
            //删除该共享内存区
            ret=shmctl( shmid,IPC_RMID,0 );
            if( ret==0 )
                printf( "Shared memory removed /n" );
            else
                printf( "Shared memory remove failed /n" );
            return 0;
        }


    共享内存实例代码2

    #include <stdio.h>
    #include <sys/shm.h>
    #include <sys/ipc.h>
    #include <errno.h>
    #define MY_SHM_ID 67483
    int main(  )
        {
            //共享内存区段的挂载和脱离
            int shmid,ret;
            void* mem;
            shmid=shmget( MY_SHM_ID,0,0 );
            if( shmid>=0 )
                {
                    mem=shmat( shmid,( const void* )0,0 );
                    //shmat()返回进程地址空间中指向区段的指针
                    if( ( int )mem!=-1 )
                        {
                            printf( "Shared memory was attached in our address space at %p/n",mem );
                            //向共享区段内存写入数据
                            strcpy( ( char* )mem,"This is a test string./n" );
                            printf( "%s/n",(char*)mem );
                            //脱离共享内存区段
                            ret=shmdt( mem );
                            if( ret==0 )
                                printf( "Successfully detached memory /n" );
                            else
                                printf( "Memory detached failed %d/n",errno );
                        }
                    else
                        printf( "shmat(  ) failed/n" );
                }
            else
                printf( "shared memory segment not found/n" );
            return 0;
        }


    共享内存实例代码3

    /*内存共享区段与旗语和消息队列不同,一个区段可以被锁定。
      被锁定的区段不允许被交换出内存。这样做的优势在于,与其
      把内存区段交换到文件系统,在某个应用程序调用时再交换回内存,
      不如让它一直处于内存中,且对多个应用程序可见。从提升性能的角度
      来看,很重要的。
     */
    int shmid;
    //...
    shmid=shmget( MY_SHM_ID,0,0 );
    ret=shmctl( shmid,SHM_LOCK,0 );
    if( ret==0 )
        printf( "Locked!/n" );
    ////////////////////////////////////////////////////////////////////////
    /*使用旗语协调共享内存的例子
      使用和编译命令
      gcc -Wall test.c -o test
      ./test create
      ./test use a &
      ./test use b &
      ./test read &
      ./test remove
     */
    #include <stdio.h>
    #include <sys/shm.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #define MY_SHM_ID 34325
    #define MY_SEM_ID 23234
    #define MAX_STRING 200
    typedef struct
    {
        int semID;
        int counter;
        char string[ MAX_STRING+1 ];
    }MY_BLOCK_T;
    int main(int argc,char** argv)
        {
            int shmid,ret,i;
            MY_BLOCK_T* block;
            struct sembuf sb;
            char user;
            //make sure there is a command
            if( argc>=2 )
                {
                    //create the shared memory segment and init it
                    //with the semaphore
                  if( !strncmp(argv[ 1 ],"create",6) )
                        {
                            //create the shared memory segment and semaphore
                            printf( "Creating the shared memory/n" );
                            shmid=shmget( MY_SHM_ID,sizeof( MY_BLOCK_T ),( IPC_CREAT|0666 ) );
                            block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
                            block->counter=0;
                            //create the semaphore and init
                            block->semID=semget(MY_SEM_ID,1,( IPC_CREAT|0666 ));
                            sb.sem_num=0;
                            sb.sem_op=1;
                            sb.sem_flg=0;
                            semop( block->semID,&sb,1 );
                            //now detach the segment
                            shmdt( ( void* )block );
                            printf( "Create the shared memory and semaphore successuflly/n" );
                        }
                    else if( !strncmp(argv[ 1 ],"use",3) )
                        {
                            /*use the segment*/
                            //must specify  also a letter to write to the buffer
                            if( argc<3 ) exit( -1 );
                            user=( char )argv[ 2 ][ 0 ];
                            //grab the segment
                            shmid=shmget( MY_SHM_ID,0,0 );
                            block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
                            /*##########重点就是使用旗语对共享区的访问###########*/
                            for( i=0;i<100;++i )
                            {
                                sleep( 1 ); //设置成1s就会看到 a/b交替出现,为0则a和b连续出现
                            //grab the semaphore
                            sb.sem_num=0;
                            sb.sem_op=-1;
                            sb.sem_flg=0;
                            if( semop( block->semID,&sb,1 )!=-1 )
                                {
                                    //write the letter to the segment buffer
                                    //this is our CRITICAL SECTION
                                    block->string[ block->counter++ ]=user;
                                    sb.sem_num=0;
                                    sb.sem_op=1;
                                    sb.sem_flg=0;
                                    if( semop( block->semID,&sb,1 )==-1 )
                                        printf( "Failed to release the semaphore/n" );
                                }
                            else
                                printf( "Failed to acquire the semaphore/n" );
                            }
                           //do some clear work
                            ret=shmdt(( void*)block);
                        }
                    else if( !strncmp(argv[ 1 ],"read",4) )
                        {
                            //here we will read the buffer in the shared segment
                            shmid=shmget( MY_SHM_ID,0,0 );
                            if( shmid!=-1 )
                                {
                                    block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
                                    block->string[ block->counter+1 ]=0;
                                    printf( "%s/n",block->string );
                                    printf( "Length=%d/n",block->counter );
                                    ret=shmdt( ( void*)block );
                                 }
                            else
                                printf( "Unable to read segment/n" );
                        }
                    else if( !strncmp(argv[ 1 ],"remove",6) )
                        {
                            shmid=shmget( MY_SHM_ID,0,0 );
                            if( shmid>=0 )
                                {
                                    block=( MY_BLOCK_T* )shmat( shmid,( const void* )0,0 );
                                    //remove the semaphore
                                    ret=semctl( block->semID,0,IPC_RMID );
                                    if( ret==0 )
                                        printf( "Successfully remove the semaphore /n" );
                                    //remove the shared segment
                                    ret=shmctl( shmid,IPC_RMID,0 );
                                    if( ret==0 )
                                        printf( "Successfully remove the segment /n" );
                                }
                        }
                    else
                        printf( "Unkonw command/n" );
                }
            return 0;
        }


    linux下共享内存总结

     共享内存块提供了在任意数量的进程之间进行高效双向通信的机制。每个使用者都可以读取写入数据,但是所有程序之间必须达成并遵守一定的协议,以防止诸如在读取信息之前覆写内存空间等竞争状态的出现。不幸的是,Linux无法严格保证提供对共享内存块的独占访问,甚至是在您通过使用IPC_PRIVATE创建新的共享内存块的时候也不能保证访问的独占性。 同时,多个使用共享内存块的进程之间必须协调使用同一个键值。

    相关文章推荐:
  • linux僵尸(zombie)进程介绍及清除
  • 高手请教!linux怎样通过pid获取进程信息,如:进程名、进程状态等?
  • linux下进程占用内存空间详解
  • linux命令如何实现重启父进程而不会使其子进程退出
  • Linux进程的内核栈和用户栈概念,相互关系及切换过程
  • linux c 编写实现关闭一个进程等一分钟之后重新启动这个进程
  • Linux中最多同时可以开多少个进程,一个进程可以开多少个线程?
  • Linux守护进程 的子进程 终端处理
  • 关于Linux进程0到进程1切换的过程中相关问题的咨询
  • Linux内核进程与应用进程的通信方式有哪些?
  • 在linux,如何用共享内存来实现进程间的通讯?(这些进程没有父子关系)
  • linux下system函数调用shell命令后,怎样让主进程不等子进程返回,接着执行(在线)?
  • linux进程(线程)运行过程中如何获取本进程当前的内存使用状况,统计信息?
  • 如何linux下监控进程及其子进程占用资源?
  • linux下的进程如何访问另外一个进程建立的mutex?
  • linux中用killall命令杀死进程的时候会释放掉该进程所占有的内存吗?
  • linux内核高手进!关于调用__fork()时0号进程的子进程才能与父进程共享PID的困惑
  • 请教:在Linux下怎么样检测一个进程是否是僵尸进程??
  • 请问linux进程的哪些信息保存在内核里?哪些信息又保存在进程空间里?谢谢
  • Linux下请教一个父进程杀死子进程的问题.
  • 怎么查看Linux中所运行的进程,并且知道哪些是停止响应的,用什么命令可以杀掉停止响应的进程?


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3