进程间通信-共享内存

概括

共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

优点

共享内存是IPC通信当中传输速度最快的通信方式没有之一,理由很简单,客户进程和服务进程传递的数据直接从内存里存取、放入,数据不需要在两进程间复制,没有什么操作比这简单了。再者用共享内存进行数据通信,它对数据也没啥限制。

缺点

共享内存也并不完美,共享内存并未提供同步机制,也就是说,在一个服务进程结束对共享内存的写操作之前,并没有自动机制可以阻止另一个进程(客户进程)开始对它进行读取。这明显还达不到我们想要的,我们不单是在两进程间交互数据,还想实现多个进程对共享内存的同步访问,这也正是使用共享内存的窍门所在。基于此,我们通常会用平时常谈到和用到 信号量来实现对共享内存同步访问控制。

原理

在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

上PHP代码

共享内存的进程通信方式PHP也只封装了System V的方式,但是有两种封装方式 shm 和 shmop。

shm

<?php
$key = ftok("shared_memory.php", "x");
$shm_id = shm_attach($key, 1000);
$pid = pcntl_fork();
shm_put_var($shm_id, "key", "hello");
if($pid == 0){
    fprintf(STDOUT, "取出的数据: %s \n", shm_get_var($shm_id, "key"));
    exit();
}
shm_remove($shm_id);

shmop

<?php
$key = ftok("shared_memory_op.php", "x");
$shm_id = shmop_open($key, "c", 0666, 128);
shmop_write($shm_id, "hello", 0);
fprintf(STDOUT, "读取: %s \n", shmop_read($shm_id, 0, 5));
shmop_delete($shm_id);

底层函数解析

shm_attach 底层调用 shmget创建 shmat映射。

PHP_FUNCTION(shm_attach)
{
    sysvshm_shm *shm_list_ptr;
    char *shm_ptr;
    sysvshm_chunk_head *chunk_ptr;
    zend_long shm_key, shm_id, shm_size, shm_flag = 0666;
    bool shm_size_is_null = 1;

    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|l!l", &shm_key, &shm_size, &shm_size_is_null, &shm_flag)) {
        RETURN_THROWS();
    }

    if (shm_size_is_null) {
        shm_size = php_sysvshm.init_mem;
    }

    if (shm_size < 1) {
        zend_argument_value_error(2, "must be greater than 0");
        RETURN_THROWS();
    }

    /* get the id from a specified key or create new shared memory */
    if ((shm_id = shmget(shm_key, 0, 0)) < 0) {
        if (shm_size < (zend_long)sizeof(sysvshm_chunk_head)) {
            php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": memorysize too small", shm_key);
            RETURN_FALSE;
        }
        if ((shm_id = shmget(shm_key, shm_size, shm_flag | IPC_CREAT | IPC_EXCL)) < 0) {
            php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
            RETURN_FALSE;
        }
    }

    if ((shm_ptr = shmat(shm_id, NULL, 0)) == (void *) -1) {
        php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key, strerror(errno));
        RETURN_FALSE;
    }

    /* check if shm is already initialized */
    chunk_ptr = (sysvshm_chunk_head *) shm_ptr;
    if (strcmp((char*) &(chunk_ptr->magic), "PHP_SM") != 0) {
        strcpy((char*) &(chunk_ptr->magic), "PHP_SM");
        chunk_ptr->start = sizeof(sysvshm_chunk_head);
        chunk_ptr->end = chunk_ptr->start;
        chunk_ptr->total = shm_size;
        chunk_ptr->free = shm_size-chunk_ptr->end;
    }

    object_init_ex(return_value, sysvshm_ce);

    shm_list_ptr = Z_SYSVSHM_P(return_value);

    shm_list_ptr->key = shm_key;
    shm_list_ptr->id = shm_id;
    shm_list_ptr->ptr = chunk_ptr;
}

shm_put_var&shm_get_var 都是对内存的操作

static int php_put_shm_data(sysvshm_chunk_head *ptr, zend_long key, const char *data, zend_long len)
{
   sysvshm_chunk *shm_var;
   zend_long total_size;
   zend_long shm_varpos;

   total_size = ((zend_long) (len + sizeof(sysvshm_chunk) - 1) / sizeof(zend_long)) * sizeof(zend_long) + sizeof(zend_long); /* zend_long alligment */

   if ((shm_varpos = php_check_shm_data(ptr, key)) > 0) {
      php_remove_shm_data(ptr, shm_varpos);
   }

   if (ptr->free < total_size) {
      return -1; /* not enough memory */
   }

   shm_var = (sysvshm_chunk *) ((char *) ptr + ptr->end);
   shm_var->key = key;
   shm_var->length = len;
   shm_var->next = total_size;
   memcpy(&(shm_var->mem), data, len);
   ptr->end += total_size;
   ptr->free -= total_size;
   return 0;
}

static zend_long php_check_shm_data(sysvshm_chunk_head *ptr, zend_long key)
{
   zend_long pos;
   sysvshm_chunk *shm_var;

   ZEND_ASSERT(ptr);

   pos = ptr->start;

   for (;;) {
      if (pos >= ptr->end) {
         return -1;
      }
      shm_var = (sysvshm_chunk*) ((char *) ptr + pos);
      if (shm_var->key == key) {
         return pos;
      }
      pos += shm_var->next;

      if (shm_var->next <= 0 || pos < ptr->start) {
         return -1;
      }
   }
   return -1;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇