Linux进程间通信——共享内存

一、共享内存概述

        共享内存是一种最为高效的进程间通信方式。内核为进程提供一块内存区,每个进程可以将其映射到自己的私有空间,并且可以直接对内存进行读写,因此进程不需要对数据进行复制。当一个进程对数据进行修改时,其他进程可以立即响应。但是由于共享内存的共享性质,因此需要互斥锁和信号量机制来进行同步。

二、共享内存的应用

1.函数说明

        共享内存的实现可分为三步:

(1) 通过shmget()创建共享内存,从内存中获得一段共享内存区域
(2) 通过shmat()映射共享内存,把共享内存映射到具体的进程中
(3) 通过shmdt()撤销映射操作

2.函数格式

shmget()函数语法要点
所需头文件

#include

#include

#include

函数原型 int shmget(key_t key,int size,int shmflg)
函数传入值 key:共享内存的键值,多个进程可以通过它访问同一个共享内存,其中有个特殊值IPC_PRIVATE。它用于创建当前进程的私有共享内存
size:共享内存大小
shmflg:同open()函数的权限值,也可以用八进制表示
函数返回值 成功:共享内存段标识符
出错:-1
shmat()函数语法要点
所需头文件

#include

#include

#include

函数原型 char *shmat(int shmid,const void *shmaddr,int shmflg)
函数传入值 shmid:要映射的共享内存区域标识符
shmaddr:将共享内存映射到指定地址(若为0则表示系统自动分配地址并把该段共享内存映射到进程的地址空间)
shmflg: SHM_RDONLY:共享内存只读
默认0:共享内存可读写
函数返回值 成功:被映射的段地址
出错:-1
shmdt()函数语法要点
所需头文件

#include

#include

#include

函数原型 int shmdt(const void *shmaddr)
函数传入值 shmaddr:被映射的共享内存段地址
函数返回值 成功:0
出错:-1

(3)使用实例

/* shmem.c */
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFSIZE 2048

int main(void)
{
    pid_t pid;
    int shmid;
    char *shm_addr;
    char flag[] = "WROTE";
    char buff[BUFSIZE];   //注意这里不能使用char *buff

    if((shmid = shmget(IPC_PRIVATE,BUFSIZE,0666)) < 0)
    {
        printf("shmget error.\n");
        exit(1);
    }
    printf("Create shared-menory: %d\n",shmid);
    system("ipcs -m");

    pid = fork();
    if(pid == -1)
    {
        printf("fork error.\n");
        exit(1);
    }
    else if(pid == 0)
    {
        if((shm_addr = shmat(shmid,0,0)) == (void*)-1)
        {
            printf("Child shmat error.\n");
            exit(1);
        }
        printf("Child: Deattach shared-memory\n");
        system("ipcs -m");

        if((shmctl(shmid,IPC_RMID,NULL)) == -1)
        {
            printf("Child shmctl(IPC_RMID) error\n");
            exit(1);
        }
        printf("Child:Delete shared-memory.\n");
        system("ipcs -m");

    }
    else
    {
        if((shm_addr = shmat(shmid,0,0)) == (void*)-1)
        {
            printf("Parent shmat error.\n");
            exit(1);
        }
        printf("Parent: Attach shared-memory: %p\n",shm_addr);

        sleep(1);
        printf("Input some string:\n");
        fgets(buff,BUFSIZE,stdin);
        strncpy(shm_addr+strlen(flag),buff,strlen(buff));
        strncpy(shm_addr,flag,strlen(flag));

        if((shmdt(shm_addr)) < 0)
        {
            printf("Parent shmdt error.\n");
            exit(1);
        }
        printf("Parent: Deattach shared-memory.\n");
        system("ipcs -m");
        waitpid(pid,NULL,0);
        printf("Finished.\n");
        exit(0);
    }
}

三、信号量实现通信内存同步 实例

makefile

/* makefile */
all : shm_producer shm_customer
.PHONY : all
OBJS1 = shm_producer.o sem_com.o
OBJS2 = shm_customer.o sem_com.o
CC    = gcc
CFLAGS = -Wall -O -g

shm_producer:$(OBJS1)
        $(CC) -o shm_producer $(OBJS1) $(CFLAGS)
shm_customer:$(OBJS2)
        $(CC) -o shm_customer $(OBJS2) $(CFLAGS)
$(OBJS1):%.o:%.c
        $(CC) -c $< -o $@ 
$(OBJS2):%.o:%.c
        $(CC) -c $< -o $@

.PHONY : clean
clean:
        -$(RM) shm_producer shm_customer $(OBJS1) $(OBJS2)

sem_com.h

/* sem_com.h */
#ifndef _SEM_COM_H_
#define _SEM_COM_H_

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

int sem_init(int sem_id,int init_value);
int sem_del(int sem_id);
int sem_p(int sem_id);
int sem_v(int sem_id);

#endif

sem_com.c

/* sem_com.c*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "sem_com.h"

int sem_init(int sem_id,int init_value)
{
    union semun sem_union;
    sem_union.val = init_value;
    if(semctl(sem_id,0,SETVAL,sem_union) == -1)
    {
        perror("Initialize semaphore");
        return -1;
    }
    return 0;
}

int sem_del(int sem_id)
{
    union semun sem_union;
    if(semctl(sem_id,0,IPC_RMID,sem_union) == -1)
    {
        perror("Delete semaphore");
        return -1;
    }
    return 0;
}

int sem_p(int sem_id)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op  = -1;
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id,&sem_b,1) == -1)
    {
        perror("P operations");
        return -1;
    }
    return 0;
}

int sem_v(int sem_id)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op  = 1;
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id,&sem_b,1) == -1)
    {
        perror("V operations");
        return -1;
    }
    return 0;
}              

shm_com.h

/* shm_com.h */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SHM_BUFSIZE 2048

struct shm_buff
{
    int pid;
    char buf[SHM_BUFSIZE];
};

shm_producer.c

/* shm_producer.c */
#include "sem_com.h"
#include "shm_com.h"

int ignore_signal(void)
{
    signal(SIGINT,SIG_IGN);
    signal(SIGQUIT,SIG_IGN);
    signal(SIGSTOP,SIG_IGN);
    return 0;
}

int main()
{
    int semid,shmid;
    void *shm_addr = NULL;
    struct shm_buff *shm_buff_addr;
    char buf[SHM_BUFSIZE];

    ignore_signal();

    semid = semget((key_t)1234,1,IPC_CREAT|0666);
    if(semid == -1)
    {
        printf("Producer:semget error.\n");
        exit(1);
    }
    sem_init(semid,1);

    shmid = shmget((key_t)2345,SHM_BUFSIZE,IPC_CREAT|0666);
    if(shmid == -1)
    {
        printf("Producer:shmget error.\n");
        sem_del(semid);
        exit(1);
    }

    if((shm_addr = shmat(shmid,0,0)) == (void *)-1)
    {
        printf("Producer:shmat error.\n");
        sem_del(semid);
        exit(1);
    }
    printf("Producer:Attach shm:%p\n",shm_addr);

    shm_buff_addr = (struct shm_buff *)shm_addr;

    do
    {
        sem_p(semid);
        printf("Enter('quit' to exit):");
        if(fgets(shm_buff_addr->buf,SHM_BUFSIZE,stdin) == NULL)
        {
            printf("Producer:fgets error.\n");
            sem_v(semid);
            break;
        }
        shm_buff_addr->pid = getpid();
        sem_v(semid);
    }while(strncmp(shm_buff_addr->buf,"quit",4));

    sem_del(semid);

    if(shmdt(shm_addr) == -1)
    {
        printf("Producer: shmdt error.\n");
        exit(1);
    }
    exit(0);
}

shm_customer.c

/* shm_customer.c */
#include "sem_com.h"
#include "shm_com.h"

int main()
{
    int semid,shmid;
    struct shm_buff *shm_buff_addr;
    char buf[SHM_BUFSIZE];
    char *shm_addr;

    semid = semget((key_t)1234,1,0666);
    if(semid == -1)
    {
        printf("Customer: semget error.\n");
        exit(1);
    }

    shmid = shmget((key_t)2345,SHM_BUFSIZE,0666);
    if(shmid == -1)
    {
        printf("Customer: shmget error.\n");
        exit(1);
    }

    if((shm_addr = shmat(shmid,(void *)0,0)) == (void *)-1)
    {
        printf("Customer: shmat error.\n");
        exit(1);
    }
    printf("Customer: Attach shm: %p\n",shm_addr);

    shm_buff_addr = (struct shm_buff *)shm_addr;

    while(1)
    {
        sem_p(semid);
        printf("Customer:Producer PID:%d  :%s\n",shm_buff_addr->pid,shm_buff_addr->buf);
        if(strncmp(shm_buff_addr->buf,"quit",4) == 0)
            break;
        shm_buff_addr->pid = 0;
        memset(shm_buff_addr->buf,0,SHM_BUFSIZE);
        sem_v(semid);
    }

    if((shmdt(shm_buff_addr)) == -1)
    {
        printf("Customer:shmdt error.\n");
        exit(1);
    }

    if((shmctl(shmid,IPC_RMID,NULL)) == -1)
    {
        printf("Customer:shmctl(IPC_RMID) error.\n");
        exit(1);
    }
    exit(0);
}

 

你可能感兴趣的:(Linux)