[minishell] - Introduction

jiholee·2021년 4월 18일
0

cs

목록 보기
7/7

여러분의 쉘은 반드시:

  • 새 명령어를 기다릴 때 프롬프트를 표시해야합니다.
  • bash에서와 같이 올바른 실행 파일을 검색하고 실행합니다 (PATH 변수 또는 상대 또는 절대 경로에 기반하여)
  • bash에서와 같은 내장 기능을 구현해야합니다.
    echo with option '-n'
    cd with only a relative or absolute path
    pwd without any options
    export without any options
    unset without any options
    env without any options and any arguments
    exit without any options
  • ; 는 명령어에서 bash에서와 같이 명령어들을 분리해야합니다.
  • ’ 및 " 는 여러 줄 명령어를 제외하고 bash 에서처럼 작동해야합니다.
  • 리디렉션 < > >> 은 파일 디스크립터 집합을(❗원문: file descriptor aggregation)를 제외하고 bash 에서처럼 작동해야합니다.
  • 파이프는 | bash 에서처럼 작동해야합니다.
  • 환경 변수 ($문자)는 bash 에서처럼 작동해야합니다.
  • $? 는 bash 에서처럼 작동해야합니다.
  • ctrl-C, ctrl-D 및 ctrl- \ 는 bash에서와 동일한 결과를 가져야합니다.

Mandatory part

Program nameminishell
MakefileYes
External functsprintf, malloc, free, write, open, read, close, fork, wait, waitpid, wait3, wait4, signal, kill, exit, getcwd, chdir, stat, lstat, fstat, exec, dup, dup2, pipe, opendir, readdir, closedir, strerror, errno
Lift authorizedYes
DescriptionWrite a shell

프로그램 실행 흐름

int main(int argc, char *argv[], char **envp)
{
	get_g()->envs = get_key_value_list(envp);  // 환경변수 세팅
	while (1)
	{
		write(2, ">$ ", 3);
		if (!get_line())  // 사용자 입력받기
			continue ;
		if (!(parse_words())) // 문자열 파싱하기
			continue;
		if (!set_cmd(get_g(), -1, 1))  // 파싱 결과 구조체에 담기
		{
			free_cmd(); // 오류 발생 시 메모리 해제
			continue ;
		}
		exec_cmd();		// cmd 순서로 실행하기
		free_cmd();		// 메모리 해제
	}
	return (0);
}

환경변수

  • 환경 변수란 프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는 동적인 값들의 모임이다. 쉘에서 정의되고 실행하는 동안 프로그램에 필요한 변수를 나타낸다.
  • PATH
    특정 실행 파일을 실행하기 위해 명령어를 입력했을 때 현재 위치에 해당 파일이 없다면 실행시킬 수 없다. 명령어 입력 시 현재 디렉터리에 존재하지 않는 파일이라도 몇 개의 특정 디렉터리들 안에 해당 실행파일이 있는지 찾아보게 시키기 위해 생겨난 개념이다.

System Call

fork(), exec(), wait() 와 같은 Process 생성과 제어를 위한 System call

  • exec()
    단순 fork는 동일한 프로세스의 내용을 여러 번 동작할 때 사용한다.
    child에서는 parent와 다른 동작을 하고 싶을 때는 exec를 사용할 수 있다.
// test.c 파일
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

int main(int argc, char *argv[]) {
    printf("pid : %d", (int) getpid()); // pid : 29260
    
    int rc = fork();
    
    if (rc < 0) {                // (1) fork 실패
        exit(1);
    }									
    else if (rc == 0) {					// (2) child 인 경우 (fork 값이 0)
        printf("child (pid : %d)", (int) getpid());
        char *myargs[3];
        myargs[0] = strdup("cat");		// 내가 실행할 파일 이름
        myargs[1] = strdup("exec.c");		// 실행할 파일에 넘겨줄 argument
        myargs[2] = NULL;				// end of array
        execvp(myargs[0], myargs);		// wc 파일 실행.
        printf("this shouldn't print out"); // 실행되지 않음. ⭐️
    }
    else {								// (3) parent case
        int wc = wait(NULL);			// child의 실행이 끝날 때까지 기다림
        printf("parent : %d (wc : %d / pid : %d)", wc, rc, (int)getpid());
    }
    return 0;
}

결과

➜  webserv git:(master) ✗ gcc test.c 
➜  webserv git:(master) ✗ ./a.out 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

int main(int argc, char *argv[]) {
    printf("pid : %d", (int) getpid()); // pid : 29260
    
    int rc = fork();
    
    if (rc < 0) {                // (1) fork 실패
        exit(1);
    }
    else if (rc == 0) {                                 // (2) child 인 경우 (fork 값이 0)
        printf("child (pid : %d)", (int) getpid());
        char *myargs[3];
        myargs[0] = strdup("cat");              // 내가 실행할 파일 이름
        myargs[1] = strdup("test.c");           // 실행할 파일에 넘겨줄 argument
        myargs[2] = NULL;                               // end of array
        execvp(myargs[0], myargs);              // wc 파일 실행.
        printf("this shouldn't print out"); // 실행되지 않음. ⭐️
    }
    else {                         // (3) parent case
        int wc = wait(NULL);       // child의 실행이 끝날 때까지 기다림
        printf("\nparent of %d (wc : %d / pid : %d)", wc, rc, (int)getpid());
    }
    return 0;
}pid : 69260
parent of 69261 (wc : 69261 / pid : 69260)% 

  • dup()
    열려진 파일의 디스크립터를 복제한다.
#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);
/* dup2.c */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
	int fd;

	if (argc != 2)
	{
		fprintf(stderr, "usage: %s [file name]\n", argv[0]);
		return (1);
	}
	fd = open(argv[1], O_WRONLY);
	if (fd < 0)
	{
		perror("failed open");
		return (1);
	}
	if (dup2(fd, STDOUT_FILENO) == -1)
	{
		perror("failed dup2");
		return (1);
	}
	printf("hello world\n");
	close(fd);
	return (0);
}

👉 결과

➜  Webserv_BR31 git:(master)touch a.txt
➜  Webserv_BR31 git:(master) ✗ ./a.out a.txt
➜  Webserv_BR31 git:(master)cat a.txt
hello world

0개의 댓글