信息,内核专门留出一块内存区,内存区可以由需要访问的进程将其映射
到自己的私有地址空间,进程直接读写这一内存区,而不需要进行数据
的复制,提高了效率。由于多个进程共享内存,需要依靠同步机制如
互斥锁和信号量。
共享内存的实现分为三个步骤:
1. 创建共享内存,shmget()
shmget(创建或打开共享内存)
表头文件 #include <sys/ipc.h>
#include <sys/shm.h>
函数定义 int shmget(key_t key, size_t size, int shmflg);
函数说明 key:IPC_PRIVATE或ftok的返回值,标识的ID
size:共享内存区大小。shmflg:同 open函数的权限位,也可以用8进制表示法
返回值:成功返回内存标识符,错误返回-1
#define IPC_CREAT01000/* Create key if key does not exist. */
#define IPC_EXCL02000/* Fail if key exists. */
#define IPC_NOWAIT04000/* Return error on wait. */
2. 映射共享内存,把创建的共享内存映射到具体的进程空间,shmat()
shmat(映射共享内存)
表头文件 #include <sys/types.h>
#include <sys/shm.h>
函数定义 void *shmat(int shmid, const void *shmaddr, int shmflg);
函数说明 shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)
shmflg:SHM_RDONLY:共享内存只读,默认为0,共享内存可读写
返回值:成功映射后的地址,错误:-1
3. 撤销映射shmdt()
shmdt(共享内存从进程中分离出来)
表头文件: #include <sys/types.h>
#include <sys/shm.h>
函数定义: int shmdt(const void *shmaddr);
函数说明:shmaddr:共享内存后映射的地址
返回值:成功0,错误-1
案例:创建一个共享内存区,之后创建子进程,在父子两个进程中将共享内存
分别映射到各自的地址空间中
父进程先等待用户输入,然后用户输入的字符串写入到共享内存,之后往共享
内存的头部写入WROTE字符串表示进程已成功写入数据。子进程一直等到共享
内存的头部字符串为WROTE,然后将共享内存的有效数据(在父进程中用户输入的字符串)
在屏幕上打印。父子两个进程在工作之后,分别解除与共享内存的映射关系。
用标志字符串来实现父子之间的同步
命令ipcs,用于报告进程间通信机制状态的命令,它可以查看共享内存,消息队列
等各种进程间通信机制的情况,用system()函数调用shell命令ipcs
- sem_com.h
- -------------------------------------
- #ifndef __SEM_COM_H__
- #define __SEM_COM_H__
- /**
- *信号量相关的函数调用接口复杂,将它们封装成二维单个
- *信号量的几个基本函数。
- *信号量初始化函数init_sem()
- *P操作加一sem_p()
- *V操作减一sem_v()
- *从系统中删除信号量del_sem()
- */
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- union semun {
- int val;
- };
- extern int init_sem(int sem_id, int init_value);
- extern int sem_p(int sem_id);
- extern int sem_v(int sem_id);
- extern int del_sem(int sem_id);
- #endif
- ----------------------------------------------------
- sem_com.c
- -----------------------------------------------------
- #include "sem_com.h"
- /**信号量初始化函数*/
- int init_sem(int sem_id, int init_value)
- {
- union semun sem_union;
- sem_union.val = init_value; //init_value为初始值
- if (semctl(sem_id, 0, SETVAL, sem_union) == -1) {
- perror("Initialize semaphore");
- return -1;
- }
- return 0;
- }
- /**从系统中删除信号量的函数*/
- int del_sem(int sem_id)
- {
- union semun sem_union;
- if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1) {
- perror("Delete semaphore");
- return -1;
- }
- }
- /**P操作*/
- int sem_p(int sem_id)
- {
- struct sembuf sem_b;
- sem_b.sem_num = 0;//单个信号量的编号应该为0
- sem_b.sem_op = -1;//表示P操作
- sem_b.sem_flg = SEM_UNDO;//系统自动释放将会在系统中残留的信号量
- if (semop(sem_id, &sem_b, 1) == -1) {
- perror("semop");
- return -1;
- }
- return 0;
- }
- /**V操作*/
- int sem_v(int sem_id)
- {
- struct sembuf sem_b;
- sem_b.sem_num = 0;//单个信号量的编号应该为0
- sem_b.sem_op = 1;//表示V操作
- sem_b.sem_flg = SEM_UNDO;
- if (semop(sem_id, &sem_b, 1) == -1) {
- perror("semop V");
- return -1;
- }
- return 0;
- }
- ----------------------------------------------------
- shmem.c
- ----------------------------------------------------
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "sem_com.h"
- #define BUFFER_SIZE 2048
- pid_t pid;
- int shmid;//要映射的共享内存区标识符
- int semid;//定义信号量,用于实现共享内存的进程之间的互斥
- char *shm_addr;//共享内存映射到的地址
- char buff[BUFFER_SIZE];
- void function_init()
- {
- //创建一个信号量
- semid = semget(ftok("/home",2), 1, 0666|IPC_CREAT);
- init_sem(semid, 1);//初始值为1
- //创建共享内存
- shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666);
- if (shmid < 0) {
- perror("shmget");
- exit(1);
- } else {
- printf("create shard-memory shmid: %d\n", shmid);
- }
- }
- void function_end()
- {
- //删除信号量
- del_sem(semid);
- //解除共享内存映射
- if ((shmdt(shm_addr)) < 0) {
- perror("shmdt");
- exit(1);
- }
- //删除共享内存IPC_RMID 删除共享内存段
- if (shmctl(shmid, IPC_RMID, NULL) == -1) {
- perror("shmctl");
- exit(1);
- } else {
- printf("Delete shared-memory\n");
- }
- }
- int main()
- {
- function_init();
- pid = fork();
- if (pid == 0) {//子进程
- //映射共享内存
- shm_addr = shmat(shmid, NULL, 0);
- if (shm_addr == (void *)-1) {
- perror("Child: shmat");
- exit(1);
- } else
- {
- do{
- sem_p(semid);
- printf("\nChild:Attach shared-memory %p :%s \n", shm_addr, shm_addr);
- if (strncmp(shm_addr, "quit", 4) == 0) {
- break;
- }
- memset(shm_addr, 0, BUFFER_SIZE);
- sem_v(semid);
- } while(1);
- }
- //显示共享内存情况
- //system("ipcs -m");
- } else if(pid > 0){//父进程
- //映射共享内存
- shm_addr = shmat(shmid, 0, 0);
- //父进程向共享内存定入数据
- do {
- sem_p(semid);//信号值减一
- printf("Parent:Enter some text to the shared memory(enter 'quit' to exit):\n");
- if(fgets(buff, BUFFER_SIZE, stdin) == NULL) {
- perror("fgets");
- sem_v(semid);
- break;
- }
- strncpy(shm_addr, buff, strlen(buff));
- sem_v(semid);
- } while(strncmp(buff, "quit", 4) != 0);
- function_end();
- }
- exit(0);
- }
- ---------------------------------------------------
- shmem#./bts
- create shard-memory shmid: 1966089
- PARENTS:Enter some text to the shared memory(enter 'quit' to exit):
- hi child! "good afternoon"
- Child:Attach shared-memory 0xb78af000 :hi child! "good afternoon"
- PARENTS:Enter some text to the shared memory(enter 'quit' to exit):
- ok let's go
- Child:Attach shared-memory 0xb78af000 :ok let's go
- PARENTS:Enter some text to the shared memory(enter 'quit' to exit):
- quit
- Delete shared-memory
- Child:Attach shared-memory 0xb78af000 :quit