共享內存就是能被不同進程共同訪問的一塊內存。
函數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端都被正確地讀取出來了。