具体来说,fork() 如何处理 Linux 中从 malloc() 动态分配的内存?

发布于 2024-10-10 01:12:57 字数 1082 浏览 7 评论 0原文

我有一个带有父进程和子进程的程序。在 fork() 之前,父进程调用 malloc() 并用一些数据填充数组。在 fork() 之后,子进程需要该数据。我知道我可以使用管道,但以下代码似乎可以工作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main( int argc, char *argv[] ) {
    char *array;
    array = malloc( 20 );
    strcpy( array, "Hello" );
    switch( fork() ) {
    case 0:
        printf( "Child array: %s\n", array );
        strcpy( array, "Goodbye" );
        printf( "Child array: %s\n", array );
        free( array );
        break;
    case -1:
        printf( "Error with fork()\n" );
        break;
    default:
        printf( "Parent array: %s\n", array );
        sleep(1);
        printf( "Parent array: %s\n", array );
        free( array );
    }
    return 0;
}

输出是:

Parent array: Hello
Child array: Hello
Child array: Goodbye
Parent array: Hello

我知道在堆栈上分配的数据在子级中可用,但似乎在堆上分配的数据也可供子级使用。同样,子进程不能修改父进程在栈上的数据,子进程也不能修改父进程在堆上的数据。所以我假设子进程获得自己的堆栈和堆数据副本。

Linux 中总是这样吗?如果是这样,支持这一点的文档在哪里?我检查了 fork() 手册页,但它没有具体提到堆上动态分配的内存。

I have a program with a parent and a child process. Before the fork(), the parent process called malloc() and filled in an array with some data. After the fork(), the child needs that data. I know that I could use a pipe, but the following code appears to work:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main( int argc, char *argv[] ) {
    char *array;
    array = malloc( 20 );
    strcpy( array, "Hello" );
    switch( fork() ) {
    case 0:
        printf( "Child array: %s\n", array );
        strcpy( array, "Goodbye" );
        printf( "Child array: %s\n", array );
        free( array );
        break;
    case -1:
        printf( "Error with fork()\n" );
        break;
    default:
        printf( "Parent array: %s\n", array );
        sleep(1);
        printf( "Parent array: %s\n", array );
        free( array );
    }
    return 0;
}

The output is:

Parent array: Hello
Child array: Hello
Child array: Goodbye
Parent array: Hello

I know that data allocated on the stack is available in the child, but it appears that data allocated on the heap is also available to the child. And similarly, the child cannot modify the parent's data on the stack, the child cannot modify the parent's data on the heap. So I assume the child gets its own copy of both stack and heap data.

Is this always the case in Linux? If so, where the is the documentation that supports this? I checked the fork() man page, but it didn't specifically mention dynamically allocated memory on the heap.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

星光不落少年眉 2024-10-17 01:12:57

为进程分配的每个页面(无论是具有堆栈还是堆的虚拟内存页面)都会被复制,以便分叉进程能够访问它。

实际上,它不是从一开始就复制的,它被设置为“写入时复制”,这意味着一旦其中一个进程(父进程或子进程)尝试修改页面,它就会被复制,这样它们就不会互相伤害,并且仍然可以访问 fork() 点的所有数据。

例如,代码页(实际可执行文件映射到内存中的代码页)通常是只读的,因此在所有分叉进程之间重用 - 它们不会再次复制,因为没有人在那里写入,只能读取,等等永远不需要写时复制。

更多信息请参见此处此处

Each page that is allocated for the process (be it a virtual memory page that has the stack on it or the heap) is copied for the forked process to be able to access it.

Actually, it is not copied right at the start, it is set to Copy-on-Write, meaning once one of the processes (parent or child) try to modify a page it is copied so that they will not harm one-another, and still have all the data from the point of fork() accessible to them.

For example, the code pages, those the actual executable was mapped to in memory, are usually read-only and thus are reused among all the forked processes - they will not be copied again, since no one writes there, only read, and so copy-on-write will never be needed.

More information is available here and here.

如梦初醒的夏天 2024-10-17 01:12:57

分叉后,子级完全独立于父级,但可以继承父级的某些副本。对于堆来说,子进程在概念上将在分叉时拥有父堆的副本。然而,对子地址空间中的头的修改只会修改子地址的副本(例如通过写时复制)。

至于文档:我注意到文档通常会声明所有内容都被复制,除了等等等等。

After a fork the child is completely independent from the parent, but may inherit certain things that are copies of the parent. In the case of the heap, the child will conceptually have a copy of the parents heap at the time of the fork. However, modifications to the head in the child's address space will only modify the child's copy (e.g. through copy-on-write).

As for the documentation: I've noticed that documentation will usually state that everything is copied, except for blah, blah blah.

放肆 2024-10-17 01:12:57

简短的答案是“写时脏” - 较长的答案是..更长。

但对于所有意图和目的 - 在 C 级别可以安全地假设的工作模型是,在 fork() 之后,两个进程是绝对相同的 - 即子进程获得 100% 精确的副本 - (但对于一小段时间)位围绕 fork()) 的返回值 - 然后随着双方修改其内存、堆栈和堆而开始发散。

因此,您的结论略有偏差 - 子级开始时将与父级相同的数据复制到自己的空间中 - 然后对其进行修改 - 并将其视为已修改的 - 而父级继续使用自己的副本。

事实上,事情要复杂一些——因为它试图通过做一些肮脏的事情来避免完整的副本;除非必要,否则避免复制。

深度。

The short answer is 'dirty on write' - the longer answer is .. a lot longer.

But for all intends and purposes - the working model which at C level is safe to assume is that just after the fork() the two processes are absolutely identical -- i.e. the child gets a 100% exact copy -- (but for a wee bit around the return value of fork()) - and then start to diverge as each side modifies its memory, stack and heaps.

So your conclusion is slightly off - child starts off with the same data as parent copied into its own space - then modifies it - and see s it as modified - while the parent continues with its own copy.

In reality things are bit more complex - as it tries to avoid a complete copy by doing something dirty; avoiding to copy until it has to.

Dw.

千仐 2024-10-17 01:12:57

该示例不起作用,因为子级未更新父级。这是一个解决方案:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

typedef struct
{
  int id;
  size_t size;
} shm_t;

shm_t *shm_new(size_t size)
{
  shm_t *shm = calloc(1, sizeof *shm);
  shm->size = size;

  if ((shm->id = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) < 0)
  {
    perror("shmget");
    free(shm);
    return NULL;
  }

  return shm;
}

void shm_write(shm_t *shm, void *data)
{
  void *shm_data;

  if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
  {
    perror("write");
    return;
  }

  memcpy(shm_data, data, shm->size);
  shmdt(shm_data);
}

void shm_read(void *data, shm_t *shm)
{
  void *shm_data;

  if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
  {
    perror("read");
    return;
  }
  memcpy(data, shm_data, shm->size);
  shmdt(shm_data);
}

void shm_del(shm_t *shm)
{
  shmctl(shm->id, IPC_RMID, 0);
  free(shm);
}

int main()
{
  void *array = malloc(20);
  strcpy((char *) array, "Hello");
  printf("parent: %s\n", (char *) array);
  shm_t *shm = shm_new(sizeof array);

  int pid;
  if ((pid = fork()) == 0)
  { /* child */
    strcpy((char *) array, "Goodbye");
    shm_write(shm, array);
    printf("child: %s\n", (char *) array);
    return 0;
  }
  /* Wait for child to return */
  int status;
  while (wait(&status) != pid);
  /* */
  shm_read(array, shm);
  /* Parent is updated by child */
  printf("parent: %s\n", (char *) array);
  shm_del(shm);
  free(array);
  return 0;
}

The example doesn't work because the parent is not updated by the child. Here's a solution:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

typedef struct
{
  int id;
  size_t size;
} shm_t;

shm_t *shm_new(size_t size)
{
  shm_t *shm = calloc(1, sizeof *shm);
  shm->size = size;

  if ((shm->id = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) < 0)
  {
    perror("shmget");
    free(shm);
    return NULL;
  }

  return shm;
}

void shm_write(shm_t *shm, void *data)
{
  void *shm_data;

  if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
  {
    perror("write");
    return;
  }

  memcpy(shm_data, data, shm->size);
  shmdt(shm_data);
}

void shm_read(void *data, shm_t *shm)
{
  void *shm_data;

  if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
  {
    perror("read");
    return;
  }
  memcpy(data, shm_data, shm->size);
  shmdt(shm_data);
}

void shm_del(shm_t *shm)
{
  shmctl(shm->id, IPC_RMID, 0);
  free(shm);
}

int main()
{
  void *array = malloc(20);
  strcpy((char *) array, "Hello");
  printf("parent: %s\n", (char *) array);
  shm_t *shm = shm_new(sizeof array);

  int pid;
  if ((pid = fork()) == 0)
  { /* child */
    strcpy((char *) array, "Goodbye");
    shm_write(shm, array);
    printf("child: %s\n", (char *) array);
    return 0;
  }
  /* Wait for child to return */
  int status;
  while (wait(&status) != pid);
  /* */
  shm_read(array, shm);
  /* Parent is updated by child */
  printf("parent: %s\n", (char *) array);
  shm_del(shm);
  free(array);
  return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文