共享内存是一种简单而高效的进程间通信方法。管道,消息队列等通信方法需要在主存和用户空间之间进行4次数据复制,而共享内存只需要2次。分析如下:
管道,消息队列等:
第一次:数据产生后需要写入进程A的用户空间
第二次:把数据从进程A的用户空间复制到内核空间中
第三次:把数据从内核空间复制到进程B的用户空间中
第四次:把数据从进程B的用户空间写入输出文件
共享内存的实现是将内核空间的物理内存映射到不同进程的虚拟地址,这样不同进程通过各自的虚拟地址就可以直接访问相同的物理内存,从而实现数据的共享。
但是进程A在写用户空间时,直接修改的就是物理内存,进程B即时的就能看见修改,
所以共享内存需要配合其他进程同步机制一起使用。
下面就一个实际例子来说明写入共享内存可以即时的被其他进程看到,也说明下访问共享内存时加入同步机制的必要性。
reader进程创建一块共享内存,然后将之映射到自己的用户空间,之后每隔一秒打印出内存的所有内容。与此同时,writer进程分批向共享内存写入数据。
reader进程的代码:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
int main()
{
int i, sum, shm_id;
key_t key;
char *p;
key = ftok( ".", 'm');
if(-1 == key)
{
perror("ftok error");
return;
}
/* alloc 100 bytes for the shared memory */
shm_id = shmget( key, 100, IPC_CREAT | S_IRUSR | S_IWUSR);
if(-1 == shm_id)
{
perror("shmget error");
return;
}
/* attach the shared memory */
p = (char*) shmat( shm_id, NULL, 0);
if((void*)-1 == p)
{
perror("shmat error");
goto exit0;
}
/* clear the space and read it every second */
// memset(p, 0, 10);
do
{
printf("\n---------------------------\n");
printf("pid:%d key:0x%x shmid:%d\n", getpid(), key, shm_id);
for(i = 0 ; i < 10; i++)
{
printf("dump[%d] value:%-4d shown:'%c'\n", i, p[i], p[i]);
if('z' == p[i])
goto exit1;
}
sleep(1);
}while(1);
exit1:
system("ipcs -m");
/* detach */
if(-1 == shmdt(p))
{
perror("shmdt error");
}
else
{
printf("shmdt ok\n");
}
exit0:
system("ipcs -m");
/* free the shared memory */
if(-1 == shmctl( shm_id, IPC_RMID, NULL))
{
perror("shmctl error");
}
else
{
printf("shmctl RMID ok\n");
}
system("ipcs -m");
return ;
}
writer进程的代码:
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
int shmid, i;
char *p;
if(3 != argc || 0 > (shmid = atoi(argv[1])))
{
printf("Usage: %s shmid 01234abcdz\n", argv[0]);
exit(0);
}
/* attach the shared memory */
p = (char*)shmat( shmid, NULL, 0);
printf("p=%p, shmid=%d, %s\n", p, shmid, argv[2]);
if((void*)-1 == p)
{
perror("shmat error");
}
else
printf("shmat ok\n");
/* put sth. in the shared memroy */
for(i = 0; i < strlen(argv[2]); i++)
{
sleep(1);
p[i] = argv[2][i];
printf("wrote %d, or shown as '%c'\n", p[i], p[i]);
}
/* detach the shared memory */
if(-1 == shmdt(p))
{
perror("shmdt error");
}
else
{
printf("shmdt successfully\n");
}
return ;
}
先运行reader进程,每隔1秒打印共享内存的每一个byte,如果某个byte是字符z就退出。
后运行writer进程,运行时后面跟2个参数,共享内存id和写入的字符串:
user@test:~$ ./writer 1048586 0123abcdz
p=0x7f6dc4e48000, shmid=1048586, 0123abcdz
shmat ok
wrote 48, or shown as '0'
wrote 49, or shown as '1'
wrote 50, or shown as '2'
wrote 51, or shown as '3'
wrote 97, or shown as 'a'
wrote 98, or shown as 'b'
wrote 99, or shown as 'c'
wrote 100, or shown as 'd'
wrote 122, or shown as 'z'
shmdt successfully
由于reader进程显示很多,只截取下最后几段:
---------------------------
pid:18114 key:0x6d046289 shmid:1048586
dump[0] value:48 shown:'0'
dump[1] value:49 shown:'1'
dump[2] value:50 shown:'2'
dump[3] value:51 shown:'3'
dump[4] value:97 shown:'a'
dump[5] value:98 shown:'b'
dump[6] value:99 shown:'c'
dump[7] value:100 shown:'d'
dump[8] value:0 shown:''
dump[9] value:0 shown:''
---------------------------
pid:18114 key:0x6d046289 shmid:1048586
dump[0] value:48 shown:'0'
dump[1] value:49 shown:'1'
dump[2] value:50 shown:'2'
dump[3] value:51 shown:'3'
dump[4] value:97 shown:'a'
dump[5] value:98 shown:'b'
dump[6] value:99 shown:'c'
dump[7] value:100 shown:'d'
dump[8] value:122 shown:'z'
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 32768 user 600 393216 2 dest
0x00000000 65537 user 600 393216 2 dest
0x00000000 98306 user 600 393216 2 dest
0x00000000 360451 user 600 393216 2 dest
0x00000000 393220 user 600 393216 2 dest
0x00000000 425989 user 700 16744 2 dest
0x00000000 294918 user 600 393216 2 dest
0x00000000 327687 user 700 5273600 2 dest
0x00000000 491528 user 700 56672 2 dest
0x6d046289 1048586 user 600 100 1
shmdt ok
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 32768 user 600 393216 2 dest
0x00000000 65537 user 600 393216 2 dest
0x00000000 98306 user 600 393216 2 dest
0x00000000 360451 user 600 393216 2 dest
0x00000000 393220 user 600 393216 2 dest
0x00000000 425989 user 700 16744 2 dest
0x00000000 294918 user 600 393216 2 dest
0x00000000 327687 user 700 5273600 2 dest
0x00000000 491528 user 700 56672 2 dest
0x6d046289 1048586 user 600 100 0
shmctl RMID ok
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 32768 user 600 393216 2 dest
0x00000000 65537 user 600 393216 2 dest
0x00000000 98306 user 600 393216 2 dest
0x00000000 360451 user 600 393216 2 dest
0x00000000 393220 user 600 393216 2 dest
0x00000000 425989 user 700 16744 2 dest
0x00000000 294918 user 600 393216 2 dest
0x00000000 327687 user 700 5273600 2 dest
0x00000000 491528 user 700 56672 2 dest