- 论坛徽章:
- 0
|
进程通信之--共享内存
共享内存是最快的进程通信的形式,因为通过它进程间的通信可以不经过内核,而是在进程的内存空间中进行。从而加快了访问内存的速度。
共享内存需要某种形式的同步;
共享内存可以用于不同进程间的信息交换。
1, 文件内存映射
通过把文件映射到内存区,可以不用文件操作的I/O函数write,read等,而可以直接操作内存。
(1)
/*
* incr1.c
* 说明:该代码达不到预期的目的
*/
#include "my_unpipc.h"
#define SEM_NAME "mysem1"
int count = 0;
int
main(int argc, char **argv)
{
int i, nloop;
sem_t *mutex;
if (argc != 2) {
fprintf(stderr, "usage: incr1 \n");
exit(0);
}
nloop = atoi(argv[1]);
/*
* create, initialize, and unlink semaphore
* mysem1 将会被创建在/dev/shm/sem.mysem1
*/
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1);
if (mutex == SEM_FAILED) {
perror("sem_open()");
exit(0);
}
sem_unlink(SEM_NAME);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("child: %d\n", count++);
sem_post(mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("parent: %d\n", count++);
sem_post(mutex);
}
exit(0);
}
*运行演示:
[nobody@linux shm]$ ./incr1 3
child: 0
child: 1
child: 2
parent: 0
parent: 1
parent: 2
...
*结果分析
该程序中父子进程各自都有自己的count的拷贝,所以虽然各自可以互斥的对count进行加1操作,但加的都是各自的count变量。
(2)
/*
* incr2.c
* 说明:父子程序共享文件映射的内存区块。
* 所以父子进程所作的改动都会在文件映射的内存中反应出来。
*/
#include "my_unpipc.h"
#define SEM_NAME "mysem"
int
main(int argc, char **argv)
{
int fd, i, nloop, zero = 0;
int *ptr;
sem_t *mutex;
if (argc != 3) {
printf("usage: incr2 \n");
exit(0);
}
nloop = atoi(argv[2]);
/* open file, initialize to 0, map into memory */
fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
write(fd, &zero, sizeof(int));
/*注意mmap函数的参数 MAP_SHARED, 具体参见man mmap */
ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
/* create, initialize, and unlink semaphore for mutex */
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1);
if (mutex == SEM_FAILED) {
perror("sem_open");
exit(0);
}
/* 注意,退出时清理信号量文件 */
sem_unlink(SEM_NAME);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("child: %d\n", (*ptr)++);
sem_post(mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("parent: %d\n", (*ptr)++);
sem_post(mutex);
}
exit(0);
}
运行效果:
[nobody@linux shm]$ ./incr2 mysem 3
child: 0
child: 1
child: 2
parent: 3
parent: 4
parent: 5
...
运行结果分析:
由于父进程打开了文件,fork后,他们都各自有了该文件的描述符,文件被映射到内存后,他们各自都有指针变量ptr,但该指针变量指向同一块内存。
(3) 把共享的变量放到一个结构中
若把信号量变量放到进程的共享内存区,那么多个进程都可以看到它。这样就可以使用sem_init而不使用sem_open在共享内存区创建信号量,由于该信号量是共有的,而不是各自复制的,所以能够达到互斥的目的。
#include "my_unpipc.h"
struct shared {
sem_t mutex; /* the mutex: a Posix memory-based semaphore */
int count; /* and the counter */
} shared;
int
main(int argc, char **argv)
{
int fd, i, nloop;
struct shared *ptr;
if (argc != 3) {
printf("usage: incr3 \n");
exit(0);
}
nloop = atoi(argv[2]);
/*
* 这里也可以用匿名的mmap,可以避免创建文件,还要删除文件的操作。
* (1)
* ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
* 或
* (2)
* fd = open("/dev/zero", O_RDWR)
* ptr = mman(NULL, sizeof(int), PROT_READ | PROT_WRITE,
* MAP_SHARED, fd, 0)
* 然后用 (*ptr)++ ;
*/
/* open file, initialize to 0, map into memory */
fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
write(fd, &shared, sizeof(struct shared));
ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
/* initialize semaphore that is shared between processes */
sem_init(&ptr->mutex, 1, 1);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("child: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("parent: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
2, Posix 共享内存
Posix共享内存的作用和前面的文件的映射来共享内存的作用是一样的,只是调用的函数不一样,也不需要调用open来打开文件。
Posix共享内存的创建分为以下几步:
1) fd = shm_open()
2) ptr = mmap(..., fd, ...)
3) ftruncate(fd, length);
4) close(fd);
注意:posix共享内存的生命周期是随内核的。所以,创建完成后会在系统中一直存在。
2.1 posix共享内存用于存放互斥变量来实现多个进程间的互斥
/*
* pxshm_incr3.c
* 用posix共享内存实现进程间的互斥。
*/
#include "my_unpipc.h"
struct shared {
sem_t mutex; /* the mutex: a Posix memory-based semaphore */
int count; /* and the counter */
} shared;
int
main(int argc, char **argv)
{
int fd, i, nloop;
struct shared *ptr;
if (argc != 3) {
printf("usage: incr3 \n");
exit(0);
}
nloop = atoi(argv[2]);
/*
* 这是为了避免命名冲突的权益之计
* 如果这里不做这一步,记得在程序退出的时候删除共享内存变量。
*/
shm_unlink(argv[1]);
/* create shm, set its size, map it, close descriptor */
fd = shm_open(argv[1], O_RDWR | O_CREAT | O_EXCL, FILE_MODE);
if (fd == -1) {
perror("shm_open");
exit(0);
}
ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(0);
}
ftruncate(fd, sizeof(struct shared));
close(fd);
/* initialize semaphore that is shared between processes */
sem_init(&ptr->mutex, 1, 1);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("child: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("parent: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
执行的效果如下所示:
[hover@jsdhover pxshm]$ ./pxshm_incr3 hover 3
child: 0
child: 1
child: 2
parent: 3
parent: 4
parent: 5
2.2 用于多(clients)对一(server)间的进程通信
由于posix共享内存是文件形式,所以对于没有亲缘关系的进程也可以访问,这样我们就可以实现多个进程间的通信。
3, system v共享内存
v共享内存在现在的编程中使用得比较多,于posix共享内存相比,
posix : shm_open() --> mmap()
system v : shmget() ---> shmat()
System V共享内存的使用中不创建临时文件,使用起来比较方便。可用于多进程和多线程间的同步。 下面看一个例子:
#define TEXT_SIZE 2048
struct shared_use_at {
int written_by_you;
char some_txt[TEXT_SIZE];
}
/*
* Producer
*/
#include "my_unpipc.h"
#include "shm_com.h"
int
main(void)
{
int running = 1;
void *shared_mem = (void *)0;
struct shared_use_at *shared_stuff;
char buffer[BUFSIZ];
int shmid;
/* 这里用0666权限创建一个共享内存区 */
shmid = shmget((key_t)KEY_INT, sizeof(struct shared_use_at), 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(0);
}
/* 把共享内存的首地址连接到调用进程的地址空间 */
shared_mem = shmat(shmid, (void *)0, 0);
if (shared_mem == (void *)-1) {
perror("shmat");
exit(0);
}
printf("Memory attached at %X\n", (int)shared_mem);
shared_stuff = (struct shared_use_at *)shared_mem;
/*
* Begin to produce.
*/
while (running) {
while (shared_stuff->written_by_you == 1) { /* waiting */
sleep(1);
printf("Waitring for client...\n");
}
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_stuff->some_text, buffer, TEXT_SIZE);
shared_stuff->written_by_you = 1;
if (strncmp(buffer, "end", 3) == 0)
running = 0;
}
/*
* Retrieve shared memory.
*/
if (shmdt(shared_mem) == -1) {
perror("shmdt");
exit(0);
}
/*
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
exit(0);
}
*/
exit(0);
}
/*
* consumer:
*/
#include "my_unpipc.h"
#include "shm_com.h"
int
main(void)
{
int running = 1;
void *shared_mem = (void *)0;
struct shared_use_at *shared_stuff;
int shmid;
srand((unsigned int)getpid());
shmid = shmget((key_t)KEY_INT, sizeof(struct shared_use_at), 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(0);
}
shared_mem = shmat(shmid, (void *)0, 0);
if (shared_mem == (void *)-1) {
perror("shmat");
exit(0);
}
printf("Memory attached at %X\n", (int)shared_mem);
shared_stuff = (struct shared_use_at *)shared_mem;
shared_stuff->written_by_you = 0;
while (running) {
if (shared_stuff->written_by_you) {
printf("You wrote : %s\n", shared_stuff->some_text);
sleep(rand() % 4);
shared_stuff->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0)
running = 0;
}
}
if (shmdt(shared_mem) == -1) {
perror("shmdt");
exit(0);
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
exit(0);
}
exit(0);
}
4, 遇到的问题
(1) sem_open() 找不到文件或路径的问题
这是由于在内核中,创建信号量的默认路径是/dev/shm。当你要创建一个信号量/tmp/mysem时,实际上是创建了一个/dev/shm/sem.tmp/mysem,而这里由于/dev/shm/tmp目录根本就不存在,所以会出错。
解决方法:
*直接写信号量文件的名字,将会创建在/dev/shm中:sem_open("mysem", ...)
*
(...)
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/28197/showart_2158261.html |
|