Linux 进程间的通信(五)---共享内存通信+信号量

Linux 进程间的通信(五)—共享内存通信+信号量

共享内存通信简介

  共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制互斥锁信号量都可以.
  主要有以下几个API:shmget()、shmat()、shmdt()及shmctl()。

shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。

shmat()把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区域进行访问操作。

shmdt()调用用来解除进程对共享内存区域的映射。Shmctl()实现对共享内存区域的控制操作。


shmget函数

  shmget函数的作用是在内存中获得一段共享内存区域。shmget函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key,int size,int shmflg)

  函数返回值:该系统调用成功则返回共享内存段标识符ID,即shmid;若出错则返回-1。

  函数传入参数key为IPC结构的键值,通常取常量IPC_PRIVATE;参数size为该共享内存区大小,如果创建一个新的区域,必须指定其size参数。如果引用一个已有的区域,则size应该为0;
  参数shmflg:
    0:取共享内存标识符,若不存在则函数会报错
    IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
    IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错


shmat函数

  映射共享内存,使用函数shmat,它的作用是创建的共享内存映射到具体的进程空间去。shmat函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
char *shmat(int shmid,const void *shmaddr,int shmflg)

  函数传入参数shmid为通过shmget得到的共享内存区标识符ID;
  参数shmaddr表示 将共享内存映射到指定位置,若为0则表示把该段共享内存映射到调用进程的地址空间,推荐采用这个参数;
  参数shmflg为选项位用来设置权限,常用的选项是SHM_RDONLY,表示以只读的方式共享内存,默认为0表示以读写的方式共享内存。
  函数返回值:调用成功则返回被映射的段地址,否则返回−1。


shmdt函数

  Shmdt函数用来撤销映射,其函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr)

  传入参数Shmaddr表示被映射的共享内存段地址。函数成功则返回0,否则返回−1。


·信号量 去这里看看

·案例(通过信号对共享内存进行控制,只有当有数据写入后,才能读出其中的数据,否则就得得待有数据写入)

sharewrite_sem.c

/* ************************************************************************
 *       Filename:  sharewrite_sem.c
 *    Description:  
 *        Version:  1.0
 *        Created:  05/09/2020 02:45:05 AM
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:  YOUR NAME (WCT), 
 *        Company:  
 * ************************************************************************/

#include <sys/ipc.h>  
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int sem_id = 0;

union semun{
    
    
	int val;
	struct semid_ds *bug;
	unsigned short *arry;
};

typedef struct{
    
    
	char name[4];
	int age;
} people;

/*****************************
*	       信号量操作        *
******************************/

// 初始化设置信号令的值
static int set_semvalue(){
    
    
	union semun sem_union;

	sem_union.val = 0;
	if ( semctl( sem_id, 0, SETVAL, sem_union) == -1 ){
    
    
		return 0;
	}
	return 1;
}

// 删除不再使用信号量
static void del_semvalue(){
    
    
	union semun sem_union;

	if ( semctl ( sem_id, 0, IPC_RMID, sem_union) == -1 ){
    
    
		fprintf(stderr, "Failed to delete semaphore\n");
	}
}

// 获取某信号量的值
static int get_semvalue(){
    
    
	union semun sem_union;

	int t = semctl( sem_id, 0, GETVAL, sem_union);


	if( t == -1 ){
    
    
		// 错误处理
	}
	return t;
}

// 对信号量进行 P 操作
static 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\n");
		return 0;
	}

	return 1;
}

// 对信号量进行 V 操作
static 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\n");
		return 0;
	}

	return 1;

}
/**************************************************************************/

int main(int argc, char** argv)
{
    
    
	int shm_id,  i;
	key_t key;
	char temp;
	people *p_map;
	char* name = "/home/wct/IPC";
	key = ftok(name,0); // 将文件或目录路径转换为键值
	if(key==-1){
    
    
		perror("ftok error");
	}
	// 如果内核中存在了 key ,就返回共享内存的标识符,反之则创建
	shm_id=shmget(key,4096,IPC_CREAT); 
	// 创建信号量, 内核中存在 key 值,则返回已有信号量的标识, 反之,创建信号量
	sem_id = semget( (key_t)1234, 1, 0666 | IPC_CREAT); 
	
	set_semvalue();

	//printf( "semnum = %d\n", get_semvalue());
	if( shm_id == -1 || sem_id == -1 ){
    
    
		perror("shmget/semget error");
		return -1;
	}
	
	p_map=(people*)shmat(shm_id,NULL,0);// 将共享内存映射到该程序的进程空间,以读写的方式共享内存
	temp='a' - 1;
	for(i = 0;i<10;i++)
	{
    
    
		temp+=1;
		memcpy((*(p_map+i)).name,&temp,1);
		(*(p_map+i)).age=20+i;
		printf("Write: name: %s\tage: %d\n", ( * ( p_map + i)).name, ( *( p_map + i )).age);
		// 对信号量执行 V 操作
		if( ! semaphore_v() ){
    
    
			exit( EXIT_FAILURE );
		}
		
	//	printf( "semnum = %d\n", get_semvalue());
		sleep(rand() % 3);
	}
	
	if(shmdt(p_map)==-1){
    
    
		perror(" detach error ");
	}
	
	while( get_semvalue() > 0 ); // 等待数据被读取完

	return 0;
}

shareread_sem.c

/* ************************************************************************
 *       Filename:  shareread_sem.c
 *    Description:  
 *        Version:  1.0
 *        Created:  05/09/2020 03:08:12 AM
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:  YOUR NAME (WCT), 
 *        Company:  
 * ************************************************************************/

#include <sys/ipc.h>  
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int sem_id = 0;

union semun{
    
    
	int val;
	struct semid_ds *bug;
	unsigned short *arry;
};

typedef struct{
    
    
	char name[4];
	int age;
} people;

/*****************************
*	       信号量操作        *
******************************/

// 初始化设置信号令的值
static int set_semvalue(){
    
    
	union semun sem_union;

	sem_union.val = 0;
	if ( semctl( sem_id, 0, SETVAL, sem_union) == -1 ){
    
    
		return 0;
	}
	return 1;
}

// 删除不再使用信号量
static void del_semvalue(){
    
    
	union semun sem_union;

	if ( semctl ( sem_id, 0, IPC_RMID, sem_union) == -1 ){
    
    
		fprintf(stderr, "Failed to delete semaphore\n");
	}
}

// 获取某信号量的值
static int get_semvalue(){
    
    
	union semun sem_union;

	int t = semctl( sem_id, 0, GETVAL, sem_union);


	if( t == -1 ){
    
    
		// 错误处理
	}
	return t;
}

// 对信号量进行 P 操作
static 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\n");
		return 0;
	}

	return 1;
}

// 对信号量进行 V 操作
static 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\n");
		return 0;
	}

	return 1;

}
/**************************************************************************/

int main(){
    
    
	int shm_id, i;
	key_t key;
	people * p_map;
	key = ftok("/home/wct/IPC", 0); // 将文件或目录路径转换为键值
	if( key == -1 ){
    
    
		perror("ftok error");
	}

	// 如果内核中存在了 key ,就返回共享内存的标识符,反之则创建
	shm_id=shmget(key,4096,IPC_CREAT); 
	// 创建信号量, 内核中存在 key 值,则返回已有信号量的标识, 反之,创建信号量
	sem_id = semget( (key_t)1234, 1, 0666 | IPC_CREAT); 
	
	if( get_semvalue() > 10 || get_semvalue() < 0 ){
    
    
		set_semvalue();
	}

	if( shm_id == -1 || sem_id == -1 ){
    
    
		perror("shmget/semget error");
		return -1;
	}

	p_map = ( people * ) shmat(shm_id, NULL, 0);
	
	for( i = 0; i < 10; i ++ ){
    
    
		//printf("semnum = %d\n", get_semvalue());
		// 对信号量执行 P 操作
		if( ! semaphore_p() ){
    
    
			exit( EXIT_FAILURE );
		}
		printf("Read: name: %s\tage: %d\n", ( * ( p_map + i)).name, ( *( p_map + i )).age);
		sleep( rand() % 5 );
	}

	if( shmdt( p_map) == -1 ){
    
    
		perror("detach error");
	}
	
	del_semvalue();

	return 0;
}

在这儿你就可以随便想先运行谁就运行谁了,不受限制了

先运行,read在运行write

在这里插入图片描述

先运行,write在运行read

在这里插入图片描述

模拟几乎同时运行

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wct3344142/article/details/106117747
今日推荐