리눅스에서 pipe 명령어는 꽤 유용하게 쓰입니다. 이번 시리즈에서는 이들을 구현하기 위해서 필요한 메서드 중에서 dup2 함수를 알아보도록 하겠습니다.

 

 이는 ori 디스크립터를, new로 복사하는 함수입니다. 아앗. 뭔지 잘 모르겠군요. /proc는 괜히 있는 것이 아니니, 이것을 이용해서 하나하나 알아보도록 하겠습니다.

 


 먼저 예제를 보겠습니다. dup2.c입니다.

 

 먼저, pipe(pi)는 파이프를 생성하는 함수입니다. 파이프 하나에는 입구와 출구가 있습니다. 다음에, 5번째 줄에서 dup2 함수를 호출했는데요. 1번째 인자가 pi[1], 2번째 인자가 1입니다. 그러면, pi[1]의 디스크립터를 1번 디스크립터에 복사한다는 의미인데..

 

 

 일단, dup를 실행해 보겠습니다.  그리고 다른 세션에서는 ps -a | grep dup 명령어를 실행하였습니다. 이는, ps -a를 실행한 결과에서 dup 키워드가 나오는 행만 뽑겠다는 의미입니다. 11547 이라는 숫자가 보이는데요. 이 프로세스에 대한 내용을 봐야 겠네요. 일단, /proc/{pid}/fd로 들어가 보겠습니다.

 

 

 그리고, 여기서 ls -ail 명령어를 수행해 봅시다. 그러면, 1번 디스크립터가 다른 값으로 덮어 씌워졌음을 알 수 있습니다. pi[0]은 읽기, pi[1]은 쓰기 전용인데요. 2번째 열에서 l-wx------ 문자열을 봅시다. 1번 디스크립터의 2번째 열에 있는 내용도 l-wx------입니다. 즉, 4번이 가리키는 무언가가 1번으로 복사되었음을 알 수 있습니다. 그러면, 11547번 프로세스는 출력을 할 때, /dev/pts/0 대신에 파이프 80993에 쓴다는 의미입니다.

 

 이것을 다시 정리해 보겠습니다.

 

 

 복사 되기 전에 3번과 4번은 각각 Read로만 접근 가능한 pipe:[80993], Write로만 접근 가능한 pipe:[80993]을 가리키고 있었습니다. 간단하게 제가 생성한 pipe를 가리키고 있었습니다. 그런데, dup2(pi[1],1) 문장을 수행했습니다. pi[1]은, 쓰기 전용이고, 4번이였습니다. 즉 4번이 write pipe:[80993]을 가리켰습니다. 이것을 1번에 덮어씌웠다는 이야기는..

 

 

 4번 데스크립터가 가리키는 무언가와, 1번 데스크립터가 가리키는 무언가가 같다는 이야기입니다. ls -ail 명령어를 수행한 결과를 잘 보시면 아실 수 있습니다.

 

 


 우리는 ls -ail | grep dup2 와 같은 명령어를 쉘에서 많이 입력합니다. 이것은 어떻게 구현할까요? 파이프 하나로 명령어 2개를 실행한 것만 잘 구현해 보겠습니다. pipe2.c를 보겠습니다.

 

 

 먼저 work는 크게 어려운 건 없습니다. 단지, execve를 호출하기 위해서 있는 함수입니다.

 

 

 main 함수입니다. 조금 어려운 듯 싶으니, 하나 하나 뜯으면서 보도록 하겠습니다. 먼저 파이프를 하나 생성합니다. 그리고 fork 함수를 호출하였습니다. 이 상태에서 child와 parent의 fd는 어떻게 들어가 있을까요? 적당한 위치에 sleep 함수나 무한 루프를 도는 코드를 넣어서 부모와 자식 프로세스가 일정 시간 동안 계속 돌도록 한 다음에, fd 파일들을 확인해 보도록 하겠습니다.

 

 

 실행 파일의 이름이 pipe2이니, ps -aux | grep pipe를 쳐 보겠습니다. 그러면 2239와 2240이라는 숫자가 보이는데요.

 

 

 당연하게도, 이 프로세스의 fd 디렉토리에 있는 파일들만 확인할 겁니다. 보니까, 무언가가 복사되었음을 알 수 있습니다. 부모의 3번과 4번이 pipe:[56438], 그리고 자식의 3번과 4번이 pipe:[56438]을 가리킨다는 것을 알 수 있는데요. 이 상황을 그림으로 그려보면 아래와 같습니다.

 

 

  다음에, 우리는 어떻게 하면 좋을까요? child의 1번 fd가 파이프의 w를, parent의 0번 fd가 파이프의 r을 가리키게 하면 좋을 듯 싶군요. 그러면 자식은 dup2(pi[1],1)을, 부모는 dup2(pi[0],0)을 호출하면 됩니다.

 

 

 이를 그림으로 그리면 위와 같습니다. 다음에, 필요 없는 파일 디스크립터들을 모두 닫습니다.

 

 

 child가 pipe에 결과를 쓰면, parent가 읽어내는 구조입니다. 이를 코드화 시키면 아래와 같습니다.

 

 

 부모 쪽에서는 0번 fd에 덮어쓰고, 자식에서는 1번 fd에 덮어쓴다. 정도만 보셔도 무난합니다.

 

 

 실행 결과는 위와 같습니다.