written by yechoi

[리눅스] 쉘 구현에 필요한 C 함수(feat. 42 미니쉘) 본문

Born 2 Code/C, C++

[리눅스] 쉘 구현에 필요한 C 함수(feat. 42 미니쉘)

yechoi 2020. 9. 13. 19:58
반응형

C func for Minishell

 

시그널

sig_t signal(int sig, sig_t func);

sig 는 시그널 번호, func 는 해당 시그널을 처리할 핸들러.

 

❓왜 signal이 필요한가

ctrl + c를 예시로 들면, 이를 누르면 터미널이 꺼진다. 직접 만든 쉘을 실행하고 ctrl + c를 눌렀을 때 기대하는 건, 우리가 만든 쉘 프로그램만 꺼지는 것. 터미널 전체가 꺼지길 바라지 않는다. 그래서 ctrl + c(SIGINT)가 들어왔을 때 취해야 할 액션을 다른 함수로 바꿔줄 필요가 있다. 이러한 역할을 하는 게 signal 함수.

🔗 http://blog.naver.com/PostView.nhn?blogId=bitnang&logNo=70172674474 (시그널 종류)

 

 

파일 정보 읽기

int stat(const char *path, struct stat *buf)

첫번째 인자로 절대경로를 넘겨주고, 두번째 인자로 stat 구조체 주소를 넘겨준다. 성공하면 0, 실패하면 -1을 반환한다.

❓왜 stat이 필요할까

함수를 거치면 stat 구조체에 파일에 관한 정보가 들어오기도 하지만, 쉘을 만들 땐 성공 실패 여부가 더 중요한 정보로 쓰였다. 원하는 경로에 파일이 있는지 없는지를 확인하는 데 썼다는 것. 해당 경로에 파일이 있으면, 그 경로를 execve의 path에 인자로 넘겨줬다.

🔗 https://blog.naver.com/bitnang/70172673896

 

 

 

디렉토리 관련

char *getcwd(char *buf, size_t size)

현재 작업중인 디렉토리의 절대 경로를 복사해온다. size는 buf 배열의 크기이며, 반환값은 buf를 가리키는 포인터.

❗ pwd 구현

int chdir(const char *path)

현재 작업중인 디렉토리를 path로 변경. 성공하면 0이 반환되며, 에러가 발행하면 -1이 반환되고 errno가 설정된다.

❗cd 구현

 

 

프로세스

pid_t fork(void)

자식 프로세스일 경우 0을 반환하고, 부모 프로세스일 경우 자식 pid(프로세스 ID)를 반환함. -1이면 에러가 발생한 것.

❓ 왜 fork가 필요한가
exec 호출에 꼭 필요함. exec으로 호출된 프로그램이 현재 메모리에 올라와 있는 프로그램을 덮어서 로딩. 원 프로그램이 사라져버리는 것. 때문에 별도의 메모리 공간을 할당하고, 그 공간에서 exec을 실행해 원래 process는 보존해야 함. fork가 독립된 메모리 공간을 할당함.

🔗(추천) https://channelofchaos.tistory.com/55

 

pid_t wait(int *staloc)

부모 프로세스는 자식 프로세스가 끝날 때를 기다려 줘야 함. wait의 인자는 상태. 여러개 자식 프로세스 중에 어느 하나라도 종료하면 종료한 자식 프로세스 ID를 반환한다.

❓ 왜 wait가 필요할까

앞선 fork 함수에 따르면 process는 parent와 child로 나뉘게 된다. 원 프로그램이 실행되는 parent process는 child process가 끝날 때까지 기다려줘야 하는데, 이때 필요한 함수가 wait. 인자로 child process의 id인 0을 넣어 wait(0)하면 child process가 끝날 때까지 기다려줌.

 

pid_t waitpid(pit_t pit, int *staloc, int options)

해당 pid 값을 갖는 자식 프로세스를 기다림. 0일 때는 자신과 같은 그룹에 있는 프로세스의 자식 프로세스를 기다림. -1일 때는 모든 하위 프로세스를 기다림. -1보다 작은 값일 때는 절대값과 같은 자식 프로세스의 종료를 기다림

🔗 ehpub.co.kr/리눅스-시스템-프로그래밍-7-8-프로세스-종료-대기-및/

 

$?

쉘에서는 이전에 실행한 명령이 성공을 하였는지 실패를 하였는지를 ? 변수값에 저장을 한다. wait 류 함수에 staloc에 관련 정보가 담기는데, 이를 /256으로 나누면 $? 값.

 

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

첫번째 인자는 새 프로세스 파일의 경로. 두번째 인자는 프로그램 명. 더블 포인터인 이유는 int main(int argc, char **argv)에서 argv[0]이 프로그램 이름이었던 것과 같은 원리.

🔗 (설명) https://www.it-note.kr/157

❓왜 execve가 필요할까

우리가 명령어로 생각하는 'ls', 'echo'등은 실은 $PATH 경로 안에 있는 실행파일이다. 즉 프로그램을 실행해 명령어를 사용한다는 얘기.

🔗 https://github.com/codingeverybody/codingyahac/issues/178

 

[C언어] execv 가 실행이 생각대로 안되는 이유를 모르겠습니다. · Issue #178 · codingeverybody/codingyahac

custom shell 을 만드는 과제를 하고 있습니다. main을 실행해서 resch>> 를 보여주는 쉘 안으로 들어간 다음에, 유저인풋을 커맨드 입력으로 인식해 execv로 실행하려 합니다. (사실은 유저인풋을 input.lo

github.com

 

 

파일디스크립터 복사하기

int dup(int fildes)

파일디스크립터를 복제해 반환. fd2 = dup(fd1) 이면 fd2에 글을 쓰면 fd1과 같은 파일에 쓰임. 다만 서로 다른 파일디스크립터이기 때문에, fd1을 닫아도 fd2는 열려있음.

 

int dup2(int fildes, int fildes2)

두번째 인자를 첫번째 인자로 바꿔버림.

❓ 왜 dup2가 필요할까
redirection을 구현할 때 사용하는 함수. output redirection에서 앞선 명령어를 실행한 결과는 stdout으로 나오는데, 이를 > 뒤에 있는 파일에 덮어씌우기 위함. dup2(fd, STDOUT_FILENO); input redirection도 같은 원리. dup2(fd, STDIN_FILENO);

🔗https://sosal.kr/186 (dup과 dup2에 대한 설명)
http://www.xevious7.com/linux/lpg_6_2_2.html (dup 활용 pipe 구현)

 

 

파이프

int pipe(int pipefd[2])

서로 독립된 프로세스가 데이터를 주고받을 수 있도록 하는 함수. 풀어서 말하자면 파이프로 만든 두개의 fd로 부모, 자식 프로세스가 서로 통신할 수 있다. fd[0]은 read, fd[1]은 write. 1에 쓰고 0에서 읽음.

int main()
{
    int fd[2];
    pid_t child;
    char buff[BUFFERSIZE];

    child = fork();
    if (child == 0) // 자식프로세스가 출력하고
    {
        close(fd[0]); // 출력만 할 것이므로 입력을 닫음
        write(fd[1], string, strlen(string));
        exit(0);
    }
    else // 부모프로세스가 자식프로세스의 출력을 입력 받을 때
    {
        close(fd[1]); // 입력만 받으므로 출력은 닫아둠
        read(fd[0], buff, BUFFERSIZE - 1);
    }

🔗 https://sosal.kr/83 (pipe() 설명)
http://jullio.pe.kr/cs/lpg/lpg_6_2_2.html (pipe()를 통한 pipe 기능 구현)
https://12bme.tistory.com/226 (pipe()를 통한 pipe 기능 구현2)

 

 

종료하기

void exit(int status)

인자인 status는 종료하고 부모 프로세스에게 전달할 값이다

성공과 실패는 명령의 종료 상태값으로 알 수 있다. 보통 0은 성공을 나타내며, 0이 아닌 값은 실패를 나타낸다.

종료하기 전에 모든 열려진 파일을 자동으로 닫는다.

http://www.xevious7.com/linux/lpg_6_2_2.html

반응형