shmget 和 shm_open+mmap是两种不同的共享内存用法,

shmget是老式system V 模型,

shm_open 是POSIX下的产物.

mmap和System V共享内存的主要区别在于:

  • sysv shm是持久化的,除非被一个进程明确的删除,否则它始终存在于内存里,直到系统关机;
  • mmap映射的内存在不是持久化的,如果进程关闭,映射随即失效,除非事先已经映射到了一个文件上。

内存映射机制mmap是POSIX标准的系统调用,有匿名映射和文件映射两种。

  • 匿名映射使用进程的虚拟内存空间,它和malloc(3)类似,实际上有些malloc实现会使用mmap匿名映射分配内存,不过匿名映射不是POSIX标准中规定的。
  • 文件映射有MAP_PRIVATEMAP_SHARED两种。前者使用COW的方式,把文件映射到当前的进程空间,修改操作不会改动源文件。后者直接把文件映射到当前的进程空间,所有的修改会直接反应到文件的page cache,然后由内核自动同步到映射文件上。

shmget用法 shmget(IPC_PRIVATE,size,flag)

shm_open 创建一个共享内存(文件,/dev/shm下),返回一个fd.通过ftruncate指定文件大小

mmap通过fd attach上这个地址,进行操作

shmget

compared to shm_open+mmap, shm_get used the old System V shared memory model.

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

key: It may be used either to obtain the identifier of a previously created shared memory segment (when shmflg is zero and key does not have the value IPC_PRIVATE), or to create a new set.

获取一个特定shm

size: A new shared memory segment, with size equal to the value of size rounded up to a multi- ple of PAGE_SIZE, is created if key has the value IPC_PRIVATE or key isn’t IPC_PRIVATE, no shared memory segment corresponding to key exists, and IPC_CREAT is specified in shm- flg.

If shmflg specifies both IPC_CREAT and IPC_EXCL and a shared memory segment already ex- ists for key, then shmget() fails with errno set to EEXIST. (This is analogous to the effect of the combination O_CREAT | O_EXCL for open(2).)

如果shmflag同时指定了IPC_CREATIPC_EXCL,同时有一个已存在的KEY共享内存,shmget将会失败.

RETURN VALUE On success, a valid shared memory identifier is returned. On error, -1 is returned, and errno is set to indicate the error.

可以理解成返回一个fd,指向可用的shm segement

shm_open+mmap

#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */

int shm_open(const char *name, int oflag, mode_t mode);

int shm_unlink(const char *name);

Link with -lrt.

shm_open: 可以创建或者打开已存在的shm segement.

open is the same as shm_open.

shm_unlink: 关闭shm segement

oflag is a bit mask created by ORing together exactly one of O_RDONLY or O_RDWR and any of the other flags listed here:

   O_RDONLY
          Open the object for read access.  A shared memory object opened in this way can be
          mmap(2)ed only for read (PROT_READ) access.

   O_RDWR Open the object for read-write access.

   O_CREAT
          Create the shared memory object if it does not exist.  The user and  group  owner-
          ship  of  the object are taken from the corresponding effective IDs of the calling
          process, and the object's permission bits are set according  to  the  low-order  9
          bits  of  mode,  except that those bits set in the process file mode creation mask
          (see umask(2)) are cleared for the new object.  A set of macro constants which can
          be  used to define mode is listed in open(2).  (Symbolic definitions of these con-
          stants can be obtained by including <sys/stat.h>.)

          A new shared memory object initially has zero length--the size of the  object  can
          be  set  using  ftruncate(2).  The newly allocated bytes of a shared memory object
          are automatically initialized to 0.

   O_EXCL If O_CREAT was also specified, and a shared memory object with the given name  al-
          ready exists, return an error.  The check for the existence of the object, and its
          creation if it does not exist, are performed atomically.

   O_TRUNC
          If the shared memory object already exists, truncate it to zero bytes.

mmap

 #include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);
int munmap(void *addr, size_t length);

//See NOTES for information on feature test macro requirements.
mmap() creates a new mapping in the virtual address space of the
       calling process.  The starting address for the new mapping is
       specified in addr.  The length argument specifies the length of
       the mapping (which must be greater than 0).

RETURN VALUE
On success, mmap() returns a pointer to the mapped area.  On
       error, the value MAP_FAILED (that is, (void *) -1) is returned,
       and errno is set to indicate the error.

On success, munmap() returns 0.  On failure, it returns -1, and
errno is set to indicate the error (probably to EINVAL).

ftruncate

把文件size变到指定大小

#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
The truncate()  and ftruncate() functions cause the regular file named by path or refer-
       enced by fd to be truncated to a size of precisely length bytes.

If the file previously was larger than this size, the extra data is lost.   If  the  file
   previously was shorter, it is extended, and the extended part reads as null bytes ('\0').

   The file offset is not changed.

   If  the  size  changed, then the st_ctime and st_mtime fields (respectively, time of last
   status change and time of last modification; see inode(7)) for the file are updated,  and
   the set-user-ID and set-group-ID mode bits may be cleared.

   With  ftruncate(),  the  file must be open for writing; with truncate(), the file must be
   writable.

simply code

//get.c
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define STORAGE_ID "/SHM_TEST"
#define STORAGE_SIZE 32

int main(int argc, char *argv[])
{
	int res;
	int fd;
	char data[STORAGE_SIZE];
	pid_t pid;
	void *addr;

	pid = getpid();

	// get shared memory file descriptor (NOT a file,but act as a regular file)
	fd = shm_open(STORAGE_ID, O_RDONLY, S_IRUSR | S_IWUSR);
	if (fd == -1)
	{
		perror("open");
		return 10;
	}

	// map shared memory to process address space
	addr = mmap(NULL, STORAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0);
	if (addr == MAP_FAILED)
	{
		perror("mmap");
		return 30;
	}

	// place data into memory
	memcpy(data, addr, STORAGE_SIZE);

	printf("PID %d: Read from shared memory: \"%s\"\n", pid, data);

	return 0;
}
//set.c
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define STORAGE_ID "/SHM_TEST"
#define STORAGE_SIZE 32
#define DATA "Hello, World! From PID %d"

int main(int argc, char *argv[])
{
	int res;
	int fd;
	int len;
	pid_t pid;
	void *addr;
	char data[STORAGE_SIZE];

	pid = getpid();
	sprintf(data, DATA, pid);

	// get shared memory file descriptor (NOT a file)
	fd = shm_open(STORAGE_ID, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
	if (fd == -1)
	{
		perror("open");
		return 10;
	}

	// extend shared memory object as by default it's initialized with size 0
	res = ftruncate(fd, STORAGE_SIZE);
	if (res == -1)
	{
		perror("ftruncate");
		return 20;
	}

	// map shared memory to process address space
	addr = mmap(NULL, STORAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
	if (addr == MAP_FAILED)
	{
		perror("mmap");
		return 30;
	}

	// place data into memory
	len = strlen(data) + 1;
	memcpy(addr, data, len);

	// wait for someone to read it
	sleep(2);

	// mmap cleanup
	res = munmap(addr, STORAGE_SIZE);
	if (res == -1)
	{
		perror("munmap");
		return 40;
	}

	// shm_open cleanup
	fd = shm_unlink(STORAGE_ID);
	if (fd == -1)
	{
		perror("unlink");
		return 100;
	}

	return 0;
}

题外话 Q&A:

Q:mmap下的资源需要锁吗?

A:

需要锁:

Semaphores

共享内存的mutex

​ pthread_mutexattr_setrobust() ,将 pthread 互斥锁初始化为“robust”,如果持有互斥锁的进程死了,下一个获取它的线程将收到 EOWNERDEAD(但仍然成功获取互斥锁),以便它知道执行任何清理。然后它需要使用 pthread_mutex_consistent() 通知获取的互斥锁再次一致

如何使用?

通过mmap获取一块共享内存,共享内存内存在pthread_mutex相关信息

https://blog.csdn.net/qq_35396127/article/details/78942245

文件锁

在linux 系统中,flock函数是为解决多进程对同一文件的读写冲突的,而flock函数只能锁定整个文件,无法锁定文件的某一区域。且flock可以保证robust

A:

The operation of shm_unlink() is analogous to unlink(2): it removes a shared memory object name, and, once all processes have unmapped the object, de-allocates and destroys the contents of the associated memory region. After a successful shm_unlink(), attempts to shm_open() an object with the same name will fail (unless O_CREAT was specified, in which case a new, distinct object is created).

文件存在于/dev/shm下,如果不调用该函数,文件会一直存在