linux中怎么用qt实现两个qt与非qt进程通信间用共享内存通信

共享内存的优势
采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
图1:POSIX消息队列
图2:共享内存
Linux的2.6.x内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及System V共享内存。本文对3种共享内存形式都将进行介绍。
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
注:实际上,mmap()系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或System V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
图3:直接映射文件
图4:开辟共享内存空间
linux采用的是页式管理机制。对于用mmap()映射普通文件来说,进程会在自己的地址空间新增一块空间,空间大小由mmap()的length参数指定,注意,进程并不一定能够对全部新增空间都能进行有效访问。进程能够访问的有效地址大小取决于文件被映射部分的大小。简单的说,能够容纳文件被映射部分大小的最少页面个数决定了进程从mmap()返回的地址开始,能够有效访问的地址空间大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。如下图所示:
图5:mmap映射
这个具体差异跟系统实现有关,这里不做详细讨论。
文件详细信息
struct stat {
&& dev_t&&&& st_&&&& /* ID of device containing file */
&& ino_t&&&& st_&&&& /* inode number */
&& mode_t&&& st_&&& /* protection */
&& nlink_t&& st_&& /* number of hard links */
&& uid_t&&&& st_&&&& /* user ID of owner */
&& gid_t&&&& st_&&&& /* group ID of owner */
&& dev_t&&&& st_&&& /* device ID (if special file) */
&& off_t&&&& st_&&& /* total size, in bytes */
&& blksize_t st_ /* blocksize for file system I/O */
&& blkcnt_t& st_& /* number of 512B blocks allocated */
&& time_t&&& st_&& /* time of last access */
&& time_t&&& st_&& /* time of last modification */
&& time_t&&& st_&& /* time of last status change */
#include &sys/mman.h&
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset);
&Ofd:为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。
&OLength:映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。prot:指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NONE(不可访问)。
&Oflags:由以下几个常值指定MAP_SHARED, MAP_PRIVATE, MAP_FIXED等,其中,MAP_SHARED, MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。
&MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
&MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的&写入时复制&(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
&MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
&MAP_ANONYMOUS 建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
&MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
&Ooffset:一般设为0,表示从文件头开始映射。
&Oaddr:指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。
int munmap(void *addr, size_t length);
该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,length是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。
int msync(void *addr, size_t length, int flags);
一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。
// 调整fd所指的文件的大小到length
int ftruncate(int fd, off_t length);
// 获取fd所指的文件的详细信息
int fstat(int fd, struct stat *buf);
写共享内存
#include &sys/mman.h&
#include &fcntl.h&
#include &sys/mman.h&
#include &sys/types.h&
#include &unistd.h&
#include &stdio.h&
#include &stdlib.h&
#include &errno.h&
#include &string.h&
typedef struct
char name[32];
main(int argc, char** argv)
people* p_
char temp = 'a';
int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 00777);
if (-1 == fd)
printf("open file error = %s\n", strerror(errno));
return -1;
ftruncate(fd, sizeof(people)*10);
p_map = (people*)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == p_map)
printf("mmap file error = %s\n", strerror(errno));
return -1;
for(int i = 0; i & 10; i++)
memcpy( ( *(p_map+i) ).name, &temp, 1);
( *(p_map+i) ).name[1] = 0;
( *(p_map+i) ).age = 20+i;
temp += 1;
printf("initialize over\n");
close(fd);
munmap(p_map, sizeof(people)*10);
printf("umap ok \n");
读共享内存
#include &fcntl.h&
#include &sys/mman.h&
#include &sys/types.h&
#include &unistd.h&
#include &stdio.h&
#include &stdlib.h&
#include &errno.h&
#include &string.h&
#include &sys/stat.h&
typedef struct
char name[32];
main(int argc, char** argv)
people* p_
int fd = open(argv[1], O_CREAT|O_RDWR, 00777);
if (-1 == fd)
printf("open file error = %s\n", strerror(errno));
return -1;
fstat(fd, &filestat);
p_map = (people*)mmap(NULL, filestat.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == p_map)
printf("mmap file error = %s\n", strerror(errno));
return -1;
for(int i = 0; i & 10; i++)
printf("name = %s, age = %d\n",(*(p_map+i)).name, (*(p_map+i)).age);
close(fd);
munmap(p_map, sizeof(people)*10);
printf("umap ok \n");
[root@rocket ipc]# g++ -g -o ipc_mmap_writer ipc_mmap_writer.cpp
[root@rocket ipc]# ./ipc_mmap_writer /tmp/mmap_text.file
initialize over
[root@rocket ipc]# g++ -g -o ipc_mmap_reader ipc_mmap_reader.cpp
[root@rocket ipc]# ./ipc_mmap_reader /tmp/mmap_text.file
name = a, age = 20
name = b, age = 21
name = c, age = 22
name = d, age = 23
name = e, age = 24
name = f, age = 25
name = g, age = 26
name = h, age = 27
name = i, age = 28
name = j, age = 29
查看mmap文件
[root@rocket ipc]# ll /tmp/mmap_text.file
-rwxr-xr-x. 1 root root 360 Oct 14 02:55 /tmp/mmap_text.file
POSIX共享内存
POSIX共享内存使用方法有以下两个步骤:
&O通过shm_open创建或打开一个POSIX共享内存对象
&O调用mmap将它映射到当前进程的地址空间
和通过内存映射文件进行通信的使用上差别在于mmap描述符参数获取方式不一样:通过open或shm_open。如下图所示:
图6:Posix内存映射文件
POSIX共享内存和POSIX消息队列,有名信号量一样都是具有随内核持续性的特点。
在Linux 2.6.x中,对于POSIX信号量和共享内存的名字会在/dev/shm下建立对应的路径名
[root@rocket shm]# ll /dev/shm/|grep mem
-rwxr-xr-x. 1 root root&&&&& 360 Oct 14 05:23 shm_from_mem.txt
#include &sys/mman.h&
#include &sys/stat.h&&&&&&&& /* For mode constants */
#include &fcntl.h&& &&&&&&&& /* For O_* constants */
// 打开一个共享内存的文件句柄
int shm_open(const char *name, int oflag, mode_t mode);
注意这里的名字具有形式 /somename,即必须以 / 为开头,因为POSIX共享内存对应的文件是位于/dev/shm这个特殊的文件系统内。
// 删除一个共享内存的名字,但只有所有程序都关闭,才会真的删除
int shm_unlink(const char *name);
写共享内存
#include &sys/mman.h&
#include &fcntl.h&
#include &sys/mman.h&
#include &sys/types.h&
#include &unistd.h&
#include &stdio.h&
#include &stdlib.h&
#include &errno.h&
#include &string.h&
typedef struct
char name[32];
main(int argc, char** argv)
people* p_
char temp = 'a';
int fd = shm_open(argv[1], O_CREAT|O_RDWR, 00777);
if (-1 == fd)
printf("open file error = %s\n", strerror(errno));
return -1;
ftruncate(fd, sizeof(people)*10);
p_map = (people*)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == p_map)
printf("mmap file error = %s\n", strerror(errno));
return -1;
for(int i = 0; i & 10; i++)
memcpy( ( *(p_map+i) ).name, &temp, 1);
( *(p_map+i) ).name[1] = 0;
( *(p_map+i) ).age = 20+i;
temp += 1;
printf("initialize over\n");
close(fd);
munmap(p_map, sizeof(people)*10);
printf("umap ok \n");
读共享内存
#include &fcntl.h&
#include &sys/mman.h&
#include &sys/types.h&
#include &unistd.h&
#include &stdio.h&
#include &stdlib.h&
#include &errno.h&
#include &string.h&
#include &sys/stat.h&
typedef struct
char name[32];
main(int argc, char** argv)
people* p_
int fd = shm_open(argv[1], O_CREAT|O_RDWR, 00777);
if (-1 == fd)
printf("open file error = %s\n", strerror(errno));
return -1;
fstat(fd, &filestat);
p_map = (people*)mmap(NULL, filestat.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == p_map)
printf("mmap file error = %s\n", strerror(errno));
return -1;
for(int i = 0; i & 10; i++)
printf("name = %s, age = %d\n",(*(p_map+i)).name, (*(p_map+i)).age);
close(fd);
munmap(p_map, sizeof(people)*10);
printf("umap ok \n");
删除共享内存
#include &fcntl.h&
#include &sys/mman.h&
#include &sys/types.h&
#include &unistd.h&
#include &stdio.h&
#include &stdlib.h&
#include &errno.h&
#include &string.h&
#include &sys/stat.h&
main(int argc, char** argv)
int ret = shm_unlink(argv[1]);
if (-1 == ret)
printf("unlink shm error = %s\n", strerror(errno));
return -1;
printf("unlink ok \n");
[root@rocket ipc]# g++ -g -o ipc_posix_mmap_writer ipc_posix_mmap_writer.cpp -lrt
[root@rocket ipc]# ./ipc_posix_mmap_writer /shm_from_mem.txt
initialize over
[root@rocket ipc]# g++ -g -o ipc_posix_mmap_reader ipc_posix_mmap_reader.cpp -lrt
[root@rocket ipc]# ./ipc_posix_mmap_reader /shm_from_mem.txt
name = a, age = 20
name = b, age = 21
name = c, age = 22
name = d, age = 23
name = e, age = 24
name = f, age = 25
name = g, age = 26
name = h, age = 27
name = i, age = 28
name = j, age = 29
[root@rocket ipc]# ./ipc_posix_mmap_unlink /shm_from_mem.txt
[root@rocket ipc]# ./ipc_posix_mmap_unlink /shm_from_mem.txt
unlink shm error = No such file or directory
[root@rocket ipc]# ll /dev/shm/|grep mem
[root@rocket ipc]#
可以看到/dev/shm下面的shm_from_mem.txt已经被删除了。
System V共享内存
系统调用mmap()通过映射一个普通文件实现共享内存。System V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shmid_kernel结构联系起来的)。进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。System V共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构注同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。
每一个共享内存区都有一个控制结构struct shmid_kernel,shmid_kernel是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁,定义如下:
struct shmid_kernel /* private to the kernel */
&&&&&&&& struct kern_ipc_perm&&&&& shm_
&&&&&&&& struct file *&&&&&&&&&&&&&&& shm_file;
&&&&&&&& int&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&& unsigned long&&&&&&&&&& shm_
&&&&&&&& unsigned long&&&&&&&&&& shm_
&&&&&&&& time_t&&&&&&&&&&&&&&&&&&&&&&&& shm_
&&&&&&&& time_t&&&&&&&&&&&&&&&&&&&&&&&& shm_
&&&&&&&& time_t&&&&&&&&&&&&&&&&&&&&&&&& shm_
&&&&&&&& pid_t&&&&&&&&&&&&&&&&&&&&&&&&&&& shm_
&&&&&&&& pid_t&&&&&&&&&&&&&&&&&&&&&&&&&&& shm_
该结构中最重要的一个域应该是shm_file,它存储了将被映射文件的地址。每个共享内存区对象都对应特殊文件系统shm中的一个文件,一般情况下,特殊文件系统shm中的文件是不能用read()、write()等方法访问的,当采取共享内存的方式把其中的文件映射到进程地址空间后,可直接采用访问内存的方式对其访问。
图7:System V共享内存内核结构
内核通过数据结构struct ipc_ids shm_ids维护系统中的所有共享内存区域。上图中的shm_ids.entries变量指向一个ipc_id结构数组,而每个ipc_id结构数组中有个指向kern_ipc_perm结构的指针。到这里读者应该很熟悉了,对于系统V共享内存区来说,kern_ipc_perm的宿主是shmid_kernel结构,shmid_kernel是用来描述一个共享内存区域的,这样内核就能够控制系统中所有的共享区域。同时,在shmid_kernel结构的file类型指针shm_file指向文件系统shm中相应的文件,这样,共享内存区域就与shm文件系统中的文件对应起来。
在创建了一个共享内存区域后,还要将它映射到进程地址空间,系统调用shmat()完成此项功能。由于在调用shmget()时,已经创建了文件系统shm中的一个同名文件与共享内存区域相对应,因此,调用shmat()的过程相当于映射文件系统shm中的同名文件过程,原理与mmap()大同小异。
#include &sys/ipc.h&
#include &sys/shm.h&
// 获取共享内存区域
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);
// 将path和proj_id转换成System V IPC key
key_t ftok(const char *pathname, int proj_id);
写共享内存
#include &sys/mman.h&
#include &fcntl.h&
#include &sys/mman.h&
#include &sys/types.h&
#include &unistd.h&
#include &stdio.h&
#include &stdlib.h&
#include &errno.h&
#include &string.h&
#include &sys/ipc.h&
#include &sys/shm.h&
typedef struct
char name[32];
int main(int argc, char** argv)
int shm_id,i;
people* p_
char temp = 'a';
const char* name = "/dev/shm/my_systemv_shm1";
key = ftok(name,0);
if (key == -1)
perror("ftok error");
return -1;
shm_id=shmget(key, 4096, IPC_CREAT);
if(shm_id == -1)
perror("shmget error");
return -1;
p_map=(people*)shmat(shm_id,NULL,0);
for(int i = 0; i & 10; i++)
memcpy( ( *(p_map+i) ).name, &temp, 1);
( *(p_map+i) ).name[1] = 0;
( *(p_map+i) ).age = 20+i;
temp += 1;
printf("initialize over\n");
if(shmdt(p_map) == -1)
perror(" detach error ");
return -1;
读共享内存
#include &sys/mman.h&
#include &fcntl.h&
#include &sys/mman.h&
#include &sys/types.h&
#include &unistd.h&
#include &stdio.h&
#include &stdlib.h&
#include &errno.h&
#include &string.h&
#include &sys/ipc.h&
#include &sys/shm.h&
typedef struct
char name[32];
int main(int argc, char** argv)
int shm_id,i;
people* p_
const char* name = "/dev/shm/my_systemv_shm1";
key = ftok(name,0);
if (key == -1)
perror("ftok error");
return -1;
shm_id=shmget(key, 4096, IPC_CREAT);
if(shm_id == -1)
perror("shmget error");
return -1;
p_map=(people*)shmat(shm_id,NULL,0);
for(int i = 0; i & 10; i++)
printf( "name:%s, ",(*(p_map+i)).name );
printf( "age %d\n",(*(p_map+i)).age );
if(shmdt(p_map) == -1)
perror(" detach error ");
return -1;
[root@rocket ipc]# g++ -g -o ipc_systemv_mmap_writer ipc_systemv_mmap_writer.cpp
[root@rocket ipc]# touch /dev/shm/my_systemv_shm1
[root@rocket ipc]# ./ipc_systemv_mmap_writer
initialize over
[root@rocket ipc]# g++ -g -o ipc_systemv_mmap_reader ipc_systemv_mmap_reader.cpp
[root@rocket ipc]# ./ipc_systemv_mmap_reader
name:a, age 20
name:b, age 21
name:c, age 22
name:d, age 23
name:e, age 24
name:f, age 25
name:g, age 26
name:h, age 27
name:i, age 28
name:j, age 29
观察一下共享内存:
[root@rocket ipc]# ./get_ipc_key /dev/shm/my_systemv_shm1
key = 1084739
[root@rocket ipc]# ipcs
------ Shared Memory Segments --------
key&&&&&&& shmid&&&&& owner&&&&& perms&&&&& bytes&&&&& nattch&&&& status&&&&&
0x&&&&&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest&&&&&&&&
0x69&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest& &&&&&&
0x38&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest&&&&&&&&
0x07&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest&&&&&&&&
0x076&&&& root&&&&&& 0&&&&&&&&& 4096&&&&&& 0 &&
看到我们新建的共享内存了吧?删除也很简单:
[root@rocket ipc]# ipcrm -m 131076
[root@rocket ipc]# ipcs
------ Shared Memory Segments --------
key&&&&&&& shmid&&&&& owner&&&&& perms&&&&& bytes&&&&& nattch&&&& status&&&&&
0x&&&&&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest&&&&&&&&
0x69&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest&&&&&&&&
0x38&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest&&&&&&&&
0x07&&&&& gdm&&&&&&& 600&&&&&&& 393216&&&& 2&&&&&&&&& dest
总结及3种共享内存比较
共享内存是最快的IPC形式,在开发中,我们一定要充分利用好共享内存的特性,取得事半功倍的效果。
利用文件(open)映射共享内存区域
会保存在磁盘上,不会丢失
Posix shared memory
利用/dev/shm文件系统(shm_open)映射共享内存区域
随内核持续,内核自举后会丢失
SystemV shared memory
利用特殊文件系统shm中的文件映射共享内存区域
随内核持续,内核自举后会丢失
阅读(...) 评论()一&共享内存介绍
&&&&&共享内存可以从字面上去理解,就把一片内存共享出来,让不同的进程去访问它,修改它。语言函数分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
但有一点特别要注意:共享内存并未提供同步机制。也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
二&共享内存的使用
&int&shmget(key_t&key,&size_t&size,&int&shmflg);
◇的函数成功时返回一个与相关的共享内存标识符(非负整数),用于后续的共享内存函数。
& &&☆其它的进程函数并提供一个键,再由系统生成一个相应的共享内存标识符(函数的返回值),只有函数才直接使用信号量键,所有其他的信号量函数使用由函数返回的信号量标识符。
◇以字节为单位指定需要共享的内存容量。
◇是权限标志,它的作用与函数的参数一样,如果要想在标识的共享内存不存在时,创建它的话,可以与做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
void&*shmat(int&shm_id,&const&void&*shm_addr,&int&shmflg);
◇函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。
◇是由函数返回的共享内存标识。
◇指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
◇是一组标志位,通常为。
int&shmdt(const&void&*shmaddr);
◇是函数返回的地址指针,调用成功时返回,失败时返回。
◇是函数返回的共享内存标识符。
◇是要采取的操作,它可以取下面的三个值&:
:把结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖的值。
:如果进程有足够的权限,就把共享内存的当前关联值设置为结构中给出的值
:删除共享内存段
◇是一个结构指针,它指向共享内存模式和访问权限的结构。
struct shmid_ds
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct shared_use_st
int written;/* 作为一个标志,非0:表示可读,0表示可写 */
char text[TEXT_SZ];/* 记录写入和读取的文本 */
#include&unistd.h&
#include&stdlib.h&
#include&stdio.h&
#include&sys/shm.h&
#include"shmdata.h"
#define MEM_KEY
int main()
int running =1;
//程序是否继续运行的标志
//分配的共享内存的原始首地址
struct shared_use_st *shared;//指向shm
int shmid;
//共享内存标识符
//创建共享内存
shmid = shmget((key_t)MEM_KEY,sizeof(struct shared_use_st),0666|IPC_CREAT);
if(shmid ==-1)
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid,0,0);
if(shm ==(void*)-1)
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
printf("\nMemory attached at %X\n",(int)shm);
//设置共享内存
shared =(struct shared_use_st*)shm;
shared-&written =0;
while(running)//读取共享内存中的数据
//没有进程向共享内存定数据有数据可读取
if(shared-&written !=0)
printf("You wrote: %s", shared-&text);
sleep(rand()%3);
//读取完数据,设置written使共享内存段可写
shared-&written =0;
//输入了end,退出循环(程序)
if(strncmp(shared-&text,"end",3)==0)
running =0;
else//有其他进程在写数据,不能读取数据
//把共享内存从当前进程中分离
if(shmdt(shm)==-1)
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
//删除共享内存
if(shmctl(shmid, IPC_RMID,0)==-1)
fprintf(stderr,"shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
#include&unistd.h&
#include&stdlib.h&
#include&stdio.h&
#include&string.h&
#include&sys/shm.h&
#include"shmdata.h"
#define MEM_KEY
int main()
int running =1;
void*shm = NULL;
struct shared_use_st *shared = NULL;
char buffer[BUFSIZ +1];//用于保存输入的文本
int shmid;
//创建共享内存
shmid = shmget((key_t)MEM_KEY,sizeof(struct shared_use_st),0666|IPC_CREAT);
if(shmid ==-1)
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid,(void*)0,0);
if(shm ==(void*)-1)
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
printf("Memory attached at %X\n",(int)shm);
//设置共享内存
shared =(struct shared_use_st*)shm;
while(running)//向共享内存中写数据
//数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本
while(shared-&written ==1)
printf("Waiting...\n");
//向共享内存中写入数据
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared-&text, buffer, TEXT_SZ);
//写完数据,设置written使共享内存段可读
shared-&written =1;
//输入了end,退出循环(程序)
if(strncmp(buffer,"end",3)==0)
running =0;
//把共享内存从当前进程中分离
if(shmdt(shm)==-1)
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
◇创建共享内存,然后将它连接到自己的地址空间。在共享内存的开始处使用了一个结构。该结构中有个标志,当共享内存中有其他进程向它写入数据时,共享内存中的被设置为,程序等待。当它不为时,表示没有进程对共享内存写入数据,程序就从共享内存中读取数据并输出,然后重置设置共享内存中的为,即让其可被进程写入数据。
◇取得共享内存并连接到自己的地址空间中。检查共享内存中的,是否为,若不是,表示共享内存中的数据还没有被完,则等待其他进程读取完成,并提示用户等待。若共享内存的为,表示没有其他进程对共享内存进行读取,则提示用户输入文本,并再次设置共享内存中的为,表示写完成,其他进程可对共享内存进行读操作。
的使用方式,例如,只有当为时进程才可以向共享内存写入数据,而当一个进程只有在不为时才能对其进行读取,同时把进行加操作,读取完后进行减操作。这就有点像文件锁中的读写锁的功能。咋看之下,它似乎能行得通。但是这都不是原子操作,所以这种做法是行不能的。试想当为时,如果有两个进程同时访问共享内存,它们就会发现为,于是两个进程都对其进行写操作,显然不行。当为时,有两个进程同时对共享内存进行读操作时也是如些,当这两个进程都读取完是,就变成了
/*server.c:向共享内存中写入People*/
#include&stdio.h&
#include&sys/types.h&
#include&sys/ipc.h&
#include&sys/sem.h&
#include&string.h&
#include"credis.h"
int semid;
int shmid;
/*信号量的P操作*/
struct sembuf sem_p;
sem_p.sem_num=0;/*设置哪个信号量*/
sem_p.sem_op=-1;/*定义操作*/
if(semop(semid,&sem_p,1)==-1)
printf("p operation is fail\n");
/*semop函数自动执行信号量集合上的操作数组。
   int semop(int semid, struct sembuf semoparray[], size_t nops);
   semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。*/
/*信号量的V操作*/
struct sembuf sem_v;
sem_v.sem_num=0;
sem_v.sem_op=1;
if(semop(semid,&sem_v,1)==-1)
printf("v operation is fail\n");
int main()
structPeople{
char name[10];
key_t semkey;
key_t shmkey;
semkey=ftok("../test/VenusDB.cbp",0);//用来产生唯一的标志符,便于区分信号量及共享内存
shmkey=ftok("../test/main.c",0);
/*创建信号量的XSI IPC*/
semid=semget(semkey,1,0666|IPC_CREAT);//参数nsems,此时为中间值1,指定信号灯集包含信号灯的数目
//0666|IPC_CREAT用来表示对信号灯的读写权限
第一位:0表示这是一个8进制数
第二位:当前用户的经权限:6=110(二进制),每一位分别对就 可读,可写,可执行,6说明当前用户可读可写不可执行
第三位:group组用户,6的意义同上
第四位:其它用户,每一位的意义同上,0表示不可读不可写也不可执行
if(semid==-1)
printf("creat sem is fail\n");
//创建共享内存
shmid=shmget(shmkey,1024,0666|IPC_CREAT);//对共享内存
if(shmid==-1)
printf("creat shm is fail\n");
/*设置信号量的初始值,就是资源个数*/
union semun{
struct semid_ds *buf;
unsignedshort*array;
sem_u.val=1;/*设置变量值*/
semctl(semid,0,SETVAL,sem_u);//初始化信号量,设置第0个信号量,p()操作为非阻塞的
/*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/
structPeople*addr;
addr=(structPeople*)shmat(shmid,0,0);//将共享内存映射到调用此函数的内存段
if(addr==(structPeople*)-1)
printf("shm shmat is fail\n");
/*向共享内存写入数据*/
strcpy((*addr).name,"xiaoming");
/*注意:①此处只能给指针指向的地址直接赋值,不能在定义一个
struct People people_1;addr=&people_1;因为addr在addr=(struct People*)shmat(shmid,0,0);时,已经由系统自动分配了一个地址,这个地址与共享内存相关联,所以不能改变这个指针的指向,否则他将不指向共享内存,无法完成通信了。
注意:②给字符数组赋值的方法。刚才太虎了。。*/
(*addr).age=10;
/*将共享内存与当前进程断开*/
if(shmdt(addr)==-1)
printf("shmdt is fail\n");
2、clinet.c
/*client.c:从共享内存中读出People*/
#include&stdio.h&
#include&sys/types.h&
#include&sys/ipc.h&
#include&sys/sem.h&
int semid;
int shmid;
/*信号量的P操作*/
struct sembuf sem_p;
sem_p.sem_num=0;
sem_p.sem_op=-1;
if(semop(semid,&sem_p,1)==-1)
printf("p operation is fail\n");
/*信号量的V操作*/
struct sembuf sem_v;
sem_v.sem_num=0;
sem_v.sem_op=1;
if(semop(semid,&sem_v,1)==-1)
printf("v operation is fail\n");
int main()
key_t semkey;
key_t shmkey;
semkey=ftok("../test/client/VenusDB.cbp",0);
shmkey=ftok("../test/client/main.c",0);
structPeople{
char name[10];
/*读取共享内存和信号量的IPC*/
semid=semget(semkey,0,0666);
if(semid==-1)
printf("creat sem is fail\n");
shmid=shmget(shmkey,0,0666);
if(shmid==-1)
printf("creat shm is fail\n");
/*将共享内存映射到当前进程的地址中,之后直接对进程中的地址addr操作就是对共享内存操作*/
structPeople*addr;
addr=(structPeople*)shmat(shmid,0,0);
if(addr==(structPeople*)-1)
printf("shm shmat is fail\n");
/*从共享内存读出数据*/
printf("name:%s\n",addr-&name);
printf("age:%d\n",addr-&age);
/*将共享内存与当前进程断开*/
if(shmdt(addr)==-1)
printf("shmdt is fail\n");
/*IPC必须显示删除。否则会一直留存在系统中*/
if(semctl(semid,0,IPC_RMID,0)==-1)
printf("semctl delete error\n");
if(shmctl(shmid,IPC_RMID,NULL)==-1)
printf("shmctl delete error\n");
五 父子进程共享内存的个子
#include&stdio.h&
#include&sys/types.h&
#include&sys/ipc.h&
#include&sys/sem.h&
#define SHM_KEY 0x33
#define SEM_KEY 0x44
union semun {
struct semid_ds *buf;
unsignedshort*array;
int P(int semid)
struct sembuf sb;
sb.sem_num =0;
sb.sem_op =-1;
sb.sem_flg = SEM_UNDO;
if(semop(semid,&sb,1)==-1){
perror("semop");
int V(int semid)
struct sembuf sb;
sb.sem_num =0;
sb.sem_op =1;
sb.sem_flg = SEM_UNDO;
if(semop(semid,&sb,1)==-1){
perror("semop");
int main(int argc,char**argv)
pid_t pid;
int i, shmid, semid;
union semun semopts;
/* 创建一块共享内存, 存一个int变量 */
if((shmid = shmget(SHM_KEY,sizeof(int), IPC_CREAT |0600))==-1){
perror("msgget");
/* 将共享内存映射到进程, fork后子进程可以继承映射 */
if((ptr =(int*)shmat(shmid, NULL,0))==(void*)-1){
perror("shmat");
/* 创建一个信号量用来同步共享内存的操作 */
if((semid = semget(SEM_KEY,1, IPC_CREAT |0600))==-1){
perror("semget");
/* 初始化信号量 */
semopts.val =1;
if(semctl(semid,0, SETVAL, semopts)&0){
perror("semctl");
if((pid = fork())&0){
perror("fork");
}elseif(pid ==0){/* Child */
/* 子进程对共享内存加1 */
for(i =0; i &100; i++){
printf("child: %d\n",*ptr);
}else{/* Parent */
/* 父进程对共享内存减1 */
for(i =0; i &100; i++){
printf("parent: %d\n",*ptr);
waitpid(pid);
/* 如果同步成功, 共享内存的值为0 */
printf("finally: %d\n",*ptr);
阅读(...) 评论()

我要回帖

更多关于 java实现进程间通信 的文章

 

随机推荐