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 );
}