Linux环境编程之共享内存区(一):共享内存区简单介绍

共享内存区是可用IPC形式中最快的。一旦内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核。然而往该共享内存区存放信息或从中取走信息的进程间通常须要某种形式的同步。不再涉及内核是指:进程不再通过运行不论什么进入内核的系统调用来彼此传递数据。内核必须建立同意各个进程共享该内存区的内存映射关系。然后一直管理内存区。

默认情况下通过fork派生的子进程并不与其父进程共享内存区。

mmap函数把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间。使用该函数的目的有:

1、使用普通文件以提供内存映射I/O。

2、使用特殊文件以提供匿名内存映射。

3、使用shm_open以提供无亲缘关系进程间的Posix共享内存区。

#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

addr指定描写叙述符fd应被映射到的进程内空间的起始地址。

若为空指针,则由内核自己去选择起始地址。不管哪种情况。该函数的返回值都是描写叙述符fd所映射到内存区的起始地址。

len是映射到调用进程地址空间中的字节数,它从被映射文件开头起第offset个字节处開始算。

offset通常设置为0。

prot參数指定内存区映射区的保护。

有下面四种:PROT_READ(数据可读)、PROT_WRITE(数据可写)、PROT_EXEC(数据可运行)、PROT_NONE(数据不可訪问)。

flags有下面三种:MAP_SHARED(变动是共享的)、MAP_PRIVATE(变动是私有的)、MAP_FIXED(准确地解释addr參数)。

父子进程之间共享内存区的方法之中的一个是,父进程在调用fork前先指定MAP_SHARED调用mmap。

mmap成功返回后,fd參数能够关闭。该操作对由mmap建立的映射关系没有影响。

为某个进程的地址空间删除一个映射关系,我们调用munmap。

(int munmap(void *addr, size_t len))

调用msync来运行同步。

(int msync(void *addr, size_t len, int flags))

注意:不是全部文件都能进程内存映射,比如:试图把一个訪问终端或套接字的描写叙述符映射到内存将导致mmap返回一个错误。这些类型的描写叙述符必须使用read和write来訪问。

mmap的还有一个用途是在无亲缘关系的进程间提供共享的内存区。

程序实例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

#define SEM_NAME "mysem"
#define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)

int
main(int argc, char **argv)
{
	int		fd, i, nloop, zero = 0;
	int		*ptr;
	sem_t	*mutex;

	if(argc != 3){
		printf("usage:incrl <pathname> <#nloops>\n");
		return -1;
	}
	nloop = atoi(argv[2]);

	/*open file , initialize to 0, map into memory*/
	fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
	write(fd, &zero, sizeof(int));
	ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	close(fd);

	/*create, initialize, and unlink semaphore*/
	mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1);
	if(mutex < 0){
		printf("sem_open error.\n");
		return -1;
	}
	sem_unlink(SEM_NAME);

	setbuf(stdout, NULL);		/*stdout is unbuffered*/
	if(fork() == 0){
		for(i = 0; i < nloop; i++){
			sem_wait(mutex);
			printf("child:%d\n", (*ptr)++);
			sem_post(mutex);
		}
		exit(0);
	}

	/*parent*/
	for(i = 0; i < nloop; i++){
		sem_wait(mutex);
		printf("parent:%d\n", (*ptr)++);
		sem_post(mutex);
	}
	exit(0);
}

注意:内存映射一个普通文件时,内存中映射区的大小通常等于该文件的大小。然而文件大小和内存映射区大小能够不同。

原文地址:https://www.cnblogs.com/llguanli/p/8286792.html

时间: 2024-10-20 01:04:55

Linux环境编程之共享内存区(一):共享内存区简单介绍的相关文章

Linux环境编程之共享内存区(一):共享内存区简介

Spark生态圈,也就是BDAS(伯克利数据分析栈),是伯克利APMLab实验室精心打造的,力图在算法(Algorithms).机器(Machines).人(People)之间通过大规模集成,来展现大数据应用的一个平台,其核心引擎就是Spark,其计算基础是弹性分布式数据集,也就是RDD.通过Spark生态圈,AMPLab运用大数据.云计算.通信等各种资源,以及各种灵活的技术方案,对海量不透明的数据进行甄别并转化为有用的信息,以供人们更好的理解世界.Spark生态圈已经涉及到机器学习.数据挖掘.

Linux环境编程之共享内存区(二):Posix共享内存区

现在将共享内存区的概念扩展到将无亲缘关系进程间共享的内存区包括在内.Posix提供了两种在无亲缘关系进程间共享内存区的方法: 1.内存映射文件:由open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间中的一个文件.(上一节就是这种技术) 2.共享内存区对象:由shm_open打开一个Posix名字(也许是在文件系统中的一个路径名),所返回的描述符由mmap函数映射到当前进程的地址空间.(本节内容) Posix共享内存区涉及以下两个步骤要求: 1.指定一个名字参数调用shm_open

Linux环境编程之进程(二):程序的存储空间布局

引言: 一个写好的程序一般要存放在存储器中,那么程序中的代码.数据等各部分,是如何有规律的存放在存储器中的呢? (一) 一个存储的程序可分为五部分:正文段.初始化数据段.非初始化数据段.栈.堆.其典型的存储安排如下图: 正文段:这是由CPU执行的机器指令的部分.通常,正文段是可共享的,所以即使是频繁执行的程序在存储器中也只需要一个副本,另外正文段常常是只读的,以防止程序由于意外而修改其自身的指令. 初始化数据段:通常称为数据段,它包含了程序中需要明确地赋初值的变量. 非初始化数据段:通常称为bs

Linux环境编程之同步(四):Posix信号量

信号量是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语.有三种类型:Posix有名信号量,使用Posix IPC名字标识:Posix基于内存的信号量,存放在共享内存区中:System V信号量,在内核中维护.这三种信号量都可用于进程间或线程间的同步. 图1 由两个进程使用的一个二值信号量 图2 由两个进程使用的一个Posix有名二值信号量 图3 由一个进程内的两个线程共享的基于内存的信号量 一个进程可以在某个信号量上执行的三种操作: 1.创建一个信号量,这要求调用者指定初始值,对

Linux环境编程之同步(一):互斥锁

同步的内容在<UNP2>里把它看作了进程间通信,我觉得其实同步只是进程间通信的一种协作方式一种协作的手段,不能称之为进程间通信的一种形式,所以标题用了"同步",而没有用IPC进程间通信. 互斥锁是同步的基本组成部分,它们总是用来同步一个进程内的各个线程的.如果互斥锁或条件变量存放在多个进程间共享的某个内存区,那么Posix还允许它用于这些进程间的同步. 互斥锁用于保护临界区以保证任何时刻只有一个线程在执行其中的代码,或者任何时刻只有一个进程在执行其中的代码.保护临界区的代码

Linux环境编程之文件I/O(三):文件的读写

(一) 当我们打开了一个文件后,一般对文件的操作就是读写.读写函数分别是read.write. #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); 参数: fd:利用open.creat得到的文件描述符. buf:buf是void *类型,用于表示通用指针,此处指所读取到的数据的内存缓冲. count:需要读取的数据量. 返回值:成功执行时,返回所读取的数据的字节数,一般等于或小于所请求读取的数据字节数.若已到文

Linux环境编程之高级I/O(一):非阻塞I/O、记录锁

引言:高级I/O包括非阻塞I/O.记录锁.系统V流机制.I/O多路转接(select和poll函数).readv和writev函数以及存储映射I/O. (一)非阻塞I/O 可能会使进程永远阻塞的一类系统调用有: 1.如果某些文件类型的数据并不存在,则读操作可能会使调用者永远阻塞. 2.如果数据不能立即被上述同样类型的文件接受,则写操作也会使调用者永远阻塞. 3.在某种条件发生之前,打开某些类型的文件会阻塞. 4.对已经加上强制记录锁的文件进行读.写. 5.某些ioctl操作. 6.某些进程间通信

Linux环境编程之进程(四):创建新进程、执行程序和进程终止

引言: 对于每个进程,都有一个非负整数表示的唯一进程ID.虽然进程的ID是唯一的,但却是可重用的.系统中有一些专用的进程.如ID为0的进程通常是调度进程,也成交换进程或系统进程(它是内核进程).进程ID为1通常是init进程,它是一个普通的用户进程.一些与进程ID有关的函数: #include <unistd.h> pid_t getpid(void);   //返回值:调用进程的进程ID pit_t getppid(void); //返回值:调用进程的父进程ID uid_t getuid(v

Linux环境编程之文件I/O(四):文件I/O的数据结构

(一) Linux系统支持不同进程间共享打开的文件.内核使用三种数据结构表示打开的文件:进程表项.文件表项.v节点表. 1.进程表项:每个进程在进程表中都有一个记录项,记录项中年包含有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项.与每个文件描述符相关联的是: a.文件描述符标志 b.指向一个文件表项的指针 2.内核为所有打开文件维持一张文件表.每个文件表项包含: a.文件状态标志,如读写.添加.同步和非阻塞等. b.当前文件偏移量 c.指向该文件v节点表项的指针 3.每个打开的文

Linux环境编程之文件I/O(五):fcntl函数

引言: 对于一个普通的文件,我们可以想到的对它的操作有,读取文件的内容.写数据到文件中,这些都是前面提到的read.write函数的作用.除此之外,还可以获取文件的其他性质,并对这些性质进行修改,比如文件的描述符.文件描述符标记.文件状态标志等等.这些对文件性质的修改就由fcntl函数完成. 函数介绍: #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); 参数: fd: