- 두 프로세스 간에 통신 할 수 있도록 인터페이스 제공
- 일반적으로 이름 없는 파이프 (익명 파이프) - 부모자식 간 통신
- 기본적으로 단방향
- grep pipe test.c | more 명령도 파이프 이용한 명령
앞 부분의 표준 출력이 뒤 명령의 표준 입력으로 들어감
간단한 파이프 - popen, pclose (생성, 닫기)
쉘을 실행해야 해서 비효율적, 주고받을 수 있는 데이터도 제한적임
#incldue <stdio.h>
FILE *popen(const char *command, const char *mode);
int pclose(FILE *stream);
//쓰기 전용
int main(){
FILE *fp; //pipe의 file pointer
int a;
fp = popen("wc -l", "w"); //자식 process가 wc -l 명령 수행
for(a=0 ; a<100 ; a++){ //부모 process가 pipe에 100라인을 씀
fprintf(fp, "test line\n");
}
pclose(fp);
}
//결과 : 100
//읽기 전용
int main(){
FILE *fp;
char buf[256];
fp = popen("date","r"); //date 명령 실행 결과를 pipe에 저장
fgets(buf, sizeof(buf), fp); //date 명령 실행한 것 저장
printf("line: %s\n",buf);
pclose(fp);
}
- popen
- command : 쉘 명령
- mode : "r" 또는 "w", 각각 읽기 / 쓰기 전용
- 내부적으로 fork 함수를 실행해 자식 프로세스 만들고 command에서 지정한 명령을 exec 함수로 자식이 실행하도록 함
- pclose
- waitpid 함수를 수행해 자식 프로세스들이 종료하기를 기다림
- 자식 프로세스의 종료 상태가 리턴됨 (실패 시 -1 리턴)
pipe - 복잡한 파이프 생성 (이름 없는 파이프)
- kernel 공간에서 만들어짐
- 항상 combination 되어야 함
- write 시 파이프에 충분한 공간 있으면 파이프에 저장
- 공간 없으면 다른 프로세스에 의해 자료가 읽혀져 파이프에 충분한 공간 마련될 때까지 일시 중단됨
중단 안하고 싶으면 nonblock 하기 -> error code가 2로 return 됐을 때, 중단 안시키고 다른 일 시킬 수 O - 쓰기 전용 파일 기술자 닫았을 때
- 자료 쓰기 위해 해당 파이프 개방한 다른 프로세스 아직 존재하면 O
- 쓰기 프로세스 더이상 없으면 파이프로부터 자료 읽으려는 프로세스 깨우고 0 복귀
==파일의 끝 도달한 것 같은 효과
- 읽기 전용 파일 기술자 닫았을 때
- 자료 읽기 위해 해당 파이프 개방한 다른 프로세스 존재하면 O
- 없으면 자료 쓰기 기다리는 모든 프로세스는 커널로부터 SIGPIPE 시그널 받고 -1 복귀, 오류 발생함
#include <unistd.h>
int pipe(int fildes[2]);
int main(){
int fd[2];
pid_t pid;
char buf[257];
int len, status;
pipe(fd);
pid = fork();
if(pid == 0){
close(fd[1]); //쓰기용 파일 기술자 닫음 (자식은 읽을거라서)
write(1, "child process:", 15); //표준 출력
len = read(fd[0], buf, 256); //파이프에서 읽기
write(1, buf, len);
close(fd[0]);
}
else if(pid>0){
close(fd[0]); //읽기용 파일 기술자 닫음 (부모는 쓸 거라서)
buf[0] = '\0';
write(fd[1], "test message\n",14); //파이프에 텍스트 출력
close(fd[1]);
waitpid(pid, &status, 0);
}
}
- fildes[2] : 파이프로 사용할 파일 기술자 2개 (읽기, 쓰기)
- pipe 함수는 인자로 크기 2인 정수형 배열 받고 파일 기술자 2개 저장
- fildes[0]은 읽기 전용, fildes[1]은 쓰기 전용
- 생성에 성공하면 0, 실패하면 -1 리턴
pipe 함수 통신 과정
- pipe 함수 호출해 파이프로 사용할 파일기술자 생성 (fd[2])
- fork 함수로 자식 프로세스 생성, pipe도 자식 프로세스로 복사됨
- 통신 방향 결정 (파이프는 기본적으로 단방향임, 자식과 부모 각각에서 안쓰는거 닫아주기)
- 파이프 쓰기 부분 닫혀 있는데 읽으려고 함 -> 0 이나 EOF 리턴
- 파이프 읽기 부분 닫혀 있는데 쓰려고 함 -> SIGPIPE 시그널 발생
ps -ef | grep telnet 구현
int main(){
int fd[2];
pid_t pid;
pipe(fd);
pid = fork();
if(pid == 0){
close(fd[1]);
if(fd[0] != 0){
dup2(fd[0], 0);
close(fd[0]);
}
execlp("grep", "grep", "telnet", (char *)NULL);
exit(1);
}
else if(pid > 0){
close(fd[0]);
if(fd[1] != 1){
dup2(fd[1], 1);
close(fd[1]);
}
execlp("ps","ps", "-ef", (char *)NULL);
wait(NULL);
}
}
봉쇄되지 않는 read/write
- if(fcntl(filedes, F_SETFL, O_NONBLOCK) == -1) perror
- 무작정 블락하지 않고 올 때까지 다른일 함
- filedes가 파이프에 대한 쓰기 전용 파일기술자면 파이프에 대한 write는 파이프가 가득 차 있더라도 봉쇄 X
대신 write는 -1로 복귀, errno를 EAGAIN으로 지정함 - 파이프에 대한 읽기 전용 파일 기술자면 즉시 -1로 복귀, Errno는 EAGAIN으로 지정 (상대 process 생성 X 경우)
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define MSGSIZE 6
int parent(int *);
int child (int *);
char *msg1 = "hello";
char *msg2 = "bye!!";
main() {
int pfd[2];
pipe(pfd);
if (fcntl(pfd[0], F_SETFL, O_NONBLOCK) == -1) perror ("fcntl call");
switch (fork()) {
case -1 : perror("fork call");
case 0 : child(pfd);
default : parent(pfd);
}
}
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;
}
case 0:
printf("End of conversation\n");
exit(0);
default:
printf("MSG = %s\n", buf);
}
}
}
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);
}