Linux进程通信之共享内存

目录

1、shmget

2、shmat

3、shmdt

4、shmctl

5、实操


        共享内存,顾名思义就是申请一段公共的内存空间,供多个进程共同访问;各个进程分工对这段内存进行读写,达到通信的目的。

        各个进程从自己的逻辑空间到这段物理内存位置,建立一个映射。

        共享内存接口未提供对共享内存访问的同步控制机制,这需要我们自己完成。就可以使用信号量机制来实现。

        共享内存机制接口都在 头文件<sys/shm.h> 中声明。

1、shmget

        用于申请一段新的共享内存段,或通过key获取一个已有的共享内存段的标识符。

函数原型

int shmget(key_t key, size_t size, int shmflg);

        key:一个键值 ,可以是任意一个合法范围内的数值。各个进程通过同一个键值,来获取一段相同的共享内存的标识符。

        size:以字节为单位指定共享内存段的大小。

        shmflg:包含9个bit的权限标志,作用与创建文件时使用的mode标志一样。权限标志用来指定第一个申请该共享内存的进程 、同组的进程和其他进程对这段共享内存的访问权限。由一个 特殊标志IPC_CREAT 与 权限标志 按位或才能申请一个新的共享内存段。

        成功返回共享内存标识符;失败返回-1

2、shmat

        用于建立本进程的逻辑地址与共享内存的映射。

函数原型

void *shmat(int shm_id, const void *shm_addr, int shmflg);

        shm_id:shmget()获取到的共享内存标识符。

        shm_addr:指定将共享内存连接到当前进程中的地址位置。可以为NULL由系统来选择与共享内存建立映射的地址

        shmflg:有两个可能取值是SHM_RND(这个标志与shm_addr联用,用来控制共享内存连接的地址)和SHM_RDONLY(对连接的共享内存只读)。SHM_RDONLY被设置后,即使在shmget()时设置有读外的其他权限,其他权限也会失效

        成功返回将共享内存连接到本进程的内存中的地址;失败返回NULL。

3、shmdt

        用于释放本进程与共享内存的连接。而不是释放共享内存段。

函数原型

int shmdt(const void* shmaddr);

        shmaddr:shmat()返回的将共享内存连接到本进程的内存中的地址。

        成功返回0;失败返回-1。

4、shmctl

        用于控制共享内存段。设置获取共享内存的访问权限,或释放共享内存段

函数原型

int shmctl(int shm_id, int command, struct shmid_ds *buf);

        shmid:共享内存标识符。

        buf:结构体shmid_ds的指针。

struct shmid_ds{
    uid_t shm_perm.uid;//进程所属用户的id
    uid_t shm_perm.gid;//进程所属用户的组id
    mode_t shm_perm.mode;//访问权限
}

        command:要采取的动作:

命令说明
IPC_STAT将共享内存当前的权限值保存到shmid_ds结构中
IPC_SET把共享内存当前的权限值设置为shmid_ds结构中保存的
IPC_RMID删除共享内存段

5、实操

        Reader进程读取共享内存内容,Writer进程向共享内存中写入数据。使用信号量控制同步。

        信号量使用Linux信号量中封装的信号量接口。

//sem.h
#include<stdio.h>
#include<unistd.h>
#include<sys/sem.h>
#include<stdlib.h>

union semun{
    int value;
};

void sem_init();//信号量初始值
void sem_p();
void sem_v();
void sem_destroy();          
//sem.c
#include"sem.h"
 
static int semid;//静态全局变量 .data区 本文件有效

void sem_init(){
    semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
    //创建失败(已存在,直接获取)
    if(semid==-1){
        semid=semget((key_t)1234,1,0600);//已存在,获取
        if(semid==-1) exit(1);
    }
    else{
        union semun a;
        a.value=1;
        if(semctl(semid,0,SETVAL,a)==-1){//第二个参数 下标
            perror("semctl err");
            exit(1);
        }
    }
}

void sem_p(){
    struct sembuf buf;
    buf.sem_num=0;
    buf.sem_op=-1;
    buf.sem_flg=SEM_UNDO;

    if(semop(semid,&buf,1)==-1){
        perror("semop err");
        exit(1);
   }
}

void sem_v(){

    struct sembuf buf;
    buf.sem_num=0;
    buf.sem_op=1;
    buf.sem_flg=SEM_UNDO;

    if(semop(semid,&buf,1)==-1){
        perror("semop err");
        exit(1);
    }
}

void sem_destroy(){
    if(semctl(semid,0,IPC_RMID)==-1){
        perror("semctl err");
        exit(1);
    }
}
//Reader.c
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

#include<sys/shm.h>

#include"../信号量/sem.h"

int main(){
    sem_init();

    int shmid = shmget((key_t)1234,1024, 0666 | IPC_CREAT);
    if(shmid==-1){
        fprintf(stderr,"shmget failed\n");
        exit(1);
    }

    char *m=shmat(shmid,NULL,0);
    if(m==NULL){
        fprintf(stderr,"shmat failed\n");
        exit(1);
    }

    while(1){
        printf("Waiting for writer...\n");
        sem_p();
        printf("read: %s\n",m);
        sleep(rand()%4);
        sem_v();
        if(strncmp(m,"end",3)==0){
            break;
        }
    }

    if(shmdt(m)==-1){//删除连接
        fprintf(stderr,"shmdt failed\n");
        exit(1);
    }
    //再由读进程负责释放共享内存段
    if(shmctl(shmid,IPC_RMID,NULL)==-1){
        fprintf(stderr,"shmctl(IPC_RMID) failed\n");
        exit(1);
    }

    sem_destroy();
    exit(0);
}
//Writer.c
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

#include<sys/shm.h>

#include"../信号量/sem.h"

int main(){
    sem_init();

    int shmid = shmget((key_t)1234, 1024, 0666 | IPC_CREAT);
    if(shmid==-1){
        fprintf(stderr,"shmget failed\n");
        exit(1);
    }

    char *m=shmat(shmid,NULL,0);
    if(m==NULL){
        fprintf(stderr,"shmat failed\n");
        exit(1);
    }
    printf("Memory attached at %p\n",m);

    while(1){
        sem_p();

        char buff[1024]={0};
        //从键盘输入数据
        printf("Enter some text: ");
        fgets(buff,1024,stdin);

        strcpy(m,buff);

        sem_v();

        if(strncmp(buff,"end",3)==0){
            break;
        }
    }

    if(shmdt(m)==-1){//写进程只删除连接
        fprintf(stderr,"shmdt failed\n");
        exit(1);
    }

    exit(0);
}

         先执行写进程,申请到共享内存段,进行p操作,获取到对共享内存的操作权限,等待输入。读进程根据相同的键值,获取到共享内存的标识符,建立映射,执行p操作阻塞。

        从键盘输入 hello ,写进程将hello写入共享内存段,执行v操作。此时,读进程p操作成功,获取到对共享内存操作的权限,并从共享内存段中读到 hello 。再随机等待几秒后执行v操作,再使写进程能p操作成功。

        输入end后结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值