Pipes: Pipe programming secrets
Angrave System Programming/Inter-process Communication & Scheduling 2019. 3. 7. 12:32Pipe Gotchas
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> int main() { int fd[2]; pipe(fd); // You must read from fd[0] and write from fd[1] printf("Reading from %d, writing to %d\n", fd[0], fd[1]); pid_t p = fork(); if (p > 0) { /* I have a child therefore I am the parent*/ write(fd[1],"Hi Child!",9); /*don't forget your child*/ wait(NULL); } else { char buf; int bytesread; // read one byte at a time. while ((bytesread = read(fd[0], &buf, 1)) > 0) { putchar(buf); } } return 0; }
부모는 파이프로 H,i,(space),C....등의 바이트를 보냅니다(파이프가 가득 차면 block됩니다). child는 파이프로부터 한 번에 한 바이트씩 읽기 시작합니다. 위의 경우에서 child process는 하나의 character를 읽고 출력할 것입니다. 하지만 while loop를 벗어날 방법이 없습니다! 더 이상 읽을 바이트가 남아있지 않을 때 단순히 block되어 다음을 기다립니다.
putchar 호출은 character를 쓰지만 stdout buffer를 flush하지 않습니다. 예를 들어 하나의 프로세스에서 다른 프로세스로 전송했을 때 아직 출력되지 않습니다. 메세지를 보기 위해서는 fflush(stdout)같이 버퍼를 비워주어야 (또는 printf("\n")을 호출) 합니다. 더 좋은 방법은 아래와 같이 마지막 메세지 마커를 체크해 루프를 벗어나는 것입니다.
while ((bytesread = read(fd[0], &buf, 1)) > 0) { putchar(buf); if (buf == '!') break; /* End of message */ }
이렇게 한다면 메세지는 child process가 종료될 때 버퍼가 비워집니다.
Want to use pipes with printf and scanf? Use fdopen!
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { char *name = "Fred"; int score = 123; int filedes = open("mydata.txt", "w", O_CREAT, S_IWUSR | S_IRUSR); FILE *f = fdopen(filedes, "w"); fprintf(f, "Name:%s Score:%d\n", name, score); fclose(f);
파일에 쓰기 위해서 이렇게 하는 것은 불필요합니다. pipe를 통해 이미 file descriptor를 가지고 있으므로 file descriptor를 사용하는 fopen을 사용하면 됩니다. fdopen을 사용하면 file descriptor를 file pointer로 변환할 수 있습니다.
아래는 파이프를 사용해 거의 동작하는 예시입니다. 에러를 찾을 수 있을까요?(힌트: 부모는 아무것도 출력하지 않습니다!)
#include <unistd.h> #include <stdlib.h> #include <stdio.h> int main() { int fh[2]; pipe(fh); FILE *reader = fdopen(fh[0], "r"); FILE *writer = fdopen(fh[1], "w"); pid_t p = fork(); if (p > 0) { int score; fscanf(reader, "Score %d", &score); printf("The child says the score is %d\n", score); } else { fprintf(writer, "Score %d", 10 + 10); fflush(writer); } return 0; }
자식과 부모 모두 종료되면 파이프 리소스가 사라진다는 것에 주목합시다. 위의 예에서 child는 byte를 전송하고 parent는 파이프에서 받습니다. 하지만, 줄이 끝나는 character가 보내지지 않으므로 fscanf는 계속 다음 바이트를 요구할 것입니다. 이러한 문제를 보장하기 위해서 아래와 같이 new line을 삽입해 주면 해결됩니다.
change: fprintf(writer, "Score %d", 10 + 10); to: fprintf(writer, "Score %d\n", 10 + 10);
So do we need to fflush too?
When do I need two pipes?
Closing pipes gotchas
If all file descriptors referring to the read end of a pipe have been closed,
then a write(2) will cause a SIGPIPE signal to be generated for the calling process.
오직 writer만(독자가 아닌) 이 신호를 사용할 수 있습니다. writer가 파이프를 닫는다고 reader에게 알려주기 위해서는 특별한 바이트를 쓰거나 "Bye"라는 메세지를 써야합니다.
아래의 예는 동작하지 않는 signal catching의 예입니다. 왜 동작하지 않을까요?
#include <stdio.h> #include <stdio.h> #include <unistd.h> #include <signal.h> void no_one_listening(int signal) { write(1, "No one is listening!\n", 21); } int main() { signal(SIGPIPE, no_one_listening); int filedes[2]; pipe(filedes); pid_t child = fork(); if (child > 0) { /* I must be the parent. Close the listening end of the pipe */ /* I'm not listening anymore!*/ close(filedes[0]); } else { /* Child writes messages to the pipe */ write(filedes[1], "One", 3); sleep(2); // Will this write generate SIGPIPE ? write(filedes[1], "Two", 3); write(1, "Done\n", 5); } return 0; }
위 코드에서 실수는 파이프에 여전히 reader가 있다는 것입니다. child는 여전히 첫 번째 file descriptor를 열어 상세를 기억하고 있습니다. 모든 reader는 closed되어야합니다.
fork를 할 때 일반적으로 사용하지 않는 각각의 파이프를 close합니다. 예를 들어 parent가 reading end를 닫고 child가 writing end를 닫습니다. (2개의 파이프가 있다면 반대로 똑같이 해줍니다.)
What is filling up the pipe? What happens when the pipe becomes full?
Are pipes process safe?
The lifetime of pipes
'Angrave System Programming > Inter-process Communication & Scheduling' 카테고리의 다른 글
Files: Working with files (0) | 2019.03.13 |
---|---|
Pipes: Pipe programming secrets (2) (0) | 2019.03.08 |
Pipes : Introduction to pipes (0) | 2019.03.07 |
Virtual Memory: Introduction to Virtual Memory(2) (0) | 2019.03.06 |
Virtual Memory: Introduction to Virtual Memory (2) | 2019.02.11 |