为什么使用共享内存 共享内存(Shared Memory)是操作系统提供的一种机制,允许多个进程或者现成访问同一块内存空间 ,这样可以快速的交换数据和消息。
使用共享内存的原因主要包括以下几点:
高性能的数据交换 :共享内存通常比其他通信机制(如管道、消息队列、Socket等)更快,因为它减少了数据复制的次数。在共享内存中,数据只需写入一次,可以被多个进程直接访问。
同步机制 :共享内存通常与同步原语(如互斥锁、信号量等)结合使用,以控制多个进程或线程对共享数据的访问,保证数据的一致性和完整性。
方便的数据共享 :在多进程或多线程应用中,共享内存提供了一种简单的方式来共享大量数据,无需通过繁琐的数据传递过程。
减少资源消耗 :使用共享内存可以减少系统资源的使用,因为它不需要像其他通信机制那样进行数据的多次复制。
在嵌入式系统中,使用共享内存的原因与在通用计算环境中使用共享内存的原因类似,但也具有一些特定的考虑因素。以下是在嵌入式系统中使用共享内存的几个主要原因:
资源限制 :嵌入式系统通常具有有限的资源,包括内存、处理能力和功耗。共享内存可以减少内存占用,因为它允许多个组件或任务共享相同的数据,而不需要为每个组件或任务复制数据。
性能要求 :嵌入式系统经常需要在严格的实时性能要求下运行。共享内存提供了快速的通信机制,可以减少数据传输延迟,这对于满足实时性要求至关重要。
低功耗 :通过减少数据传输和复制,共享内存有助于降低功耗,这对于电池供电的嵌入式设备来说尤为重要。
简化设计 :共享内存可以简化系统设计,因为它减少了需要管理的通信接口数量。这对于减少硬件复杂性和降低成本非常有用。
通信效率 :在多处理器或多核嵌入式系统中,共享内存可以提供高效的处理器间通信,这对于并行处理和任务分配非常重要。
graph LR
进程A <--> 共享内存 <--> 进程B
进程C <--> 共享内存 <--> 进程...
共享内存的应用 共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成。
函数原型 shmget(得到一个共享内存标识符或创建一个共享内存对象)
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 54 55 56 57 58 59 60 61 62 #include <sys/ipc.h> #include <sys/shm.h> key_t ftok (const char *__pathname, int __proj_id) ;int shmget (key_t key, size_t size, int shmflg) ;void *shmat (int __shmid, const void *__shmaddr, int __shmflg) ; int shmdt (const void *__shmaddr) ;int shmctl (int shmid, int cmd, struct shmid_ds *buf) ;
示例 创建两个C程序,process_a.c 和 process_b.c。 两个程序访问同一个内存变量,使用ftok操作同一个可访问的pathname文件路径和同一个proj_id标识, 可以使用不同的pathname和不同的proj_id来标识不同的文件索引节点上的不同IPC标识。 其中process_a程序,更新写入 IPC shmKey为(process_a, 65)的内存变量,而process_b程序,读取shmKey同样为(process_a, 65)的内存变量。
process_a.c 文件,编译gcc ./process_a.c -o process_a
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 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <error.h> #include <string.h> #include <unistd.h> #define SHM_SIZE 1024 int main () { key_t key = ftok("process_a" , 65 ); int shmid = shmget(key, SHM_SIZE, IPC_CREAT|0600 ); if (shmid < 0 ) { perror("shmget error" ); return -1 ; } char *shm_value1 = (char *) shmat(shmid, NULL , 0 ); int i = 0 ; while (i<10 ) { printf ("process_a now set shm_value1 to [shm_value1=%d]\n" , i); sprintf (shm_value1, "shm_value1=%d" , i); i++; sleep(3 ); } shmdt(shm_value1); return 0 ; }
process_b.c 文件 编译
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 54 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <error.h> #include <string.h> #include <unistd.h> #define SHM_SIZE 1024 int main () { key_t shmKey = ftok("process_a" , 65 ); if (-1 == shmKey) { perror("获取65的shmKey异常" ); return -1 ; } int shmid = shmget(shmKey, SHM_SIZE, IPC_CREAT|0600 ); if (shmid < 0 ) { perror("shmget error" ); return -1 ; } char *shm_value1 = (char *) shmat(shmid, NULL , 0 ); int i = 0 ; while (i<10 ) { sleep(1 ); printf ("process_b read: %s\n" , shm_value1); i++; } shmdt(shm_value1); return 0 ; }
开两个终端分别运行两个程序,两个程序都谁先运行都可以,因为都使用了ftok和shmget创建了指定的共享内存,不会重复创建。
运行process_a,写入共享内存,3秒更新写一次;运行process_b,读取共享内存,1秒读一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 keyinwang@WondersWL:~/Projects/DemoC/shared_mem$ ./process_a process_a now set shm_value1 to [shm_value1=0] process_a now set shm_value1 to [shm_value1=1] process_a now set shm_value1 to [shm_value1=2] process_a now set shm_value1 to [shm_value1=3] process_a now set shm_value1 to [shm_value1=4] process_a now set shm_value1 to [shm_value1=5] // ...... keyinwang@WondersWL:~/Projects/DemoC/shared_mem$ ./process_b process_b read: shm_value1=1 process_b read: shm_value1=1 process_b read: shm_value1=2 process_b read: shm_value1=2 process_b read: shm_value1=2 process_b read: shm_value1=3
注意当process_a中途结束或者结束运行时,再次运行process_b时,发现共享内存是最后改动的值,而不是空值,说明即使程序结束运行,而共享内存依然存在,这是因为我们没有使用shmctl删除共享内存。因此就一直保留在内存指定地址中。
提示 为了提供共享内存的可读性和复用性,我们可以将pathname节点和proj_id 抽象提取出来表示不通的共享内存变量,并可以设置不通变量的指定shmSize。
如可以定义一个头文件shm_util.h
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 #define SHMID_DEVICE_STATUS 1 #define SHMSIZE_STATUS 8 #define SHMPATHNAME_PROCESS "/home/keyinwang/Projects/DemoC/shared_mem/process_c" #define SHMID_BATTERY_VOLTAGE 2 #define SHMSIZE_VOL 6
共享内存写进程process_c.c
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 54 55 56 57 58 59 60 61 62 63 64 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <error.h> #include <string.h> #include <unistd.h> #include "shm_util.h" int main () { key_t shmKey = ftok(SHMPATHNAME_PROCESS, SHMID_DEVICE_STATUS); if (-1 == shmKey) { perror("获取 SHMPATHNAME_PROCESS SHMID_DEVICE_STATUS 的shmKey异常" ); return -1 ; } int shmid = shmget(shmKey, SHMSIZE_STATUS, IPC_CREAT|0600 ); if (shmid < 0 ) { perror("shmget SHMID_DEVICE_STATUS error" ); return -1 ; } int *shm_device_status = (int *) shmat(shmid, (void *)0 , 0 ); shmKey = ftok(SHMPATHNAME_PROCESS, SHMID_BATTERY_VOLTAGE); shmid = shmget(shmKey, SHMSIZE_VOL, IPC_CREAT|0600 ); if (shmid < 0 ) { perror("shmget SHM_BATTERY_VOLTAGE error" ); return -1 ; } char *shm_battery_voltage = (char *) shmat(shmid, NULL , 0 ); int i = 0 ; while (i<10 ) { *shm_device_status = i; printf ("set int *shm_device_status=%d\n" , *shm_device_status); sprintf (shm_battery_voltage, "%d.000V" , i); printf ("set char *shm_battery_voltage=%s\n" , shm_battery_voltage); sleep(3 ); i++; } shmdt(shm_device_status); shmdt(shm_battery_voltage); return 0 ; }
共享内存读进程process_d.c
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 54 55 56 57 58 59 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <error.h> #include <string.h> #include <unistd.h> #include "shm_util.h" int main () { key_t shmKey = ftok(SHMPATHNAME_PROCESS, SHMID_DEVICE_STATUS); if (-1 == shmKey) { perror("获取 SHMPATHNAME_PROCESS SHMID_DEVICE_STATUS 的shmKey异常" ); return -1 ; } int shmid = shmget(shmKey, SHMSIZE_STATUS, IPC_CREAT|0600 ); if (shmid < 0 ) { perror("shmget SHMID_DEVICE_STATUS error" ); return -1 ; } int *shm_device_status = (int *) shmat(shmid, (void *)0 , 0 ); shmKey = ftok(SHMPATHNAME_PROCESS, SHMID_BATTERY_VOLTAGE); shmid = shmget(shmKey, SHMSIZE_VOL, IPC_CREAT|0600 ); if (shmid < 0 ) { perror("shmget SHM_BATTERY_VOLTAGE error" ); return -1 ; } char *shm_battery_voltage = (char *) shmat(shmid, NULL , 0 ); int i = 0 ; while (i<10 ) { sleep(1 ); printf ("process_d read int *shm_device_status: %d\n" , *shm_device_status); printf ("process_d read char *shm_battery_voltage: %s\n" , shm_battery_voltage); i++; } shmdt(shm_device_status); shmdt(shm_battery_voltage); return 0 ; }
编译Makefile
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 TARGETS = process_c process_d SRCS1=process_c.c OBJS1=$(SRCS1:.c=.o) SRCS2=process_d.c OBJS2=$(SRCS2:.c=.o) all: $(TARGETS) process_c: $(OBJS1) $(CC) $^ $(LIBS) $(LDFLAGS) $(LOCAL_LDFLAGS) -o $@ process_d: $(OBJS2) $(CC) $^ $(LIBS) $(LDFLAGS) $(LOCAL_LDFLAGS) -o $@ process_c.o: process_c.c $(CC) $(CFLAGS) $(INCLUDES) -c $^ -o $@ process_d.o: process_d.c $(CC) $(CFLAGS) $(INCLUDES) -c $^ -o $@ clean: rm -rf $(TARGETS) $(OBJS1) $(OBJS2) .PHONY : all clean
编译执行
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 keyinwang@WondersWL:~/Projects/DemoC/shared_mem$ make cc -c process_c.c -o process_c.o cc process_c.o -o process_c cc -c process_d.c -o process_d.o cc process_d.o -o process_d // 终端1执行 keyinwang@WondersWL:~/Projects/DemoC/shared_mem$ ./process_c set int *shm_device_status=0 set char *shm_battery_voltage=0.000V set int *shm_device_status=1 set char *shm_battery_voltage=1.000V set int *shm_device_status=2 set char *shm_battery_voltage=2.000V set int *shm_device_status=3 set char *shm_battery_voltage=3.000V // 终端2执行 keyinwang@WondersWL:~/Projects/DemoC/shared_mem$ ./process_d process_d read int *shm_device_status: 2 process_d read char *shm_battery_voltage: 2.000V process_d read int *shm_device_status: 2 process_d read char *shm_battery_voltage: 2.000V process_d read int *shm_device_status: 3 process_d read char *shm_battery_voltage: 3.000V
至此,完成了多个进程只想的共享内存使用。