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

在這裏插入圖片描述

模擬幾乎同時運行

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章