進程間通信(IPC)4 ------ 共享內存(配合使用信號量進行同步)

      共享內存就是能被不同進程共同訪問的一塊內存。

      函數shmget()用以創建一個共享內存,或者訪問一個已存在的共享內存。原型如下:

#include <linux/shm.h>
int shmget(key_t key, size_t size, int shmflg);

  • 參數key是由ftok()函數或得的鍵值。
  • 參數size以字節爲單位制定內存的大小。創建新的共享內存時,size必須大於0;如果訪問一個現存的共享內存,則設置size爲0。
  • 參數shmflg是操作標誌位。一般爲IPC_CREATE。

      通過shmat()函數將共享內存附加到需要使用共享內存的進程的地址空間。這樣該進程與共享內存之間就建立了連接。shmat()函數調用成功就會返回一個指向共享內存區的指針,通過該指針就可以訪問共享內存區了。shmat函數的原型爲:

#include <linux/shm.h>
void * shmat(int shmid, const void *shmaddr, int shmflg);

  • 參數shmid爲shmget的返回值。
  • 參數shmaddr一般設爲NULL,表示由內核選擇一個空閒的內存區。

      當進程結束使用共享內存區時,要通過函數shmdt()斷開與共享內存區的連接。一般來說,當一個進程終止時,它所附加的共享內存區都會自動脫落。其原型爲:

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


      函數shmctl()用以對共享內存區進行控制。

#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);


  • 參數shmid爲共享內存區的標識符。
  • cmd爲操作標誌位,有以下3種操作:IPC_RMID,刪除共享內存區;IPC_SET,設置共享內存區的shmid_ds結構;IPC_STAT,讀取共享內存區的shmid_ds結構。
  • buf爲指向shmid_ds結構體的指針。

      以下示例演示共享內存和信號量的配合使用。要求是,一個進程讀共享內存的時候,其他進程不能寫內存;一個進程寫共享內存的時候,其他進程不能讀內存。

//share.h
//writer和reader兩個進程裏都會用到的共同的數據和函數,都放到這個頭文件裏
//讓writer和reader包含此頭文件即可,避免重複在2個文件裏輸入相同的代碼
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>

#define SHM_SIZE 1024

union semun{
	int			val;
	struct semid_ds		*buf;
	unsigned short		*array;
};

/*創建信號量函數*/
int createsem (const char * pathname,  int proj_id,  int members,  int init_val)
{
	key_t		msgkey;
	int		index,  sid;
	union semun	semopts;
			
	if ((msgkey = ftok(pathname, proj_id)) == -1)
	{
		perror ("ftok error!\n");
		return -1;
	}

	if ((sid = semget (msgkey, members, IPC_CREAT | 0666)) == -1)
	{
		perror ("semget call failed.\n");
		return -1;
	}

	/*初始化操作*/
	semopts.val = init_val;
	for (index = 0; index < members; index++)
	{
		semctl (sid, index, SETVAL, semopts);
	}
	return (sid);
}

/*打開信號量函數*/
int opensem(const char * pathname, int proj_id)
{
	key_t	msgkey;
	int	sid;         
                                                                                                 
 	if ((msgkey = ftok(pathname, proj_id)) == -1)
	{
		perror ("ftok error!\n");
		return -1;
	}

	if ((sid = semget(msgkey, 0, IPC_CREAT | 0666)) == -1)
	{
		perror("semget call failed.\n");
		return -1;
	}

	return (sid);

}

/*P操作函數*/
int sem_p(int semid, int index)
{
	struct sembuf buf = {0,-1,IPC_NOWAIT};

	if (index < 0)
	{
		perror("index of array cannot equals a minus value!");
		return -1;
	}

	buf.sem_num = index;
	if (semop (semid ,& buf,1) == -1)
	{
		perror ("a wrong operation to semaphore occurred!");
		return -1;
	}
	return 0;
} 

/*v操作函數*/
int sem_v (int semid, int index)
{
	struct sembuf buf = {0, +1, IPC_NOWAIT};

	if (index < 0)
	{
		perror("index of array cannot equals a minus value!");
		return -1;
	}

	buf.sem_num = index;
	if (semop (semid,& buf,1) == -1)
	{
		perror ("a wrong operation to semaphore occurred!");
		return -1;
	}

	return 0;
} 

/*刪除信號集函數*/
int sem_delete (int semid)
{
	return (semctl(semid, 0, IPC_RMID));
}

/*等待信號爲1*/
int wait_sem( int semid, int index)
{
	
	while (semctl (semid, index, GETVAL, 0) == 0)
	{
		sleep (1);
	}

	return 1 ;
}


/*創建共享內存函數*/
int createshm( char * pathname, int proj_id, size_t size)
{
	key_t	shmkey;
	int		sid;
			
	/*獲取鍵值*/
	if ((shmkey = ftok(pathname, proj_id)) == -1)
	{
		perror("ftok error!\n");
		return -1;
	}

	if ((sid = shmget(shmkey, size, IPC_CREAT | 0666)) == -1)
	{
				
		perror ("shmget call failed.\n");
		return -1;
	}
	return (sid);
}    

//writer.c

#include "sharemem.h"

#define SHM_SIZE 1024

int main()
{
	int	  semid, shmid;
	char  *shmaddr;
	char  write_str[SHM_SIZE];
	
	if ((shmid = createshm (".", 'm', SHM_SIZE)) == -1)
	{
		exit(1);
	}
	
	if ((shmaddr = shmat (shmid, (char *)0, 0)) ==(char *)-1)
	{
		perror ("attach shared memory error!\n");
		exit (1);
	}
	
	if ((semid = createsem (".", 's', 1, 1)) == -1)
	{
		exit (1);
	}
	
	while (1)
	{
		wait_sem (semid, 0);
		sem_p (semid, 0);  /*P操作*/
		
		printf ("writer: ");
		fgets (write_str, 1024, stdin);
		int len = strlen (write_str) - 1;
		write_str[len] = '\0';
		strcpy (shmaddr, write_str);
		sleep (10);	 /*使reader處於阻塞狀態*/
		
		sem_v (semid, 0);  /*V操作*/
		sleep (10);	 /*等待reader進行讀操作*/
		
	}
}

//reader.c

#include "sharemem.h"

int main()
{
	int	semid, shmid;
	char	*shmaddr;
	
	if ((shmid = createshm(".", 'm', SHM_SIZE)) == -1)
	{
		exit (1);
	}
	
	if((shmaddr = shmat (shmid, (char *)0, 0)) == (char *)-1)
	{
		perror ("attach shared memory error!\n");
		exit (1);
	}
	
	if((semid = opensem("." ,'s')) == -1)
	{
		exit (1);
	}
	
	while(1)
	{
		printf("reader: ");
		wait_sem(semid,0);		/* 等待信號值爲1 */
		sem_p(semid,0);			/* P操作 */
		
		printf("%s\n", shmaddr);
		sleep(10);			/* 使writer處於阻塞狀態 */
		
		sem_v(semid,0);			/* V操作 */
		sleep(10);			/* 等待writer進行寫操作 */
	}
}

      writer和reader兩個程序(進程)在進入共享內存之前,首先都檢查信號集中信號的值是否爲1(是否能進入共享內存區),如果不爲1,調用sleep()函數進入睡眠狀態直到信號的值變爲1。進入共享內存區之後,將信號的值減1(相當於加鎖),這樣就實現了互斥地訪問共享資源。在退出共享內存時,將信號加1(相當於解鎖)。

      同時在兩個終端運行writer和reader,看到在writer端輸入的文字,過一會後, 在reader端都被正確地讀取出來了。



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