- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我有一个通过 mmap
获得的内存段与 MAP_ANONYMOUS
.
如何分配相同大小的第二个内存段,该内存段引用第一个内存段并在 Linux(目前工作 Linux 2.6.36)中进行复制写入?
我想要和fork
完全一样的效果,只是不创建新进程。我希望新映射保持相同的过程。
整个过程必须在原始页面和复制页面上都是可重复的(就像父和子将继续 fork
一样)。
我不想分配整个段的直接副本的原因是因为它们有多个 GB 大,我不想使用可以共享的写时复制的内存。
我尝试过的:mmap
该段共享,匿名。
关于重复 mprotect
将其设为只读并使用 remap_file_pages
创建第二个映射也是只读的。
然后使用 libsigsegv
拦截写尝试,手动复制页面然后mprotect
两者都可以读写。
有诀窍,但很脏。我基本上是在实现我自己的虚拟机。
遗憾 mmap
ing /proc/self/mem
当前 Linux 不支持,否则会出现 MAP_PRIVATE
在那里映射可以解决问题。
写时复制机制是 Linux VM 的一部分,必须有一种方法可以在不创建新进程的情况下使用它们。
备注:
我在 Mach VM 中找到了合适的机制。
以下代码在我的 OS X 10.7.5 上编译并具有预期的行为:Darwin 11.4.2 Darwin Kernel Version 11.4.2: Thu Aug 23 16:25:48 PDT 2012; root:xnu-1699.32.7~1/RELEASE_X86_64 x86_64 i386
gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#ifdef __MACH__
#include <mach/mach.h>
#endif
int main() {
mach_port_t this_task = mach_task_self();
struct {
size_t rss;
size_t vms;
void * a1;
void * a2;
char p1;
char p2;
} results[3];
size_t length = sysconf(_SC_PAGE_SIZE);
vm_address_t first_address;
kern_return_t result = vm_allocate(this_task, &first_address, length, VM_FLAGS_ANYWHERE);
if ( result != ERR_SUCCESS ) {
fprintf(stderr, "Error allocating initial 0x%zu memory.\n", length);
return -1;
}
char * first_address_p = first_address;
char * mirror_address_p;
*first_address_p = 'a';
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
results[0].rss = t_info.resident_size;
results[0].vms = t_info.virtual_size;
results[0].a1 = first_address_p;
results[0].p1 = *first_address_p;
vm_address_t mirrorAddress;
vm_prot_t cur_prot, max_prot;
result = vm_remap(this_task,
&mirrorAddress, // mirror target
length, // size of mirror
0, // auto alignment
1, // remap anywhere
this_task, // same task
first_address, // mirror source
1, // Copy
&cur_prot, // unused protection struct
&max_prot, // unused protection struct
VM_INHERIT_COPY);
if ( result != ERR_SUCCESS ) {
perror("vm_remap");
fprintf(stderr, "Error remapping pages.\n");
return -1;
}
mirror_address_p = mirrorAddress;
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
results[1].rss = t_info.resident_size;
results[1].vms = t_info.virtual_size;
results[1].a1 = first_address_p;
results[1].p1 = *first_address_p;
results[1].a2 = mirror_address_p;
results[1].p2 = *mirror_address_p;
*mirror_address_p = 'b';
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
results[2].rss = t_info.resident_size;
results[2].vms = t_info.virtual_size;
results[2].a1 = first_address_p;
results[2].p1 = *first_address_p;
results[2].a2 = mirror_address_p;
results[2].p2 = *mirror_address_p;
printf("Allocated one page of memory and wrote to it.\n");
printf("*%p = '%c'\nRSS: %zu\tVMS: %zu\n",results[0].a1, results[0].p1, results[0].rss, results[0].vms);
printf("Cloned that page copy-on-write.\n");
printf("*%p = '%c'\n*%p = '%c'\nRSS: %zu\tVMS: %zu\n",results[1].a1, results[1].p1,results[1].a2, results[1].p2, results[1].rss, results[1].vms);
printf("Wrote to the new cloned page.\n");
printf("*%p = '%c'\n*%p = '%c'\nRSS: %zu\tVMS: %zu\n",results[2].a1, results[2].p1,results[2].a2, results[2].p2, results[2].rss, results[2].vms);
return 0;
}
最佳答案
我试图实现同样的目标(事实上,它看起来更简单,因为我只需要拍摄事件区域的快照,我不需要拍摄副本的副本)。我没有找到一个好的解决方案。
直接内核支持 (或缺少):通过修改/添加模块应该可以实现这一点。但是,没有简单的方法可以从现有的 COW 区域设置新的 COW 区域。 fork ( copy_page_rank
) 使用的代码复制一个 vm_area_struct
从一个进程/虚拟地址空间到另一个(新的),但假设新映射的地址与旧映射的地址相同。如果要实现“重新映射”功能,则必须修改/复制该功能以复制 vm_area_struct
带地址翻译。
BTRFS : 为此,我想到在 btrfs 上使用 COW。我写了一个简单的程序来映射两个 reflink-ed 文件并尝试映射它们。但是用/proc/self/pagemap
查看页面信息显示文件的两个实例不共享相同的缓存页面。 (至少除非我的测试是错误的)。因此,您不会通过这样做获得太多 yield 。相同数据的物理页不会在不同实例之间共享。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdio.h>
void* map_file(const char* file) {
struct stat file_stat;
int fd = open(file, O_RDWR);
assert(fd>=0);
int temp = fstat(fd, &file_stat);
assert(temp==0);
void* res = mmap(NULL, file_stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
assert(res!=MAP_FAILED);
close(fd);
return res;
}
static int pagemap_fd = -1;
uint64_t pagemap_info(void* p) {
if(pagemap_fd<0) {
pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
if(pagemap_fd<0) {
perror("open pagemap");
exit(1);
}
}
size_t page = ((uintptr_t) p) / getpagesize();
int temp = lseek(pagemap_fd, page*sizeof(uint64_t), SEEK_SET);
if(temp==(off_t) -1) {
perror("lseek");
exit(1);
}
uint64_t value;
temp = read(pagemap_fd, (char*)&value, sizeof(uint64_t));
if(temp<0) {
perror("lseek");
exit(1);
}
if(temp!=sizeof(uint64_t)) {
exit(1);
}
return value;
}
int main(int argc, char** argv) {
char* a = (char*) map_file(argv[1]);
char* b = (char*) map_file(argv[2]);
int fd = open("/proc/self/pagemap", O_RDONLY);
assert(fd>=0);
int x = a[0];
uint64_t info1 = pagemap_info(a);
int y = b[0];
uint64_t info2 = pagemap_info(b);
fprintf(stderr, "%" PRIx64 " %" PRIx64 "\n", info1, info2);
assert(info1==info2);
return 0;
}
mprotect
+ mmap
匿名页面 :它在您的情况下不起作用,但解决方案是对我的主内存区域使用 MAP_SHARED 文件。在快照上,文件被映射到其他地方并且两个实例都受到 mprotected。在写入时,快照中映射了一个匿名页面,数据被复制到这个新页面中,原始页面不 protected 。但是,此解决方案不适用于您的情况,因为您将无法在快照中重复该过程(因为它不是一个普通的 MAP_SHARED 区域,而是一个带有一些 MAP_ANONYMOUS 页面的 MAP_SHARED。此外,它不会随着副本的数量而扩展:如果我有很多 COW 副本,我将不得不对每个副本重复相同的过程,并且不会为副本复制此页面。而且我无法在原始区域映射匿名页面,因为无法映射副本中的匿名页面。无论如何,此解决方案不起作用。
mprotect
+ remap_file_pages
:这看起来是不接触 Linux 内核的唯一方法。缺点是,一般来说,在进行复制时,您可能必须为每个页面进行 remap_file_page 系统调用:进行大量系统调用可能效率不高。对共享页面进行重复数据删除时,您至少需要: remap_file_page 新的写入页面的新/空闲页面,m-un-protect 新页面。每页都需要引用计数。
mprotect()
基于方法的扩展性非常好(如果您像这样处理大量内存)。在 Linux 上,
mprotect()
不适用于内存页粒度,但适用于
vm_area_struct
粒度(您在/prod//maps 中找到的条目)。做一个
mprotect()
在内存页粒度上会导致内核不断拆分和合并vm_area_struct:
mm_struct
;O(log #vm_area_struct)
但它仍然可能对性能产生负面影响;vm_area_struct
.我不认为他们这是为页面粒度映射而设计的:remap_file_pages() 没有针对这个用例进行非常优化,因为它需要每页一个系统调用。
madvide(start, end, MADV_MERGEABLE)
手动复制它。地区。您需要启用 KSM(在 root 中):
echo 1 > /sys/kernel/mm/ksm/run
echo 10000 > /sys/kernel/mm/ksm/pages_to_scan
它有效,但对我的工作量而言效果不佳,但这可能是因为页面最终没有被大量共享。缺点是您仍然必须进行复制(您不能拥有高效的 COW),然后内核将取消合并页面。它会在复制时产生页面和缓存错误,KSM 守护进程线程会消耗大量 CPU(我有一个 CPU 在整个模拟中以 A00% 运行)并且可能会消耗日志缓存。因此,您在复制时不会获得时间,但可能会获得一些内存。如果您的主要动机是从长远来看使用更少的内存并且您不太关心避免副本,那么此解决方案可能适合您。
关于c - 在进程内的写内存上分配副本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16965505/
我是 Linux 的新手,并且继承了保持我们的单一 Linux 服务器运行的职责。这是我们的SVN服务器,所以比较重要。 原来在我之前维护它的人有一个 cron 任务,当有太多 svnserve 进程
Node 虽然自身存在多个线程,但是运行在 v8 上的 JavaScript 是单线程的。Node 的 child_process 模块用于创建子进程,我们可以通过子进程充分利用 CPU。范例:
Jenkins 有这么多进程处于事件状态是否正常? 我检查了我的设置,我只配置了 2 个“执行者”... htop http://d.pr/i/RZzG+ 最佳答案 您不仅要限制 Master 中的执
我正在尝试在 scala 中运行这样的 bash 命令: cat "example file.txt" | grep abc Scala 有一个特殊的流程管道语法,所以这是我的第一个方法: val f
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我需要一些帮助来理解并发编程的基础知识。事实上,我读得越多,就越感到困惑。因此,我理解进程是顺序执行的程序的一个实例,并且它可以由一个或多个线程组成。在单核CPU中,一次只能执行一个线程,而在多核CP
我的问题是在上一次集成测试后服务器进程没有关闭。 在integration.rs中,我有: lazy_static! { static ref SERVER: Arc> = {
我正在使用 Scala scala.sys.process图书馆。 我知道我可以用 ! 捕获退出代码和输出 !!但是如果我想同时捕获两者呢? 我看过这个答案 https://stackoverflow
我正在开发一个C++类(MyClass.cpp),将其编译为动态共享库(MyClass.so)。 同一台Linux计算机上运行的两个不同应用程序将使用此共享库。 它们是两个不同的应用程序。它不是多线程
我在我的 C 程序中使用 recvfrom() 从多个客户端接收 UDP 数据包,这些客户端可以使用自定义用户名登录。一旦他们登录,我希望他们的用户名与唯一的客户端进程配对,这样服务器就可以通过数据包
如何更改程序,以便函数 function_delayed_1 和 function_delayed_2 仅同时执行一次: int main(int argc, char *argv[]) {
考虑这两个程序: //in #define MAX 50 int main(int argc, char* argv[]) { int *count; int fd=shm
请告诉我如何一次打开三个终端,这样我的项目就可以轻松执行,而不必打开三个终端三次然后运行三个exe文件。请问我们如何通过脚本来做到这一点,即打开三个终端并执行三个 exe 文件。 最佳答案 在后台运行
我编写了一个监控服务来跟踪一组进程,并在服务行为异常、内存使用率高、超出 CPU 运行时间等时发出通知。 这在我的本地计算机上运行良好,但我需要它指向远程机器并获取这些机器上的进程信息。 我的方法,在
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?将问题更新为 on-topic对于堆栈溢出。 8年前关闭。 Improve this qu
我有一个允许用户上传文件的应用程序。上传完成后,必须在服务器上完成许多处理步骤(解压、存储、验证等...),因此稍后会在一切完成后通过电子邮件通知用户。 我见过很多示例,其中 System.Compo
这个问题对很多人来说可能听起来很愚蠢,但我想对这个话题有一个清晰的理解。例如:当我们在 linux(ubuntu, x86) 上构建一个 C 程序时,它会在成功编译和链接过程后生成 a.out。 a.
ps -eaf | grep java 命令在这里不是识别进程是否是 java 进程的解决方案,因为执行此命令后我的许多 java 进程未在输出中列出。 最佳答案 简答(希望有人写一个更全面的): 获
我有几个与内核态和用户态的 Windows 进程相关的问题。 如果我有一个 hello world 应用程序和一个暴露新系统调用 foo() 的 hello world 驱动程序,我很好奇在内核模式下
我找不到很多关于 Windows 中不受信任的完整性级别的信息,对此有一些疑问: 是否有不受信任的完整性级别进程可以创建命名对象的地方? (互斥锁、事件等) 不受信任的完整性级别进程是否应该能够打开一
我是一名优秀的程序员,十分优秀!