Linux内存映射

目录

背景

一、什么是内存映射?        

 二、mman函数

 1.权限问题

2.总线错误 

3.内存权限

4.读文件内容

5.映射与文件

 6.非法参数错误

 7.偏移量大小

 8.映射内存大小

8.1 申请6k,访问5k

8.2 申请2k,访问3k

8.3 返回值检查

三、内存映射实现

 四、匿名内存映射

 五、munmap函数

六、System V共享内存

1.ftok函数

2.shmget函数

 3.shmat函数

 4. system V 共享内存使用步骤


背景

        在传统的文件访问中,进程通常使用系统调用(例如open()read()write()close()等)来操作文件。这些系统调用允许进程打开文件、从文件中读取数据、向文件中写入数据以及关闭文件。这种方法需要通过系统调用在用户空间和内核空间之间进行数据传输,因此可能会产生一定的性能开销。  

        而使用内存映射技术,即将文件的内容映射到进程的虚拟内存空间中,使得进程可以直接访问内存而无需通过系统调用来读取或写入文件的内容。这样做可以提高文件访问的效率,因为读写文件的操作直接转化为对内存的操作,避免了频繁的系统调用。

        内存映射是一种将磁盘文件的内容映射到进程的虚拟内存空间中的技术。这意味着文件的内容被映射到进程的地址空间中,使得进程可以直接通过访问内存的方式来读取或写入文件的内容,而无需调用传统的read和write系统调用。这种方式能够提高对文件的访问效率,并且简化了对文件的操作。

内存映射的主要优点包括:

  1. 性能优势:由于文件内容直接映射到了内存中,因此读取文件的操作可以直接通过内存访问完成,避免了频繁的磁盘IO操作,从而提高了读取文件的速度。

  2. 简化操作:通过内存映射,文件被映射到了进程的地址空间中,进程可以像操作内存一样对文件进行读写,这样就简化了对文件的操作流程。

  3. 共享内存:多个进程可以共享同一个文件的内存映射,这样可以实现进程间的数据共享,而无需进行显式的数据传输。

一、什么是内存映射?        

        使一个磁盘文件与内存中的一个缓冲区相映射,进程可以像访问普通内存一样对文件进行访问,不必再调用read,write。

 二、mmap函数

 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);                

功能:创建共享内存映射

函数返回值:成功返回创建的映射区首地址,失败返回MAP_FAILED( ((void *) -1) ),设置errno值。

参数说明:


addr:指定要映射的内存地址,一般设置为 NULL 让操作系统自动选择合适的内存地址。

length:必须>0。映射地址空间的字节数,它从被映射文件开头 offset 个字节开始算起。

prot:指定共享内存的访问权限。
可取如下几个值的可选:
    PROT_READ(可读), 
    PROT_WRITE(可写), 
    PROT_EXEC(可执行),
    PROT_NONE(不可访问)。

flags:由以下几个常值指定:
    MAP_SHARED(共享的),
    MAP_PRIVATE(私有的), 
    MAP_ANONYMOUS(匿名映射,用于血缘关系进程间通信)
    MAP_FIXED(表示必须使用 start 参数作为开始地址,如果失败不进行修正),
    其中,MAP_SHARED , MAP_PRIVATE必选其一,而 MAP_FIXED 则不推荐使用。
   
fd:表示要映射的文件句柄。如果匿名映射写-1。

offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射。

        共享内存映射(Shared Memory Mapping)是一种通过将文件的一部分映射到进程的地址空间中来实现进程间通信的方法。在UNIX和类UNIX系统中,这通常使用mmap系统调用来完成。

共享内存映射的理解可以从以下几点展开:

  1. 进程间通信(IPC): 共享内存映射提供了一种高效的进程间通信方式。多个进程可以将同一个文件的部分内容映射到它们各自的地址空间中,从而实现数据的共享。

  2. 内存共享: 通过共享内存映射,多个进程可以直接访问同一块物理内存,避免了数据复制和额外的内核参与,因此效率较高。

  3. 数据一致性: 通过共享内存映射,进程可以直接读写共享内存中的数据,从而实现数据的共享和同步。在多个进程访问同一块共享内存时,需要考虑数据的一致性和同步的问题。

  4. 性能优化:共享内存映射允许多个进程直接访问相同的物理内存区域,而不需要在内核空间和用户空间之间进行数据的复制。这减少了系统调用的次数和数据的拷贝次数,从而提高了通信的效率。相比之下,管道、消息队列等通信方式需要在内核中复制数据,然后在用户空间中进行数据传递,因此性能通常较低。在共享内存中,多个进程可以通过对共享内存区域的直接读写来进行通信,这使得通信速度更快,特别是在大量数据需要传递时。因此,共享内存映射常用于对性能要求较高的应用中,例如数据共享、进程间同步等。

  5. 总的来说,共享内存映射提供了一种高效、灵活的进程间通信方式,适用于需要高性能和数据共享的应用场景。

注意事项:

 1.权限问题

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

int main()
{
	void* addr;
	int fd;
	fd = open("test",O_RDONLY);
	if(fd < 0)
	{
		perror("open");
		return 0;
	}
//	int len = lseek(fd,0,SEEK_END);
	addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		return 0;
	}
	return 0;
}

 文件的打开权限是只读,运行结果为:Permission denied

linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
mmap: Permission denied
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat mman.c

 文件打开权限修改为可读可写即可,因为PROT_READ | PROT_WRITE为可读可写,要求:

        当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护),如果不满足报非法参数(Invalid argument)错误。

2.总线错误 

        用于映射的文件大小必须>0,当映射文件大小为0时,指定非0大小创建映射区,访问映射地址会报总线错误

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

int main()
{
	void* addr;
	int fd;
	fd = open("test",O_RDWR);
	if(fd < 0)
	{
		perror("open");
		return 0;
	}
//	int len = lseek(fd,0,SEEK_END);
	addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		return 0;
	}


	memcpy(addr,"abcdefg",7);
	return 0;
}
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
Bus error (core dumped)
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ 

test文件里为空, 不满足:用于映射的文件大小必须>0。直接在test里加个空格都行。

linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat test
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim test
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat test
 
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat test
ablinux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ 

文件足够大,就可以显示出abcdef:

ablinux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim test
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ls -l test
-rwxrwxrwx 1 root root 104 Mar  8 00:44 test
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat test
abcdefg                                         

3.内存权限

	addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_PRIVATE,fd,0);

        当MAP_PRIVATE时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,操作只在内存有效,不会写到物理磁盘,且不能在进程间共享。

linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat test
                                   
              
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim test

4.读文件内容

        注意进行强制类型转换,不然会有警告。

	printf("read = %s\n",(char*)addr);	
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat test
abcdefg                            
              
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
read = abcdefg                            
              

5.映射与文件

        映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。

linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ cat test
999999999                          
           
int main()
{
	void* addr;
	int fd;
	fd = open("test",O_RDWR);
	if(fd < 0)
	{
		perror("open");
		return 0;
	}
//	int len = lseek(fd,0,SEEK_END);
	addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		return 0;
	}
	close(fd);

	memcpy(addr,"999999999",9);

//	printf("read = %s\n",(char*)addr);	
	return 0;
}

 6.非法参数错误

        指定0大小创建映射区,报非法参数错误(Invalid argument)

	addr = mmap(NULL,0,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
mmap: Invalid argument
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ 

可以通过lseek取文件大小:

int len = lseek(fd,0,SEEK_END);
printf("%d\n",len);
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ls -l test
-rwxrwxrwx 1 root root 51 Mar  8 01:20 test
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
51
read = 999999999                          
              

linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ 
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int filedes, off_t offset, int whence) ;

返回值:成功:返回文件新的偏移量(成功) 

lessk()函数的参数说明:
第一个参数是文件描述符;
第二个参数是偏移量,
参数 offset可正可负,负数时向文件开头偏移,正数相对于文件末尾偏移
第三个参数是有三个选项:
1.SEEK_SET:将文件指针偏移到传入字节数处(文件头开始偏移)
2.SEEK_CUR:将文件指针偏移到当前位置加上传入字节数处;((当前位置开始偏移)
3.SEEK_END:将文件指针偏移到文件末尾加上传入字节数处(作为拓展作用,必须再执行一次写操作)

 7.偏移量大小

        文件偏移量必须为0或者4K的整数倍(不是,会报非法参数Invalid argument错误)

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
最后一个参数是偏移量  
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
mmap: Invalid argument
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ 
	addr = mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,1);

 8.映射内存大小

        映射大小可以大于文件大小,但只能访问文件page的内存地址,否则报总线错误 ,超出映射的内存大小报段错误。

        实际是根据文件大小分配空间的,文件大小是51。你申请2k的空间,实际分配的是4k的空间,而你申请6k,实际分配的还是4k空间,所以报总线错误。

8.1 申请6k,访问5k
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
Bus error (core dumped)
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ 

	addr = mmap(NULL,6000,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	memcpy((addr+5000),"999999999",9);
8.2 申请2k,访问3k
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ vim mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mman mman.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mman
read = 999999999
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ 
	addr = mmap(NULL,2000,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	memcpy((addr+3000),"999999999",9);

 

8.3 返回值检查

        mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。

    void* addr;    
    addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		return 0;
	}

三、内存映射实现

在C语言中,memcpy函数用于将数据从一个内存地址复制到另一个内存地址。它的原型如下:

 void *memcpy(void *dest, const void *src, size_t n);

  • dest:目标内存地址,即要复制到的位置。
  • src:源内存地址,即要从哪里复制数据。
  • n:要复制的字节数。

注意:

        在 C 语言中,双引号括起来的字符串常量,比如 "a",表示一个字符数组,该数组包含字符 'a' 和一个结尾的空字符 '\0'。该字符串在内存中的地址实际上是该字符数组的起始地址。

        因此,当使用 "a" 这样的字符串常量作为参数传递给 memcpy 函数时,实际上传递的是字符数组的起始地址,而不是字符 'a' 的地址。

                memcpy(addr + i, "a", 1);

        在这里,"a" 表示字符数组的起始地址,即字符 'a' 在内存中的地址。

 mmanw.c

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

int main()
{
	void* addr;
	int fd;
	fd = open("test",O_RDWR);
	if(fd < 0)
	{
		perror("open");
		return 0;
	}
	int len = lseek(fd,0,SEEK_END);
	addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		return 0;
	}
	close(fd);
	int i = 0;
	while(i<2048)
	{
		memcpy((addr+i),"a",1);
		i++;
		sleep(1);
	}
	return 0;
}

mmanr.c

#include<stdio.h>

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

int main()
{
	void* addr;
	int fd;
	fd = open("test",O_RDWR);
	if(fd < 0)
	{
		perror("open");
		return 0;
	}
	int len = lseek(fd,0,SEEK_END);
	addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		return 0;
	}
	close(fd);
	while(1)
	{
		printf("read = %s\n",(char*)(addr));
		sleep(1);
	}
	return 0;
}

 四、匿名内存映射

        void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);                

         fd:表示要映射的文件句柄。如果匿名映射写-1。

        匿名映射适用于具有亲缘关系的进程之间。

linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ gcc -o mmap_n mmap_n.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mmap_n.c
./mmap_n.c: line 13: syntax error near unexpected token `('
./mmap_n.c: line 13: `int main()'
linux@linux:/mnt/hgfs/linuxshare/linux_code/mman$ ./mmap_n
read father value = 1234567890
#include<stdio.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>


int main()
{
	void* addr;
	//匿名映射不需要文件操作

	addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,-1,0);
	if(addr == MAP_FAILED)
	{
		perror("mmap");
		return 0;
	}
	

	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
		perror("pid");
		return 0;
	}
	else if(pid > 0)
	{
		memcpy(addr,"1234567890",10);

		wait(NULL);
	}
	else
	{
		sleep(1);
		printf("read father value = %s\n",(char*)addr);
	}

	return 0;
}

        使用mmap函数创建一个大小为2048字节的共享内存区域。mmap的第一个参数是指定的映射地址(这里为NULL表示由系统决定),然后指定映射的大小为2048字节,设置读写权限为可读可写,使用MAP_SHARED标志来创建一个共享映射,MAP_ANONYMOUS标志表示不与任何文件关联 。

        使用fork函数创建一个子进程。如果fork返回负值,则表示创建失败,父进程会输出错误信息并退出。如果fork返回0,则表示当前代码运行在子进程中,子进程将等待1秒后,从共享内存中读取数据并打印输出。如果fork返回正值,则表示当前代码运行在父进程中,父进程使用memcpy将字符串"1234567890"复制到共享内存中,然后等待子进程结束。

为什么?

父进程在写入数据后,可能会导致子进程读取数据,
而如果父进程提前退出,操作系统会清理共享内存,
这可能导致子进程读取到的数据不完整或者无效。

通过调用wait(NULL),父进程等待子进程执行完毕,
确保子进程能够正常读取共享内存中的数据。
只有在子进程执行完毕后,父进程才会继续执行后续的操作,包括退出程序,
这样就不会导致共享内存被提前清理。

 五、munmap函数

int munmap(void *addr, size_t length);

​​​​​​​返回值:成功返回0,失败返回-1,并设置errno值。

函数参数:

addr:调用mmap函数成功返回的映射区首地址

length:映射区大小(即:mmap函数的第二个参数)

        在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。 

如下代码中该参数就是addr:

addr = mmap(NULL,2048,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,-1,0);

六、System V共享内存

1.ftok函数

 key_t  ftok(const char *path,  int id);

        其中参数path是指定的文件名,这个文件必须是存在的而且可以访问的。id是子序号,它是一个8bit的整数。即范围是0~255当函数执行成功,则会返回key_t键值,否则返回-1。

#include<sys/types.h>
#include<sys/ipc.h>
#include<stdio.h>
int main()
{
	key_t key;
	key = ftok("keytest",100);
	if(key < 0)
	{
		perror("ftok");
		return 0;
	}
	printf("key = %x\n",key);
	return 0;
}
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ vim ftok.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ touch keytest
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ls
ftok.c  keytest
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ gcc -o ftok ftok.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./ftok
key = 64180004
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ 

2.shmget函数

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

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

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

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

#include<sys/types.h>
#include<sys/ipc.h>
#include<stdio.h>
int main()
{
	key_t key;
	key = ftok("keytest",100);
	if(key < 0)
	{
		perror("ftok");
		return 0;
	}
	printf("key = %x\n",key);
	
	int shmid;
	shmid = shmget(key,512,IPC_CREAT | 0666);
	if(shmid < 0)
	{
		perror("shmget");
		return 0;
	}

	printf("shmid = %d\n",shmid);
	return 0;

}
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ gcc -o ftok1 ftok.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./ftok1
key = 64180004
shmid = 2064395
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ gcc -o ftok1 ftok.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./ftok1
key = 64180004
shmid = 2064395
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ipcs 

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 1376256    linux      600        524288     2          dest         
0x00000000 327681     linux      600        524288     2          dest         
0x00000000 524290     linux      600        524288     2          dest         
0x00000000 688131     linux      600        524288     2          dest         
0x00000000 1212420    linux      600        524288     2          dest         
0x00000000 1015813    linux      600        16777216   2                       
0x00000000 1048582    linux      600        33554432   2          dest         
0x00000000 1867783    linux      600        524288     2          dest         
0x00000000 1572872    linux      600        524288     2          dest         
0x00000000 1638409    linux      600        2097152    2          dest         
0x00000000 1900554    linux      600        1048576    2          dest         
0x64180004 2064395    linux      666        512        0                       

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages 

 3.shmat函数

void *shmat(int shm_id, const void *shm_addr, int shmflg);

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

第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。

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

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

 4. system V 共享内存使用步骤

        4.1创建/打开共享内存

        4.2映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问

        4.3 读写共享内存

        4.4 撤销共享内存映射

        4.5 删除共享内存对象

读写共享内存:

linux进程间通信--消息队列相关函数(ftok)详解_ftok("/tmp", 'a');-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/andylauren/article/details/78821655​​​​​​​

#include<sys/types.h>
#include<sys/ipc.h>
#include<stdio.h>
#include<string.h>

int main()
{
	key_t key;
	key = ftok("keytest",100);
	if(key < 0)
	{
		perror("ftok");
		return 0;
	}
	printf("key = %x\n",key);
	
	int shmid;
	shmid = shmget(key,512,0666);
	if(shmid < 0)
	{
		perror("shmget");
		return 0;
	}

	printf("shmid = %d\n",shmid);
	
	char* buf;
	buf = (char*)shmat(shmid,NULL,0);
	if(buf<0)
	{
		perror("shmat");
		return 0;
	}
//	strcpy(buf,"hello,world");	
	printf("share buf = %s\n",buf);
	
	return 0;

}

可以多次读取: 

linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ gcc -o shm_r shm_r.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./shm_r
key = 6418001d
shmid = 2457612
share buf = hello,world
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./shm_r
key = 6418001d
shmid = 2457612
share buf = hello,world
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./shm_r
key = 6418001d
shmid = 2457612
share buf = hello,world
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./shm_r
key = 6418001d
shmid = 2457612
share buf = hello,world
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./shm_r
key = 6418001d
shmid = 2457612
share buf = hello,world

撤销后再次访问发生段错误:

	printf("share buf = %s\n",buf);

	shmdt(buf);
	printf("share buf = %s\n",buf);
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ vim shm_r.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ gcc -o test shm_r.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./test
key = 6418001d
shmid = 2457612
share buf = hello,world
Segmentation fault (core dumped)
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ 

但其实共享内存还存在:

linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ipcs

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 1376256    linux      600        524288     2          dest         
0x00000000 327681     linux      600        524288     2          dest         
0x00000000 524290     linux      600        524288     2          dest         
0x00000000 688131     linux      600        524288     2          dest         
0x00000000 1212420    linux      600        524288     2          dest         
0x00000000 1015813    linux      600        16777216   2                       
0x00000000 1048582    linux      600        33554432   2          dest         
0x00000000 1867783    linux      600        524288     2          dest         
0x00000000 1572872    linux      600        524288     2          dest         
0x00000000 1638409    linux      600        2097152    2          dest         
0x00000000 1900554    linux      600        1048576    2          dest         
0x64180004 2064395    linux      666        512        0                       
0x6418001d 2457612    linux      666        512        0      

需要删除共享内存,避免内存消耗:

int  shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid, IPC_RMID, NULL);删除共享内存
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ./test
key = 6418001d
shmid = 2457612
share buf = hello,world
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ vim shm_r.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ ipcs

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 1376256    linux      600        524288     2          dest         
0x00000000 327681     linux      600        524288     2          dest         
0x00000000 524290     linux      600        524288     2          dest         
0x00000000 688131     linux      600        524288     2          dest         
0x00000000 1212420    linux      600        524288     2          dest         
0x00000000 1015813    linux      600        16777216   2                       
0x00000000 1048582    linux      600        33554432   2          dest         
0x00000000 1867783    linux      600        524288     2          dest         
0x00000000 1572872    linux      600        524288     2          dest         
0x00000000 1638409    linux      600        2097152    2          dest         
0x00000000 1900554    linux      600        1048576    2          dest         
0x64180004 2064395    linux      666        512        0                       

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

linux@linux:/mnt/hgfs/linuxshare/linux_code/system$ 

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值