5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步

5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步

一.实验目的

·使用共享内存对象实现无关进程间通信的方法
·掌握POSIX共享内存对象的使用方法

二.实验背景

·共享内存的基础是内存映射
·用户进程建立内存映射的操作函数时mmap,其原型:
#include
5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第1张图片
·调用munmap 函数会删除创建的映射区域;其函数原形:
5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第2张图片

·调用msync来执行同步,其函数原形:
在这里插入图片描述

·stat和fstat 两个函数获取文件的信息
5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第3张图片

·POSIX提供两种在无关进程间共享内存区的方法:内存文件映射、共享内存对象

三.关键代码及分析

打开和搬销POSIX共享内存对象的函数如下:

      int shm_open(const char * name, int oflag, mode_t mode);
      int shm_unlink(const char * name);

Shm_open函数创建并打开个可用于无关进程使用的POSIX共享内存对象,其名称为name, oflag参数可以为O_RDONLY或者O_RDWR.但二者必选其一,在此基础上还可以选择O_CREAT、O_EXCL、O_TRUNC等标识。Shm_unlink则执行与shm_open相反的操作,删除指定名称的POSIX共享内存对象。普通文件或共享内存对象的大小都可以通过调用ftruncate 函数修改,其原型如下:

      int ftruncate(int fd, off_t length);
  其中fd是要调整的文件描述符, length是以字节为单位指定的调整后的文件长度。

基于POSIX共享内存区对象的个示例如下,其中 shm-posix-producer程序利用共享内存对象向shm-posix-consumer发送消息,后者接收到消息后将其显示在标准输出设备上。

int main()
{
	const int SIZE = 4096;
	const char *name = "OS";
	const char *message0= "Studying ";
	const char *message1= "Operating Systems ";
	const char *message2= "Is Fun!";

	int shm_fd;
	void *ptr;

	/* create the shared memory segment */
	shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666); //创建打开共享内存对象

	/* configure the size of the shared memory segment */
	ftruncate(shm_fd,SIZE);   // 调整共享内存对象的指定大小

	/* now map the shared memory segment in the address space of the process */
	ptr = mmap(0,SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
	if (ptr == MAP_FAILED) {
		printf("Map failed\n");
		return -1;
	}

	/**
	 * Now write to the shared memory region.
 	 *
	 * Note we must increment the value of ptr after each write.
	 */
	sprintf(ptr,"%s",message0);
	ptr += strlen(message0);
	sprintf(ptr,"%s",message1);
	ptr += strlen(message1);
	sprintf(ptr,"%s",message2);
	ptr += strlen(message2);

	return 0;
}
int main()
{
	const char *name = "OS";
	const int SIZE = 4096;

	int shm_fd;
	void *ptr;
	int i;

	/* open the shared memory segment */
	shm_fd = shm_open(name, O_RDONLY, 0666);
	if (shm_fd == -1) {
		printf("shared memory failed\n");
		exit(-1);
	}

	/* now map the shared memory segment in the address space of the process */
	ptr = mmap(0,SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
	if (ptr == MAP_FAILED) {
		printf("Map failed\n");
		exit(-1);
	}

	/* now read from the shared memory region */
	printf("%s",ptr);

	/* remove the shared memory segment */
	if (shm_unlink(name) == -1) {
		printf("Error removing %s\n",name);
		exit(-1);
	}

	return 0;
}

producer进程调用shm_open以O_CREAT|O_RDWR方式创建、打开共享内存对象.返回其描述符shm_fd.在调整了共享内存对象shm_fd尺寸为指定大小后,调用mmap将其映射到ptr指向的共享内存区域,映射标志与对象打开时的标志一致,然后使用ptr进行共享内存对象的操作。producer 对共享内存对象的操作是将字符串name和message0一起写入到共性内存对象中。而consumer 程序在调用shm_ open和mmap后则可以直接访问共享内存对象并输出producer写人的内容。

四.实验结果与分析

· //编译连接生成可执行程序
· gcc -o shm-posix-consumer shm-posix-consumer.c -lrt
· gcc -o shm-posix-producer shm-posix-producer.c -lrt

5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第4张图片

· //执行生产的可执行程序
· ./shm-posix-producer
· ./shm-posix-consumer

在这里插入图片描述

程序中producer和consumer进程是先后运行的,从程序结构上来说producer先向共享内存区域写人数据,然后consumer 输出共享内存区域中的数据。如果producer运行过程中不断向共享内存输人数据,每次输入后consumer可以自动地显示内容.应该怎么做呢?仔细分析,就可以发现这其实是一个单向同步制约问题,可以使用信号量机制来完成。方法是:每次producer写入数据后调用信号量的signal函数,而consumer则在显示共享内存区域内容之前先调用信号量的wait操作函数。

习题:

5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第5张图片

16-1:

使用内存映射文件时需要先用open函数打开文件,然后调用mmap函数把得到的描述符映射到当前进程地址空间。

16-2:

首先打开一个POSIX IPC对象,然后调用mmap将返回的描述符映射到当前进程的地址空间。打开和撤销POSIX共享内存对象的函数如下:
int shm_open(const char *name,int oflag,mode_t mode);
int shm_unlink(const char *name)

16-3:

将mmap函数的flags参数指定MAP_SHARED|MAP_ANONYMOUS,把fd参数指定为-1,offset参数则忽略。

16-4:

内存映射文件的优点在于可以保密文件,减少I/O操作。内存映射,并不是将文件加载到内存。内存映射首先申请一段地址空间,并映射到物理存储器,而这里的物理存储器就是文件所在的磁盘,类似虚拟内存(pagefile);当有需要时,程序不需要先把它加到内存,而是直接从磁盘读取。从这里看,IO操作减少了(不需要先加载到内存)。

练习:

5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第6张图片

//client_pid_fifo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>   
#include <sys/types.h>   
#include <limits.h>  

#define DESTTXT  "dest.txt"
#define SOURTXT  "data.txt"
char * NAMEDPIPE

int main()
{
	const int SIZE = 4096;
	const char *name = "OS";

	int shm_fd;
	void *ptr;
	
	int pipe_fd, sour_fd;  
    int count = 0;  
    int bytes_sent = 0;   
    int bytes_read = 0; 
    char buffer[PIPE_BUF + 1]; 
	
	int pid = getpid();
	char str2[6] = { 0 };
	char str1[] = {"client_"};
	char str3[] = {"_fifo"};
	sprintf(str2, "%d", pid);

	
	sprintf(str2, "%d", pid);
	
	NAMEDPIPE = (char *)malloc(strlen(str1) + strlen(str2) + strlen(str3) +1);
	
	strcat(NAMEDPIPE, str1);
	strcat(NAMEDPIPE, str2);
	strcat(NAMEDPIPE, str3);

	/* create the shared memory segment */
	shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);

	/* configure the size of the shared memory segment */
	ftruncate(shm_fd,SIZE);

	/* now map the shared memory segment in the address space of the process */
	ptr = mmap(0,SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
	if (ptr == MAP_FAILED) {
		printf("Map failed\n");
		return -1;
	}

	/**
	 * Now write to the shared memory region.
 	 *
	 * Note we must increment the value of ptr after each write.
	 */
	sprintf(ptr,"%s",NAMEDPIPE);
	 
  
    if(access(NAMEDPIPE, F_OK) == -1)  
    {  
        //管道文件不存在, 创建命名管道  
        if(mkfifo(NAMEDPIPE, 0666) != 0)  
        {  
            fprintf(stderr, "Could not create fifo %s\n", NAMEDPIPE);  
            exit(EXIT_FAILURE);  
        }  
    }  
  
    printf("Process %d opening FIFO O_WRONLY\n", getpid());  
    //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件  
    pipe_fd = open(NAMEDPIPE, O_WRONLY);  
    sour_fd = open(SOURTXT, O_RDONLY);  
    if(pipe_fd == -1) 
	exit(EXIT_FAILURE);
    if(sour_fd == -1) 
	exit(EXIT_FAILURE);	
    printf("Process %d result %d\n", getpid(), pipe_fd);  
    //从目标文件读取数据     
    bytes_read = read(sour_fd, buffer, PIPE_BUF);
    buffer[bytes_read] = '\0';  
    while( bytes_read > 0)  
    {  

           //向FIFO文件写数据  
            count = write(pipe_fd, buffer, bytes_read);  
            if(count == -1)  
            {  
                fprintf(stderr, "Write error on pipe\n");  
                exit(EXIT_FAILURE);  
            }  
            //累加写的字节数,并继续读取数据  
            bytes_sent += count;
    	    bytes_read = read(sour_fd, buffer, PIPE_BUF);
	    buffer[bytes_read] = '\0';    
    } 
 
    close(pipe_fd);  
    close(sour_fd);    
    printf("Process %d finished, %d bytes sent\n", getpid(), bytes_sent); 
    exit(EXIT_SUCCESS);  

	return 0;
}
//server_fifo.c
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <fcntl.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <limits.h>  
#include <string.h> 
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h> 

#define DESTTXT  "dest.txt"
#define SOURTXT  "data.txt"

char NAMEDPIPE[1024] = { 0 };

int main()
{
	const char *name = "OS";
	const int SIZE = 4096;

	int shm_fd;
	void *ptr;
	int i;
	
	int pipe_fd, dest_fd;  
    int count = 0;  
    int bytes_read = 0;  
    int bytes_write = 0;  
    char buffer[PIPE_BUF + 1];  

	/* open the shared memory segment */
	shm_fd = shm_open(name, O_RDONLY, 0666);
	if (shm_fd == -1) {
		printf("shared memory failed\n");
		exit(-1);
	}

	/* now map the shared memory segment in the address space of the process */
	ptr = mmap(0,SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
	if (ptr == MAP_FAILED) {
		printf("Map failed\n");
		exit(-1);
	}

	/* now read from the shared memory region */
	sprintf(NAMEDPIPE, "%s",(char *)ptr);

	/* remove the shared memory segment */
	if (shm_unlink(name) == -1) {
		printf("Error removing %s\n",name);
		exit(-1);
	}
	
	 //清空缓冲数组  
    memset(buffer, '\0', sizeof(buffer));    
    //以只读阻塞方式打开管道文件,注意与fifo_write01.c文件中的FIFO同名  
    pipe_fd = open(NAMEDPIPE, O_RDONLY);  
    if(pipe_fd == -1) 
	exit(EXIT_FAILURE);	
    printf("Process %d opening FIFO O_RDONLY\n", getpid());  
    //以只写方式创建保存数据的文件  
    dest_fd = open(DESTTXT, O_WRONLY|O_CREAT, 0644);  
    if(dest_fd == -1) 
	exit(EXIT_FAILURE);	
    printf("Process %d result %d\n",getpid(), pipe_fd);         

    do  
    {  
	 count = read(pipe_fd, buffer, PIPE_BUF);
         //把读取的FIFO中数据保存在文件DESTTXT中             
         bytes_write = write(dest_fd, buffer, count);  
         bytes_read += count;  
    }while(count >0) ;

	 
    close(pipe_fd);  
    close(dest_fd);   
    printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);  
    exit(EXIT_SUCCESS);  

	return 0;
}

·gcc -o client_pid_fifo client_pid_fifo.c -lrt
·gcc -o server_fifo server_fifo.c -lrt

在这里插入图片描述

· ./client_pid_fifo &
·./server_fifo

5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第7张图片
5 基于共享内存的进程间通信-实验2:使用共享内存对象实现进程同步_第8张图片

你可能感兴趣的:(操作系统)