[42서울 / pipex] 리다이렉션과 파이프

Hans Park·2022년 1월 15일
2

42서울 본과정

목록 보기
12/15
post-thumbnail

seunpark's 42Project Score

🚀 pipex


🚀 개발 전 기록사항

파이프

  • 파이프 구현은 알 것 같음
    • 부모에서 쓰기로 보내고, 자식에서 읽기로 받아다가 수행
    • 부모는 쓰기 후 자식이 끝날때까지 대기
    • 보너스에선 어케해야하지....?
  • 파이프 안쓰는 한쪽은 막던데, 어차피 close할거 미리하는건지, 다른 이유가 있는건지
    ⇒먼저막은거임 ⇒ 막아야 사용가능
  • 파일을 열어 읽은 내용을 출력 시 dup2를 이용해 표준출력으로 내보내고, 자식에서 표준입력으로 받아서 처리?

execve

  • 명령어의 path는?
  • c언어 main 매개변수인 envp에서 path를 찾아 :을 스플릿하고 전부 확인해봐야하겠음

🚀 고려사항


코드는 짧은 것을 알고 있으나 많은 이론 지식을 알고 있어야 한다.
예외처리 및 어떤 문제 발생 시 왜 일어나고 어떻게 해결하는지 defense하는게 관건으로 생각한다.

🖋 문제해결순서

이번에는 따로 코드설명을 넣지 않으려 합니다.

  1. 메인 매개변수 개수 예외처리 체크
  2. pipe 함수 예외처리 체크
  3. fork 예외처리
  4. pid값을 통한 부모자식 분기
    1. 부모프로세스에 waitpid를 적용하여, 자식프로세스가 끝나야 작동되도록
  5. 메인 매개변수 인덱스 1, 2를 활용하여 아래와 같이 실행
    1. 파일 만들기
    2. 표준입출력 fd 변경
    3. 명령어 실행
  6. 메인 매개변수 인덱스 3, 4를 활용하여 아래와 같이 실행
    1. 파일 만들기 (권한도 같이 부여)
    2. 표준 입출력 fd 변경
    3. 명령어 실행

🚀 이론

🖋 리다이렉션(Redirection)

리다이렉션(Redirection)

리눅스에서 프로그램은 보통 세 개의 파일 서술자를 열게 됨.

  • 표준입력(STDIN)
  • 표준출력(STDOUT)
  • 표준에러(STDERR)

1)cmd > file_name

  • cmd에서 출력하는 것을 file_name에 덮어쓰기 저장
  • 아래의 경우에는 실행되지 않음.
    왜냐하면 stdout이 아닌 stderr로 출력된 내용이기 때문.

💡 위 명령어의 경우
ls -l 10 > tt.txtls -l 10 1> tt.txt와 같은 동작을 한다. 즉, fd 1의 값인 stdout을 저장한다는 뜻이다.
ls -l 10 2> tt.txt로 에러문구를 저장할 수 있다.

2) cmd >> file_name

cmd에서 출력하는 것을 file_name의 마지막에 추가하여 저장

3) cmd < file_name

file_name의 파일 내용을 표준입력으로 사용한다는 의미.

💡 ls 명령어같이 표준입력을 받지 않는 경우 argv(argument vector)를 통해 사용자 입력을 받고 있음.

🖋 파이프(|)

cmd1 | cmd2

cmd1의 표준출력cmd2의 표준입력으로 받는다.

🖋 허용함수

💡 42를 통해 사용해본 함수는 예제를 넣지 않음.

access

  • 헤더 : unistd.h
  • 매개변수 :
    • const char *pathname : 체크하고자 할 디렉토리 또는 파일명
    • int mode : 마스크 값을 통해 파일 존재 여부 및 권한 여부 확인
      • R_OK : 파일 존재여부, 읽기 권한 여부
      • W_OK: 파일 존재여부, 쓰기 권한 여부
      • X_OK : 파일 존재여부, 실행 권한여부
      • F_OK : 파일 존재여부
  • 반환값 : 성공시 0, 실패시 -1
int main(void)
{
	char *path = "./test.txt";

	if (0 == access(path, F_OK))
	{
		write(1, "@\n", 2);
	}
	else
	{
		write(1, "$\n", 2);
	}
	return (0);
}

open

  • 헤더 : fcntl.h
  • 매개변수 :
    • const char *filename
    • int flags
      • O_RDONLY : 읽기전용
      • O_WRONLY : 쓰기전용
      • O_RDWR : 읽고쓰기
  • 반환값 : 성공 시 양의 정수인 파일 디스크립터, 실패 시 -1
  • 헤더 : unistd.h
  • 매개변수 :
    • const char *pathname
  • 반환값 : 성공 시 0, 실패 시 -1
  • 설명:
    시스템 호출을 사용하여, 파일에 대한 디렉토리 항목을 지우고 링크 개수를 감소시킴.
    즉, 하드링크의 이름을 삭제하고 하드링크 카운트를 1 감소한다.

rm 프로그램이 이 호출을 사용한다.

💡 하드링크, 심볼릭 링크
[리눅스, 유닉스] 파일링크 ln 명령어- 심볼릭 링크(소프트링크)와 하드링크 원리, I-node 아이노드

C언어 링크 삭제 함수 unlink()

close

  • 헤더 : unistd.h
  • 매개변수 :
    • int close
  • 반환값 : 성공 시 0, 실패 시 -1
  • 설명:
    open함수로 연 파일을 사용중지한다.

read

  • 헤더 : unistd.h
  • 매개변수 :
    • int fd : 파일 디스크립터
    • void *buf : 파일을 읽어드릴 버퍼
    • size_t nbytes : 버퍼크기
  • 반환값 : 성공 시 읽은 바이트 수, 실패 시 -1

💡 사전 조사라 모르는 내용이 대부분이지만, 우선 대강의 내용을 알고 과제를 진행하면서 숙달해보자

fork

  • 헤더 : sys/wait.h
  • 매개변수 :
    • int *wstate : 종료상태를 알 수 있고, NULL 전달 가능
  • 반환값 : 부모 프로세스는 자식프로세스의 PID, 자식 프로세스에겐 0 반환
  • 설명:
    해당 c언어의 프로세스가 두개 생성된다 볼 수 있음.

fork 함수 사용하여 프로세스 생성
<설명 굳>

wait

  • 헤더 : unistd.h
  • 매개변수 :
  • 반환값 : 성공 시 종료된 자식 프로세스 id, 실패 시 -1
  • 설명:
    fork함수를 호출하는 프로세스는 부모 프로세스가 되고, 새롭게 생성되는 프로세스는 자식 프로세스가 됨.
    fork함수에 의해 생성된 자식 프로세스는 부모 프로세스의 메모리를 그대로 복사하여 가짐.
    fork함수 시점부터, 자식 프로세스는 fork함수의 아래 코드가 실행된다 보면 됨.
    부모의 버퍼까지 복사함

리눅스 wait 함수 : 자식 프로세스가 종료될 때 까지 기다린다.
[리눅스] 프로세스 생성과 특징, 종료 (fork, wait), 예제 코드

<고아프로세스 설명있음>

waitpid

  • 헤더 : sys/wait.h
  • 매개변수 :
    • pid_t pid : 감시할 자식 프로세스 id
      • -1 : 여러 자식 중 하나라도 종료되면 복귀
      • 0 : 현재 프로세스의 그룹 ID와 같은 그룹의 자식 프로세스가 종료되면 복귀
      • 양수 : pid에 해당하는 자식 프로세스가 종료되면 복귀
    • int *status : 자식 프로세스의 종료 상태 정보
    • int options : 대기를 위한 옵션
      • WNOHANG : 자식 프로세스가 종료되었는지 실행 중인지만 확인하고 바로 복귀. 부모프로세스는 block되지 않음
      • 0 : 자식 프로세스가 종료될때까지 block됨.
  • 반환값 : 성공 시 종료된 자식 프로세스 id, 실패 시 -1
  • 설명:
    자식 프로세스가 종료될 때까지 대기.

pipe

  • 헤더 : unistd.h
  • 매개변수 :
    • int fd[2] : fd가 2개임
  • 반환값 : 성공적으로 호출 시 0, 실패 시 -1
  • 설명:
    프로세스 간 통신을 할 때 사용하는 커뮤니케이션의 방식.
    - 하나의 파이프 및 파이프에 대한 두 개의 파일 디스크립터가 생성
    - 하나의 파이프를 프로세스들이 공유

[리눅스] 파이프(pipe) 개념과 예제
[Programming/C] pipe() 함수
<부모자식간 표준입출력 방법>

dup

  • 헤더 : unistd.h
  • 매개변수 :
    • int fd : fd로 전달받은 파일디스크립트를 복제하여 반환
  • 반환값 : 성공 시 파일 디스크립트 중 가장 낮은 숫자, 실패 시 -1
  • 설명:

dup2

  • 헤더 : unistd.h
  • 매개변수 :
    • int fd : fd로 전달받은 파일디스크립트를 복제하여 반환
    • int fd2 : 새 디스크립트의 값을 fd2로 지정하고, 만약 fd2가 열려있다면, 닫은 후 복제가 됨.
  • 반환값 : 성공 시 새 파일 디스크립트, 실패 시 -1
  • 설명:

[리눅스] dup, dup2 설명 및 쉬운 사용법, 사용 예제(그림 포함)

execve

  • 헤더 : unistd.h
  • 매개변수 :
    • const char *path : 전체 파일 명
    • const char *arg : 인수 목록
    • char * const envp[] : 환경설정목록
  • 반환값 : 실패 시 -1
  • 설명:
    첫번째는 명령어를 넣는다. 다만 앞에 /bin/@@형식의 path가 있어야 하는 것 같다.
    두번째는 인자를 넣는다. main에서 넘어온 인수 배열에서 앞(실행파일)에만 빼고 넣으면 될거같다. 끝에 NULL 넣기
    세번째는 main에서 받아온 envp 그대로
#include <unistd.h>
#include <stdio.h>

int main(int argc, char * const *argv, char **envp)
{
	char *arr[] = {"ls", "-al", NULL};
	int returnv = execve("/bin/ls", arr, envp);
	printf("value = %d\n", returnv);
}

perror

  • 헤더 : stdio.h
  • 매개변수 :
    • const char *str : 시스템 오류 메세지 다음에 이어 출력할 사용자 정의 메세지
  • 반환값 :
  • 설명: 전역변수 errno의 값을 해석하여 이에 해당하는 오류메세지를 표준 오류 출력 스트림에 출력함

strerror

  • 헤더 : cstring.h
  • 매개변수 :
    • int errnum : 오류번호, 이 값을 통해 발생하였떤 오류에 알맞는 오류 메세지를 가리키는 포인터를 리턴시킴
  • 반환값 : 위 설명
  • 설명:

exit

  • 헤더 : stdlib.h
  • 매개변수 :
    • int status : 에러코드
  • 반환값 :
  • 설명:
    현재의 c프로그램 자체를 완전 종료, 모든 열려진 파일 자동으로 닫음, 출력버퍼 속 데이터는 모두 쓰기 완료, 정상 종료 시 exit(0), 에러시 대채로 1

유의사항

pipe함수

pipe에서 안쓰는 fd를 닫아주지 않으면 쓰기가 일어날 수 있다고 판단하여 EOF를 반환하지 않고, 프로세스를 종료하지 않음.
What happens if a child process won't close the pipe from writing, while reading?

execve함수

해당 함수를 사용하면 프로세스의 바이너리 파일이 바뀌게 되어 아무리 아래에 프로그래밍하여도 작동하지 않는다.

free를 하지 않은 공간이 있을 경우에도 프로세스를 종료할 때, 할당한 메모리를 OS에서 자동으로 해제시켜준다.

다만, malloc한 공간의 주소를 잃어버릴 경우 결과 상관없이 leaks를 조심하여햐 한다.

출처

[리눅스] 재지정, 리다이렉션(redirection: >, <)과 파이프(|) 개념과 쉬운 설명
<리다이렉션>

IPC - 03. pipe() 를 통한 프로세스 통신
<pipe와 fd[2]를 잘 설명한 사이트>

[리눅스] dup, dup2 설명 및 쉬운 사용법, 사용 예제(그림 포함)
<dup2 함수 설명>

리눅스 waitpid 함수 : 자식 프로세스를 기다린다.
<wait와 프로세스 등을 자세히 정리한 블로그>

6.2.2 Creating Pipes in C
<pipe와 dup에 관한 자세한 설명>

What happens if a child process won't close the pipe from writing, while reading?
<왜 안쓰는 fd는 close 해야 하는지>

profile
장안동 개발새발

2개의 댓글

comment-user-thumbnail
2022년 1월 15일

와우~ 과제를 하며 공부한 내용이 총망라돼있네요.
과제 처음 하는 사람들에게 도움도 되고, 나중에 복습할 때도 좋을 것 같습니다.
나중에 저도 과제 정리할 때 많은 참고가 될 것 같네요. 잘 봤습니다!👍

답글 달기
comment-user-thumbnail
2022년 1월 16일

곧 pipex해야 해서 자료 찾고 있었는데, 좋은 곳에 쓰겠습니다 감사합니다 🙌🏻

답글 달기