免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3004 | 回复: 2
打印 上一主题 下一主题

进程通信之--共享内存 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-23 09:29 |只看该作者 |倒序浏览

                                进程通信之--共享内存
    共享内存是最快的进程通信的形式,因为通过它进程间的通信可以不经过内核,而是在进程的内存空间中进行。从而加快了访问内存的速度。
    共享内存需要某种形式的同步;
    共享内存可以用于不同进程间的信息交换。
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

论坛徽章:
0
2 [报告]
发表于 2012-09-25 20:02 |只看该作者
牛人!!
请问下,SYSTEM V的共享内存周期是?

论坛徽章:
0
3 [报告]
发表于 2013-03-13 09:15 |只看该作者
very good !!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP