首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >mmap ed共享内存段上的mprotect

mmap ed共享内存段上的mprotect
EN

Stack Overflow用户
提问于 2013-09-24 15:45:56
回答 2查看 386关注 0票数 7

当两个进程共享用shm_open打开的内存段,然后得到mmap-ed时,对一个进程中共享内存的一部分执行mprotect是否会影响另一个进程在同一部分上看到的权限?换句话说,如果一个进程将共享内存段的一部分变为只读,它是否也成为另一个进程的只读?

EN

回答 2

Stack Overflow用户

发布于 2022-05-27 01:04:51

我总是喜欢分两部分来回答这些问题。

第1部分-让我们来测试一下

让我们考虑一个相对类似于shm_open(3)的例子。

共享头- shared.h

代码语言:javascript
复制
#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)

struct shmbuf {
    char   buf[4096];
    sem_t  sem;
};

创建者进程- creator.c (使用gcc creator.c -o creator -lrt -lpthread编译)

代码语言:javascript
复制
#include <ctype.h>
#include <string.h>
#include "shared.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];
    int fd = shm_open(shmpath, O_CREAT | O_EXCL | O_RDWR,
                      S_IRUSR | S_IWUSR);
    if (fd == -1)
        errExit("shm_open");

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

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

    if (sem_init(&shm->sem, 1, 0) == -1)
        errExit("sem_init");

    if (mprotect(&shm->buf, sizeof(shm->buf), PROT_READ) == -1)
        errExit("mprotect");

    if (sem_wait(&shm->sem) == -1)
        errExit("sem_wait");

    printf("got: %s\n", shm->buf);

    shm_unlink(shmpath);

    exit(EXIT_SUCCESS);
}

编写过程- writer.c (使用gcc writer.c -o writer -lrt -lpthread编译)

代码语言:javascript
复制
#include <string.h>
#include "shared.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];

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

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

    strcpy(&shm->buf[0], string);

    if (sem_post(&shm->sem) == -1)
        errExit("sem_post");

    exit(EXIT_SUCCESS);
}

会发生什么事?创建者进程创建一个新的共享内存对象,“设置”它的大小,将其映射到内存中(如shm),使用mprotect只允许写入缓冲区(shm->buf),并等待信号量知道写入器何时完成(稍后我们将讨论它的内容)。

写入进程启动,打开相同的共享内存对象,将我们告诉它的任何内容写入其中,并向信号量发出信号。

的问题是,即使创建者将保护更改为只读,写入者是否能够写入共享内存对象?

让我们来找出答案。我们可以使用以下方法运行它:

代码语言:javascript
复制
# ./creator.c /shmulik &
# ./writer.c /shmulik hi!
got: hi!
#
[1]+  Done                    ./creator /shmulik

如您所见,作者能够写入共享内存,尽管创建者将其保护设置为只读。

也许造物主做错了什么?让我们尝试在creator.c中添加下面一行

代码语言:javascript
复制
    if (mprotect(&shm->buf, sizeof(shm->buf), PROT_READ) == -1)
        errExit("mprotect");

    memset(&shm->buf, 0, sizeof(shm->buf)); // <-- This is the new line

    if (sem_wait(&shm->sem) == -1)
        errExit("sem_wait");

让我们重新编译并再次运行创建者:

代码语言:javascript
复制
# gcc creator.c -o creator -lrt -lpthread
# ./creator /shmulik
Segmentation fault

如您所见,mprotect按预期工作。

让作者映射共享内存,然后更改保护,怎么样?不会有什么改变的。mprotect只影响调用它的进程(以及它的后代)的内存保护。

第二部分-让我们理解它

首先,您必须理解shm_open是一个glibc方法,它不是一个系统调用。您可以从glibc获得他们的网站源代码,只需自己查找shm_open即可。

shm_open的底层实现是对open的常规调用,就像手册页建议的那样。

正如我们已经看到的,大多数魔法发生在mmap中。在调用mmap时,我们必须使用MAP_SHARED (而不是MAP_PRIVATE),否则每个进程都会首先得到一个私有内存段,显然其中一个不会影响另一个进程。

当我们调用mmap时,跳数大致如下:

在最后一点上,您可以看到我们采用了进程的内存管理上下文mm并分配了一个新的虚拟内存区域vma

代码语言:javascript
复制
struct mm_struct *mm = current->mm;
...
vma = vm_area_alloc(mm);
...
vma->vm_page_prot = vm_get_page_prot(vm_flags);

此内存区域不与其他进程共享。由于mprotect只更改每个进程vma上的vm_page_prot,因此不会影响映射相同内存空间的其他进程。

票数 2
EN

Stack Overflow用户

发布于 2022-05-25 03:57:37

mprotect()的POSIX规范建议,共享内存保护中的更改应该影响使用该共享内存的所有进程。

详细说明的两个错误条件是:

  • prot参数指定MAP_PRIVATE映射的PROT_WRITE,并且没有足够的内存资源来锁定私有页面。
  • prot参数在MAP_PRIVATE映射上指定PROT_WRITE,如果需要的话,它将需要比系统能够为锁定私有页面提供的空间更多的空间。

这些强烈建议使用MAP_SHARED映射的内存不应该因为缺少复制内存而失败。

还请参阅mmap()的POSIX规范。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18986264

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档