42Seoul - pipex

devicii·2022년 4월 4일
1
post-thumbnail

선행지식

1. 프로세스와 스레드?

- 프로세스

OS로부터 자원을 할당받은 작업의 단위

우리가 windonw의 .exe나 mac의 .dmg 확장자를 프로그램이라고 부른다.
프로그램의 특징은 아래와 같다.

  • 메모리에 올라가 있지 않다.
  • 정적이다. 즉 실행되지 않고 가만히 있다.

이제 우리가 위와 같은 특징을 가진 .exe(프로그램)을 클릭해서 실행한다면 실행하는 순간 파일은 컴퓨터 메모리에 올라가고 동적인 상태가 된다.

이 상태의 프로그램을 프로세스라고 한다.

프로그램은 코드 파일이고, 그 코드 파일을 실행한 결과가 프로세스이다.

- 스레드

프로세스가 할당받은 자원을 이용하는 실행 흐름의 단위

예전에는 프로그램의 실행을 한 개의 프로세스만 사용했다고 한다. 그런데 요즘에는 유튜브를 보면서 롤도 함께 해야하는 세상이다.

이런 문제를 해결하기 위해 개발자들은 이런 생각을 했다. '한 프로그램을 처리하기 위한 프로세스를 여러 개 만들면 안될까?' 그러나 이것은 불가능했다.

이유는 운영체제는 안정성을 위해 프로세스마다 할당된 메모리 내의 정보에만 접근할 수 있도록 제약을 걸었다. 이를 벗어나는 정보에 접근하면 에러가 발생한다.

이렇게 프로세스를 다중으로 만들 수는 없어 만들어낸 것이 프로세스보다 더 작은 실행 단위 개념인 스레드이다.

스레드의 특징은?

스레드는 프로세스와 다르게 스레드 간 메모리를 공유하며 작동한다. 스레드끼리 프로세스의 자원을 공유하며 프로세스 실행 흐름의 일부가 된다.

스레드는 프로세스의 코드에 정의된 절차에 따라 실행되는 특정한 수행 경로이다

2. IPC란?

프로세스들끼리 의사소통을 하는 것을 IPC라고 한다.

프로세스가 통신이 가능하다는 것은 서로 다른 프로세스가 데이터를 주고 받을 수 있다는 것이고, 컴퓨터 내부에서 효율적으로 정보를 주고 받기 위한 통신의 일종이다.

인터넷 통신을 IPC의 확장이라고 이해할 수 있다. (프로세스 사이의 통신이 서버-클라이언트 간 통신과 유사하기 때문이다.)

3.pipe


IPC 통신 중 하나이며, 통신을 위한 메모리 버퍼를 생성해서 프로세스가 데이터를 주고 받게끔 한다.

파이프의 특징

  • 통신할 프로세스가 명확한 경우에만
  • 부모-자식 or 형제 프로세스 간 통신에만 사용
  • 외부 프로세스에서 사용할 수 없다.
  • 파이프는 두 개의 프로세스를 연결하고, 하나의 프로세스는 쓰기만 가능하고, 다른 하나는 읽기만 가능하다. 단방향으로만 통신이 가능하다.

! 파이프의 특성 중 유의할 것은 fork() 함수에 의해 복사되지 않는다. 그래서 부모와 자식은 같은 프로세스를 가르키게 된다.

4. 리다이렉션(Redirection)

명령어의 결과는 표준 출력 방식인 모니터에 출력이 된다.
그러나 리다이렉션을 이용하면 명령의 출력을 변경할 수 있습니다.
또한 리다이렉션을 이용하여 파일에 기록도 할 수 있다.

기존 파일의 내용을 삭제하고 새로운 결과를 저장할 때는 >,
기존 파일의 내용 뒤에 결과를 추가할 때는 >>를 사용한다.

< or << : 입력 방향 재지정

> or >> : 출력 방향 재지정

> : 덮어쓰기

>> : 내용 뒤에 추가하기

로직 이해하기

예시

  1. 과제에서는 아래와 같이 프로그램이 실행되어야 한다고 정의한다.
./pipex file1 cmd1 cmd2 file2

./pipex는 실행파일 이름일 것이고 file1, file2는 임의로 만든 파일명이다.
또한 cmd1 cmd2는 두 개의 명령어를 의미한다.
  1. 그리고는 아래와 같은 명령어와 같은 행동을 하는 프로그램을 만들라고 한다.
< infile ls -l | wc -l > outfile <예시 코드> 

1. infile의 내용이 ls -l의 표준 입력으로 사용된다.
2. ls -l의 표준 출력(ls -l의 아웃풋)wc -l의 표준 입력으로 사용된다.
3. wc -l의 표준 출력은 outfile에 찍힌다.
  1. 위의 흐름을 요약하면 아래와 같다.
infile ---> ls -l ---> wc -l ---> outfile
		    *     *      *    *
		  <in>  <out>  <in> <out> 
          

관련함수

pipe()

IPC 통신인 파이프를 생성하기 위한 함수


#include <unistd.h>

int pipe(int fd[2]);

파라미터

  • fd[2]: 파일 디스크립터 배열 0은 읽기 1은 쓰기.

반환값

  • 성공 : 0
  • 실패 : -1

fork()

새로운 프로세스를 생성하는 함수

#include <unistd.h>

pid_t fork(void);

반환값

  • 성공 : pid
  • 실패 : -1

waitpid()

프로세스의 종료 상태를 반환하는 함수이다.

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *stat_loc, int options)

파라미터

  • pid: 프로세스 아이디
  • *stat_loc: 프로세스의 상태
  • options: 비트마스크

반환값

  • 성공 : pid
  • 실패 : -1, 그 밖: 0

dup2()

존재하는 두 개의 파일 디스크립터를 복제하는 함수

#include <unistd.h>

int dup2(inf fd, int fd2)

파라미터

  • fd : 파일 디스크립터
  • fd2 : 새로운 파일 디스크립터)

반환값

  • 성공 : 새로운 fd
  • 실패 : -1

perror()

시스템 에러를 출력하는 함수

#include <stdio.h>

void perror(const char *str);

파라미터

  • *str : 추가적으로 넣고싶은 커스텀 메시지

access()

파일의 접근 권한을 확인하는 함수

#include <unistd.h>

int access(const char *path, int mode);

파라미터

  • *path : 파일 경로
  • mode : 적절한 mask값 (X_OK는 파일 존재 여부 및 실행/검색 권한 여부)

반환값

  • 성공: 0
  • 실패 : -1, set errno

execve()

파일을 실행하는 함수

#include <unistd.h>

int execve(const char *path, char argv[], char envp[]);

파라미터

  • *path 디렉토리 포함 전체 파일 이름
  • argv[] : 인수 목록,
  • envp[] : 환경 설정 목록

반환값

  • 실패 : -1

오류

  1. 평가를 진행하던 중 아래와 같은 문제점을 발견했다.
    아래와 같은 명령어를 실행하면 아래와 같은 결과를 갖게 된다.
./pipex infile "ls -l" "wc -l" outfile 

  1. 그런데 outfile2라는 파일을 새로 만들면 어떻게 될까 ?
    명령어를 사용하기 이전이지만 이미 파일이 생성됐다고 생각하고 13에서 outfile2를 한 개 더한 값인 14가 나오게 된다. 계속 이어서 새로운 파일을 만들어봐도 똑같다.

새로운 outfile3

  1. 쉘 커맨드에선 생성되지 않은 파일을 미리 생성됐다고 가정해서 출력을 해주는 탓에 기존의 내 코드에는 문제가 있었다.
    그래서 해당 프로세스가 실행되는 시점에 infile과 outfile을 만들었지만, 이를 해결해주기 위해서 main이 실행되면 infile과 outfile이 바로 생성되도록 수정했다.

int	main(int argc, char **argv, char **envp)
{
	int		fd[2];
	int		infile;
	int		outfile;
	pid_t	pid;

	infile = open(argv[1], O_RDONLY, 0777);
	outfile = open(argv[4], O_RDWR | O_CREAT | O_TRUNC, 0777);
	if (infile == -1 || outfile == -1)
		error();
	if (argc == 5)
	{
		if (pipe(fd) == -1)
			error();
		pid = fork();
		if (pid == -1)
			error();
		if (pid == 0)
			child_proc(argv, envp, fd, infile);
		waitpid(pid, 0, 0);
		parent_proc(argv, envp, fd, outfile);
	}
	return (0);
}


참고

프로세스와 스레드의 차이

profile
Life is a long journey. But code Should be short :)

0개의 댓글