一、实验目的
1、了解采用共享内存通信的原理。
2、掌握共享内存的创建及使用方法。
二、实验内容
1、创建写进程,其功能为创建共享内存并映射到当前进程地址空间。接着从标准输入向共享内存中循环写入数据,直至写入“end”后结束退出。
2、读进程使用和写进程相同的key打开共享内存,将共享内存映射到当前进程地址空间,并从共享内存中循环读取数据并输出至终端。
3、通过信号量方式解决读写进程的同步问题。
4、通信完成后,撤销共享内存和进程间的映射,并删除共享内存,删除信号量。
三、实验源程序及结果截图
w.c(写入内存的代码)
#include "shm_com.h"
#include "sem_com.h"
#include<signal.h>
int ignore_signal(void){
signal(SIGINT,SIG_IGN);
signal(SIGSTOP,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
return 0;
}
int main() {
//最早使用的是struct shm_buff shm_buff_inst[10];
struct shm_buff *shm_buff_inst;
void *shared_add=NULL;
char buffer[BUFSIZ];
int i=-1;
int len=1;/*i和len是用来移动指针的*/
// 生成KEY
key_t key = ftok(".", 'c');
//判断key是否生成成功
if (key < 0) {
perror("生成KEY错误");
exit(-1);
}
ignore_signal();/*防止程序非正式退出*/
// 定义信号量,用于实现共享内存进程之间的互斥
int semid = semget(key, 1, 0666|IPC_CREAT);
if (semid < 0) {
perror("创建信号量失败");
exit(-1);
}
// 初始化信号量为1
init_sem(semid,1);
// 创建共享内存
int shmid = shmget(ftok(".",'d'), 10240, 0666|IPC_CREAT);
if (shmid <0) {
perror("创建共享内存失败");
del_sem(semid);
exit(1);
}
// 将共享内存地址映射到当前进程地址空间
shared_add=shmat(shmid,(void *)0, 0);
if(shared_add==(void*)-1){
perror("映射出错");
del_sem(semid);
exit(1);
}
shm_buff_inst=(struct shared_use_at *)shared_add;
printf("映射地址为 %X\n", (int) shm_buff_inst);
do{
sem_p(semid);
printf("写入共享内存中(当输入end结束):");
i++;
/*向内存写入数据*/
if(fgets((shm_buff_inst+i*len)->buffer,SHM_BUFF_SZ,stdin)==NULL){
perror("fgets出错");
sem_v(semid);
break;
}
(shm_buff_inst+i*len)->pid=getpid();//获取进程号
sem_v(semid);
}while(strncmp((shm_buff_inst+i*len)->buffer,"end",3)!=0);
//删除映射
if(shmdt(shared_add)==1){
perror("删除映射出错:");
exit(1);
}
exit(0);
}
r.c(读取共享内存的代码)
#include "sem_com.h"
#include "shm_com.h"
int main(){
void *shared_add=NULL;
struct shm_buff *shm_buff_inst;//创建了一个结构体指针,存储每次输入的内容的进程号和字符串
char buffer[BUFSIZ];
int i=-1;
int len= 1;
// 生成KEY
key_t key = ftok(".", 'c');
if (key < 0) {
perror("生成KEY错误");
exit(-1);
}
// 获得信号量
int semid = semget(key, 1, 0666);
if (semid < 0) {
perror("信号量不存在");
exit(1);
}
// 创建共享内存sizeof(struct shm_buff)
int shmid = shmget(ftok(".", 'd'),10240, 0666 | IPC_CREAT );//共享内存的空间定的是10240
if (shmid < 0) {
perror("创建共享内存失败");
exit(1);
}
// 映射共享内存
shared_add = shmat(shmid, (void *)0, 0);
if(shared_add==(void*)-1){
perror("映射出错");
del_sem(semid);
exit(1);
}
printf("映射地址为 %x\n", (int) shared_add);
shm_buff_inst=(struct shm_buff *)shared_add;/*出问题的地方*/
do{
sem_p(semid);
i++;
printf("进程为%d,内容为%s\n",(shm_buff_inst+i*len)->pid,(shm_buff_inst+i*len)->buffer);
/*向内存读出数据*/
if(strncmp((shm_buff_inst+i*len)->buffer,"end",3)==0){
break;
}
(shm_buff_inst+i*len)->pid=0;
memset((shm_buff_inst+i*len)->buffer,0,SHM_BUFF_SZ);
sem_v(semid);
}while(1);
//删除映射
if(shmdt(shared_add)==-1){
shmdt(shared_add);
exit(0);
}
//删除信号量
del_sem(semid);
//删除共享内存
if(shmctl(shmid,IPC_RMID,NULL)==-1){
perror("删除共享内存失败");
exit(-1);
}
exit(0);
}
sem_com.h
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<unistd.h>
int init_sem(int sem_id, int init_value);
int del_sem(int sem_id);
int sem_p(int sem_id);
int sem_v(int sem_id);
sem_com.c代码
#include"sem_com.h"
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
/*信号量初始化*/
int init_sem(int sem_id, int init_value) {
union semun sem_union;
sem_union.val = init_value;
if (semctl(sem_id, 0, SETVAL, sem_union) < 0) {
printf("init_sem");
return -1;
}
return 0;
}
/*从系统中删除信号量*/
int del_sem(int sem_id) {
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) < 0) {
printf("del_sem");
return -1;
}
return 0;
}
/*P操作*/
int sem_p(int sem_id) {
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) < 0) {
printf("sem_p");
return -1;
}
return 0;
}
/*V操作*/
int sem_v(int sem_id){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) < 0){
printf("sem_v");
return -1;
}
return 0;
}
shm_com.h
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define SHM_BUFF_SZ 2048
struct shm_buff{
int pid;
char buffer[SHM_BUFF_SZ];
};
makefile
makelib: sem_com.o
gcc sem_com.c -fPIC -shared -o libsem.so
all: makelib
cp libsem.so /lib
gcc -o read r.c -L. -lsem
gcc -o write w.c -L. -lsem
进入文件夹: cd exp6
编译makefile: sudo make all
执行write文件: ./write
执行read文件: ./read
四、实验问题总结
1.权限不够问题:
在执行makefile时,提示权限不够,linux下有超级用户(root)和普通用户,普通用户不能直接操作没有权限的目录。在命令前加一个sudo即可。
2.共享内存 errno 13 Permission denied
修改key的值即可。
3.共享内存 errno 22 Invalid argument
猜测是由于之前打开的共享内存并没有关闭,重启虚拟机解决了这个问题