2017-10-22 15 views
1

현재 운영체제와 동시성을 연구하고 있습니다. 프로세스 스케줄러에 관한 제 실습 중 하나는 C 언어를 사용하여 Linux에서 여러 프로세스가 밀리 초 단위로 "병렬"로 작동하는 방식을 파악하는 것입니다.Linux에서 C의 fork()가 생성 한 하위 프로세스에서 쉘의 출력 리디렉션이 어떻게 작동합니까?

/* This file's name is Task05_3.c */ 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <sys/time.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <errno.h> 
#include <string.h> 

int kill(pid_t pid, int sig); 
unsigned usleep(unsigned seconds); 

#define NUMBER_OF_PROCESSES 7 
#define MAX_EXPERIMENT_DURATION 4 

long int getDifferenceInMilliSeconds(struct timeval start, struct timeval end) 
{ 
    int seconds = end.tv_sec - start.tv_sec; 
    int useconds = end.tv_usec - start.tv_usec; 
    int mtime = (seconds * 1000 + useconds/1000); 
    return mtime; 
} 

int main(int argc, char const *argv[]) 
{ 
    struct timeval startTime, currentTime; 
    int diff; 

    int log[MAX_EXPERIMENT_DURATION + 2] = {-1}; 
    /* initialization */ 
    for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k) 
     log[k] = -1; 

    gettimeofday(&startTime, NULL); 

    pid_t pid_for_diss = 0; 

    for (int i = 0; i < NUMBER_OF_PROCESSES; ++i) 
    { 
     pid_for_diss = fork(); 
     if (pid_for_diss < 0) { 
      printf("fork error, errno(%d): %s\n", errno, strerror(errno)); 
     } else if (pid_for_diss == 0) { 
      /* This loop is for logging when the child process is running */ 
      while (1) { 
       gettimeofday(&currentTime, NULL); 
       diff = getDifferenceInMilliSeconds(startTime, currentTime); 
       if (diff > MAX_EXPERIMENT_DURATION) 
       { 
        break; 
       } 
       log[diff] = i; 
      } 
      // for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k) 
      // { 
      //  if (log[k] != -1) 
      //  { 
      //   printf("%d, %d\n", log[k], k); 
      //  } 
      // } 
      // exit(0); 
      break; 
     } 
    } 

    /* This loop is for print the logged results out */ 
    if (pid_for_diss == 0) 
    { 
     for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k) 
     { 
      if (log[k] != -1) 
      { 
       printf("%d, %d\n", log[k], k); 
      } 
     } 
     kill(getpid(), SIGKILL); 
    } 

    int status; 
    while (wait(&status) != -1);// -1 means wait() failed 
    printf("Bye from the parent!\n"); 
} 

기본적으로, 여기에 내 생각은 부모 프로세스가 포크 (7 개 자식 프로세스를 생성)과 경쟁하도록 강제 while 루프로 설정하는 내가 루프를 설정하는 것이 있습니다 : 여기 내 코드입니다 CPU 사용 시간. 그리고 자식 프로세스가 실행될 때마다, 부모 프로세스의 현재 시간과 시작 시간의 차이를 실행중인 자식 프로세스에 속한 어레이에 기록합니다. 그런 다음 7 개의 프로세스가 while 루프를 깰 때마다 각 하위 프로세스가 로그 된 결과를 인쇄하기위한 루프를 또 하나 설정합니다.

그러나 Linux 컴퓨터에서 .csv 파일로 출력을 리디렉션하려고하면 이상한 일이 발생했습니다. 먼저 내 for 루프에서 인쇄 할 루프를 설정합니다 (코드에서 볼 수 있듯이) , 나는 bash는 직접 ./Task05_3를 실행하고 여기에 결과 :

[email protected]:osc$ gcc -std=c99 Task05_3.c -o Task05_3 
[email protected]:osc$ ./Task05_3 
5, 0 
4, 0 
6, 0 
4, 1 
1, 0 
4, 2 
4, 3 
4, 4 
0, 0 
1, 1 
6, 1 
1, 2 
1, 3 
1, 4 
5, 1 
5, 2 
5, 3 
5, 4 
6, 2 
6, 3 
2, 0 
6, 4 
2, 1 
2, 2 
2, 3 
2, 4 
0, 1 
3, 0 
0, 2 
0, 3 
0, 4 
3, 1 
3, 2 
3, 3 
3, 4 
Bye from the parent! 
[email protected]:osc$ 

당신은 (모두 부모 프로세스와 자식 프로세스에서) 모든 결과가 터미널에 인쇄하고 그 결과의되었음을 여기에서 볼 수 있습니다 자식 프로세스는 무작위 순서로 (이것은 표준 출력에 동시에 쓰는 여러 프로세스로 인해 발생할 수 있습니다). 그러나 ./Task05_3 > 5output_c.csv에 의해 실행하려고하면 대상이되는 .csv 파일에 부모 프로세스의 결과 만 포함되어 있습니다. 다음과 같이 보입니다. Result_in_csv01

내 첫 번째 질문은 .csv 파일 만 상위 프로세스의 프롬프트가 포함되어 있습니까? 그것은 bash에서 입력 한 명령어가 부모 프로세스의 출력 만 리디렉션하고 자식 프로세스의 출력 스트림과는 아무런 관련이 없기 때문입니까?

주요 for 루프 내에 for 루프 (인쇄용)를 넣고 (위 코드의 for 루프를 참조하십시오) ./Task05_3 > 5output_c.csv 코드를 실행하면 더 혼란스러운 일이 발생합니다. 파일은 이제 다음과 같이 보입니다 : Result_in_csv02

이제 모든 결과가 포함됩니다! 그리고 자식 프로세스의 결과의 순서는 더 이상 임의가 아닙니다! (실행중인 하위 프로세스가 결과를 모두 인쇄 할 때까지 다른 자식 프로세스가 대기 상태 인 것은 분명합니다. 그래서 두 번째 질문은 단순히 for 루프의 위치를 ​​변경 한 후에 이것이 일어날 수있는 방법이라는 것입니다.

추신.

[email protected]:osc$ cat /proc/version 
Linux version 3.10.0-693.2.2.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)) #1 SMP Tue Sep 12 22:26:13 UTC 2017 

그리고 GCC 버전은 다음과 같습니다 : 표준 입출력 기능을 통해

[email protected]:osc$ gcc --version 
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16) 
Copyright (C) 2015 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

답변

4

출력은 기본적으로 버퍼링에에 리눅스 기계는 내 코드를 실행. 즉, 즉시 작성되지는 않지만 내부 구조 (FILE 내부)에 누적 될 때까지 ... 어떤 일이 발생합니다. 세 가지 가능성이 있습니다.

  • FILE은 버퍼링되지 않습니다. 그러면 출력이 즉시 기록됩니다.
  • 버퍼링 됨. 출력은 버퍼가 가득 차거나 '\n' (줄 바꿈)이 표시 될 때 기록됩니다.
  • 버퍼가 버퍼 됨. 출력은 버퍼가 가득 차면 기록됩니다.

fflush을 사용하여 항상 수동으로 쓰기를 수행 할 수 있습니다.

사용자가 여는 파일 (fopen)은 기본적으로 블록 버퍼링됩니다. stderr은 버퍼링되지 않은 채로 시작됩니다. stdout은 터미널을 참조하는 경우 줄 버퍼이며, 그렇지 않으면 버퍼링됩니다.

자녀가 전체 줄 인쇄 (printf("%d, %d\n", log[k], k);)를 처리합니다. 즉, stdout이 터미널에가는 한 모든 것이 즉시 나타납니다 (버퍼링 된 행이기 때문에).

출력을 파일로 리디렉션하면 stdout은 블록 버퍼가됩니다. 버퍼가 꽤 클 수 있으므로 모든 출력이 버퍼에 누적됩니다 (가득 채워지지는 않습니다). 일반적으로 FILE 핸들이 닫히면 (버퍼가 fclose) 버퍼가 플러시 (즉, 기록 및 비우기)되며, 일반적으로 프로그램이 종료되면 (예 : return에서 main 또는 exit) 모든 열린 파일이 자동으로 닫힙니다.

그러나이 경우 (치명적인, 잡히지 않는) 신호를 보내서 프로세스를 종료하십시오. 즉, 파일이 닫히지 않고 버퍼가 작성되지 않고 내용이 손실됩니다. 그래서 출력이 표시되지 않습니다. 두 번째 버전에서


, 당신은 자신에게 신호를 보내는 대신 exit를 호출합니다. 그러면 atexit 핸들러를 호출하고, 열려있는 모든 파일을 닫고, 버퍼를 플러시하는 정상적인 정리를 수행합니다. 그런데


, 대신 kill(getpid(), X) 당신은 raise(X)를 작성할 수 있습니다. 더 짧고 이식성이 뛰어납니다 ( raise은 표준 C입니다).

+0

답장과 설명에 감사드립니다. 정말 분명하고 도움이됩니다. 그건 그렇고, 내 자식 프로세스의 출력이 줄 버퍼가있는 경우에 터미널에 무작위로 인쇄 되었기 때문에 파일로 리다이렉트 할 때 왜 내 자식 프로세스의 출력이 나타나는 지 궁금했다. 조금 더 구체적으로 설명해 주시겠습니까? Thx again ~ – TyBcodar

+0

@TyBcodar 두 번째 버전에서는 각 자식에서 (exit (0) 호출의 일부로) 하나의 원자'write()'만 수행되기 때문에 단일 프로세스에 대한 모든 출력 즉시 함께 나타납니다. 첫 번째 버전에서는 모든 프로세스의 모든 라인이 독립적으로 즉시 작성됩니다. – melpomene