9. 파이프 상명대학교소프트웨어학부
파이프 시그널은이상한사건이나오류를처리하는데는이용하지만, 한프로세스로부터다른프로세스로대량의정보를전송하는데는부적합하다. 파이프 한프로세스를다른관련된프로세스에연결시켜주는단방향의통신채널 2
pipe() Usage #include <unistd.h> int pipe(int filedes[2]); 3
< ex_1.c > #include <unistd.h> #include <stdio.h> #define MSGSIZE 16 char *msg1 = "hello, world #1"; char *msg2 = "hello, world #2"; char *msg3 = "hello, world #3"; main() { char inbuf[msgsize]; int p[2], j; /* 파이프를개방한다 */ if(pipe(p) == -1) { perror("pipe call"); exit(1); 4
< ex_1.c > /* 파이프에쓴다 */ write(p[1], msg1, MSGSIZE); write(p[1], msg2, MSGSIZE); write(p[1], msg3, MSGSIZE); /* 파이프로부터읽는다. */ for(j = 0; j < 3; j++) { read (p[0], inbuf, MSGSIZE); printf ("%s\n", inbuf); exit (0); 5
pipe() process write( ) p[1] read( ) p[0] < 첫번째파이프사용예 > 6
< ex_2.c > #include <unistd.h> #include <stdio.h> #include <sys/types.h> #define MSGSIZE 16 char *msg1 = "hello, world #1"; char *msg2 = "hello, world #2"; char *msg3 = "hello, world #3"; main() { char inbuf[msgsize]; int p[2], j; pid_t pid; /* 파이프를개방한다. */ if (pipe (p) == -1) { perror ("pipe call"); exit (1); 7
< ex_2.c > switch (pid = fork()){ case -1: perror ("fork call"); exit (2); case 0: write (p[1], msg1, MSGSIZE); write (p[1], msg2, MSGSIZE); write (p[1], msg3, MSGSIZE); break; default: for (j = 0; j < 3; j++) { read (p[0], inbuf, MSGSIZE); printf ("%s\n", inbuf); wait (NULL); exit (0); 8
pipe() child parent write( ) p[1] p[1] write( )) read( ) p[0] p[0] read( )) < 두번째파이프사용예 > 9
< ex_3.c > #include <unistd.h> #include <stdio.h> #include <sys/types.h> #define MSGSIZE 16 char *msg1 = "hello, world #1"; char *msg2 = "hello, world #2"; char *msg3 = "hello, world #3"; main() { char inbuf[msgsize]; int p[2], j; pid_t pid; /* 파이프를개방한다. */ if (pipe(p) == -1) { perror ("pipe call"); exit (1); 10
< ex_3.c > switch (pid = fork()){ case -1: perror ("fork call"); exit (2); case 0: close (p[0]); write (p[1], msg1, MSGSIZE); write (p[1], msg2, MSGSIZE); write (p[1], msg3, MSGSIZE); break; default: close (p[1]); for (j = 0; j < 3; j++) { read (p[0], inbuf, MSGSIZE); printf ("%s\n", inbuf); wait (NULL); exit (0); 11
pipe() child parent p[0] write( read( ) write( ) p[1] read( ) < 세번째파이프사용예 > 12
파이프의크기 파이프에들어있는자료가일정량을초과하면, 그후의 write 는봉쇄된다. 파이프의용량을초과할가능성이있는 write 가시도되면프로세스는다른프로세스에의하여자료가읽혀져파이프에충분한공간이마련될때까지수행이일시중단된다. 13
< ex_4.c > #include <signal.h> #include <unistd.h> #include <limits.h> int count; void alrm_action(int); main() { int p[2]; int pipe_size; char c = 'x'; static struct sigaction act; act.sa_handler = alrm_action; sigfillset (&(act.sa_mask)); /* 파이프를생성한다 */ if (pipe(p) == -1) { perror ("pipe call"); exit (1); 14
< ex_4.c > /* 파이프의크기를결정한다. */ pipe_size = fpathconf (p[0], _PC_PIPE_BUF); printf ("Maximum size of write to pipe: %d bytes\n", pipe_size); sigaction (SIGALRM, &act, NULL); while (1) { alarm (20); write(p[1], &c, 1); alarm(0); if ((++count % 1024) == 0) printf ("%d characters in pipe\n", count); void alrm_action (int signo){ printf ("write blocked after %d characters\n", count); exit (0); 15
파이프닫기 쓰기전용파일기술자를닫았을때 : 자료를쓰기위해해당파이프를개방한다른프로세스가존재하는경우에는아무일도일어나지않는다. 파이프에자료를쓰는프로세스가더이상없고, 파이프가비어있으면, 그파이프로부터자료를읽으려는프로세스는아무자료도읽을수가없다. 파이프로부터자료를읽기를기다리며잠들어있던프로세스를모두깨우고, 이들의 read() 호출은 0을반환한다. 따라서자료를읽는프로세스에게는보통파일의끝에도달한것과같은효과가발생한다. 16
파이프닫기 읽기전용파일기술자를닫았을때 : 자료를읽기위해해당파이프를개방한프로세스가아직남아있는경우에는아무일도발생하지않는다. 파이프로부터자료를읽어들이는프로세스가더이상없으면그파이프에자료를쓸수있기를기다리던모든프로세스는커널로부터 SIGPIPE 시그널을받는다. 이때시그널이포착되지않으면해당프로세스는종료한다. 시그널이포착되면인터럽트루틴이수행된후에 write() 는 -1을반환한다. 이후에그파이프에자료를쓰려고시도하는프로세스도역시 SIGPIPE 시그널을받는다. 17
봉쇄되지않는 read 와 write 파이프에대한 read, write는봉쇄될수있다. 하지만, 어떤파이프에서자료를얻을때까지여러개의파이프를차례로조사 (poll) 하고자할때는봉쇄되면안된다. 이에 fcntl() 을사용하여해결한다. 예 ) fcntl(filedec, F_SETFL, O_NONBLOCK) 18
< ex_5.c > #include <fcntl.h> #include <errno.h> #define MSGSIZE 6 int parent (int *); int child (int *); char *msg1 = "hello"; char *msg2 = "bye!!"; main() { int pfd[2]; /* 파이프를개방한다 */ if(pipe (pfd) == -1) printf ("pipe call"); /* p[0] 의 O_NONBLOCK 플래그를 1 로설정한다 */ if (fcntl (pfd[0], F_SETFL, O_NONBLOCK) == -1) printf ("fcntl call"); 19
< ex_5.c > switch(fork()){ case 0: /* 자식 */ child(pfd); default: /* 부모 */ parent (pfd); int child(int p[2]) { int count; close (p[0]); for (count= 0; count < 3; count++) { write (p[1], msg1, MSGSIZE); sleep(3); /* 마지막메시지를보낸다 */ write (p[1], msg2, MSGSIZE); exit (0); 20
< ex_5.c > int parent (int p[2]) { int nread; char buf[msgsize]; close (p[1]); for(;;) { switch (nread = read(p[0], buf, MSGSIZE)){ case -1: /* 파이프에아무것도없는지검사한다. */ if (errno == EAGAIN){ printf ("(pipe empty)\n"); sleep (1); break; else printf ("read call"); case 0: /* 파이프가닫혔음. */ printf ("End of conversation\n"); exit (0); default: printf ("MSG=%s\n", buf); 21
다수의파이프취급 child 1 parent write( ) p[1] p[0] write( read( ) child 2 write( ) q[1] q[0] read( )) 22
select() Usage #include <sys/time.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); 23
< ex_6.c > #include <sys/time.h> #include <sys/wait.h> #include <stdio.h> #define MSGSIZE 6 char *msg1 = "hello"; char *msg2 = "bye!!"; void parent(int [][]); int child(int []); main() { int pip[3] [2]; int i; for (i = 0; i < 3; i++) { if (pipe(pip[i]) == -1) printf ("pipe call"); switch (fork()){ case 0: child (pip[i]); parent (pip); exit (0); 24
< ex_6.c > /* 부모는세개의파이프에전부귀를기울이고있다. */ void parent(int p[3][2]) { char buf[msgsize], ch; fd_set set, master; int i; /* 모든원하지않는화일기술자를닫는다 */ for (i = 0; i < 3; i++) close (p[i] [1]); /* select 시스템호출의비트마스크를설정한다. */ FD_ZERO (&master); FD_SET (0, &master); for (i = 0; i <3; i++) FD_SET (p[i] [0], &master); 25
< ex_6.c > /* 타임아웃없이 select 를호출한다. 사건이발생할때까지 select 는봉쇄될것이다 */ while (set = master, select (p[2] [0]+1, &set, NULL, NULL, NULL) > 0) { /* 표준입력, 즉화일기술자 0 에있는정보를잊어버리면안됨. */ if (FD_ISSET(0, &set)){ printf ("From standard input..."); read (0, &ch, 1); printf ("%c\n", ch); for (i = 0; i < 3; i++){ if (FD_ISSET(p[i] [0], & set)){ if (read(p[i][0], buf[msgsize])>0) { printf ("Message from child%d\n", i); printf ("MSG=%s\n",buf); /* 서버는모든자식이죽으면주프로그램으로복귀한다. */ if (waitpid (-1, NULL,WNOHANG) == -1) return; 26
< ex_6.c > int child(int p [2]) { int count; close (p[0]); for (count = 0; count < 2; count++) { write (p[1], msg1, MSGSIZE); /* 임의의시간동안중지한다. */ sleep (getpid() % 4); /* 최종메시지를보낸다. */ write (p[1], msg2, MSGSIZE); exit (0); 27
파이프와 exec() 호출 parent wait( ) grandchild (command1) child (command2) p[0] write( read( ) write( ) p[1] read( ) 28
< ex_7.c > #include <stdio.h> int join (char *com1[], char *com2[]) { int p[2], status; switch (fork()){ case 0: /* 자식 */ break; default: /* 부모 */ wait(&status); return (status); if (pipe(p) == -1) printf ("pipe call in join"); 29
< ex_7.c > switch (fork()){ case 0: dup2 (p[1],1); /* 표준출력이파이프로가게한다. */ close (p[0]); /* 화일기술자를절약한다. */ close (p[1]); default: execvp (com1[0], com1); printf("1st execvp call in join"); dup2(p[0], 0); /* 표준입력이파이프로부터오게한다 */ close (p[0]); close (p[1]); execvp (com2[0], com2); printf ("2nd execvp call in join"); 30
< ex_7.c > main() { char *one[4] = {"ls", "-l", "/usr/lib", NULL; char *two[3] = {"grep", " d", NULL; int ret; ret = join (one, two); printf ("join returned %d\n", ret); exit (0); 31
FIFO 와이름형파이프 파이프의결점 부모와자식프로세스간에만사용할수있다. 파이프는영구히존재할수없다. FIFO 파이프의결점을보완하기위한파이프의변종 영구적이며, UNIX 파일이름을부여받는다. 32
mkfifo() Usage #include <sys/types.h> #include <sys/stat.h> int mkfifo(const chat *pathname, mode_t mode); 33
< sendmsg.c > #include <fcntl.h> #include <stdio.h> #include <errno.h> #define MSGSIZ 63 char *fifo = "fifo"; main (int argc, char **argv) { int fd, j, nwrite; char msgbuf[msgsiz+1]; if ((fd = open(fifo, O_WRONLY O_NONBLOCK)) < 0) printf ("fifo open failed"); for ( j = 1; j < argc; j++) { if (strlen(argv[j]) > MSGSIZ) { fprintf (stderr, "message too long %s\n", argv[j]); continue; strcpy (msgbuf, argv[j]); if ((nwrite = write (fd, msgbuf, MSGSIZ+1)) == -1) printf ("message write failed"); exit (0); 34
< receivemsg.c > #include <fcntl.h> #include <stdio.h> #include <errno.h> #define MSGSIZ 63 char *fifo = "fifo"; main (int argc, char **argv) { int fd; char msgbuf[msgsiz+1]; if (mkfifo(fifo, 0666) == -1) { if (errno!= EEXIST) printf ("receiver: mkfifo"); if ((fd = open(fifo, O_RDWR)) < 0) printf ("fifo open failed"); for(;;) { if (read(fd, msgbuf, MSGSIZ+1) <0) printf ("message read failed"); printf ("message received:%s\n", msgbuf); 35