Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/156.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 进程共享文件描述符表但不共享虚拟内存时的munmap()_C++_Linux_Memory_Linux Kernel_Mmap - Fatal编程技术网

C++ 进程共享文件描述符表但不共享虚拟内存时的munmap()

C++ 进程共享文件描述符表但不共享虚拟内存时的munmap(),c++,linux,memory,linux-kernel,mmap,C++,Linux,Memory,Linux Kernel,Mmap,我有未命名的进程间共享内存区域,这些区域是通过mmap创建的。进程是通过克隆系统调用创建的。进程共享文件描述符表(CLONE\u FILES),文件系统信息(CLONE\u FS)。进程不共享内存空间(预先映射到克隆调用的区域除外): 我的问题是——如果在fork之后,一个(或两个)进程调用munmap(),会发生什么 我的理解是munmap()将做两件事: 取消映射内存区域(在我的情况下,它不会在进程之间传播) 如果是匿名映射,请关闭文件描述符(在我的例子中,它是在进程之间传播的) 我假设

我有未命名的进程间共享内存区域,这些区域是通过
mmap
创建的。进程是通过
克隆
系统调用创建的。进程共享文件描述符表(
CLONE\u FILES
),文件系统信息(
CLONE\u FS
)。进程共享内存空间(预先映射到
克隆
调用的区域除外):

我的问题是——如果在fork之后,一个(或两个)进程调用
munmap()
,会发生什么

我的理解是
munmap()
将做两件事:

  • 取消映射内存区域(在我的情况下,它不会在进程之间传播)
  • 如果是匿名映射,请关闭文件描述符(在我的例子中,它是在进程之间传播的)
我假设
MAP\u ANONYMOUS
创建某种由内核处理的虚拟文件(可能位于
/proc
?),该文件在
munmap()上自动关闭

因此。。。另一个进程将映射到内存中一个未打开甚至可能不再存在的文件

这对我来说非常困惑,因为我找不到任何合理的解释

简单测试 在本测试中,这两个进程都能够发出一个
munmap()
而没有任何问题

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sched.h>
int main() {
  int *value = (int*) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
                           MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    *value = 0;
  if (syscall(SYS_clone, CLONE_FS | CLONE_FILES | SIGCHLD, nullptr)) {
        sleep(1);
        printf("[parent] the value is %d\n", *value); // reads value just fine
        munmap(value, sizeof(int));
        // is the memory completely free'd now? if yes, why?
    } else {
        *value = 1234;
        printf("[child] set to %d\n", *value);
        munmap(value, sizeof(int));
        // printf("[child] value after unmap is %d\n", *value); // SIGSEGV
        printf("[child] exiting\n");
    }
}
内核似乎会保留一个匿名映射的引用计数器,
munmap()
递减该计数器。一旦计数器达到零,内核将最终回收内存

程序运行时几乎与分配大小无关。指定4B的ALLOC_大小只需不到12秒,而1MB的分配只需13秒多一点


使用下面的程序指定变量分配大小
1ul,我可以根据经验得出一些结论(尽管我不能保证它们是正确的):

  • mmap()
    占用的时间与分配区域大致相同(这是由于linux内核的高效内存管理。映射内存不会占用空间,除非写入内存)
  • mmap()
    所需时间更长,具体取决于已存在映射的数量。前1000个MMAP大约需要0.05秒;拥有64000个映射后的1000 mmaps大约需要34秒。我还没有检查linux内核,但是在索引中插入映射区域可能需要
    O(n)
    而不是某些结构中可行的
    O(1)
    。内核补丁可能;但可能除了我之外,这对任何人都不是问题:-)
  • munmap()。这将正确释放共享内存区域
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义NUM_迭代100000次

#定义ALLOC_SIZE 1您试过了吗?我试过了--我将在原始问题中添加详细信息。我能够成功地发布两个munmap()(每个进程一个)。问题是:内核如何知道现在可以释放内存?它是否在内部保留一个计数器,记录有多少进程映射到该区域?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sched.h>
int main() {
  int *value = (int*) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
                           MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    *value = 0;
  if (syscall(SYS_clone, CLONE_FS | CLONE_FILES | SIGCHLD, nullptr)) {
        sleep(1);
        printf("[parent] the value is %d\n", *value); // reads value just fine
        munmap(value, sizeof(int));
        // is the memory completely free'd now? if yes, why?
    } else {
        *value = 1234;
        printf("[child] set to %d\n", *value);
        munmap(value, sizeof(int));
        // printf("[child] value after unmap is %d\n", *value); // SIGSEGV
        printf("[child] exiting\n");
    }
}
#include <cassert>
#include <thread>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sched.h>

#define NUM_ITERATIONS 100000
#define ALLOC_SIZE 4ul<<0

int main() {
    printf("iterations = %d\n", NUM_ITERATIONS);
    printf("alloc size = %lu\n", ALLOC_SIZE);
    assert(ALLOC_SIZE >= sizeof(int));
    assert(ALLOC_SIZE >= sizeof(bool));
    bool *written = (bool*) mmap(NULL, ALLOC_SIZE, PROT_READ | PROT_WRITE,
                               MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    for(int i=0; i < NUM_ITERATIONS; i++) {
        if(i % (NUM_ITERATIONS / 100) == 0) {
            printf("%d%%\n", i / (NUM_ITERATIONS / 100));
        }
    int *value = (int*) mmap(NULL, ALLOC_SIZE, PROT_READ | PROT_WRITE,
                             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
        *value = 0;
        *written = 0;
      if (int rv = syscall(SYS_clone, CLONE_FS | CLONE_FILES | SIGCHLD, nullptr)) {
            while(*written == 0) std::this_thread::yield();
            assert(*value == i);
            munmap(value, ALLOC_SIZE);
            waitpid(-1, NULL, 0);
        } else {
            *value = i;
            *written = 1;
            munmap(value, ALLOC_SIZE);
            return 0;
        }
    }
    return 0;
}
#include <cassert>
#include <cinttypes>
#include <thread>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sched.h>

#define NUM_ITERATIONS 100000
#define ALLOC_SIZE 1ul<<30
#define CLOCK_TYPE CLOCK_PROCESS_CPUTIME_ID
#define NUM_ELEMS 1024*1024/4

struct timespec start_time;

int main() {
    clock_gettime(CLOCK_TYPE, &start_time);
    printf("iterations = %d\n", NUM_ITERATIONS);
    printf("alloc size = %lu\n", ALLOC_SIZE);
    assert(ALLOC_SIZE >= NUM_ELEMS * sizeof(int));
    bool *written = (bool*) mmap(NULL, sizeof(bool), PROT_READ | PROT_WRITE,
                               MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    for(int i=0; i < NUM_ITERATIONS; i++) {
        if(i % (NUM_ITERATIONS / 100) == 0) {
            struct timespec now;
            struct timespec elapsed;
            printf("[%3d%%]", i / (NUM_ITERATIONS / 100));
            clock_gettime(CLOCK_TYPE, &now);
            if (now.tv_nsec < start_time.tv_nsec) {
                elapsed.tv_sec = now.tv_sec - start_time.tv_sec - 1;
                elapsed.tv_nsec = now.tv_nsec - start_time.tv_nsec + 1000000000;
            } else {
                elapsed.tv_sec = now.tv_sec - start_time.tv_sec;
                elapsed.tv_nsec = now.tv_nsec - start_time.tv_nsec;
            }
            printf("%05" PRIdMAX ".%09ld\n", elapsed.tv_sec, elapsed.tv_nsec);
        }
    int *value = (int*) mmap(NULL, ALLOC_SIZE, PROT_READ | PROT_WRITE,
                             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
        *value = 0;
        *written = 0;
      if (int rv = syscall(SYS_clone, CLONE_FS | CLONE_FILES | SIGCHLD, nullptr)) {
            while(*written == 0) std::this_thread::yield();
            assert(*value == i);
            munmap(value, ALLOC_SIZE);
            waitpid(-1, NULL, 0);
        } else {
            for(int j=0; j<NUM_ELEMS; j++)
                value[j] = i;
            *written = 1;
            //munmap(value, ALLOC_SIZE);
            return 0;
        }
    }
    return 0;
}