linux進程間內存共享和信號量協作[基於哈工大操作系統實驗]

進程間的協作一直是做產品的永恆需求和必需技術,但是卻是一片技術灰色地帶,這裏我們基於哈工大的操作系統實驗,用實際代碼講解如何完成內存共享,和信號量協作


先給出一套完整可用的樣例程序:

生產者producer.c:


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#define PRODUCT 1000
#define KEYS 0x345379
#define SIZE 4096
#define SHM_EXIST_ERROR -1
int main()
{
	int shmid,*myArray,tmp_counter,id_counter =0;
	key_t mem_key;
	sem_t *shop_sig,*consumer_sig;
	int * tests;
	mem_key=KEYS;
	shmid = shmget(mem_key,SIZE,0666);
	if(shmid != SHM_EXIST_ERROR)
	{
		tests = (int*)shmat(shmid,NULL,0);
		if ( tests!= (void *)-1)
		{
			shmdt(tests);
			shmctl(shmid,IPC_RMID,0) ;
		}
	}
	shmid=shmget(mem_key,SIZE,IPC_CREAT|0666|IPC_EXCL);
	if(shmid==SHM_EXIST_ERROR)
	{printf("ERROR: shmget");return 0;}
	myArray=(int *)shmat(shmid,NULL,0);
	shop_sig = sem_open("shop_signal",O_CREAT,0644,1);
	consumer_sig = sem_open("consumer_signal",O_CREAT,0644,0);

	for(tmp_counter=1;tmp_counter<11;tmp_counter++)
	{
		myArray[tmp_counter]=-1;
	}
	myArray[0]=0;
	while(id_counter++<PRODUCT)
	{
		sem_wait(shop_sig);
		if(myArray[0]<10)
		{
			myArray[myArray[0]+1]=id_counter;
			myArray[0]++;
		}
		sem_post(consumer_sig);
	}
	myArray[0]=-1;
	sem_post(consumer_sig);
	printf("shop is quitting\n");
	sem_close(shop_sig);sem_close(consumer_sig);
	return 0;
}


消費者進程 consumer.c


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#define PRODUCT 1000
#define KEYS 0x345379
#define SIZE 4096
#define SHM_EXIST_ERROR -1
int main()
{
	int shmid,*myArray,tmp_counter;
	key_t mem_key;
	sem_t *shop_sig,*consumer_sig;
	mem_key=KEYS;
	shmid=shmget(mem_key,SIZE,0666);
	if(shmid == SHM_EXIST_ERROR)
	{
		printf("ERROR: shmget failure\n");return 0;
	}
	myArray=(int *)shmat(shmid,NULL,0);
	shop_sig = sem_open("shop_signal",O_CREAT,0644,1);
	consumer_sig = sem_open("consumer_signal",O_CREAT,0644,0);
	tmp_counter=0;
	while(myArray[0]!=-1)
	{
		sem_wait(consumer_sig);
		if(myArray[0]>0)
		{
			printf("con: %d\n",myArray[myArray[0]]);
			myArray[0]--;
		}
		sem_post(shop_sig);
	}
	printf("consumer is quitting\n");
	sem_close(shop_sig);sem_close(consumer_sig);
	sem_unlink("shop_signal");sem_unlink("consumer_signal");
	shmdt(myArray);
	shmctl(shmid,IPC_RMID,NULL);
	return 0;
}


控制檯編譯語法:

>gcc -o producer producer.c -lpthread
>gcc -o consumer consumer.c -lpthread


控制檯運行語法:

>sudo ./producer &
>sudo ./consumer

因爲涉及高權限的信號和內存操作,所以需要superUser權限來do  (這就是sudo)

控制檯&符號會將程序置於後臺運行,這樣就可以運行兩個程序了,而且似乎可以遞歸地運行多個(不同機器表現略不同)。

注:樣例代碼請同學們嚴重修改後再使用。

想學技術的小夥伴可以繼續看:


我們現在開始講技術,上述涉及兩種技術:1,信號量  2,共享內存 我們一一道來


一,信號量

信號量是:請見百度百科

信號量的抽象用法:請見百度百科

linux信號量具體實現:

struct _sem {
	__uint32_t	_magic;
	struct _usem	_kern;
};

typedef	struct _sem	sem_t;

semaphore 單詞是打信號和打旗語的意思

1,sem_t * sem_ open(const char * name , int flag, mode_t authority_mode, int init_value );

2,int sem_wait(sem_t * sem);

3,int sem_post(sem_t * sem);

4,int sem_close(sem_t* sem);

5,int sem_unlink(const char * sem_name);


sem_open返回一個有名信號量的系統分配指針,參數字符串name 是名字,

       flag是創建方式【O_CREAT:沒有則創建,有則返回;  O_EXCL:沒有就報錯;僅給權限值:0666、 有就返回,沒有就報錯】,

       mode是權限模式(四個用戶態的權限,一般用0666最高或0644中等),value是信號的初始值,僅在創建新信號量時用於賦值。

sem_wait 等待指定信號量大於0,這時信號量減一。

sem_opst 指定信號量值加一。

sem_close 保存指定信號量狀態到系統文件,斷開信號量連接,不刪除信號量,可以再次open不創建新的,並直接讀取原有信號值。

sem_unlink 從系統刪除指定名字信號量的存儲文件,清空值和連接,下次使用同名信號量時open函數將重建文件並初始化值。


二,共享內存

key_t 是一個宏定義,原型是signed_Int32

size_t 是一個宏定義 ,原型是 自由機器整形Integer 就是...整形

0, key_t ftok( const char * fname, int id );

1, int shmget(key_t key,size_t size,int authority_mode);                                     shared memory get

2, void * shmat(int shmid,const void *shmaddr , int location);                          shared memory allocate

3, shmdt(void * memory);                                                                                 shared memory delete

4, shmctl(int shmid , int cmd , struct shmid_ds *buf);                               shared memory change table link


ftok 根據你給出的存在的文件路徑找到文件的INODE,取出節點編號加上參數2,返回給你。一般較少使用這個函數

       注意:你可以自己定義key ,沒有任何問題,定義的key 可以任意賦值,建議大於10000,不要碰受保護的共享內存。

shmget 根據你給出的key 和大小新建或返回內存索引號碼。其中參數3 mode 【IPC_CREAT:沒有則創建,有則返回 ; IPC_EXCL:沒有就報錯】。

       注意:UBUNTU-linux是帶有嚴格權限的用戶系統,所以ubuntu下用get時,參數三要加上權限才能允許執行,例如shmget(mykey,4096,IPC_CREAT|0666);

       注意:用shmget(mykey,4096,06xx)形式的語句打開一塊確定存在的內存時,權限標誌要和申請時一致,或者權限更小,否則報錯。

shmat 根據內存索引號返回可用內存地址指針(強轉即可) ,shmaddr是你設置的物理地址,設置爲null讓系統自動分配,location是返回指針相對於內存0的偏移值,一般給0

shmdt 保存指定內存數據到系統文件,斷開內存連接,不釋放內存,可以再次open不創建新的,並直接讀取上次的內存數據。

shmctl 改變內存狀態,常用於釋放共享內存,參數2是操作類型

【IPC_STAT:得到共享內存的狀態,把共享內存的shmid_ds結構複製到buf中
IPC_SET:改變共享內存的狀態,把buf所指的shmid_ds結構中的uid、gid、mode複製到共享內存的shmid_ds結構內
IPC_RMID 從系統釋放指定索引ID的內存,清空備份文件和連接,下次使用同KEY內存時將重建共享文件和內存並初始化內存。】

參數3給NULL;


從shmat得到指針後,可以像數組一樣使用,例如:

int * mymem =(int *) shamt(shmid,NULL,0);

mymem[i]=i;

struct Node *  mynode = (struct Node * ) shmat(shmid,NULL,0);

mynode->data = 1;







講技術,說人話

Aurora極光城

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