일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- gitlab
- Unity
- jupyter lab
- c# 윈폼
- C# delegate
- Flutter
- Data Structure
- 플러터
- c언어
- docker
- HTML
- 포인터
- 다트 언어
- vim
- Python
- C++
- Houdini
- 유니티
- c#
- c# 추상 클래스
- git
- dart 언어
- jupyter
- c# winform
- Algorithm
- 도커
- 깃
- C언어 포인터
- github
- 구조체
- Today
- Total
nomad-programmer
[Programming/C] pipe() 함수 본문
자식 프로세스가 생성하는 데이터를 실시간으로 읽는 방법
pipe() 함수는 데이터 스트림 두 개를 연다.
# |
데이터 스트림 |
0 |
표준 입력 |
1 |
표준 출력 |
2 |
표준 에러 |
3 |
파이프의 읽는 쪽 (ex: fd[0]) |
4 |
파이프의 쓰는 쪽 (ex: fd[1]) |
자식 프로세스가 부모 프로세스에 데이터를 보내야 하므로, 자식 프로세스의 표준 출력과 부모 프로세스의 표준 입력에 연결된 파이프가 필요하다. 파이프는 pipe() 함수로 생성한다.
이 함수는 연결된 두 스트림을 만들어 테이블에 추가한다. 한쪽 스트림에 쓴 데이터는 다른 쪽 스트림에서 바로 읽을 수 있다.
pipe() 함수가 디스크립터 테이블에 두 항목을 만들 때, 디스크립터들을 항목이 두 개 있는 배열에 저장한다.
// 디스크립터들이 배열에 저장됨
int fd[2];
if (pipe(fd) == -1) {
fprintf(stderr, "error");
}
pipe() 함수는 파이프를 생성하고 만들어진 디스크립터를 알려준다. fd[1]은 파이프에 쓰는 디스크립터이며, fd[0]은 파이프로부터 읽는 디스크립터이다. 일단 디스크립터를 얻으면 부모와 자식 프로세스가 사용할 수 있다.
자식 프로세스의 데이터를 부모 프로세스에 넘긴다고 가정하고 예를 들자면...
자식 프로세스에서는 파이프의 읽는 쪽인 fd[0]을 닫고, 표준 출력이 fd[1] 디스크립터가 가리키는 스트림을 가리키게 변경한다.
// 자식 프로세스는 파이프로부터 읽지 않기 때문에 파이프의 읽는 쪽을 닫는다.
// 자식 프로세스는 표준 출력을 파이프의 쓰는 쪽으로 연결한다.
close(fd[0]);
dup2(fd[1], 1);
이렇게하면 자식 프로세스가 표준 출력으로 보내는 모든 데이터가 파이프에 쓰여진다.
부모 프로세스에서는 쓰지 않기 때문에 파이프의 쓰는 쪽인 fd[1]을 닫고 프로세스의 표준 입력을 디스크립터 fd[0]이 가리키는 스트림으로 리다이렉션한다.
// 부모 프로세스는 표준 입력을 파이프의 읽는 쪽으로 리다이렉션한다.
dup2(fd[0], 0);
close(fd[1]);
이제 자식 프로세스가 쓰는 모든 데이터를 부모 프로세스의 표준 입력을 통해 읽을 수 있다.
pipe() 함수 예제
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[]) {
// 파이프를 생성한 후 디스크립터를 fd[0], fd[1]에 저장
int fd[2];
if (pipe(fd) == -1) {
fprintf(stderr, "pipe error: %s\n", strerror(errno));
return 1;
}
pid_t pid = fork();
if (pid == -1) {
fprintf(stderr, "fork error: %s\n", strerror(errno));
return 1;
}
// 자식 프로세서가 처리하는 코드
if (pid == 0) {
// 표준 출력을 파이프의 쓰는 쪽으로 설정
dup2(fd[1], 1);
// 자식 프로세스는 파이프에서 읽지 않으므로 읽는 쪽을 닫는다.
close(fd[0]);
int ste = execlp("ps", "ps", "aux", NULL);
if (ste == -1) {
fprintf(stderr, "run error: %s\n", strerror(errno));
return 1;
}
}
// 부모 프로세스만 여기에 도달한다.
// 표준 입력을 파이프의 읽는 쪽으로 리다이렉션한다.
dup2(fd[0], 0);
// 부모 프로세스는 파이프에 쓰지 않으므로 파이프의 쓰는 쪽을 닫는다.
close(fd[1]);
char line[255];
// 표준 입력이 파이프에 연결되어 있으므로 표준 입력에서 읽는다.
// fd[0]을 사용해도 된다.
while (fgets(line, sizeof(line), stdin) != 0) {
printf("%s", line);
}
return 0;
}
// 결과
/*
ps aux 명령의 결과가 출력된다.
*/
파이프는 프로세스를 연결하는 훌륭한 방법이다. 프로세스를 실행하고 환경을 제어할 뿐만 아니라 출력을 가져올 수 있다.
정리
부모와 자식 프로세스는 파이프로 통신한다.
pipe() 함수는 파이프와 2개의 디스크립터를 생성한다.
2개의 디스크립터는 파이프의 읽는 쪽과 쓰는 쪽이다.
표준 입력과 출력을 파이프로 리다이렉션할 수 있다.
부모와 자식 프로세서는 서로 파이프의 반대쪽을 사용한다.
파일에 기반한 파이프는 이름을 갖고 있다. (mkfifo())
파이프는 언제나 한 방향으로만 작동한다. 그러나 파이프를 두 개 만들어 하나는 부모가 자식에게, 다른 하나는 자식이 부모에게 데이터를 보낼 수 있다.
'Programming > C' 카테고리의 다른 글
[Programming/C] 소켓과 서버 (0) | 2020.06.20 |
---|---|
[Programming/C] 시그널 (Signal) (0) | 2020.06.20 |
[Programming/C] 입출력의 리다이렉션 (0) | 2020.06.19 |
[Programming/C] fork() 함수 (0) | 2020.06.19 |
[Programming/C] errno.h 헤더 파일 (0) | 2020.06.19 |