简体   繁体   English

在fork()系统调用中令人困惑

[英]Confusing in fork( ) system call

I have created a parent and a child process using fork( ), and both share a memory address called "ptr". 我已经使用fork()创建了一个父进程和一个子进程,并且都共享一个称为“ ptr”的内存地址。 But i am confused due to the one output of a program: 但由于程序的一个输出,我感到困惑:

1) Address of ptr: 123456 NOTE: Same address for both parent and child, so expected is if one process alters this address, it should reflect for the other too process too as the address is same. 1)ptr的地址:123456注意:父母和孩子的地址都相同,因此可以预期的是,如果一个进程更改了该地址,则该地址也应该反映给另一个进程。

2) Parent: *ptr=44 2)父母:* ptr = 44

3) Child: *ptr=33 3)儿童:* ptr = 33

4) Printing values: Parent still retains old value: printf("ptr = %d",*ptr); 4)打印值:父级仍保留旧值:printf(“ ptr =%d”,* ptr); // Output: still 44, exp is 33 Child prints 33, the expected value. //输出:仍为44,exp为33子级打印33,即期望值。 printf("ptr = %d",*ptr);//Prints 33 fine printf(“ ptr =%d”,* ptr); //精确打印33

Question1 ) Can anyone tell me, how the values are different? 问题1 )谁能告诉我,这些值有何不同? Although the pointer address is same for both the parent and the child? 虽然指针地址对于父母和孩子都是相同的?

Question2 ) I am working on a memory leak tool which is giving double free, error as it is seeing parent and the child free the same address. Question2 )我正在使用一个内存泄漏工具,它提供了双重释放,因为看到父母和孩子都释放了相同的地址,所以出现了错误。 However, it is not a case of double free, as we see. 但是,正如我们所见,这不是双重免费的情况。 How to sort this problem? 如何解决这个问题? As the memory address the tool sees for the parent and the child is the same addrerss? 作为工具的内存地址,对于父级和子级来说,该地址是相同的地址吗?

PS: Please see the below code snippet: PS:请参见以下代码片段:

#include <sys/types.h>
#include <unistd.h>
#include <cstdlib>
int main()
{
 int pid, *ptr
 ptr=(int*)malloc(sizeof(int));
 *ptr=33; // Parent keeps the data as 33, before forking.

 if(pid==0){*ptr=44;} // Child modifies data, which is ignored by parent

 // Now we print the memory address and the value both by child and parent
  if(pid==0)
  {
    printf("Child data: %u\n",*ptr);
    printf("Child address: %u\n",ptr);
  }
  if(pid>0)
  {
    printf("Parent data: %u\n",*ptr);
    printf("Parent address: %u\n",ptr);
  }
}

Output: Child data: 44 Child address: 123456 输出:子数据:44子地址:123456

Parent data: 33 (how come still old value?) Parent address: 123456 (How come same address but data different than child?) 父级数据:33(为什么还旧值?)父级地址:123456(为什么同一个地址但数据与子级不同?)

if(pid==0){*ptr=44;} // Child modifies data, which is ignored by parent
Question1) Can anyone tell me, how the values are different? 问题1)谁能告诉我,这些值有何不同? Although the pointer address is same for both the parent and the child? 虽然指针地址对于父母和孩子都是相同的?

This is the whole idea. 这就是整个想法。 They may have the same address, but these addresses are virtual . 它们可能具有相同的地址,但是这些地址是virtual Each process has its own address space. 每个进程都有其自己的地址空间。 What fork() does is creates a new process, and makes its virtual memory layout look like the parent. fork()作用是创建一个新进程,并使它的虚拟内存布局看起来像父进程。

See the Wikipedia article on page tables and similar topics for some illustrations of how this works. 有关页码工作原理的一些说明,请参见页表和类似主题Wikipedia文章

-- (Long aside follows) -- -(长时间不谈)-

What typically happens at a fork() is that page tables for both the parent and child are set up such that the pages are marked as read-only. fork()通常发生的情况是,父级和子级的页表都已设置为将页标记为只读。 When a write instruction happens for some location the kernel gets a page fault , which the CPU generates at a bad memory access. 当在某个位置发生写指令时,内核会得到页面错误 ,这是CPU在内存访问错误时生成的。 The kernel will allocate new memory for the trapped process, map it into the right address by manipulating its page table, copy the old buffer to the newly allocated one and let the write continue. 内核将为被捕获的进程分配新的内存,通过操作其页表将其映射到正确的地址,将旧缓冲区复制到新分配的缓冲区中,然后继续写操作。 This is called copy-on-write . 这称为写时复制 This makes the initial fork quick and keeps memory consumption down for pages that are not written in either process. 这可以使初始派生快速进行,并可以减少两个进程中未写入的页面的内存消耗。

The previous paragraph is all just an optimization of the fork programming model. 上一段只是对fork编程模型的优化。 They say early Unix didn't do this -- it did a full memory copy of the whole process. 他们说早期的Unix并没有这样做-它做了整个过程的完整内存副本。 I've also heard that Cygwin's fork() does a full copy. 我还听说Cygwin的fork()了完整复制。

But the virtual address has nothing to do with the physical address of the memory. 但是虚拟地址与内存的物理地址无关。 The CPU uses it as a "key" for the page table, which defines where the actual memory is. CPU将其用作页表的“键”,页表定义了实际内存的位置。 The page table may also say that the page is not valid, in which case the kernel has an opportunity to do a "fixup" (perform copy-on-write, recall the page from swap space, etc.) or kill the process in the case of a legitimately invalid pointer access. 页表也可能说该页无效,在这种情况下,内核有机会进行“修复”(执行写时复制,从交换空间中调用该页等)或终止进程。指针访问合法无效的情况。

You misunderstood how memory works in a Unix-like system: the memory of parent and child are independent. 您误解了内存在类Unix系统中的工作方式:父级和子级的内存是独立的。 If you want them to communicate, you can set up explicitly shared memory , or IPC. 如果希望他们进行通信,则可以设置显式共享内存或IPC。

Even if you think of memory as a large buffer with individual addresses, there is more to it. 即使您将内存视为具有单个地址的大型缓冲区,但还有更多内容。

The above view is true enough for physical memory, but modern processors include a MMU chip (Memory Management Unit), this chip map pages of physical memory to virtual memory. 上面的视图对于物理内存已经足够真实,但是现代处理器包括MMU芯片(内存管理单元),该芯片将物理内存的页面映射到虚拟内存。 The virtual memory is also used for mapping (virtual) memory addresses to disk (swap) when there is not enough physical memory on a given system for the use of running programs. 当给定系统上没有足够的物理内存来使用正在运行的程序时,虚拟内存还用于将(虚拟)内存地址映射到磁盘(交换)。

When running a C program in user space (or even a program writen in assembler) what you access to is virtual memory, and the addresses are addresses of virtual memory. 在用户空间中运行C程序(甚至是用汇编程序编写的程序)时,您访问的是虚拟内存,而地址就是虚拟内存的地址。 To keep things simple for compilers and program loaders, on modern operating system every process has it's own independant memory address space and addresses spaces are unrelated between one another (like if every process could access to the whole memory space of the machine). 为了使编译器和程序加载器更简单,在现代操作系统上,每个进程都有其自己的独立内存地址空间,并且地址空间彼此无关(例如,每个进程都可以访问计算机的整个内存空间)。 Of course if the process access to some virtual memory page not mapped to physical memory (or swapped to disk) this will cause a "segmentation fault". 当然,如果进程访问某些未映射到物理内存(或交换到磁盘)的虚拟内存页面,则将导致“分段错误”。

When a process is created using fork, the memory space of the father is duplicated in the child (ie: the is the same data at the same virtual addresses for both). 使用fork创建进程时,父级的内存空间在子级中是重复的(即:两者在相同的虚拟地址处是相同的数据)。 After the fork they will diverge when memory is changed in one of the process and not in the other. 在分叉之后,如果在一个过程中更改了内存,而在另一个过程中没有更改,则它们将分开。 The actual mechanism is slightlty more complex, it's usually copy-on-write, whenever a modification is performed on a memory page a copy of this page is done, if no change is made the two processes can access for reading at the same physical memory. 实际的机制稍微复杂一点,通常是写时复制,每当对内存页面进行修改时,都会完成该页面的副本,如果不进行任何更改,则两个进程可以访问同一物理内存以进行读取。 That explains what you see when changing values in parent of child process : you see either was was put before forking (common between both processes) or different values if it was changed after the fork. 这就解释了在子进程的父进程中更改值时所看到的结果:您看到要么在派生之前放入(在两个进程之间通用),要么在分叉之后更改了不同的值。

To make processes comunicate between one another you have to use some communication layer (socket, file, pipe, shared memory, etc.). 为了使进程之间相互通信,您必须使用一些通信层(套接字,文件,管道,共享内存等)。 And don't belive that using shared memory is especially simple of faster compared to the other methods, that is not true. 而且不要相信与其他方法相比,使用共享内存特别简单,而且速度更快,这是不正确的。

By the way, that's the difference between processes and threads. 顺便说一下,这就是进程和线程之间的区别。 Every process has it's own memory while threads share the same memory space. 每个进程都有自己的内存,而线程共享相同的内存空间。 What you thought true for processes (created by fork) is basically true for threads. 您认为对进程(由fork创建)正确的对于线程基本上是正确的。

Shared memory space is also basically true for kernel level programming, but then fork wouldn't be available anyway. 共享内存空间对于内核级编程也基本上是正确的,但是无论如何叉都将不可用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM