Linux使用共享内存通信的进程同步退出问题

  两个甚至多个进程使用共享内存(shm)通信,总遇到同步问题。这里的“同步问题”不是说进程读写同步问题,这个用信号量就好了。这里的同步问题说的是同步退出问题,到底谁先退出,怎么知道对方退出了。举个例子:进程负责读写数据库A,进程B负责处理数据。那么进程A得比进程B晚退出才行,因为要保存进程B处理完的数据。可是A不知道B什么时候退出啊。A、B是无关联的进程,也不知道对方的pid。它们唯一的关联就是读写同一块共享内存。正常情况下,进程B在共享内存中写个标识:进程A你可以退出了,也是可以的。不过进程B可能是异常退出,连标识都来不及写。其次,共享内存用来做数据通信的,加这么个标识感觉不太好,有滥用的感觉。

  采用socket通信没有这个问题,因为进程B退出怎么也会导致socket断开,哪怕是超时。但shm却没有协议来检测这些行为,如果自己也做一个未免太麻烦。那就从共享内存下手吧。

  共享内存是由内核来管理的,一个进程删除本身打开的共享内存并不影响另一个进程的共享内存,哪怕都是同一块共享内存。这是因为共享内存在内核中一个引用计数,一个进程使用该共享内存就会导致引用计数加1。如果其中一个进程调用了删除函数,只有这个计数为0才会真正删除共享内存。那么,需要最后才退出的进程检测这个计数就可以了。

  在System V的共享内存中,创建一个共享内存会初始化一个结构:

struct shmid_ds {
              struct ipc_perm shm_perm;    /* Ownership and permissions */
              size_t          shm_segsz;  /* Size of segment (bytes) */
              time_t          shm_atime;  /* Last attach time */
              time_t          shm_dtime;  /* Last detach time */
              time_t          shm_ctime;  /* Last change time */
              pid_t          shm_cpid;    /* PID of creator */
              pid_t          shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
              shmatt_t        shm_nattch;  /* No. of current attaches */
              ...
          };

使用shmctl函数可以读取该结构体,其中的shm_nattch就是使用该共享内存的进程数。

不过,现在有了新的POSIX标准,当然要用新标准了。shm_open创建的共享内存也具有“一个进程删除本身打开的共享内存并不影响另一个进程的共享内存”的特点。可是用shm_open创建的共享内存不再有上面的结构,那么,内核是怎么管理shm_open创建共享内存??看下面的源码:

/* shm_open - open a shared memory file */

/* Copyright 2002, Red Hat Inc. */

#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>

int
shm_open (const char *name, int oflag, mode_t mode)
{
  int fd;
  char shm_name[PATH_MAX+20] = "/dev/shm/";

/* skip opening slash */
  if (*name == '/')
    ++name;

/* create special shared memory file name and leave enough space to
    cause a path/name error if name is too long */
  strlcpy (shm_name + 9, name, PATH_MAX + 10);

fd = open (shm_name, oflag, mode);

if (fd != -1)
    {
      /* once open we must add FD_CLOEXEC flag to file descriptor */
      int flags = fcntl (fd, F_GETFD, 0);

if (flags >= 0)
        {
          flags |= FD_CLOEXEC;
          flags = fcntl (fd, F_SETFD, flags);
        }

/* on failure, just close file and give up */
      if (flags == -1)
        {
          close (fd);
          fd = -1;
        }
    }

return fd;
}

我嚓,这就是创建一个普通的文件啊,只是创建的位置在/dev/shm下(也就是RAM上)。再来看看删除共享内存的函数shm_unlink:

/* shm_unlink - remove a shared memory file */

/* Copyright 2002, Red Hat Inc. */

#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>

int
shm_unlink (const char *name)
{
  int rc;
  char shm_name[PATH_MAX+20] = "/dev/shm/";

/* skip opening slash */
  if (*name == '/')
    ++name;

/* create special shared memory file name and leave enough space to
    cause a path/name error if name is too long */
  strlcpy (shm_name + 9, name, PATH_MAX + 10);

rc = unlink (shm_name);

return rc;
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/471ac506d308df0d9313fecd686dd345.html