进程通讯—共享内存 1.共享内存的定义与原理 1.1定义 共享内存是指允许多个不相关的进程访问同一个逻辑内存区域。这种内存区域在物理上通常是同一段内存,但在虚拟地址空间中,不同进程通过各自的页表将其映射到这一共同的物理内存区域。
1.2原理 在Linux系统中,每个进程都有自己的进程控制块(PCB)和地址空间(Addr Space),以及与之对应的页表。通过页表,进程的虚拟地址被映射到物理地址。当两个或多个进程通过页表将虚拟地址映射到同一块物理内存区域时,这块区域即成为共享内存。这样,当一个进程修改共享内存中的数据时,其他进程通过访问共享内存就能立即看到这些改动。
2.共享内存的特点
高效性 :共享内存是进程间通信中最快的方式之一,因为它减少了数据复制的次数,直接在内存中进行数据交换。
灵活性 :进程可以灵活地读写共享内存中的数据,实现复杂的数据交换和同步机制。
复杂性 :共享内存的使用需要额外的同步机制(如信号量、互斥锁等)来避免数据竞争和条件竞争等问题。
2.创建共享内存对象 2.1库函数shm_open()和 shm_unlink() shm_open() 可以开启一块内存共享对象,我们可以像使用一般文件描述符一般使用这块内存对象
2.2函数描述: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <sys/mman.h> int shm_open (const char *name, int oflag, mode_t mode) ;int shm_unlink (const char *name) ;
3.分配共享内存大小 3.1库函数truncate()和 ftruncate() ** **truncate 和 ftruncate 都可以将文件缩放到指定大小,二者的行为类似:如果文件 被缩小,截断部分的数据丢失,如果文件空间被放大,扩展的部分均为\0 字符。缩放前后 文件的偏移量不会更改。缩放成功返回 0,失败返回-1。
** **不同的是,前者需要指定路径,而后者需要提供文件描述符;ftruncate 缩放的文件 描述符可以是通过 shm_open()开启的内存对象,而 truncate 缩放的文件必须是文件系 统已存在文件,若文件不存在或没有权限则会失败
3.2函数描述: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <unistd.h> #include <sys/types.h> int truncate (const char *path, off_t length) ;int ftruncate (int fd, off_t length) ;
4.映射内存地址 4.1库函数mmap() ** **mmap 系统调用可以将一组设备或者文件映射到内存地址,我们在内存中寻址就相当于 在读取这个文件指定地址的数据。父进程在创建一个内存共享对象并将其映射到内存区后, 子进程可以正常读写该内存区,并且父进程也能看到更改。使用 man 2 mmap 查看该系统
4.2函数描述: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <sys/mman.h> void *mmap (void *addr, size_t length, int prot, int flags, int fd, off_t offset) ;int munmap (void *addr, size_t length) ;
5.测试 5.1Makefile 1 2 3 4 5 CC :=gcc shard_memory: shard_memory.cpp -$(CC) -o $@ $^ -./$@ "This is the way" -rm ./$@
5.2代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h> #include <string.h> int main (int argc,char *argv[]) { char shm_name[100 ]; sprintf (shm_name,"/letter%d" ,getpid ()); int fd=shm_open (shm_name,O_RDWR | O_CREAT,0664 ); if (fd<0 ){ perror ("shm_open error" ); exit (EXIT_FAILURE); } ftruncate (fd,1024 ); void *share=mmap (NULL ,1024 ,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0 ); if (share==MAP_FAILED){ perror ("mmap error" ); exit (EXIT_FAILURE); } close (fd); pid_t pid=fork(); if (pid<0 ){ perror ("fork error" ); exit (EXIT_FAILURE); }else if (pid==0 ){ strcpy ((char *)share,"This is the way" ); }else { waitpid (pid,NULL ,0 ); printf ("%s\n" ,(char *)share); int res=munmap (share,1024 ); if (res==-1 ){ perror ("munmap error" ); exit (EXIT_FAILURE); } } shm_unlink (shm_name); return 0 ; }
6.临时文件系统 ** **Linux 的临时文件系统(tmpfs)是一种基于内存的文件系统,它将数据存储在 RAM 或者在需要时部分使用交换空间(swap)。tmpfs 访问速度快,但因为存储在内存,重启 后数据清空,通常用于存储一些临时文件。
** **我们可以通过 df -h 查看当前操作系统已挂载的文件系统。
7.内存共享对象在临时文件系统中的表示 内存共享对象在临时文件系统中的表示位于/dev/shm 目录下。 为看到共享对象在临时文件系统中的表示,我们修改程序,在创建后不销毁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h> #include <string.h> int main () { char *share; pid_t pid; char shmName[100 ]={0 }; sprintf (shmName,"/letter%d" ,getpid ()); printf ("shmName: %s\n" , shmName); int fd; fd = shm_open (shmName, O_CREAT | O_RDWR, 0644 ); if (fd < 0 ) { perror ("共享内存对象开启失败!\n" ); exit (EXIT_FAILURE); } while (1 ); return 0 ; }