반응형

mysh.h

//header for myshell by 이창민, ckdals0115@naver.com

//mysh.h

#include<unistd.h>

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

#include<wait.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<fcntl.h>

#include<signal.h>

#include<errno.h>

#define MAX 100

//static int child_pid = 0;

//static int COUNT = 0;

//static char* COMMAND_PATH = "Desktop/Sys/mysh/";

void redirection_com(char** commands, int i);

void background_com(char** commands, int i);

char** get_tokens(char* CommandLine);

void execute_com(char** command);

 

mysh.c

//myshell by 이창민, ckdals0115@naver.com
//mysh.c 
#include"mysh.h"
void main(){
	write(STDOUT_FILENO,"+------------------------------------------------------------------------------------+\n",88);
	write(STDOUT_FILENO,"|MYSHELL                                                                             |\n",88);
	write(STDOUT_FILENO,"|------------------------------------------------------------------------------------|\n",88);
	write(STDOUT_FILENO,"|QUIT : quit                                                                         |\n",88);
	write(STDOUT_FILENO,"+------------------------------------------------------------------------------------+\n",88);

	while(1){
		//write시 쓰레기값 없게 초기화
		char CommandLine[MAX]={0};
		char PWD[MAX]={0};
		char** command;
		int read_size;
		int errno;
		/*char *getcwd(char *buf, int maxlen); => get pwd "unistd.h*/
		getcwd(PWD,MAX);
 	/////////////////////////////////////////////////////////////////
	//화면에 출력, write가 printf보다 빠르므로 write를 써봣음..
		write(STDOUT_FILENO, "@myshell:~", 20);
		write(STDOUT_FILENO, PWD, MAX);
		write(STDOUT_FILENO, "$ ",3);
	/////////////////////////////////////////////////////////////////
	//읽기
		/*입력받은 커맨드 command로 읽음*/
		read_size = read(STDIN_FILENO, CommandLine, MAX);
		//input enter
		if(read_size == 1){
			continue;}
	////////////////////////////////////////////////////////////////
	//명령어 구분하고 실행
		command = get_tokens(CommandLine);
		if(!strcmp(command[0],"quit")){
			printf("+------------------------------------------------------------------------------------+\n");
			printf("|BASH                                                                                |\n");
			printf("+------------------------------------------------------------------------------------+\n");
			exit(1);}
		else{
			execute_com(command);
		}
		
	}
}

mysh_funcs.c

//functions for myshell by 이창민, ckdals0115@naver.com
//mysh_funcs.c
/*
token구하기
명령수행
-shell bulitin
-/bin
-redirection
-background
 */
#include"mysh.h"
/////////////////////////////////////////////////////////////////////////////////////
////////CommandLine 쪼갬
char** get_tokens(char* CommandLine) {
	char **tokens;//토큰들을 저장할 배열
	char *token;
	int COUNT=0;
	//\n 대신 \0넣음
	CommandLine[strlen(CommandLine) - 1] = '\0';
	//토큰 저장할 배열 크기
	tokens = malloc((sizeof(char*))*MAX);
	//" "을 구분자로 자름
	token = strtok(CommandLine, " ");
	while(token != NULL){
		//\0포함해야하므로 +1
		tokens[COUNT] =malloc(strlen(token)+1);
		//동적할당 후 내용복사 
		strncpy(tokens[COUNT], token, strlen(token)+1);
		token = strtok(NULL, " ");
		COUNT++;
	}
	//토큰들 입력 끝나면 null
	tokens[COUNT] = NULL;
	return tokens;
}
//////////////////////////////////////////////////////////////////////////////////////
/////명령을 받으면 수행 
void execute_com(char **commands){
	int fork_process;
	int status;
	int i=0;
	char *dir;
	///////////////////////////////////////////////
	/////////shell builtin command  
	if(!strcmp(commands[0], "cd")){
		if(commands[1] == NULL){
			printf("USEAGE : cd directory\n");
			}
		chdir(commands[1]);
	}
	else if(!strcmp(commands[0], "pwd")){
		printf("Present Working Directory: %s\n",getcwd(dir, MAX));
	}
	///////////////////////////////////////////////
	///////////저장되어있는 명령어(/bin안에 존재)
	else {
		if((fork_process = fork())==0){
		//execv'P' 는 PATH에 등록된 모든 디렉토리에 있는 프로그램을 실행
			//명령어 끝까지 읽어서 각각 상황에 따라 실행
			while(commands[i]){
				if(!strcmp(commands[i],">")){
					redirection_com(commands, i);
				}
				else if(!strcmp(commands[i],"&")){
					background_com(commands, i);
				}
				i++;
			}
			//잘못된 commands[0]가 들어오면 -1 반환함
			if(execvp(commands[0],commands)==-1){
				printf("%s : command not found\n",commands[0]);
				exit(1);
			}
		}
		else if(fork_process < 0){
			printf("fork error\n");
			exit(1);
		}
		else
			wait(&status);
	}
}

//////////////////////////////////////////////////////////////////////
///////////명령어에 리다이렉션이 있을경우 수행
//ex) cat a.txt > b.txt
void redirection_com(char** commands, int i){
	char* to = commands[i+1];
	char* from = commands[i-1];
	int to_fd;

	to_fd = open(to, O_WRONLY | O_CREAT, 0641);
	if(to_fd < 0){
		printf("open error\n");
		exit(1);
	}
	//STDOUT_FILENO이 to_fd를 가리키게함
	dup2(to_fd, STDOUT_FILENO);
	//>자리를 NULL로
	commands[i] = NULL;
	//명령어 수행
	if(execvp(commands[0], commands)==-1){
		printf("No such command : %s\n",commands[0]);
                exit(1);
	}
}
/////////////////////////////////////////////////////////////////////////
////////명령어에 &이 있을경우 background processing 수행
void background_com(char** commands, int i){
	commands[i]=NULL;
	int fork_process;
	if((fork_process = fork())==0){
		if(execvp(commands[0], commands)==-1){
                printf("No such command : %s\n",commands[0]);
                exit(1);
		}
	}
	else if(fork_process<0){
		printf("fork error\n");
		exit(1);
	}
	//기다리지 않음
	else{
		exit(1);
	}
};

while1.c (단순 무한 루프)

#include<stdio.h>
int main(){
	while(1){
	}
	return 0;
}

Makefile

mysh: mysh.o mysh_funcs.o
gcc -o mysh mysh.o mysh_funcs.o

mysh.o: mysh.h mysh.c
gcc -c mysh.c

mysh_funcs.o: mysh.h mysh_funcs.c
gcc -c mysh_funcs.c

 

현재 ~디렉토리 $를 해주기 위해 getcwd를 사용하여 현재 디렉토리를 가져와 터미널에 출력하였다.

read systemcall을 사용하여 STDIN을 읽었다. 그리고 만약 엔터 \n만 치게된다면 다시 커맨드를 입력받을 수 있게 하였다.

명령어가 들어오면 execute_com를 실행하여 명령어를 실행하게 하였고, quit를 입력하면 myshell이 종료되게 하였다.

 

execute_com을 하려면 일단 입력받은 커맨드를 쪼개서 구분을 하여 명령을 처리해야한다. 따라서 get_tokens라는 토큰을 얻는 함수를 생성하였다. strtok함수를 이용하여 “ ”을 구분자로 하여 token화 하였다.

 

커멘드를 token으로 쪼개어 명령을 수행하는데, shell builtin 명령어들은 /bin에 저장되어있는 것이 아니라 bash가 동작할 때 수행하므로 각 명령어에 맞는 함수들을 사용하여 구현하였다.

shell builtin명령어들은 종류가 많이 있지만 각각 경우들을 비교하려면 수행시간이 길어지므로 cdpwd만 구현을 하였다.

 

따라서, strcmp함수를 사용하여 shell builtin명령어가 들어오면 builtin명령어인지 구분하여 실행을 한다.

 

그 외의 명령어들은 /bin에 있으므로 execvp를 사용하여 명령어를 실행한다. 환경변수 path에 디렉토리가 있으면 파일명만 입력하면 되므로 commands[0]을 인자로 넘겨준다.

 

리다이렉션의 경우, “from_file > to_file”의 형태이므로 STDOUT_FILENOto_file fd를 가리키게 한다.

그리고 “>” 앞의 명령어들을 실행하여 to_file에 그 결과들이 수행되게 한다.

 

백그라운드의 경우, fork를 한 후 부모프로세스는 자식프로세스를 wait하지 않게 하여 자식 프로세스가 백그라운드 프로세스로 돌아가도록 한다.

 

나머지 잘못된 사용을 하였을 시 잘못되었다고 예외처리들을 해주었다.

 

bashbuiltin 명령어들을 모두 구현하기 위해선 모든 builtin 명령어에 해당하는지 검사를 하고, 또 그에 따른 적절한 함수들을 사용해야하므로 코드가 길어지고 명령어 수행시간이 길어질 것 같았다.

따라서 cd, pwd와 같은 간단한 명령어만 수행하였다.

여기서 굳이 shell에서 builtin 명령어, 그 외 명령어로 구분지어 하나는 파일로 저장을 하고 하나는 쉘 내부에서 작동하게 하였는지 궁금하여 찾아보니, builtin 명령어들은 자주 사용되어 파일로 디스크에 저장해두면 매번 디스크에서 불러와야하므로 효율을 높이기 위해 램에 상주시키는 것을 알게 되었다.

728x90
반응형
블로그 이미지

아상관없어

,