System V 共享内存

System V 共享内存

fork() 创建的子进程会继承其父进程附加的共享内存段

API 函数

创建或打开共享内存

#include <sys/types.h>
#include <sys/shm.h>

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

参数 key:由 IPC_PRIVATE 或 ftok() 生成的键

参数 size:表示需分配共享内存的字节数

内核是以系统分页大小的整数被来分配共享内存的,所以会 size 会被提升到整数倍

如果用来回去已有标识符,那么参数 size 需要小于等于共享内存的大小

参数 shmflg:位掩码和指定文件权限

  • IPC_CREAT:如果不存在 key 对应的段,则创建一个新的
  • IPC_EXCL:如果同时指定 IPC_CREAT,那么 key 存在的对应的共享内存,那么返回 EEXIST 错误
  • S_IRUSR、S_IWUSR 等等

使用共享内存

#include <sys/types.h>
#include <sys/shm.h>

void *shmat( int shmid, const void * shmaddr, int shmflg );

返回共享内存段的地址

参数 shmaddr:

  • NULL,段会被附加到内核所选择的合适地址处
  • 不为NULL(不推荐
    • 没设置SHM_RND,段被附加到 shmaddr 指定的地址处,地址必须是系统分页大小的整数倍
    • 设置了SHM_RND,段被映射到的地址为 shmaddr 被舍入到最近的常量SHMLBA的倍数

参数 shmflg:

  • SHM_RDONLY:只读,试图修改会导致段错误发生(SIGSEGV信号)
  • SHM_REMAP:替换位于 shmaddr 处的已有映射
  • SHM_RND:将 shmaddr 四舍五入为 SHMLBA 字节的倍数

将共享内存段分离出其虚拟地址空间,这并不是删除共享内存

#include <sys/types.h>
#include <sys/shm.h>

int shmdt( const void *shmaddr );

参数 shmaddr:由 shmat() 调用返回的一个值

共享内存控制操作

#include <sys/types.h>
#include <sys/shm.h>

int shmctl( int shmid, int cmd, struct shmid_ds *buf );

参数 cmd:

  • IPC_RMID 删除共享内存
  • IPC_STAT:将数据结构 shmid_ds 复制到 buf
  • IPC_SET:用 buf 更新数据结构 shmid_ds

示例代码

父进程写入数据,子进程读出数据。使用信号量进行同步:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>

#if _SEM_SEMUN_UNDEFINED
union semun
{
 int val;
 struct semid_ds *buf;
 unsigned short *array;
 #if defined(__linux__)
     struct seminfo *__buf;
 #endif
};
#endif

#define SEM_KEY 0x1234 /*信号量的KEY*/
#define SHM_KEY 0x5678 /*共享内存的KEY*/

#define WRITE_SEM 0
#define READ_SEM  1

#define OBJ_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP )

#define BUFFER_SIZE 1024
struct shmseg {
    int cnt;
    char buf[BUFFER_SIZE];
};

/**
 * @brief 设置信号量集中的信号量值为1
 *
 * @param semId 信号量集
 * @param semum 信号量
 * @return int 成功返回0
 */
int initSemAvailable( int semId, int semNum )
{
    union semun arg;

    arg.val = 1;
    return semctl( semId, semNum, SETVAL, arg );
}

/**
 * @brief 设置信号量集中的信号量值为0
 *
 * @param semId 信号量集
 * @param semum 信号量
 * @return int 成功返回0
 */
int initSemInUse( int semId, int semNum )
{
    union semun arg;

    arg.val = 0;
    return semctl( semId, semNum, SETVAL, arg );
}


/**
 * @brief 保留信号量
 *
 * @param semId 信号量集标识符
 * @param semNum 信号量编号
 * @return int 成功返回0 失败返回-1
 */
int reserveSem( int semId, int semNum )
{
    struct sembuf sops;

    sops.sem_num = semNum;
    sops.sem_op = -1; /*信号量-1*/
    sops.sem_flg = SEM_UNDO;

    /*如果在信号量等于0时减1会导致阻塞*/
    while( semop( semId, &sops, 1 ) == -1 )
    {
        if( errno != EINTR) { /*在阻塞时被信号打断了*/
            perror( "EINTR\n" );
            return -1;
        }
    }

    return 0;
}

/**
 * @brief 释放信号量
 *
 * @param semId 信号量集标识符
 * @param semNum 信号量编号
 * @return int 成功返回0,失败返回-1
 */
int releaseSem( int semId, int semNum )
{
    struct sembuf sops;

    sops.sem_num = semNum;
    sops.sem_op = 1; /*信号量+1*/
    sops.sem_flg = SEM_UNDO;

    return semop( semId, &sops, 1 );
}

int main( void )
{
    int semid = semid = semget( SEM_KEY, 2, IPC_CREAT | OBJ_PERMS ); /*创建信号量集*/
    if( semid == -1 ) {
        perror("semget failed\n");
    }
    if( initSemAvailable( semid, WRITE_SEM ) == -1 ) {
        perror("initSemAvailable failed\n");
    }
    if( initSemInUse( semid, READ_SEM ) == -1 ) {
        perror("initSemInUse failed\n");
    }

    struct shmseg *shmp;
    int shmid = shmget( SHM_KEY, sizeof( struct shmseg ), IPC_CREAT | OBJ_PERMS ); /*创建共享内存*/
    if( shmid == -1 ) {
        perror("shmget failed\n");
    }

    shmp = shmat( shmid, NULL, 0); /*返回共享内存的地址*/
    if( shmp == (void *) -1 ) {
        perror("shmat failed\n");
    }

    switch( fork() ) {
        case -1:
         perror("fork failed\n");
         break;
        case 0:
            /*设置为不可读*/
            if( reserveSem( semid, READ_SEM ) == -1 ) {
                perror("reserveSem\n");
            }
            if( write( STDOUT_FILENO, shmp->buf, shmp->cnt ) != shmp->cnt ) {
                perror("write");
            }
            /*设置为可写*/
            if( releaseSem( semid, WRITE_SEM ) == -1 ) {
                perror("releaseSem");
            }
            _exit( EXIT_SUCCESS );
            break;
        default:
            /*设置不可写*/
            if( reserveSem( semid, WRITE_SEM ) == -1 ) {
                perror("reserveSem \n");
            }
            printf( "please input:\n" );
            /*从标准输入读取数据放入到共享内存*/
            shmp->cnt = read( STDIN_FILENO, shmp->buf, BUFFER_SIZE );
            if( shmp->cnt == -1 ) {
                perror("read\n");
            }
            /*设置为可读*/
            if( releaseSem( semid, READ_SEM ) == -1 ) {
                perror("releaseSem failed\n");
            }
            break;
    }
    wait( NULL ); /*等待子进程结束*/
    shmdt( shmp ); /*剥离共享内存*/
    /*删除共享内存*/
    if( shmctl( shmid, IPC_RMID, 0 ) == -1 ) {
        perror("shmctl");
    }
    /*删除信号量*/
    if( semctl( semid, 0, IPC_RMID ) != 0 ) {
        perror("semctl");
    }
    exit( EXIT_SUCCESS );
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值