1 12 장파이프
2 12.1 파이프
파이프원리 $ who sort 파이프 3 물을보내는수도파이프와비슷 한프로세스는쓰기용파일디스크립터를이용하여파이프에데이터를보내고 ( 쓰고 ) 다른프로세스는읽기용파일디스크립터를이용하여그파이프에서데이터를받는다 ( 읽는다 ). 한방향 (one way) 통신
파이프생성 파이프는두개의파일디스크립터를갖는다. 하나는쓰기용이고다른하나는읽기용이다. #include <unistd.h> int pipe(int fd[2]) 파이프를생성한다. 성공하면 0 을실패하면 -1 를리턴한다. 4
파이프사용법 (1) 한프로세스가파이프를생성한다. (2) 그프로세스가자식프로세스를생성한다. (3) 쓰는프로세스는읽기용파이프디스크립터를닫는다. 읽는프로세스는쓰기용파이프디스크립터를닫는다. (4) write() 와 read() 시스템호출을사용하여파이프를통해데이터를송수신한다. (5) 각프로세스가살아있는파이프디스크립터를닫는다. 5
파이프사용법 자식생성후 자식에서부모로보내기 6
pipe.c 1 #include <unistd.h> 2 #define MAXLINE 100 3 /* 파이프를통해자식에서부모로 4 데이터를보내는프로그램 */ 5 int main( ) 6 { 7 int n, length, fd[2]; 8 int pid; 9 char message[maxline], line[maxline]; 10 11 pipe(fd); /* 파이프생성 */ 12 13 if ((pid = fork()) == 0) { /* 자식프로세스 */ 14 close(fd[0]); 15 sprintf(message, "Hello from PID %d\n", getpid()); 16 length = strlen(message)+1; 17 write(fd[1], message, length); 18 else { /* 부모프로세스 */ 19 close(fd[1]); 20 n = read(fd[0], line, MAXLINE); 21 printf("[%d] %s", getpid(), line); 22 23 24 exit(0); 25 7
8 12.2 쉘파이프구현
표준출력을파이프로보내기 자식프로세스의표준출력을파이프를통해부모프로세스에게보내려면어떻게하여야할까? 쓰기용파이프디스크립터 fd[1] 을표준출력을나타내는 1 번파일디스크립터에복제 dup2(fd[1],1) 9
stdpipe.c 1 #include <stdio.h> 2 #include <unistd.h> 3 #define MAXLINE 100 4 5 /* 파이프를통해자식에서실행되는명령어출력을받아프린트 */ 6 int main(int argc, char* argv[]) 7 { 8 int n, pid, fd[2]; 9 char line[maxline]; 10 11 pipe(fd); /* 파이프생성 */ 12 10 13 if ((pid = fork()) == 0) { // 자식프로세스 14 close(fd[0]); 15 dup2(fd[1],1); 16 close(fd[1]); 17 printf("hello! pipe\n"); 18 printf("bye! pipe\n"); 19 else { // 부모프로세스 20 close(fd[1]); 21 printf(" 자식프로세스로부터받은결과 \n"); 22 while ((n = read(fd[0], line, MAXLINE))> 0) 23 write(stdout_fileno, line, n); 24 25 26 exit(0); 27
명령어표준출력을파이프로보내기 프로그램 pexec1.c는부모프로세스가자식프로세스에게 명령줄인수로받은명령어를실행하게하고 그표준출력을파이프를통해받아출력한다. 11
pexec1.c 1 #include <stdio.h> 2 #include <unistd.h> 3 #define MAXLINE 100 4 5 /* 파이프를통해자식에서실행되는명령어출력을받아프린트 */ 6 int main(int argc, char* argv[]) 7 { 8 int n, pid, fd[2]; 9 char line[maxline]; 10 11 pipe(fd); /* 파이프생성 */ 12 13 if ((pid = fork()) == 0) { // 자식프로세스 14 close(fd[0]); 15 dup2(fd[1],1); 16 close(fd[1]); 17 execvp(argv[1], &argv[1]); 18 else { // 부모프로세스 19 close(fd[1]); 20 printf(" 자식프로세스로부터받은결과 \n"); 21 while ((n = read(fd[0], line, MAXLINE))> 0) 22 write(stdout_fileno, line, n); 23 24 25 exit(0); 26 12
쉘파이프 쉘파이프기능 [shell] command1 command2 자식프로세스가실행하는 command1 의표준출력을파이프를통해서부모프로세스가실행하는 command2 의표준입력으로전달 13
shellpipe.c #include <stdio.h> #include <string.h> #include <unistd.h> #define READ 0 #define WRITE 1 int main(int argc, char* argv[]) { char str[1024]; char *command1, *command2; int fd[2]; printf("[shell]"); fgets(str,sizeof(str),stdin); str[strlen(str)-1] ='\0'; if(strchr(str,' ')!= NULL) { // 파이프사용하는경우 command1 = strtok (str," "); command2 = strtok (NULL, " "); 14
shellpipe.c 15 pipe(fd); if (fork() ==0) { close(fd[read]); dup2(fd[write],1); close(fd[write]); execlp(command1, command1, NULL); perror("pipe"); else { close(fd[write]); dup2(fd[read],0); close(fd[read]); // 쓰기용파이프를표준출력에복제 // 읽기용파이프를표준입력에복제 execlp(command2, command2, NULL); perror("pipe");
16 12.3 파이프함수
popen() 자식프로세스에게명령어를실행시키고그출력 ( 입력 ) 을파이프를통해받는과정을하나의함수로정의 #include <stdio.h> FILE *popen(const char *command, const char *type); 성공하면파이프를위한파일포인터를실패하면 NULL을리턴한다. int pclose(file *fp); 성공하면 command 명령어의종료상태를실패하면 -1을리턴한다. fp = popen(command, "r"); fp = popen(command, w"); 17
pexec2.c #include <stdio.h> #define MAXLINE 100 /* popen() 함수를이용해자식에서실행되는명령어출력을받아프린트 */ int main(int argc, char* argv[]) { char line[maxline]; FILE *fpin; if ((fpin = popen(argv[1],"r")) == NULL) { perror("popen 오류 "); return 1; printf(" 자식프로세스로부터받은결과 \n"); while (fgets(line, MAXLINE, fpin)) fputs(line, stdout); pclose(fpin); return 0; 18
19 12.4 이름있는파이프
이름있는파이프 (named pipe) ( 이름없는 ) 파이프 이름이없으므로부모자식과같은서로관련된프로세스사이의통신에만사용될수있었다. 이름있는파이프 다른파일처럼이름이있으며파일시스템내에존재한다. 서로관련없는프로세스들도공유하여사용할수있다. 20
이름있는파이프를만드는방법 p 옵션과함께 mknod 명령어 $mknod mypipe p $chmod ug+rw mypipe $ls -l mypipe prw-rw-r-- 1 chang faculty 0 4월 11일 13:03 mypipe mkfifo() 시스템호출 #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 이름있는파이프를생성한다. 성공하면 0을실패하면 -1을리턴한다. 21
npreader.c #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define MAXLINE 100 /* 이름있는파이프를통해읽은내용을프린트한다. */ int main( ) { int fd; char str[maxline]; unlink("mypipe"); mkfifo("mypipe", 0660); fd = open("mypipe", O_RDONLY); while (readline(fd, str)) printf("%s \n", str); close(fd); return 0; int readline(int fd, char *str) { int n; do { n = read(fd, str, 1); while (n > 0 && *str++!= NULL); return (n > 0); 22
npwriter.c #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define MAXLINE 100 /* 이름있는파이프를통해메시지를출력한다. */ int main( ) { int fd, length, i; char message[maxline]; sprintf(message, "Hello from PID %d", getpid()); length = strlen(message)+1; do { fd = open("mypipe", O_WRONLY); if (fd == -1) sleep(1); while (fd == -1); for (i = 0; i <= 3; i++) { write(fd, message, length); sleep(3); close(fd); return 0; 23
파이프를이용한일대일채팅 이프로그램은채팅서버와채팅클라이언트프로그램으로구성된다. 채팅서버에서채팅클라이언트로데이터를보내는데하나의파이프가필요하고 반대로채팅클라이언트에서채팅서버로데이터를보내는데또하나의파이프가필요하다. 24
chatserver.c #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define MAXLINE 256 main() { int fd1, fd2, n; char msg[maxline]; if (mkfifo("./chatfifo1", 0666) == -1) { perror("mkfifo"); exit(1); if (mkfifo("./chatfifo2", 0666) == -1) { perror("mkfifo"); exit(2); 25 fd1 = open("./chatfifo1", O_WRONLY); fd2 = open("./chatfifo2", O_RDONLY); if (fd1 == -1 fd2 == -1) { perror("open"); exit(3); printf("* 서버시작 \n"); while(1) { printf("[ 서버 ] : "); fgets(msg, MAXLINE, stdin); n = write(fd1, msg, strlen(msg)+1); if (n == -1) { perror("write"); exit(1); n = read(fd2, msg, MAXLINE); printf("[ 클라이언트 ] -> %s\n", msg);
chatclient.c #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define MAXLINE 256 main() { int fd1, fd2, n; char inmsg[maxline]; fd1 = open("./chatfifo1", O_RDONLY); fd2 = open("./chatfifo2", O_WRONLY); if(fd1 == -1 fd2 == -1) { perror("open"); exit(1); printf("* 클라이언트시작 \n"); while(1) { n = read(fd1, inmsg, MAXLINE); printf("[ 서버 ] -> %s\n", inmsg); printf("[ 클라이언트 ] : "); fgets(inmsg, MAXLINE, stdin); write(fd2, inmsg, strlen(inmsg)+1); 26
핵심개념 파이프는데이터를한방향으로보내는데사용된다. 파이프는두개의파일디스크립터를갖는다. 하나는쓰기용이고다른하나는읽기용이다. 이름있는파이프는파일처럼파일시스템내에존재하고이름이있으며서로관련없는프로세스들도공유하여사용할수있다. 27