好好活就是有意义的事,有意义的事就是好好活
Linux手册翻译 —— shm_open(3)
Linux手册翻译 —— shm_open(3)

Linux手册翻译 —— shm_open(3)

shm_open(3) – Linux manual page (man7.org)

\color{#A00000}{NAME}
shm_open, shm_unlink - create/open or unlink POSIX shared memory objects
\color{#A00000}{SYNOPSIS}
#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() 创建并打开一个新的或打开一个现有的 POSIX 共享内存对象。 POSIX 共享内存对象实际上是一个句柄,不相关的进程可以使用它来 mmap(2) 共享内存的同一区域。 shm_unlink() 函数执行相反的操作,删除先前由 shm_open() 创建的对象。

shm_open() 的操作类似于 open(2)。 name 指定要创建或打开的共享内存对象。 为了确保可移植性,共享内存对象应以 /somename 形式的进行命名; 即,以 null 结尾的最多 NAME_MAX(即 255)个字符的字符串,并且,首字符是斜杠,后跟一个或多个非斜杠的字符。

oflag 是一个位掩码,它通过将 O_RDONLY 或 O_RDWR 中的一个与以下列出的任意标志进行 OR 运算来创建:

O_RDONLY

以只读模式打开。 以这种方式打开的共享内存对象只能被 mmap(2) 用于读取 (PROT_READ) 访问。

O_RDWR

以读写模式打开。

O_CREATE

如果共享内存对象不存在,则创建它。 对象的用户和组所有权取自调用进程对应的有效ID,对象的权限位按照参数mode的低9位设置,不过在process file mode创建掩码中设置的那些位 (请参阅umask(2))会被清除。 open(2) 中列出了一组可用于定义mode的宏常量。 (这些常量的符号定义可以通过包含<sys/stat.h>来获得。)

一个新的共享内存对象最初的长度为零——对象的大小可以使用 ftruncate(2) 设置。 共享内存对象新分配的字节会自动初始化为 0。

O_EXCL

如果同时指定了 O_CREAT,并且给定name的共享内存对象已经存在,则返回错误。 检查对象是否存在,以及不存在时创建新对象的过程是原子的。此标识是为了避免: 如果对象存在,但是制定了O_CREATE,那么默认的操作是打开该对象,这样就不知道是打开了原有的还是创建了新的,而O_EXCL确保,一定是创建一个新的,否则就要报错

O_TRUNC

如果对象存在,则将其长度大小截断为0

以上所有的值,都包含在<fcntl.h>中。

成功完成后,shm_open() 返回一个引用共享内存对象的新文件描述符。 此文件描述符保证是进程中先前未打开的编号最小的文件描述符。 并且文件描述符被设置了 FD_CLOEXEC 标志(请参阅 fcntl(2))。

文件描述符通常用于后续调用 ftruncate(2)(用于新创建的对象)和 mmap(2)。 调用 mmap(2) 后,可能会关闭文件描述符而不影响内存映射。

shm_unlink() 的操作类似于 unlink(2):它删除一个共享内存对象名称,并且一旦所有进程都取消映射该对象(执行unmap(2)),就释放并销毁相关内存区域的内容。 在成功 shm_unlink() 之后,尝试 shm_open() 具有相同名称的对象失败(除非指定了 O_CREAT,在这种情况下会创建一个新的、不同的对象)。

成功时,shm_open() 返回一个文件描述符(一个非负整数)。 成功时,shm_unlink() 返回 0。失败时,两个函数都返回 -1 并设置 errno 以指示错误。

  • EACCES shm_unlink() 共享内存对象的权限被拒绝。
  • EACCES 在指定mode下对 shm_open() name 的权限被拒绝,或者指定了 O_TRUNC 并且调用者没有对象的写权限。
  • EEXIST O_CREAT 和 O_EXCL 都被指定给 shm_open() 并且 name 指定的共享内存对象已经存在。
  • EINVAL shm_open() 的名称参数无效。
  • EMFILE 已达到打开文件描述符数量的每个进程限制。
  • ENAMETOOLONG 名称长度超过 PATH_MAX。
  • ENFILE 已达到打开文件总数的系统范围限制。
  • ENOENT 尝试 shm_open() 一个不存在的名称,并且未指定 O_CREAT。
  • ENOENT 尝试对不存在的对象进行shm_unlink()。
┌───────────────────────────────┬───────────────┬────────────────┐
│Interface                      │ Attribute     │ Value          │
├───────────────────────────────┼───────────────┼────────────────┤
│shm_open(), shm_unlink()       │ Thread safety │ MT-Safe locale │
└───────────────────────────────┴───────────────┴────────────────┘
 POSIX.1-2001, POSIX.1-2008.

POSIX.1-2001 says that the group ownership of a newly created shared memory object is set to either the calling process’s effective group ID or “a system default group ID”. POSIX.1-2008 says that the group ownership may be set to either the calling process’s effective group ID or, if the object is visible in the filesystem, the group ID of the parent directory.

POSIX 未指定 O_RDONLY 和 O_TRUNC 组合的行为。 在 Linux 上,这将成功截断现有的共享内存对象——在其他 UNIX 系统上可能不是这样。

Linux 上的 POSIX 共享内存对象实现使用通常安装在 /dev/shm 下的专用 tmpfs(5) 文件系统。

下面的程序使用 POSIX 共享内存和 POSIX 未命名信号量来交换一条数据。 “bounce”程序(必须首先运行)讲“send”程序放入共享内存的字符串的转化为大写。 一旦数据被修改,“send”程序就会打印修改后的共享内存的内容。 两个程序的示例执行如下:

$ ./pshm_ucase_bounce /myshm &
[1] 270171
$ ./pshm_ucase_send /myshm hello
HELLO

Program source: pshm_ucase.h

下面的两个程序都包含以下头文件。 它的主要目的是定义一个结构,可以理解为两个进程的通讯格式。

#include <sys/mman.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
  } while (0)

#define BUF_SIZE 1024   /* Maximum size for exchanged string */

/* Define a structure that will be imposed on the shared
   memory object */

struct shmbuf {
    sem_t sem1;            /* POSIX unnamed semaphore */
    sem_t sem2;            /* POSIX unnamed semaphore */
    size_t cnt;             /* Number of bytes used in 'buf' */
    char buf[BUF_SIZE];   /* Data being transferred */
};

Program source: pshm_ucase_bounce.c

“bounce”程序创建一个新的共享内存对象,其名称在其命令行参数中给出,并调整对象的大小以匹配头文件中定义的 shmbuf 结构的大小。 然后它将对象映射到进程的地址空间,并将对象内部的两个 POSIX 信号量初始化为 0。

“send”程序通过第一个信号量通知数据写入,“bounce”程序将“send”程序放入内存中的数据转为大写,然后通过第二个信号量告诉“send”程序 它现在可以访问共享内存。

/** pshm_ucase_bounce.c
*   Licensed under GNU General Public License v2 or later.
*/
#include <ctype.h>
#include "pshm_ucase.h"

int
main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s /shm-path\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    char *shmpath = argv[1];

    /* Create shared memory object and set its size to the size
       of our structure. */

    int fd = shm_open(shmpath, O_CREAT | O_EXCL | O_RDWR,
                      S_IRUSR | S_IWUSR);
    if (fd == -1)
        errExit("shm_open");

    if (ftruncate(fd, sizeof(struct shmbuf)) == -1)
        errExit("ftruncate");

    /* Map the object into the caller's address space. */

    struct shmbuf *shmp = mmap(NULL, sizeof(*shmp),
                               PROT_READ | PROT_WRITE,
                               MAP_SHARED, fd, 0);
    if (shmp == MAP_FAILED)
        errExit("mmap");

    /* Initialize semaphores as process-shared, with value 0. */

    if (sem_init(&shmp->sem1, 1, 0) == -1)
        errExit("sem_init-sem1");
    if (sem_init(&shmp->sem2, 1, 0) == -1)
        errExit("sem_init-sem2");

    /* Wait for 'sem1' to be posted by peer before touching
       shared memory. */

    if (sem_wait(&shmp->sem1) == -1)
        errExit("sem_wait");

    /* Convert data in shared memory into upper case. */

    for (int j = 0; j < shmp->cnt; j++)
        shmp->buf[j] = toupper((unsigned char) shmp->buf[j]);

    /* Post 'sem2' to tell the peer that it can now
       access the modified data in shared memory. */

    if (sem_post(&shmp->sem2) == -1)
        errExit("sem_post");

    /* Unlink the shared memory object. Even if the peer process
       is still using the object, this is okay. The object will
       be removed only after all open references are closed. */

    shm_unlink(shmpath);

    exit(EXIT_SUCCESS);
}

Program source: pshm_ucase_send.c

“send”程序有两个命令行参数:先前由“bounce”程序创建的共享内存对象的路径名和要复制到该对象中的字符串。

程序打开共享内存对象并将对象映射到它的地址空间。 然后它将第二个参数中指定的数据复制到共享内存中,并发布第一个信号量,它告诉“反弹”程序它现在可以访问该数据。 在“bounce”程序发布第二个信号量之后,“send”程序在标准输出上打印共享内存的内容。

/** pshm_ucase_send.c
 *  Licensed under GNU General Public License v2 or later
 */
#include <string.h>
#include "pshm_ucase.h"

int
main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s /shm-path string\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    char *shmpath = argv[1];
    char *string = argv[2];
    size_t len = strlen(string);

    if (len > BUF_SIZE) {
        fprintf(stderr, "String is too long\n");
        exit(EXIT_FAILURE);
    }

    /* Open the existing shared memory object and map it
       into the caller's address space. */

    int fd = shm_open(shmpath, O_RDWR, 0);
    if (fd == -1)
        errExit("shm_open");

    struct shmbuf *shmp = mmap(NULL, sizeof(*shmp),
                               PROT_READ | PROT_WRITE,
                               MAP_SHARED, fd, 0);
    if (shmp == MAP_FAILED)
        errExit("mmap");

    /* Copy data into the shared memory object. */

    shmp->cnt = len;
    memcpy(&shmp->buf, string, len);

    /* Tell peer that it can now access shared memory. */

    if (sem_post(&shmp->sem1) == -1)
        errExit("sem_post");

    /* Wait until peer says that it has finished accessing
       the shared memory. */

    if (sem_wait(&shmp->sem2) == -1)
        errExit("sem_wait");

    /* Write modified data in shared memory to standard output. */

    write(STDOUT_FILENO, &shmp->buf, len);
    write(STDOUT_FILENO, "\n", 1);

    exit(EXIT_SUCCESS);
}

一条评论

  1. Pingback:Linux手册翻译 —— shm_overview(7) – Kingdo Station

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注