신기하고 이상하고 너무 궁금한 현상이..!

Seven..의 이미지

아래 간단하고 직관적인 코드가 있습니다.
보시고 잠시어떻게 돌아갈지 생각을..해주세요..

#include <stdio.h>

int main()
{
        int pid, i;
        printf("Step 1 Start\n");
        for(i=0; i<10; i++)
        {
                printf("Step 2 Fork\n");
                pid=fork();
                if(pid==0)
                {//if child
                        printf("Step 3 I am CHILD\n");
                        exit(0);
                }
                else
                {//if parent
                        sleep(1);
                }
        }
        return 0;
}

1 2 3 2 3 2 3 이렇게 10번 반복하며 printf를 뿌려주고 종료되는
프로그램이겠죠?

실행하면 예상대로 1 2 3 2 3 이렇게 나옵니다.

Quote:
[chyunsoo@localhost tmp]$ ./test
Step 1 Start
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD
Step 2 Fork
Step 3 I am CHILD

그런데 표준출력과 표준 에러를 txt 로 저장을 하면!

Quote:
[chyunsoo@localhost tmp]$ ./test &> test.txt &
[2] 1809
[chyunsoo@localhost tmp]$
[2]+ Done ./test >&test.txt
[chyunsoo@localhost tmp]$ cat test.txt
Step 1 Start
Step 2 Fork
Step 3 I am CHILD
Step 1 Start
Step 2 Fork
Step 2 Fork
Step 3 I am CHILD
Step 1 Start
Step 2 Fork
Step 2 Fork
Step 2 Fork
Step 3 I am CHILD
Step 1 Start
Step 2 Fork
Step 2 Fork
Step 2 Fork
Step 2 Fork
Step 3 I am CHILD

위의 출력은 잘라서 가져온겁니다저렇게 step2가
점점 반복해서많이 찍히는현상이 발생합니다..

미치겠네요;; 왜저런지이유를 모르겠습니다 ㅠ.ㅜ

bugiii의 이미지

stderr 로 fprintf 해보시고 결과 부탁드립니다.

pynoos의 이미지

bugiii 님과 비슷한 얘기지만..

printf 한 다음 fflush(stdout) 을 해보세요.

Seven..의 이미지

Quote:
stderr 로 fprintf 해보시고 결과 부탁드립니다.

제가 맞게 이해한건지 모르겠네요..

./test 1> test.txt 2> test_err.txt &

이렇게 돌렸습니다.

test.txt는 위와 같고
test_err.txt <--stderr는 비어있는 문서가 되는군요..

ps. 인용에..
누구님의 글.. 이런거.. 어떻게 하나요? :lol:

VENI VIDI VICI

Seven..의 이미지

꺄울.. 해결되었습니다.
fflush(stdout)..

처음 보는 함수인데;;

stdout의 버퍼(?) 같은곳을 비우는 함수인가요?

저거 써주는 위치에 따라서 결과가 좀 달라지기도 했거든요;;

왜 그런 현상이 일어나는지 알고싶네요..

버퍼-> 파일 기록할때
버퍼의 처음부터 무조건 기록하게 되서 그런건가요? 흐음..

VENI VIDI VICI

sangwoo의 이미지

Seven.. wrote:
꺄울.. 해결되었습니다.
fflush(stdout)..

처음 보는 함수인데;;

stdout의 버퍼(?) 같은곳을 비우는 함수인가요?

저거 써주는 위치에 따라서 결과가 좀 달라지기도 했거든요;;

왜 그런 현상이 일어나는지 알고싶네요..

버퍼-> 파일 기록할때
버퍼의 처음부터 무조건 기록하게 되서 그런건가요? 흐음..

파일에 printf를 비롯한 stdio 함수로 기록할 경우는, 기본적으로
Fully bufferred 입니다. 즉, 파일에 바로 기록되는 것이 아니고,
buffer에 데이터가 쌓여 있다가 한번에 기록하게 되죠.
버퍼가 비워지는 경우는 fflush()를 호출하거나, 버퍼 크기가 꽉 차거나,
(보통 4096바이트입니다.) 이 경우와 같이 프로세스가 종료되거나 하는
경우입니다.
또 하나, fork()를 하는 경우는 프로세스의 어드레스 스페이스가 복사되므로,
printf의 버퍼도 복사됩니다.
이제, 두번째 "Step 3 I am CHILD"를 출력하는 child의 경우를 봅시다.
fork()이전의 parent는, "Step 2..." 문자열을 2번 printf한 상태입니다. 즉 2줄의
"Step 2..." 문자열이 버퍼에 저장됩니다. 이때 fork() 를 하여 생성된 child역시
버퍼에 두 줄의 "Step 2..." 문자열이 있습니다. 따라서 exit() 전에 모든 버퍼가
flush되면서 모든 내용이 파일에 기록되는 것입니다.
그 다음도 버퍼에 내용이 추가된 채로 (즉, 올바르게 flush() 되지 않은 채로)
child가 fork() 되기 때문에, "Step 2..." 의 문자열이 하나씩 증가하게 되는 것입니다. ^^

----
Let's shut up and code.

최종호의 이미지

전에 맥락은 좀 다르지만 원인이 이 비슷한 질문이 있었던 것 같은데요

http//bbs.kldp.org/viewtopic.php?t=29452

질문하신 분이 follow-up 을 잘 안해주셔서 흐지부지 끝났던 것 같습니다.

여러분들이 지적하신대로 standard I/O 함수의 버퍼링 모드와

쉘에서 제공하는 리다이렉션, fork 의 특성으로 인해 일어나는 문제인데요,

(자세한 사항은 Stevens의 Advanced Programming

in the Unix Environment의 pp. 122-125와 p. 189 를 참조하세요.)

(아래 내용은 위의 책에서 따옵니다.) 버퍼링 모드는

1. fully buffered sangwoo님이 설명해 주셨고,

2. line buffered new line문자 ('\n')가 나올 때마다 I/O 수행

3. unbuffered buffering 안함

가 있습니다.

ANSI C 에서는

1. 표준입출력은 대화형장치(ex> 터미널) 이 아닌 경우에만 (if and only if)
fully buffered 모드이고,

2. 표준에러는 절대 fully buffered 되지 않는다

고 되어있습니다.

SVR 4와 4.3+BSD에서는 default로

1. 표준에러는 항상 unbuffered 모드이다.

2. 터미널 장치인 경우 다른 모든 스트림은 line buffered 모드를 사용한다.

그 외에는 fully buffered 모드를 사용한다.

입니다.

따라서 redirection을 하지 않은 상태에서는 printf()를 수행하면 이는 line buffered 모드로 수행됩니다.

이때 shell에서 표준출력을 redirection 해서 파일로 보낸다면 해당 스트림은

line buffered모드에서 fully buffered 모드로 변경됩니다.

그래서

printf("Step 1 Start\n");

를 수행해도 버퍼에만 들어있지 실제로 I/O가 수행되지 않은 상태이고요.

이 상태에서 fork()를 수행하면 아직 flush되지 않은 버퍼까지 복사되니까

fork가 일어나기 전에 수행된 printf 문의 출력(되어야하는)결과까지

파일에 적히게 되는거죠.

bugii 님은 stderr를 써서 버퍼링모드가 틀린 스트림을 쓰는 방안을 제시하셨고,

pynoos 님은 강제적으로 flush 시키는 방안을 제시하셨고요.

다른 분이 쓰신 내용을 포함하고 싶으시면

누구누구 wrote:

여기에 인용하고 싶은 내용

을 쓰시면 됩니다.

입력창 아래 옵션항목에 'BBCode 사용 불가' 가 꺼져 있어서 BBCode를

사용하실 수 있어야 하고요,

다른 분 올린 글의 제목행 오른쪽 맨 끝에 보면 인용 버튼이 있어서

인용하신 후 필요하신 부분만 편집하시는 식으로 하실 수도 있습니다.

누구누구 wrote:
<- 이건 손으로 직접 쳐도 되고,

입력창 윗쪽에 있는 Quote 버튼을 누르셔도 됩니다.

아래 링크를 참조하세요.

http//bbs.kldp.org/viewtopic.php?t=336

Seven..의 이미지

자세하고 명쾌한 설명 감사합니다.
오랫동안 머릿속에 헤메이던게
광명이 찾아왔네요:lol:
헤^^
감사합니다^^

또 좋은거 많이 배워가네요 항상^^ 배워가기만 하는군요^^

VENI VIDI VICI

yielding의 이미지

최종호님의 탁월한 답변에 한표 ^^;

Life rushes on, we are distracted

bugiii의 이미지

저도 최종호님에게 원츄~~~

최종호의 이미지

~
Stevens의 APUE 공부를 충실히 이행한 개발자라면 누구나 어려움없이

쉽게 답변할 수 있는 평이한 수준의 문항으로 질문이 출제..... 쿨럭...

.... 죄송합니다.... (__)
~

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.