전체적인 목표
- argument로 4개의 인자를 받음(file1 cmd1 cmd2 file2)
- 해당 argument의 동작방식이 쉘에서 ./pipex < file1 cmd1 | cmd2 > file2 와 동일하게 동작
- 의미는 pipex프로그램에서 file1이 프로그램의 표준입력이 되어서 cmd1을 적용을 시킨 것을 파이프(|)로 이어받아서 cmd2를 적용시킨 후, file2에 저장을 하면 되는 흐름
- 주요내용
-> PIPE란?
-> 프로세스(부모, 자식, 고아)에 대한 이해
-> fork함수
-> pipe함수
-> execve함수
-> 전체적인 작성 개요
함수 원형
pid_t fork(void)
성공 시 -> 부모 프로세스에서는 자식 프로세스의 PID값을 반환
-> 자식 프로세스에서는 0 값을 반환
실패 시 -> 음수 값(-1) 반환
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t pid;
int x;
x = 0;
pid = fork();
if(pid > 0)
{ // 부모 코드
x = 1;
printf("부모 PID : %ld, x : %d , pid : %d\n",(long)getpid(), x, pid);
}
else if(pid == 0)
{ // 자식 코드
x = 2;
printf("자식 PID : %ld, x : %d\n",(long)getpid(), x);
}
else
{ // fork 실패
printf("fork Fail! \n");
return -1;
}
return 0
}

함수 원형
int pipe(int fd[2])
-> fd[1]: 읽기, fd[0]: 쓰기
-> fd[2]가 할당되어 초기화 되어있지 않더라도 pipe함수에서 자동으로 할당 및 초기화가 이루어짐
성공 시 -> 0반환
실패 시 -> -1반환
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX_BUF 1024
#define READ 0
#define WRITE 1
int main(){
int fd[2];
pid_t pid;
char buf[MAX_BUF];
if(pipe(fd) < 0){
printf("pipe error\n");
exit(1);
}
if((pid=fork())<0){
printf("fork error\n");
exit(1);
}
printf("\n");
if(pid>0){ //parent process
close(fd[READ]);
strcpy(buf,"message from parent\n");
write(fd[WRITE],buf,strlen(buf));
}else{ //child process
close(fd[WRITE]);
read(fd[READ],buf,MAX_BUF);
printf("child got message : %s\n",buf);
}
exit(0);
}

함수 원형
int dup2(int old_fd, int new_fd)
-> old_fd에 new_fd를 복사하는것으로, new_fd를 닫고, 해당 new_fd를 불러내면 old_fd가 불러와지는 기능
-> 성공 시 new_fd반환
-> 실패 시 -1반환
#include <unistd.h>
int main()
{
int fd1 = 5;
int fd2 = 1;
write(fd1, "error\n", 6);
printf("%d\n", fd1);
dup2(fd2, fd1);
write(fd1, "hello\n", 6);
printf("%d\n", fd1);
}

int execve(const char *pathname, char *const argv[], char *const envp[]);
-> 성공 시 pathname에 해당하는 프로그램을 실행, 해당 프로세스 종료
-> 실패 시, -1반환
-> pathname: 실행할 프로그램의 경로와 이름
-> argv: 프로그램에 전달할 커맨드 라인 인수들을 담고 있는 문자열 배열
-> envp: 프로그램에 전달할 환경 변수들을 담고 있는 문자열 배열
#include <stdio.h>
#include <unistd.h>
int main() {
char *args[] = {"/bin/echo", "hello_world", NULL };
char *env[] = {"$USER"};
execve("/bin/echo", args, env);
// execve 함수가 성공하면 이 아래 코드는 실행되지 않습니다.
printf("This line will not be printed.\n");
return 0;
}

보너스의 경우 처리할 케이스가 다중 cmd 및 Shell의 heredoc기능
다중 cmd의 경우, 처음에는 Mandetory파트와 동일하지만, 2번째 cmd부터 부모로부터 읽어와서 다시 exec함수를 통해 cmd를 반복해서 처리
2-1. 마지막은 Mandetory파트와 동일하게 STDOUT에 있는 내용을 outputfile에 쓰기작업 실행
Shell의 heredoc기능은 LIMITER로 지정된 문자열이 STDIN에 읽히기 전까지 STDIN에서 내용을 계속 읽어옴
3-1. cmd << LIMITER | cmd2 >> outputfile 의 형식을 구현해야함
3-2. STDIN이 read로 열려있는 경우, 대기상태로 입력을 계속 받아 이를 임시파일에 저장
3-3. Mandetory파트의 경우를 사용해서 임시파일을 저장 및 읽기를 통해서 cmd2를 실행시킨 결과를 outputfile에 전송
3-4. 생성한 임시파일을 unlink()함수를 통해서 임시파일을 삭제하면 heredoc기능 종료