programing

구체적으로, 포크()는 리눅스에서 malloc()에서 동적으로 할당된 메모리를 어떻게 처리합니까?

powerit 2023. 6. 7. 23:19
반응형

구체적으로, 포크()는 리눅스에서 malloc()에서 동적으로 할당된 메모리를 어떻게 처리합니까?

저는 부모와 자녀가 함께 하는 프로그램이 있습니다.포크() 이전에 부모 프로세스는 malloc()를 호출하고 일부 데이터로 배열을 채웠습니다.포크() 이후에 아이는 해당 데이터가 필요합니다.파이프를 사용할 수 있지만 다음 코드가 작동하는 것 같습니다.

#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() man 페이지를 확인했는데 힙에 동적으로 할당된 메모리에 대해 구체적으로 언급하지 않았습니다.

프로세스에 할당된 각 페이지(스택이 있는 가상 메모리 페이지 또는 힙 페이지)는 분기된 프로세스가 액세스할 수 있도록 복사됩니다.

실제로 시작할 때 바로 복사되지 않고 Copy-on-Write(쓰기 시 복사)로 설정되어 있습니다. 즉, 프로세스 중 하나(부모 또는 자식)가 페이지를 수정하려고 하면 페이지가 서로 손상되지 않도록 복사되며 포크() 지점의 모든 데이터에 액세스할 수 있습니다.

예를 들어, 실제 실행 파일이 메모리에 매핑된 코드 페이지는 일반적으로 읽기 전용이므로 분기된 모든 프로세스에서 재사용됩니다. 이 페이지는 다시 복사되지 않습니다. 아무도 거기에 쓰지 않고 읽기만 하기 때문에 쓰기 시 복사가 필요하지 않습니다.

자세한 내용은 여기와 여기에서 확인할 수 있습니다.

포크 후 자식은 부모로부터 완전히 독립되지만 부모의 복사본인 특정 항목을 상속받을 수 있습니다.힙의 경우, 자식은 개념적으로 포크 시점에 부모 힙의 복사본을 갖게 됩니다.그러나 자녀의 주소 공간에서 머리 부분을 수정하면 자녀의 복사본만 수정됩니다(예: 쓰기 시 복사).

설명서의 경우:저는 문서가 일반적으로 blah, blah blah를 제외한 모든 을 복사한다고 명시한다는 것을 알아차렸습니다.

짧은 대답은 '쓰면 더러워진다'입니다. - 더 긴 대답은... 훨씬 더 긴 것입니다.

그러나 모든 의도와 목적에서 - C 수준에서 안전하게 가정할 수 있는 작업 모델은 포크() 직후에 두 프로세스가 완전히 동일하다는 것입니다. 즉, 아이가 100% 정확한 복사본을 얻는다는 것입니다. (하지만 포크()의 반환 값을 중심으로 잠시 동안) - 그리고 나서 각 측면이 메모리를 수정하고 스택과 더미를 쌓으면서 분기하기 시작합니다.

따라서 부모가 자신의 공간에 복사한 것과 동일한 데이터로 시작하여 수정하고 수정한 것으로 간주하는 반면 부모는 자신의 복사본을 계속합니다.

실제로는 좀 더 복잡합니다. 더러운 일을 함으로써 완전한 복사를 피하려고 하기 때문입니다. 필요할 때까지 복사하는 것을 피하려고 하기 때문입니다.

DW.

부모가 자식에 의해 업데이트되지 않았기 때문에 예제가 작동하지 않습니다.해결책은 다음과 같습니다.

#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;
}

언급URL : https://stackoverflow.com/questions/4597893/specifically-how-does-fork-handle-dynamically-allocated-memory-from-malloc

반응형