共享内存是内核为进程建立的一个特殊内存段,它将出如今进程本身的地址空间中,其它进程能够将同一段共享内存链接(attach)到本身的地址空间。这是最快的进程间通讯方式,可是不提供任何同步功能(须要咱们信号量实现)。数组
使用共享内存实现生产者消费者任务模式。并发
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int semget(key_t key, int size, int flag); void *shmat(int shmid, void *addr, int flag); int shmdt(void *addr); int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:得到或建立一个共享内存标识符。函数
int semget(key_t key, size_t size, int shmflag);
功能:将共享内存段链接到一个进程的地址空间中。post
void *shmat(int shm_id, const void *addr, int shmflg) ;
功能:将共享内存从当前进程中分离。ui
int shmdt(const void *shmaddr) ; //其中shmaddr为shmat返回的地址。
功能:查看及修改共享内存段的shmid_ds结构,删除该结构以及相连的共享存储段标识。spa
int shmctl(int shm_id, int command, struct shmid_ds *buf) ;
第三个参数buf是一个结构指针,它指向共享内存模式和访问权限的结构指针
struct shmid_ds { uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; };
某个模块负责产生数据(生产者),另外一个模块来负责处理(消费者)。code
编写两个应用程序,其中一个应用程序实现生产者任务,一个应用程序实现消费者任务。
生产者任务和消费者任务之间经过共享内存机制实现跨进程的共享缓冲池;在信号量集合中支持一个信号量(或利用一个POSIX信号量),实现对共享缓冲池的互斥访问;缓冲区的分配计数经过修改缓冲池结构中的计数变量来实现。 blog
缓冲池结构:进程
struct shared_use_st { //为0表示对应的缓冲区未被生产者使用,可分配但不可消费;为1表示对应 的缓冲区以被生产者使用,不可分配但可消费 //5个字符串缓冲区 char Buffer[5][100]; int Index[5]; };
缓冲区的互斥访问,5个缓冲区的缓冲池做为一个临界资源:
#Producer.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <semaphore.h> #include <fcntl.h> #define TEXT_SZ 1024 //缓冲池结构 struct shared_use_st { int Index[5]; //5个缓冲池,为0表示对应的缓冲区未被生产者使用,可分配但不可消费;为1表示对应的缓冲区已被生产者使用,不可分配但可消费 char Buffer[5][TEXT_SZ]; //5个字符串缓冲区 sem_t sem; //信号量,同步功能 }; int main() { int running = 1; int i = 0; void *shm = NULL; //共享存储段链接的实际地址 struct shared_use_st *shared = NULL; char buffer[BUFSIZ + 1]; //缓冲区存放字符 int shmid; //共享内存标识符 //得到或建立一个共享内存标识符 shmid = shmget((key_t)1121, sizeof(struct shared_use_st), 0666|IPC_CREAT); if(shmid == -1) //获取或建立一个共享内存标识符失败 { exit(EXIT_FAILURE); } shm = shmat(shmid, (void*)0, 0); //返回共享存储段链接的实际地址 if(shm == (void*)-1) { exit(EXIT_FAILURE); } printf("Memory attached at %ld\n", (intptr_t)shm); shared = (struct shared_use_st*)shm; //缓冲池为共享存储段链接地址 for( ; i < 5; i++ ) { shared->Index[i] = 0; //对缓冲池初始化,Index为0表示能够生产 } sem_init(&(shared->sem),1,1); //信号量化初始化,且信号量初始值为第二个1 i = 0; while(running) //制造一个循环 { if(sem_wait(&(shared->sem)) == -1) //sem_wait为P操做,减小信号量的值 { printf("P操做 ERROR!\n"); exit(EXIT_FAILURE); } for(i = 0; i < 5 && shared->Index[i] == 1; i++) ; if(i == 5) //Index为1表示缓冲池被消费者占用 { //当五个空间都被消费者占用时输出“waiting...” sem_post(&shared->sem); //sem_post为V操做,用来增长信号量的值 sleep(1); //sleep一段时间,再次进入循环 printf("Waiting for some time...\n"); } else { sem_post(&shared->sem); //V 操做增长信号量 printf("Enter some text with keyboard: "); fgets(buffer, BUFSIZ, stdin); //读取stdin字符流最多BUFSIZ-1个,并存在buffer数组中 其中stdin是键盘输入到缓冲区的字符 strncpy(shared->Buffer[i%5], buffer,TEXT_SZ); //读取的字符串存入缓冲区shared->Buffer中 shared->Index[i%5] = 1; //表示该缓冲区被生产者使用了 if(strncmp(buffer, "end", 3) == 0) //缓冲区的字符为end时,结束循环 { running = 0; } } } //将共享内存从当前进程中分离 if(shmdt(shm) == -1) //失败 { exit(EXIT_FAILURE); } /*查看及修改共享内存段的shmid_ds结构,删除该结构以及相连的共享存储段标识 struct shmid_ds { uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; }; */ if(shmctl(shmid, IPC_RMID, 0) == -1) //失败 { exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
#Consumer.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/shm.h> #include <unistd.h> #include <semaphore.h> #include <sys/types.h> #include <sys/ipc.h> #include <fcntl.h> #define TEXT_SZ 1024 //缓冲池结构 struct shared_use_st { int Index[5]; //5个缓冲池,为0表示对应的缓冲区未被生产者使用,可分配但不可消费;为1表示对应的缓冲区被生产者使用,不可分配但可消费 char Buffer[5][TEXT_SZ]; //5个字符串缓冲区 sem_t sem; //信号量,同步功能 }; int main() { int running = 1; int i = 0; void *shm = NULL; //共享存储段链接的实际地址 struct shared_use_st *shared = NULL; //缓冲池 int shmid; //声明共享内存标识符 shmid = shmget((key_t)1121, sizeof(struct shared_use_st), 0666|IPC_CREAT); //得到或建立一个共享内存标识符 if(shmid == -1) //获取或建立一个共享内存标识符失败 { exit(EXIT_FAILURE); } //将共享内存段链接到一个进程的地址空间中,返回void *指针 shm = shmat(shmid, 0, 0); //返回共享存储段链接的实际地址 if(shm == (void*)-1) //失败 { exit(EXIT_FAILURE); } printf("Memory attached at %ld\n", (intptr_t)shm); shared = (struct shared_use_st*)shm; //缓冲池为共享存储段链接地址 while(running) { if(sem_wait(&(shared->sem)) == -1) //sem_wait为P操做,减小信号量的值 { printf("P操做 ERROR!\n"); exit(EXIT_FAILURE); } for(i = 0; i < 5 && shared->Index[i] == 0; i++) ; //五个缓冲区没有都被生产者占用 if(i != 5) { printf("You wrote: %s\n", shared->Buffer[i%5]); //打印出生产者写入的字符 shared->Index[i%5] = 0; //为0时,表示已被消费者使用 sem_post(&shared->sem); //sem_post为V操做 sleep(1); if( strncmp(shared->Buffer[i%5], "end", 3) == 0 ) //缓冲区的字符为end时,结束循环 { running= 0; } } //五个空间都被占用,输出waiting... else { sem_post(&shared->sem); //V操做 sleep(1); printf("Waiting for some time...\n"); } } //将共享内存从当前进程中分离 if(shmdt(shm) == -1) //分离失败 { exit(EXIT_FAILURE); } if(shmctl(shmid, IPC_RMID, 0) == -1) //失败 { exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }