Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

dup 활용

dup에 대햐여

dup 함수가 하는일은 실제로 간단하다. 단순히 파일지정번호를 복사하는 일이다. 그런데 솔직히 말해서 "dup는 파일지정번호를 복사하기 위해서 사용한다" 라고 해보았자.. 도대체 이걸 어디에 사용할수 있을지, 절대 감이 오질 않는다.

이래서야 쓰고 싶어도 써먹을수가 없으니 보통 슬픈일이 아니다. 그래서 이번문서에서는, 몇가지 예를 들어서 dup 의 활용방법에 대해서 고민? 을 해보고자 한다.

일단은 dup 함수에 대해서 알아보겠다.

dup 에는 2가지 함수가 있다. 바로 dup() 와 dup2() 인데 다음과 같이 사용할수 있다.
#include <unistd.h> 

int dup(int oldfd);
int dup2(int oldfd, int newfd);
2개의 함수가 하는 일은 같다. 다만 dup2 의 경우에는 복사할 새로운 파일지정번호를 지시할수 있는반면, dup 의 경우는 커널에서 자동으로 할당해준다는 점이 다르다. dup 를 사용하게 되면 남는 파일지정번호중 가장 작은 번호로 자동할당 된다. dup 혹은 dup2(이하 dup계열) 함수를 사용해서 파일지정자를 복사했을경우 디 파일지정자들은 락, 파일위치, 플래그등을 서로 공유하게 된다.

dup을 이용한 재지향 구현

위에서 살펴보았듯이, dup 함수의 기본개념자체는 간단하긴 하지만 이걸 어디에 사용할수 있을런지가 애매모호 하다. 도대체 파일지정자를 공유해서 무얼할수 있는거지? 란 생각을 하게 될것이다.

그래서 이번장에서는 dup 를 통한 간단한 예를 들어보도록 할것이다.

예를 통해서 구현하고자 하는 내용은 바로 쉘의 리다이렉션(재지향) 기능이다. 우리는 쉘을 사용하면서 다음과 같은 재지향 기법을 흔하게 사용할것이다.
[root@localhost test]# cat < test.txt
dark rain.
...
...
[root@localhost test]# 
여기에서 나느 리다이렉션 기능을 가지는 테스트용 간이 쉘(:12)을 만들 것이다. 이 간이 쉘은 명령을 내릴경우 해당 명령을 fork&exec 기법으로 실행한다.

쉘을 제대로 구현하는 것은 너무 많은 노력과 시간이 드는 작업이므로 여기에서는 dup 의 활용에 촛점을 맞추어서 단지 단일 명령을 실행인자 처리 없이 실행시키는 기능과, 재지향 중 "<", 즉 파일을 읽어서 명령어로 보내는 기능만을 가지는 간단한 쉘을 만들것이다.

예제 : dup_sh.cc
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <vector>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <algorithm>
#include <fcntl.h>

vector<string> makearg(string);

int main()
{
    char *buf;
    vector<string> arg;
    int pid = 0;
    int fd;
    int redirect = 0;

    // SIGCHLD 시그널을 무시한다. 
    signal(SIGCHLD, SIG_IGN);
    buf = (char *)malloc(sizeof(char) *255);
    while(1)
    {
        redirect = 0;
        memset (buf, 0x00, 255);

        // 사용자 입력대기용 프롬프트를 띄운다.
        write(1, "# ", 3);
        if (read(0, buf, 255) > 1)
        {
            buf[strlen(buf) -1] = 0x00;
            if (strncmp (buf, "exit", 4) == 0) 
                exit(0);

            // 만약 읽어들인 문자열중 "<" 이 있다면 
            // 재지향으로 판단하고 makearg 함수를 호출하여 
            // 명령부분과 파일부분을 나눈후 
            // 파일을 open 한다. 
            if (strstr (buf, "<") != NULL)
            {
                arg = makearg(buf);
                fd = open(arg[1].c_str(), O_RDONLY);
                if (fd < 0)
                {
                    fprintf(stderr, "file not found : %s\n", arg[1].c_str());
                    exit(0);
                }
                redirect = 1;
                strcpy(buf, arg[0].c_str());
            }    

            pid = fork();
            if (pid == 0)
            {
                // 만약 재지향이 사용되었다면 
                // 열린 파일을 표준입력 0 으로 복사한다.  
                if (redirect)
                    dup2(fd, 0);

                // 명령부분을 execl 계열 함수를 이용해서 
                // 실행시킨다.  
                execlp(buf, buf, NULL);
            }
            else if(pid > 0)
            {
                if (redirect)
                    close(fd);
                wait(0);
            }
        }
    }
}

// 문자열을 "<" 를 기준으로 
// 나누고 공백문자를 제거한뒤에 vector 로 
// 되돌려준다. 
vector<string> makearg(string str)
{
    int num;
    vector<string> return_str;

    string::iterator mi;
    mi = str.begin();
    while (mi != str.end())
    {
        if (*mi == ' ') 
            str.erase(mi);
        *mi++;
    }
    num = str.find("<");
    return_str.push_back(str.substr(0,num));
    return_str.push_back(str.substr(num+1));

    return return_str;
}
위의 프로그램은 STL(:12) 을 이용했음으로 g++ 컴파일러를 이용해서 컴파일 해야 한다. 위의 예제는 다음과 같이 실행 가능할것이다.

[root@s210-205-210-195 dup]# ./dup3
# ls
dump  dup1  dup1.c  dup2  dup2.c  dup3  dup3.c  sh  sh.c
# tail < dup3.c
                if (*mi == ' ') 
                        str.erase(mi);
                *mi++;
        }
        num = str.find("<");
        return_str.push_back(str.substr(0,num));
        return_str.push_back(str.substr(num+1));

        return return_str;
}
테스트 결과 간단한 명령 실행과, 재지향 기능이 작동하는 것을 확인할 수 있을 것이다. 시간이 남는 다면 위의 코드를 약간 수정해서 명령실행결과를 (표준출력)을 파일로 재지향 하도록 해보기 바란다.