728x90
반응형

허용 함수를 다 안다는 전제로 글을 작성했습니다. 모르면 아래 글 보기.

https://jinho-study.tistory.com/1138

 

42 서울 pipex 정리 2 (access, dup2, execve, perror, strerror)

이어서 나머지 함수들을 알아보자. https://jinho-study.tistory.com/1137 pipex 정리 1 ( 프로세스, fork, pipe, wait, waitpid) 개요 pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어

jinho-study.tistory.com

 

전체적으로 봤을 때 로직은 아래와 같이 생각보다 간단하다. 실제로 파일 3개면 구현 가능하다.

int	main(int ac, char **av, char **envp)
{
	int		i;

	if (ac != 5)
		에러 출력 후 종료
	i = -1;
	while (++i < 2)
	{
		if (i == 0)
       		명령어 파싱 -> 첫 번째 프로세스 실행(infile을 stdin으로 받고 명령어 실행)
		else if (i == 1)
			명령어 파싱 -> 두 번째 프로세스 실행(첫 번째 프로세스 실행 결과를 stdin으로 
            받고 명령어 실행 결과를 outfile에 저장)
	}
	포크된 프로세스 만큼 wait를 해준다. 멘덴토리의 경우에는 2번
	i = -1;
	while (++i < 2)
		wait(NULL);
}

다만 여기서 알고 넘어가야 될게 2가지가 있다. minishell 내용 같긴 한데 pipex 하면서 알고 간다면

minishell 할 때 아주 큰 도움이 된다.

1 ) 쉘에서 명령어들은 병렬로 돌아간다. 

예를 들어 sleep 3 | sleep 3은 6초를 기다릴 것 같지만, 실제로는 병렬로 돌기에 3초를 기다린다.

병렬이더라도 cat | cat 같은 경우에는 애초에 앞에서 입력이 들어와야 넘어가기 때문에 프로그램이 끝나지 않는다. 

2) 파이프 개수만큼 fork 되는 것이 아니라 파이프 개수 + 1 만큼 fork 된다.

실행되는 명령어가 2개(ls | cat)라서 pipe가 한 개라고 fork를 한번 하지 않는다.

실제로 bash에서 그냥 exit은 당연히 꺼지지만 exit | exit 은 bash가 꺼지지 않는다. 

bash 꺼짐
bash 안 꺼짐

그냥 exit은 fork가 안되고 바로 실행돼서 bash가 꺼지지만, exit | exit의 경우에는 fork가 2번 실행되어서

프로세스가 3개라 bash가 꺼지지 않는다. 이러한 구조를 가지는 것은 자식 프로세스에서 명령어를 수행하고

부모(main) 프로세스에서 모든 자식 프로세스를 기다리게 하기 위함이 아닐까?라고 작성자는 이해했다. 

 

각 프로세스 별로 구현해야 되는 것들을 순서대로 쭉 나열해 보면

1) 첫 번째 프로세스 

1.1) infile 읽고 pipe 세팅

자식 프로레스)

infile을 stdin으로 dup2, fd[1]을 stdout으로 dup2 해준다.

여기서 fd[0]만 사용 안 하니까 fd[0]만 close 하는 경우가 많은데, 둘 다 close 해줘야 된다.

fd[0]만 close 하면 yes | head -1 같은 테스트의 경우 프로그램이 끝나지 않는다.

부모 프로레스)

fd[0]을 stdin으로 dup2 해준다. 여기서 fd[0]은 자식 프로세스에서의 명령어 실행 결과가 된다.

결국 첫 번째 명령어의 실행 결과가 stdin으로 들어와 있는 상태라고 생각하면 된다.

여기서도 마찬가지로 fd[0], fd[1] 둘 다 close 해준다.

static void	dup_child_1(char **av, int *fd)
{
	int	infile;

	infile = open(av[1], O_RDONLY);
	if (infile == -1)
		perror_exit("infile error");
	if (dup2(infile, STDIN_FILENO) == -1)
		perror_exit("dup2 error");
	if (dup2(fd[1], STDOUT_FILENO) == -1)
		perror_exit("dup2 error");
	close(fd[0]);
	close(fd[1]);
}

static void	child_process_1(char **av, char **envp)
{
	int		fd[2];
	pid_t	pid;

	if (pipe(fd) == -1)
		perror_exit("pipe error");
	pid = fork();
	if (pid == -1)
		perror_exit("fork error");
	if (pid == 0)
	{
		dup_child_1(av, fd);
		execute(av[2], envp);
	}
	if (dup2(fd[0], STDIN_FILENO) == -1)
		perror_exit("dup2 error");
	close(fd[0]);
	close(fd[1]);
}

1.2) 명령어 파싱 후 실행

env 실행 결과

메인에서 인자로 받는 envp 안에는 이런 식으로 환경변수들이 저장되어 있다.

여기서 우리가 필요한 것은 PATH=/Users/jinhokim/.brew/bin:/Users/jinhokim/.brew/bin:/usr/local/bin:/usr/bin:/

bin:/usr/sbin:/sbin:/usr/local/munki이다. PATH= 뒤의 문자열을 : 기준으로 split 하게 되면 

["/bin", "/usr/bin", ...] 같은 결과가 되는데 총 6개의 경로가 나오게 된다. 이 경로들 안에 명령어 파일들이 들어있다.

예를 들어 ls는 /bin/ 안에 들어있는데 그렇기에 쉘에서 /bin/ls를 실행하면 ls가 실행된다.

/bin/ls

이제 저 6개의 경로와 첫 번째 명령어를 ft_strjoin한 결과를 access 함수를 통해 실행 가능한 파일인지 확인하고

없는 명령어의 경우에는 에러 출력, 있는 명령어의 경우에는 그냥 execve를 실행해주면 된다.

// path -> ["/bin", "/usr/bin", ...]
while (path[i])
	{
	// joined_cmd = "/ls"
	// ret_cmd = "/bin/ls"
        ret_cmd = ft_strjoin(path[i++], joined_cmd);
		if (access(ret_cmd, X_OK) != -1)
		{
			free(joined_cmd);
			return (ret_cmd);
		}
		free(ret_cmd);
	}
	free(joined_cmd);
	return (NULL);
}

 

2) 두 번째 프로세스 

2.1) outfile에 실행 결과 저장

자식 프로레스)

outfile을 생성하고 stdout으로 dup2 해준다. 그러고 위에 설명했던 명령어 파싱 후 실행을 반복하면 된다.

stdout이 outfile에 연결되어 있기 때문에 이 프로세스에서 표준 출력되는 내용들은 모두 outfile 안에 저장된다.

부모 프로레스)

아무것도 할 필요가 없다.

static void	child_process_2(char **av, char **envp)
{
	pid_t	pid;
	int		outfile;

	pid = fork();
	if (pid == -1)
		perror_exit("fork error");
	if (pid == 0)
	{
		outfile = open(av[4], O_RDWR | O_CREAT | O_TRUNC, 0644);
		if (outfile == -1)
			perror_exit("outfile error");
		if (dup2(outfile, STDOUT_FILENO) == -1)
			perror_exit("dup2 error");
		execute(av[3], envp);
	}
}

 

 

728x90
반응형
728x90
반응형

어제는 계속 개인과제를 했고 오늘 마지막 시험을 보고 지금 집에 왔다. 

딱 공부했던 데까지는 정말 금방 풀었는데.. 갑자기 새로운 문제가 나와서 6번 정도 다시 풀고 그냥 빨리 나왔다.

클러스터에 간 첫날이 어제 같은데 벌써 끝이 났다. 시간이 진짜 빨리 간 것 같다. 

붙으면 더 좋겠지만 한 달 동안 재밌는 경험이었던 것 같다. 돈도 받고! 

본과정 붙어서 합격 후기를 올리고 싶다.. 제발!!

728x90
반응형

'42 SEOUL > 라피신(La Piscine)' 카테고리의 다른 글

42 서울 본과정 합격 후기  (22) 2020.08.04
42 서울 라피신 24일차  (0) 2020.07.22
42 서울 라피신 23일차  (0) 2020.07.22
42 서울 라피신 18, 19, 20, 21, 22일차  (0) 2020.07.20
42 서울 라피신 17일차  (0) 2020.07.15
728x90
반응형

금요일에 시험을 무난하게 보고 나서 토, 일요일에는 계속 팀플만 했다. 3명이서 머리 박고 2일 동안 하니까 완성이 되긴 했는데 아직도 이유를 모르겠는 오류가 존재한다.. 시간이 없으니 일단 제출을 했다. 이번엔 점수를 좀 받았으면 좋겠다. 오늘은 조금 쉬다가 개인과제를 밀려고 한다. 내일은 마지막 팀플하는 날인데 문제가 생각보다 괜찮아서 빨리 하고 개인과제를 쫙 밀어버리고 싶다. 

728x90
반응형

'42 SEOUL > 라피신(La Piscine)' 카테고리의 다른 글

42 서울 라피신 24일차  (0) 2020.07.22
42 서울 라피신 23일차  (0) 2020.07.22
42 서울 라피신 17일차  (0) 2020.07.15
42 서울 라피신 15, 16일차  (0) 2020.07.15
42 서울 라피신 13, 14일차  (0) 2020.07.12

+ Recent posts