Linux共享内存使用常见陷阱与分析 - 51CTO.COM http://os.51cto.com/art/201311/418977_all.htmIPC---共享内存

  共享内存就是允许两个或多个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据时,不需要在客户进程和服务器进程之间幅值,因此是最快的一种IPC。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程

注意:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量。

接口函数

1、shmget函数

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

  第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。

第二个参数,size以字节为单位指定需要共享的内存容量

第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

2、shmat函数

 #include <sys/types.h>
 #include <sys/shm.h>

 void *shmat(int shmid, const void *shmaddr, int shmflg);

  第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。

第一个参数,shm_id是由shmget函数返回的共享内存标识。

第二个参数,shm_addr指定共享内存连接到当前进程中的起始地址位置

      共享内存段链接到哪个地址与shmaddr参数以及在shmflag中是否指定SHM_RND位有关

      如果shmaddr为0,则此段连接到由内核选择的第一个可用地址上

      如果shmaddr非0,并且没有指定SHM_RND,则链接到shmaddr所指定的地址上

      如果shmaddr非0,且指定了SHM_RND,则链接的地址自动向下调整为SHMLBA的整数倍,公式为:(shmaddr-shmaddr% SHMLBA)

      通常shmaddr为0,表示让系统来选择共享内存的地址。

第三个参数,shm_flg是一组标志位,通常为0。

调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

3、shmdt函数

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

  该函数用于将共享内存从当前进程中分离。注意:将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。

参数shmaddr是shmat函数返回的地址指针

调用成功时返回0,失败时返回-1.

4、shmctl函数

 #include <sys/ipc.h>
 #include <sys/shm.h>

  int shmctl(int shmid, int cmd, struct shmid_ds *buf);

  与信号量的semctl函数一样,用来控制共享内存

第一个参数,shm_id是shmget函数返回的共享内存标识符。

第二个参数,command是要采取的操作,它可以取下面的三个值 :

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

IPC_RMID:删除共享内存段

第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。

shmid_ds结构至少包括以下成员:

 struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Last change time */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...
           };

  

示例

memory_write.c为创建共享内存并向里面写入由中断输入得数据,memory_read.c为读取共享内存中的内容。两个程序运行在两个不相关的进程中,为了保证两个进程间的读写同步,设置了WR_RD标记。

memory_write.c的程序如下

 #include <stdio.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <stdlib.h>
 #include <string.h>

#define MAX_SIZE   2048
#define BUF_SIZE   2047

#define ERR_EXIT(m) 	do  	{   		perror(m);   		exit(EXIT_FAILURE);  	}while(0)

struct share_memory
{
	int  WR_RD;          //非0表示可写,0表示可读
	char text[MAX_SIZE];
};

int main()
{
	int shmid;
	char buffer[BUF_SIZE+1];   //用于保存输入的文本
	shmid = shmget(1234,sizeof(struct share_memory),0666 | IPC_CREAT);    //创建共享内存
	if(shmid==-1)
	    ERR_EXIT("shmget failed\n");

	struct share_memory *sh_mem;
	void *sh=NULL;
	sh = shmat(shmid,0,0);     //将共享内存连接到当前进程的地址空间
	if(sh ==(void *)-1)
		ERR_EXIT("shmat failed\n");
	else
		printf("Memory attached at9 %x\n",(int)sh);   

	sh_mem=(struct share_memory *)sh;
	sh_mem->WR_RD=1;   // 置为可写
	int flag=1;

	while(1)
	{
		while(!sh_mem->WR_RD)     //共享内存为可读,表示内存里的数据还未被读取,不能写,此时堵塞
		{
			if(flag)
			{
				printf("memory is not empty,waiting.....\n");
				flag=0;
			}

			sleep(1);
		}

		flag=1;
		/*向共享内存写入数据*/
		printf("Enter some text(Enter ‘end‘ to quit): \n");
		fgets(buffer,BUF_SIZE,stdin);
		strncpy(sh_mem->text,buffer,sizeof(buffer));

		sh_mem->WR_RD=0;    //写完数据之后设置共享内存可读

		if(strncmp(buffer,"end",3)==0)
			break;                //输入end结束循环

	}

	if(shmdt(sh)==-1)
		ERR_EXIT("shmdt failed\n");    //将共享内存从当前进程分离

	sleep(2);
	return 0;

}

  

memory_read.c的程序如下

 #include <stdio.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>

#define MAX_SIZE   2048

#define ERR_EXIT(m) 	do  	{   		perror(m);   		exit(EXIT_FAILURE);  	}while(0)

struct share_memory
{
	int  WR_RD;          //非0表示可写,0表示可读
	char text[MAX_SIZE];
};

int main()
{
	int shmid;     

	srand(getpid());

	shmid = shmget(1234,sizeof(struct share_memory),0666 | IPC_CREAT);    //创建共享内存
	if(shmid==-1)
	    ERR_EXIT("shmget failed\n");

	struct share_memory *sh_mem;
	void *sh=NULL;
	sh = shmat(shmid,0,0);     //将共享内存连接到当前进程的地址空间
	if(sh ==(void *)-1)
		ERR_EXIT("shmat failed\n");
	else
		printf("Memory attached at %x\n",(int)sh);   

	sh_mem=(struct share_memory*)sh;
	//sh_mem->WR_RD=ture;   // 置为可写

	while(1)

		if(!sh_mem->WR_RD)     //共享内存为可读,从共享内存读取数据
		{
			printf("your wrote: %s", sh_mem->text);
			sleep(rand()%3);
			sh_mem->WR_RD=1;    //读完数据之后设置共享内存可写

			if(strncmp(sh_mem->text,"end",3)==0)
				break;                //输入end结束循环
		}
		else           //表示内存为空,不能读,此时堵塞
		{
			//printf("memory is empty,waiting.....\n");
			sleep(1);
		}

	if(shmdt(sh)==-1)
		ERR_EXIT("shmdt failed\n");    //将共享内存从当前进程分离

	if(shmctl(shmid,IPC_RMID,0)==-1)
		ERR_EXIT("shmctl failed\n");

	return 0;

}

  

使用共享内存的优缺点

1、优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。

2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。

参考:

Linux进程间通信——使用共享内存       http://blog.csdn.net/ljianhui/article/details/10253345

Linux共享内存使用常见陷阱与分析      http://os.51cto.com/art/201311/418977_all.htm

时间: 2024-08-03 09:38:51

Linux共享内存使用常见陷阱与分析 - 51CTO.COM http://os.51cto.com/art/201311/418977_all.htmIPC---共享内存的相关文章

Linux共享内存使用常见陷阱与分析

所谓共享内存就是使得多个进程可以访问同一块内存空间,是最快的可用IPC形式.是针对其他通信机制运行效率较低而设计的.往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥.其他进程能把同一段共享内存段“连接到”他们自己的地址空间里去.所有进程都能访问共享内存中的地址.如果一个进程向这段共享内存写了数据,所做的改动会即时被有访问同一段共享内存的其他进程看到.共享内存的使用大大降低了在大规模数据处理过程中内存的消耗,但是共享内存的使用中有很多的陷阱,一不注意就很容易导致程序崩溃. 超过共享内

unix/linux共享内存应用与陷阱

unix/linux共享内存应用与陷阱 (2012-06-12 14:32) 标签:  linux  内存  分类: linux应用  共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区.在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做.一.应用共享内存的使用,主要有以下几个API:ftok().shmget().shmat().shmdt(

linux系统中常见的性能分析工具

一.vmstat vmstat是linux/unix上一个监控工具,能展示给定时间间隔的服务器的状态,包括操作系统的内存信息,CPU使用状态.进程信息等. 语法: vmstat [-V] [delay [count]]     #-V     打印出vmstat工具的版本信息     #delay  设置两次输出的时间间隔     #count  设置总共输出的次数 对输出参数进行讲解 procs r:运行进程数(即真正分配到cpu的进程数量),如果该值长期大于系统逻辑cpu的数量,表示cpu不

Linux下利用Valgrind工具进行内存泄露检测和性能分析

from http://www.linuxidc.com/Linux/2012-06/63754.htm Valgrind通常用来成分析程序性能及程序中的内存泄露错误 一 Valgrind工具集简绍 Valgrind包含下列工具: 1.memcheck:检查程序中的内存问题,如泄漏.越界.非法指针等. 2.callgrind:检测程序代码的运行时间和调用过程,以及分析程序性能. 3.cachegrind:分析CPU的cache命中率.丢失率,用于进行代码优化. 4.helgrind:用于检查多线

大并发连接的oracle在Linux下内存不足的问题的分析

大并发连接的oracle在Linux下内存不足的问题的分析 2010-01-28 20:06:21 分类: Oracle 最近一台装有Rhel5.3的40G内存的机器上有一个oracle数据库,数据库的SGA设置为20G,当运行业务时,一个业务高峰期时,发现swap频繁交换,CPU 100%,Load很高,基本体现为内存不足.此时的连接数在600个左右.按内存的计算:每个连接占用内存基本在5M,这样600个连接只占用3G内存,SGA内存20G,操作系统占用内存1G,这样总占用的内存为24G,而总

Linux运维之系统性能---vmstat工具分析内存的瓶颈

为了提高磁盘存取效率, Linux做了一些精心的设计, 除了对dentry进行缓存(用于VFS,加速文件路径名到inode的转换), 还采取了两种主要Cache方式:Buffer Cache和Page Cache.前者针对磁盘块的读写,后者针对文件inode的读写.这些Cache有效缩短了I/O系统调用(比如read,write,getdents)的时间. 内存活动基本上可以用3个数字来量化:活动虚拟内存总量,交换(swapping)率和调页(paging)率.其中第一个数字表明内存的总需求量,

常见性能计数器及分析

一.常见计数器 1.windows系统计数器 类别 计数器名称 计数器描述 Memory Available Mbytes 可用物理内存数 Pages/sec 表明由于硬件页面错误而从磁盘取出的页面数,或由于页面错误而写入磁盘以释放工作集空间的面数 Pages Read/sec 页的硬故障,Pages/sec的子集,为了解析对内存的引用,必须读取页文件的次数.其阈值为5,数值越低越好,大树枝表示磁盘读而不是缓存读 Page Faults/sec 此值为处理器中的页面错误的计数.当进程引用特定的虚

Linux及安全实践四——ELF文件格式分析

Linux及安全实践四——ELF文件格式分析 一.ELF文件格式概述 1. ELF:是一种对象文件的格式,用于定义不同类型的对象文件中都放了什么东西.以及都以什么样的格式去放这些东西. 二.分析一个ELF文件 以一个最简单的helloworld程序为例 1. ELF文件头 使用工具查看ELF文件头:readelf -h obj 在/usr/include/elf.h中可以找到文件头结构定义: 大小总共为64字节,换算成十六进制为0x40.在十六进制代码中找到前0x40字节,即为文件头信息部分(阅

关于Linux静态库和动态库的分析

关于Linux静态库和动态库的分析 关于Linux静态库和动态库的分析 1.什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的本质不同,因此二者库的二进制是不兼容的. 本文仅限于介绍linux下的库. 2.库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同. 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大. 共享库的代