(一)简单概念数据结构
共享内存做为一种进程间通讯的方式,其相较于其余进程间通讯方式而言最大的优势就是数据传输速率快。其内部实现的方式采用了Linux进程地址空间中的mmap文件映射区,将文件内容直接映射到各自进程的进程地址空间中,进程对各自进程地址空间的访问便可多线程
完成数据通讯,因为直接读取内存的方式,故其效率远远快于其余IPC方法,这是共享内存的一大优点。但对于共享内存来讲,保证数据同步问题是一个难点,在通常状况下能够采用管道的方式,本节内容的实例代码采用了互斥锁机制。函数
(二)System V共享内存API函数spa
(1)int shmget(key_t key, ssize_t size, int oflag)操作系统
功能:用于建立共享内存区域。线程
参数:code
key:共享内存的名字,用于惟一肯定一个共享内存;blog
size:建立的共享内存的大小;进程
oflag:共享内存的读写权限值集合,与文件建立中的mode标志是同样的。一样还能够与IPC_CREAT,IPC_EXCL等标志联合使用。内存
返回:函数返回共享内存区的标识符。
(2)void *shmat(int shmid, const void *shmaddr, int flag)
功能:将一个建立好的共享内存区附接到进程对应的进程地址空间中。
参数:
shmid:shmget函数的返回值;
shmaddr:将共享内存附接道shmaddr指定的进程地址空间的对应地址上。若是设置为NULL,将由系统指定合法的区域将共享内存映射,这是推荐的作法;
flag:两个可能取值为SHM_RND和SHM_RDONLY。
返回:函数返回映射区的起始地址。
(3)int shmdt(const void *shmaddr)
功能:将共享内存从该进程地址空间中脱离。
参数:
shmaddr:shmat函数的返回值。
返回:若是成功,函数返回0,出错返回-1。
注意:该函数只是将共享内存从进程的地址空间中脱离,不会删除该共享内存区域。而且当一个进程终止时,它当前附接的全部共享内存区都将会自动脱离。
(4)int shmctl(int shmid, int cmd, struct shmid_ds *buf)
功能:提供对共享内存区的多种操做
参数:
shmid:shmget函数的返回值;
cmd:具体操做。取值有1)IPC_RMID:从系统中删除shmid标识的共享内存区并拆除;2)IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值;3)IPC_START:向调用者返回指定的共享内存区当前的shmid_ds结构。
返回:成功返回0,出错返回-1。
(三)采用互斥锁与共享内存的进程间通讯方式
因为共享内存须要解决的一个问题就是数据同步,进程间的同步方式采用信号量能够完成,但我考虑可否采用互斥锁的方式。这里采用互斥锁最初的疑惑为:两个没有近亲关系的进程,如何可以获取到同一个互斥锁(通常互斥锁用于线程间同步的时候比较多,这里也说回来,线程之间原本就是共享数据,因此只须要解决数据同步问题便可,故尚未据说过用共享内存来解决线程通讯的 - -。)。
这里来看下,若是设置互斥锁pthread_mutex_t,来使得不一样进程能够获取到同一个互斥锁。
对于一个互斥锁pthread_mutex_t来讲,有两种方式进行初始化,一种是静态分配,一种是动态初始化。
1)利用宏PTHREAD_MUTEX_INITALIZER来初始化静态分配的互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER
2)利用pthread_mutex_init来动态初始化互斥锁
函数原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
这里第一个参数mutex很好理解,就是定义的须要初始化的互斥锁。这里的第二个参数mutexattr表明什么?Linux下互斥锁具备一系列的属性,其中pthread_mutexattr_t结构体定义了一套完整的互斥锁属性。两种经常使用的属性是pshared和type。其中pshared属性指定了是否容许跨进程共享互斥锁,可选值为:1)PTHREAD_PROCESS_SHARED,互斥锁能够被跨进程共享;2)PTHREAD_PROCESS_PRIVATE,互斥锁只能隶属于一个进程,默认属性。
咱们能够经过设置互斥锁自身属性,来达到跨进程共享互斥锁的方法。
结合实例代码来进行分析:
//shmat.h #ifndef __SHMAT_H__ #define __SHMAT_H__ #include <pthread.h> #include <string.h> #include <sys/shm.h> #include <stdlib.h> #define SM_BUF_SIZE 1024 #define SM_ID 0x1234 struct shmat_msg { int flag; pthread_mutex_t shmat_mutex; char buf[SM_BUF_SIZE]; }; #endif
头文件声明了共享内存结构体,其中shmat_mutex用于保证对共享内存的互斥访问。
//shmat-read.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> #include "shmat.h" int main(void) { int running = 1; int shm_id, ret; void *shared_memory = NULL; struct shmat_msg *sm_msg = NULL; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); //设置跨进程属性 shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666 | IPC_CREAT); //建立共享内存,返回共享内存标识符 if (shm_id < 0) { perror("fail to shmget\n"); exit(1); } shared_memory = shmat(shm_id, NULL, 0); //映射到进程虚拟地址空间 if (shared_memory == NULL) { perror("fail to shmat\n"); exit(1); } sm_msg = (struct shmat_msg*)shared_memory; //转型为shmat_msg数据结构 sm_msg->flag = 0; pthread_mutex_init(&sm_msg->shmat_mutex, &attr); //初始化共享内存的互斥锁 while (running) { pthread_mutex_lock(&sm_msg->shmat_mutex); //互斥访问 if (sm_msg->flag) { printf("read message : %s\n", sm_msg->buf); sm_msg->flag = 0; if (strncmp(sm_msg->buf, "exit", 4) == 0) running = 0; pthread_mutex_unlock(&sm_msg->shmat_mutex); } else { printf("no data to read, waiting\n"); pthread_mutex_unlock(&sm_msg->shmat_mutex); sleep(2); } } ret = shmdt(shared_memory); //脱离共享内存的映射 if (ret < 0) { perror("failed to shmdt\n"); exit(1); } if (shmctl(shm_id, IPC_RMID, 0) < 0) //删除共享内存 { perror("failed to shmctl\n"); exit(1); } return 0; }
读文件。设置了共享内存互斥锁为跨进程属性。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> #include "shmat.h" int main(void) { int running = 1; int shm_id, ret; void *shared_memory = NULL; struct shmat_msg *sm_msg = NULL; shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666); //获取共享内存 if (shm_id < 0) { perror("failed to shmget\n"); exit(1); } shared_memory = shmat(shm_id, NULL, 0); //映射 if (shared_memory == NULL) { perror("failed to shmat\n"); exit(1); } sm_msg = (struct shmat_msg*)shared_memory; char buff[100]; while (running) { pthread_mutex_lock(&sm_msg->shmat_mutex); //获取共享内存的互斥锁 if (sm_msg->flag == 0) { fgets(buff, 100, stdin); printf("write sm_msg : %s\n", buff); strncpy(sm_msg->buf, buff, sizeof(buff)); sm_msg->flag = 1; if (strncmp(sm_msg->buf, "exit", 4) == 0) running = 0; pthread_mutex_unlock(&sm_msg->shmat_mutex); } else { printf("sm_msg waiting read\n"); pthread_mutex_unlock(&sm_msg->shmat_mutex); sleep(2); } } ret = shmdt(shared_memory); if (ret < 0) { perror("failed to shmdt\n"); exit(1); } ret = shmctl(shm_id, IPC_RMID, 0); if (ret < 0) { perror("failed to shmctl\n"); exit(1); } return 0; }
写文件。
程序运行结果:
其中shmat-write程序运行后会阻塞在fgets等待用户输入。其中shmat-read与shmat-write两个程序抢占lock的时机是不肯定的,可能同一个程序会屡次抢占互斥锁,但因为没有内容可读或可写,则睡眠等待。
unlock解锁后,操做系统下次调度哪一个进程加锁是不必定的,若是须要在知足必定条件后才被调度能够采用条件变量(但通常都是在多线程环境下了)。