简单了解共享内存: http://lobert.iteye.com/blog/1746041

1 实验目的与要求

1、掌握Linux下共享内存的概念与使用方法;

2、掌握环形缓冲的结构与使用方法;

3、掌握Linux下进程同步与通信的主要机制。

实验内容

利用多个共享内存(有限空间)构成的环形缓冲,将源文件复制到目标文件,实现两个进程的誊抄。

3 实验过程与结果

实验过程:

(1)父进程使用shmget创建五个缓冲区,使用shmat挂接到父进程并形成环状缓冲区;

(2)父进程创建两个信号灯w和r,对信号灯赋初值(w为5,代表可写缓冲区数量;r为0,代表可读缓冲区数量);

(3)父进程创建子进程1和、子进程2。其中子进程1把源文件数据向缓冲区写入数据,子进程2则把缓冲区数据写入目标文件。

(4)通过对信号量的PV操作使子进程同步运行,同时,当写入目标文件的速度追上从源文件读出速度时,写操作应等待读操作。

(5)读到文件尾,将最后一次读出的字节数赋给结束标志,最后一次写入目标文件的字节数与此相等,确保誊抄正确。

(6)子进程誊写结束后,父进程删除信号量与共享缓冲区后,打印删除成功的提示信息,进程结束,

4、实验代码

#include<stdio.h>  
#include<unistd.h>  
#include<string.h>  
#include <stdlib.h>  
#include <sys/shm.h> 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/fcntl.h>

#define BLOCK_SIZES 5 //缓冲区个数
#define BUFFER_SIZE 1024 //缓冲区大小

union semun {
    int val; /* value for SETVAL */
    struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
    unsigned short *array; /* array for GETALL, SETALL */
    struct seminfo *__buf; /* buffer for IPC_INFO */
    };
union semun arg;
int semid;//信号量集合首地址
struct share_buffer  
{
	int end;  
    char buffer[BUFFER_SIZE];
	struct share_buffer *next;
};  
struct share_buffer *start=NULL;
  
char *share[BLOCK_SIZES];//指向共享缓冲区
int pid1,pid2;//子进程标识符
int running=1;
int shmid[BLOCK_SIZES];//共享缓冲区标识符  

/***对信号量数组index编号的信号量做P操作***/
void P(int semid, int index){
	struct sembuf sem={index,-1,0};
    semop(semid,&sem,1);
}

/***对信号量数组index编号的信号量做V操作***/
void V(int semid, int index){
    struct sembuf sem={index,+1,0};
    semop(semid,&sem,1);
}

void read_buf()
{
    int file_end;
	int times=0;
	const char* pathname;
	struct share_buffer *in=start;  
	int fd;//文件描述符  
	char pn[100]="/home/aloha/aaa";  
	pathname=pn; 
	if ((fd=open(pathname,O_RDONLY))==-1){
		printf("打开源文件失败\n");
		return;
	}
	else
		printf("打开源文件成功\n");
	while(running){
		P(semid,0);
		file_end=read(fd,in->buffer, BUFFER_SIZE-1);
		if(file_end!=BUFFER_SIZE-1&&file_end!=0){
			printf("最后一次从源文件读出%d字节\n",file_end);
			in->end=file_end;
			V(semid,1);
			return;
		}			
		printf("第%d次从源文件读出\n",++times);			
		in=in->next;
		V(semid,1);
	}
	exit(EXIT_SUCCESS);
}

void write_buf()
{
	int times=0;
	const char* pathname;
	struct share_buffer *out=start;  
	int fd;//文件描述符  
	char pn[100]="/home/aloha/ccdd";  
	pathname=pn; 
	if ((fd=open(pathname,O_WRONLY|O_CREAT,S_IRWXU|S_IXGRP|S_IROTH|S_IXOTH))==-1){
		printf("打开目标文件失败");
		return;
	}
	else
		printf("打开目标文件成功\n");
	while(running){	
		P(semid,1);	
		write(fd,out->buffer, BUFFER_SIZE-1);
		printf("第%d次写入到目标文件\n",++times);	
		out=out->next;
		V(semid,0);
		if(out->end!=0)//写到文件尾
		{
			printf("最后一次写入%d个字节到目标文件\n",out->end);
			write(fd,out->buffer,out->end);			
		 	return ;
		}
	}
	exit(EXIT_SUCCESS);
}
int main()  
{  
	int ret;	
	int i;  
	void* shm=NULL; 
	key_t key=1234,key1;
	struct share_buffer *head=NULL,*tail=NULL; 
	int size=sizeof(struct share_buffer);
	/***创建共享内存组***/
	for(i=0;i<BLOCK_SIZES;i++){
		shmid[i]=shmget(key,size,0666|IPC_CREAT);  
		if (shmid[i]==-1){  
		    fprintf(stderr, "shmget fail\n");  
		    exit(EXIT_FAILURE);  
		}
		key++;  
		//将共享内存连接到当前进程的地址空间  
		shm=shmat(shmid[i],(void*)0,0);  
		if(shm==(void*)-1)fprintf(stderr, "shmat fail\n");   
		//设置共享内存  
		head=(struct share_buffer *)shm;
		head->end=0;
		head->next=tail;
		if(i==0)start=head;	
		tail=head;
	}
	start->next=head;
	/***获得键值***/
	key1=ftok("/tmp", 0x66 ) ;
    if(key<0){
        perror("ftok key error") ;
        return -1 ;
    }
    /***创建两个信号量***/
    semid=semget(key,2,IPC_CREAT|0666);
    if(semid==-1){
         perror("create semget error");
         return ;
    }
    /***对两个信号量赋初值***/
	arg.val=BLOCK_SIZES;
    ret=semctl(semid,0,SETVAL,arg);//信号量semid【0】为1
	arg.val=0;
	ret =semctl(semid,1,SETVAL,arg);//信号量semid【1】为0
    if (ret < 0 ){
        perror("ctl sem error");
        semctl(semid,0,IPC_RMID,arg);
        return -1 ;
    }
	/***创建子进程***/
	 if((pid1=fork())<0){
            perror("Fail to fork");
            exit(EXIT_FAILURE);
        }
	else if(pid1==0)write_buf();
 	else {
		if((pid2=fork())<0){
			perror("Fail to fork");
			exit(EXIT_FAILURE);
		}
		else if(pid2==0)read_buf();
		else
		{
			/***等待子进程结束***/
			waitpid(pid1,NULL,0);
			waitpid(pid2,NULL,0);
			printf("子进程已结束\n");
			/***删除信号量***/
			semctl(semid,0,IPC_RMID,arg);
			semctl(semid,1,IPC_RMID,arg);
			printf("信号量删除成功\n");  
			/***删除缓冲区***/
			for(i=0;i<BLOCK_SIZES;i++)
				if (shmctl(shmid[i],IPC_RMID,0)==-1){  
				    fprintf(stderr, "shmctl(IPC_RMID) failed\n");  
				    exit(EXIT_FAILURE);  
				}
			printf("缓冲区删除成功\n");  
			printf("父进程结束\n");  
		}
	}
	return 0;  
}  

Logo

更多推荐