共享内存
共享内存主要用于实现进程间大量数据传输。
共享内存的数据结构定义:
系统对共享内存的限制:
共享内存与管道的对比:
可以看到,共享内存的优势:
1.共享内存只需复制2次,而管道需要4次
2.共享内存不需要切换内核态与用户态,而管道需要。
共享内存效率高!
int shmget (key_t __key, size_t __size, int __shmflg) :创建共享内存
第一个参数:key值
第二个参数:欲创建的共享内存段的大小(字节)
第三个参数:shmflg创建标识,包括IPC_CREAT, IPC_EXCL, IPC_NOWAIT, SHM_R(可读), SHM_W(可写)
int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) :共享内存控制
第一个参数:要操作的共享内存标识符
第二个参数:要执行的操作,IPC_RMID, iPC_SET, IPC_STAT, IPC_INFO, 超级用户还有 SHM_LOCK(锁定共享内存段), SHM_UNLOCK(解锁共享内存段)
第三个参数:临时共享内存变量信息
void *shmat (int __shmid, __const void * __shmaddr, int __shmflg) : 挂载共享内存到当前进程,返回共享内存首地址
第一个参数:要操作的共享内存标识符
第二个参数:指定共享内存映射地址,如果是0,系统选择。
第三个参数:指定共享内存段的访问权限和映射条件,0表示可读可写
int shmdt (__const void *__shmaddr) :把共享内存与当前进程分离,参数为共享内存映射地址
测试,在只读共享内存中写信息:
#include<unistd.h> #include<stdlib.h> #include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #include<errno.h> int main(int argc, char * argv[]) { key_t key; int shm_id; char *ptr; key = ftok("/", 10); shm_id = shmget(key, 100, IPC_CREAT|SHM_R); //创建shm printf("get the shm id is %d\n", shm_id); //打印id if((ptr = (char *)shmat(shm_id, NULL, SHM_RDONLY)) == NULL) //只读方式挂载 { if(shmctl(shm_id, IPC_RMID, NULL) == -1) //如果失败则删除 perror("Failed to remove memory segment"); exit(EXIT_FAILURE); } //打印挂载地址 printf("the attach add is %p\n", ptr); printf("now try to write the memory\n"); *ptr = ‘d‘; printf("*ptr =%c\n", *ptr); shmdt(ptr); shmctl(shm_id, IPC_RMID, 0); }
发生段错误:
父子进程间对共享内存的约定:
- fork()的子进程继承父进程挂载的共享内存。
- 调用exec执行新程序,则共享内存被自动卸载。
- 如果调用了exit(),挂载的共享内存与当前进程脱离关系。
下面是一个应用的例子
实现两个没有亲缘关系进程的通信,一个负责写,另一个负责接收。用信号量实现同步,即写的时候不可读,读的时候不可写。用一元信号量实现,0表示可写,1表示可读
注意:在代码实现中,实际上是读写轮流操作的,即写一次,读一次。并没有达到真正多进程的效果。
代码经验证,可以使用
发送端代码:
#include<unistd.h> #include<stdlib.h> #include<stdio.h> #include<string.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #include<sys/sem.h> #include<errno.h> int main(int argc, char *argv[]) { int running = 1; int shid; int semid; int value; void *sharem = NULL; struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_flg = SEM_UNDO; //创建信号量 if((semid = semget((key_t)123456, 1, 0666|IPC_CREAT)) == -1) { perror("semget"); exit(EXIT_FAILURE); } //初始化信号量为0 if(semctl(semid,0,SETVAL,0) == -1) { printf("sem init error"); if(semctl(semid, 0, IPC_RMID, 0) != 0) { perror("semctl"); exit(EXIT_FAILURE); } exit(EXIT_FAILURE); } //创建共享内存 shid = shmget((key_t)654321, (size_t)2048, 0600|IPC_CREAT); //创建共享内存 if(shid == -1) { perror("shmget"); exit(EXIT_FAILURE); } //挂载共享内存到当前进程 sharem = shmat(shid, NULL, 0); if(sharem == NULL) { perror("shmat"); exit(EXIT_FAILURE); } while(running) { //测试信号量值,如果为0则可写 if((value = semctl(semid, 0, GETVAL)) == 0) { printf("write data operate\n"); printf("please input something:"); scanf("%s", sharem); sem_b.sem_op = 1; //执行信号量加1操作,允许读 if(semop(semid, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); exit(EXIT_FAILURE); } } //比较是否是结束符号 if(strcmp(sharem, "end") == 0) running--; } shmdt(sharem); return 0; }
接收端代码:
#include<unistd.h> #include<stdlib.h> #include<stdio.h> #include<string.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #include<sys/sem.h> #include<errno.h> int main(int argc, char *argv[]) { int running = 1; int shid; int semid; int value; void *sharem = NULL; struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_flg = SEM_UNDO; //创建信号量 if((semid = semget((key_t)123456, 1, 0666|IPC_CREAT)) == -1) { perror("semget"); exit(EXIT_FAILURE); } //创建共享内存 shid = shmget((key_t)654321, (size_t)2048, 0600|IPC_CREAT); //创建共享内存 if(shid == -1) { perror("shmget"); exit(EXIT_FAILURE); } //挂载共享内存到当前进程 sharem = shmat(shid, NULL, 0); if(sharem == NULL) { perror("shmat"); exit(EXIT_FAILURE); } while(running) { //测试信号量值,如果为1则可读 if((value = semctl(semid, 0, GETVAL)) == 1) { printf("read data operate\n"); sem_b.sem_op = -1; //执行信号量减1操作,允许写 if(semop(semid, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); exit(EXIT_FAILURE); } printf("%s\n", sharem); } //比较是否是结束符号 if(strcmp(sharem, "end") == 0) running--; } shmdt(sharem); //删除共享内存 if(shmctl(shid, IPC_RMID, 0) != 0) { perror("shmctl"); exit(EXIT_FAILURE); } //删除信号量 if(semctl(semid, 0, IPC_RMID, 0) != 0) { perror("semctl"); exit(EXIT_FAILURE); } return 0; }