• Linux共享内存


    1.什么是共享内存
    在前面讲虚拟内存机制时,有讲到Linux的内存映射机制
    初始化虚拟内存区域时,会把虚拟内存和磁盘文件对象对应起来。
    由于内存映射机制,一个磁盘文件对象可被多个进程共享访问,也可被多个进程私有访问。
    当共享访问时,一个进程的对该对象的修改会显示到其他进程。
    当私有访问时,修改时会产生保护故障,内核会拷贝这个私有对象,修改的是这个新对象,其他进程指向的是原来的对象。
    所以,共享内存是指不同进程访问同一个逻辑内存

    2.共享内存的使用
    Linux提供了一组共享内存API,声明在头文件sys/shm.h中。
    1)shmget函数:新建共享内存
    int shmget(key_t key,size_t size,int shmflg);
    key:共享内存键值,可以理解为共享内存的唯一性标记。
    size:共享内存大小
    shmflag:创建进程和其他进程的读写权限标识。
    返回值:相应的共享内存标识符,失败返回-1

    2)shmat函数:连接共享内存到当前进程的地址空间
    void *shmat(int shm_id,const void *shm_addr,int shmflg);
    shm_id:共享内存标识符
    shm_addr:指定共享内存连接到当前进程的地址,通常为0,表示由系统来选择。
    shmflg:通常为0
    返回值:指向共享内存第一个字节的指针,失败返回-1

    3)shmdt函数:当前进程分离共享内存
    int shmdt(const void *shmaddr);

    4)shmctl函数
    和信号量的semctl函数类似,控制共享内存
    int shmctl(int shm_id,int command,struct shmid_ds *buf);
    shm_id:共享内存标识符
    command: 有三个值
          IPC_STAT:获取共享内存的状态,把共享内存的shmid_ds结构复制到buf中。
          IPC_SET:设置共享内存的状态,把buf复制到共享内存的shmid_ds结构。
          IPC_RMID:删除共享内存
    buf:共享内存管理结构体。具体结构可参考定义。

    3.共享内存需要注意的问题
    共享内存没有同步机制,当多个进程同时向共享内存读写数据时,我们需要使用互斥锁,读写锁,信号量,条件变量等来确保数据的一致性。

    4.共享内存使用示例

    我们编写了两个程序shmread.c,shmwrite.c分别对共享内存读和写。
    需要做到可写,然后可读,然后可写,然后可读,循环下去,直到写入的是"end",结束写读进程。
    我们使用了前面讲的信号量来处理读写同步的问题。
    示例代码如下:

    shm_data.h

    #pragma once                                                                    
    #define TEXT_SZ 2048
    struct shared_use_st
    {
            char text[TEXT_SZ];
    };

    shmread.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/shm.h>
    #include<sys/sem.h>
    #include"shmdata.h"
    union semun  
    {  
        int val;  
        struct semid_ds *buf;  
        unsigned short *arry;  
    }; 
    int sem_id;
    int set_semvalue()  
    {  
        //用于初始化信号量,在使用信号量前必须这样做  
        union semun sem_union;  
      
        sem_union.val = 1;  
        if(semctl(sem_id, 0, SETVAL, sem_union) == -1)  
            return 0;  
        return 1;  
    }  
      
    void del_semvalue()  
    {  
        //删除信号量  
        union semun sem_union;  
      
        if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)  
            fprintf(stderr, "Failed to delete semaphore
    ");  
    }  
    int semaphore_p()
    {
        struct sembuf sem_b;  
        sem_b.sem_num = 0;  
        sem_b.sem_op = -1;//P()  
        sem_b.sem_flg = SEM_UNDO;  
        if(semop(sem_id, &sem_b, 1) == -1)  
        {  
            fprintf(stderr, "semaphore_p failed
    ");  
            return 0;  
        }  
        return 1;   
    }
    int semaphore_v()
    {
        struct sembuf sem_b;  
        sem_b.sem_num = 0;  
        sem_b.sem_op = 1;//V()  
        sem_b.sem_flg = SEM_UNDO;  
        if(semop(sem_id, &sem_b, 1) == -1)  
        {  
            fprintf(stderr, "semaphore_v failed
    ");  
            return 0;  
        }  
        return 1;  
    }
    int main()
    {
        void *shm=NULL;
        struct shared_use_st *shared;
        int shmid;//共享内存标识符
        //创建共享内存
        shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
        if(shmid==-1)
        {
            fprintf(stderr,"shmget failed
    ");
            exit(EXIT_FAILURE);
        }
        //将共享内存连接到当前进程的地址空间
        shm = shmat(shmid,0,0);
        if(shm==(void*)-1)
        {
            fprintf(stderr,"shmat failed
    ");
            exit(EXIT_FAILURE);
        }
        printf("memory attached at %x
    ",(int)shm);
        //设置共享内存
        shared = (struct shared_use_st*)shm;
        
        //新建信号量
        sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
    
        //信号量初始化
        if(!set_semvalue())
        {
            fprintf(stderr,"init failed.
    ");
            exit(EXIT_FAILURE);
        }
        
        while(1)
        {
            if(!semaphore_p())
                exit(EXIT_FAILURE);
            fflush(stdout);
            printf("you wrote:%s",shared->text);
            if(!semaphore_v())
                exit(EXIT_FAILURE);
            if(strncmp(shared->text,"end",3)==0)
                break;
            sleep(1);
        }
        
         //删除信号量
        del_semvalue();
    
        //把共享内存从当前进程中分离
        if(shmdt(shm)==-1)
        {
            fprintf(stderr,"shmdt failed
    ");
            exit(EXIT_FAILURE);
        }
        //删除共享内存
        if(shmctl(shmid,IPC_RMID,0)==-1)
        {
            fprintf(stderr,"shmctl(IPC_RMID) failed");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    }

    shmwrite.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/shm.h>
    #include<sys/sem.h>
    #include<string.h>
    #include"shmdata.h"
    int sem_id;
    int semaphore_p()
    {
        //对信号量做减1操作,即等待P(sv)  
        struct sembuf sem_b;  
        sem_b.sem_num = 0;  
        sem_b.sem_op = -1;//P()  
        sem_b.sem_flg = SEM_UNDO;  
        if(semop(sem_id, &sem_b, 1) == -1)  
        {  
            fprintf(stderr, "semaphore_p failed
    ");  
            return 0;  
        }  
        return 1;  
    }  
    int semaphore_v()  
    {  
        //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)  
        struct sembuf sem_b;  
        sem_b.sem_num = 0;  
        sem_b.sem_op = 1;//V()  
        sem_b.sem_flg = SEM_UNDO;  
        if(semop(sem_id, &sem_b, 1) == -1)  
        {  
            fprintf(stderr, "semaphore_v failed
    ");  
            return 0;  
        }  
        return 1;  
    }  
    int main()
    {
        sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
        
        void *shm = NULL;
        struct shared_use_st *shared = NULL;
        char buffer[200];
        int shmid;
    
        //创建共享内存
        shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
        if(shm==-1)
        {
            fprintf(stderr,"shmget failed
    ");
            exit(EXIT_FAILURE);
        }
        //将共享内存连接到当前进程的地址空间
        shm = shmat(shmid,(void*)0,0);
        if(shm == (void*)-1)
        {
            fprintf(stderr,"shmat failed
    ");
            exit(EXIT_FAILURE);
        }
        printf("memory attached at %x
    ",(int)shm);
        //设置共享内存
        shared = (struct shared_use_st*)shm;
        while(1)
        {
            if(!semaphore_p())
                exit(EXIT_FAILURE);
            //向共享内存中写入数据
            fflush(stdout);
            printf("Enter some text...
    ");
            fgets(buffer,200,stdin);
            strncpy(shared->text,buffer,TEXT_SZ);
            if(!semaphore_v())
                exit(EXIT_FAILURE);
            if(strncmp(shared->text,"end",3)==0)
                break;
            sleep(1);
        }
        //把共享内存从当前进程中分离
        if(shmdt(shm)==-1)
        {
            fprintf(stderr,"shmdt failed
    ");
            exit(EXIT_FAILURE);
        }
        sleep(2);
        exit(EXIT_SUCCESS);
    }

    输出结果:

    5.共享内存的优缺点
    1)进程间通信方便,快速。
    2)没有提供同步机制,我们需要使用互斥锁,读写锁,信号量,条件变量等来确保数据的一致性。

  • 相关阅读:
    DGbroker三种保护模式的切换
    oracle11G使用DGbroker创建dg
    oracle数据泵示例
    oracle DB_LINK
    oracle数据库rman备份计划及恢复
    oracle查看对象信息
    mybatis学习:mybatis的注解开发和编写dao实现类的方式入门
    mybatis学习:mybatis的环境搭建与入门
    mybatis框架学习:
    入门servlet:request请求转发和共享数据
  • 原文地址:https://www.cnblogs.com/shijingjing07/p/5619308.html
Copyright © 2020-2023  润新知