·使用共享内存对象实现无关进程间通信的方法
·掌握POSIX共享内存对象的使用方法
·共享内存的基础是内存映射
·用户进程建立内存映射的操作函数时mmap,其原型:
#include
·调用munmap 函数会删除创建的映射区域;其函数原形:
·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
· //执行生产的可执行程序
· ./shm-posix-producer
· ./shm-posix-consumer
程序中producer和consumer进程是先后运行的,从程序结构上来说producer先向共享内存区域写人数据,然后consumer 输出共享内存区域中的数据。如果producer运行过程中不断向共享内存输人数据,每次输入后consumer可以自动地显示内容.应该怎么做呢?仔细分析,就可以发现这其实是一个单向同步制约问题,可以使用信号量机制来完成。方法是:每次producer写入数据后调用信号量的signal函数,而consumer则在显示共享内存区域内容之前先调用信号量的wait操作函数。
使用内存映射文件时需要先用open函数打开文件,然后调用mmap函数把得到的描述符映射到当前进程地址空间。
首先打开一个POSIX IPC对象,然后调用mmap将返回的描述符映射到当前进程的地址空间。打开和撤销POSIX共享内存对象的函数如下:
int shm_open(const char *name,int oflag,mode_t mode);
int shm_unlink(const char *name)
将mmap函数的flags参数指定MAP_SHARED|MAP_ANONYMOUS,把fd参数指定为-1,offset参数则忽略。
内存映射文件的优点在于可以保密文件,减少I/O操作。内存映射,并不是将文件加载到内存。内存映射首先申请一段地址空间,并映射到物理存储器,而这里的物理存储器就是文件所在的磁盘,类似虚拟内存(pagefile);当有需要时,程序不需要先把它加到内存,而是直接从磁盘读取。从这里看,IO操作减少了(不需要先加载到内存)。
//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